The Opal API gives every item two stable IDs: a 24-character submission ID and an item_ ID. Most routes accept either, but some need the submission ID, so store that one. Successful responses usually come under a data envelope, and errors use { "error": "..." }. Branch on the status code, not on the message text.

Item IDs

Every Opal item has two stable identifiers:

IdentifierFormatWhere it appears
Submission ID24-character hex string_id, id, route params, binder and fence lists
Item IDitem_ plus a UUIDdata.itemId, item URLs

The :id parameter of item routes accepts either form. Some other values must be submission IDs, because Opal looks them up directly:

ValueAccepts
:id in any /items/:id route, including /page, /download, and /publishSubmission ID or item_ ID
itemId in POST /shares and in POST /media/uploadSubmission ID or item_ ID
itemId and itemIds in POST /items/:id/binder/itemsSubmission ID only
:itemId in DELETE /items/:id/binder/items/:itemId, and order in PATCH /items/:id/binder/reorderSubmission ID only
itemId in POST /items/:id/fence/items, and :itemId in DELETE /items/:id/fence/items/:itemIdSubmission ID only
id in each POST /items/batch/move entrySubmission ID only
itemId in POST /copies/createSubmission ID only
linkedItem on tasksStored as given. Send the submission ID.
Workspace, share, copy, task, view, and API key IDsTheir own IDs, as returned by their routes

Store submission IDs in integrations. Every route accepts them, and they avoid an extra lookup.

The Data Envelope

Most API-key routes return successful data under a data envelope:

{
  "data": {}
}

These routes return a different shape:

RouteSuccess shape
GET /items/:id{ "data": item, "access": {...} }, with access beside data
GET /items/inbox, GET /items/shared{ "data": { "items": [...], "total": n } }
GET /shares/incoming, GET /shares/outgoing{ "data": { "shares": [...] } }
GET /copies/pending{ "data": { "pending": [...], "total": n } }
GET /tasks{ "data": { "tasks": [...], "total": n, "limit": n, "offset": n } }
GET /search{ "data": { "results": [...], "total": n, "query": "..." } }
GET /icons{ "data": [...], "listFailed": false }
DELETE /icons/:blobName, POST /items/:id/notify-metadata{ "success": true }, no envelope
GET /export/check{ "ok": true }, no envelope
GET /exportThe zip file itself
/keys routes (signed-in app session)Top-level success, key or keys, and message

Error Shape

Errors use this shape:

{
  "error": "Human-readable message"
}

Limit errors add fields. See Errors, Rate Limits, and Plan Limits in the Opal API. Share and publish routes add "success": false and a details field that is omitted in production. API key routes add "details": null. Branch on the status code and on code where one is present, not on the message text.

Status Codes

CodeMeaning
200Success
201Created
202Accepted: a large binder duplicate, or a reassignment with background, continues in the background
400Missing or invalid input, or a count limit (binder, batch, keys, views)
401Missing, malformed, unknown, or revoked API key
403Plan, locked account, read-only key, ownership, share permission, or item or workspace limit
404Resource not found, or not one of yours
409Conflict: workspace address taken, declining a share that was already accepted, or an export already running
413File size, storage, or item content limit
415Icon upload that is not an image
423The item is being handed to a new owner (code: "ITEM_TRANSFERRING"). Try again when that is done.
429Rate limited, or exporting everything more than once a day
500Server error
502POST /shares could not create a share record. Nothing from that request was kept (code: "BULK_SHARE_FAILED").

Shared fences and reassigning an owner use these codes too. Shared fence refusals such as FENCE_FULL, FENCE_TOO_SMALL, FENCE_IN_INBOX, and FENCE_NOT_PLACED return 409, and a reassignment the new owner’s storage has no room for returns 409 with NO_ROOM. While a reassignment is under way, changes to what it moves return 423 with ITEM_TRANSFERRING. See Fences API: Add Items, Read Children, and Move a Fence and Reassign Owner API: Hand a Shared Item, Binder, or Fence to a Member.

The 401 messages are Missing or invalid Authorization header, Invalid API key format, Invalid or revoked API key, API key owner not found, and API key authentication failed.

Pagination

List routes page with a count and an offset:

Next Steps

Get Help

Stuck on an API call or have a question? Ask in the Opal Discord or send us a message through the contact form.

FAQ

Which item ID should I store?

The submission ID, the 24-character ID returned as _id or id. Every route accepts it, and some routes accept only that form.

What does a 403 from the Opal API mean?

A 403 can mean a plan problem, a locked account, a read-only key, an ownership or share-permission problem, or an item or workspace limit. Check code in the response body, and see Errors, Rate Limits, and Plan Limits in the Opal API.

Should I read the error message to decide what to do?

No. Branch on the status code, and on code where one is present. Message text can change.

Why does GET /items/:id have an access block?

The access block sits beside data and tells you whether you are the owner or a share recipient, and which permissions you have. See Items API: List, Get, Create, Update, and Delete.