Opal has two upload routes. POST /items/upload creates a standalone file item on a workspace. POST /media/upload uploads an image, video, audio file, or other file and returns a ready-to-use block, and can insert it into a page. Both send multipart form data with the file in a field named file, and both follow your plan’s file size and storage limits.
Upload a Standalone File Item
POST /api/v1/items/upload
| Field | Required | Notes |
|---|---|---|
file | Yes | The file to upload. The field must be named file. |
workspace | Yes | Target workspace submission ID. It must be yours. |
xPosition, yPosition | No | Optional placement. |
curl -X POST https://app.opalrecall.com/api/v1/items/upload \ -H "Authorization: Bearer opal_live_xxx" \ -F "file=@report.pdf" \ -F "workspace=WORKSPACE_ID"
Creates a new file item and returns it with 201. The item gets file subtype classes such as type-file file-type-pdf. Images also store their dimensions for resize behavior. Text files up to 1 MB keep their text inside the item. Larger ones stay downloadable files.
A Markdown (.md) upload becomes a document item when it converts within the content limit. Its title is the first # heading, or the file name, and editorContent keeps the uploaded file’s URL and name as sourceFileUrl and sourceFileName.
Opal checks, in order: the plan’s max file size (413), 400 without a file or workspace, storage (413), the item limit (403), and the workspace (403 if it is not yours, 404 if it does not exist).
Do not hand-build standalone file items when this route is available. The upload route sets the file subtype classes that the grid needs.
Get a Download URL
GET /api/v1/items/:id/download
curl https://app.opalrecall.com/api/v1/items/ITEM_ID/download \ -H "Authorization: Bearer opal_live_xxx"
{
"data": {
"url": "https://..."
}
}
The URL is the item’s file (or, for a document, the first stored image, video, audio, or file block). Links to Opal’s storage come back with the current read token. Links to other sites come back as saved. This needs ownership, a share with canView, or canView on a binder or fence the item is in. It returns 404 No file URL found when the item has no file.
Upload Media for Embedding
POST /api/v1/media/upload
| Field | Required | Notes |
|---|---|---|
file | Yes | Image, video, audio, or any other file. The field must be named file. |
caption | No | Caption for image, video, and audio blocks. |
itemId | No | Submission or item_ ID. If given, the block is inserted into that item. |
position | No | start or end. Defaults to end when inserting. |
curl -X POST https://app.opalrecall.com/api/v1/media/upload \ -H "Authorization: Bearer opal_live_xxx" \ -F "file=@cover.png" \ -F "itemId=ITEM_ID" \ -F "position=end"
Response (201):
{
"data": {
"url": "https://...blob.core.windows.net/...",
"blockType": "image",
"block": {
"id": "media_ab12cd34",
"type": "image",
"props": {
"url": "...",
"caption": "",
"previewWidth": 800
},
"children": []
},
"dimensions": {
"width": 1024,
"height": 768
},
"fileName": "cover.png",
"fileSize": 204800,
"mimeType": "image/png",
"embedded": true
}
}
The file extension decides the block type:
| Block type | Extensions |
|---|---|
image | jpg, jpeg, png, gif, webp, svg, bmp, tiff, heic, heif, ico |
video | mp4, mov, avi, mkv, webm, m4v |
audio | mp3, wav, ogg, m4a, flac, aac, wma |
file | Anything else |
SVGs are sanitized, and risky file types are stored to download rather than open. Inserting into an item needs edit access to it (owner, a canEdit share, or canEdit on a binder or fence it is in). Without it the route returns 403 before anything is uploaded. The block and an empty paragraph after it are added at the start or end. If the item’s content would go over 2 MB, the route returns 413 and uploads nothing.
Which Route to Use
- To create a standalone file item in Opal, use
POST /items/upload. - To put an image, video, audio file, or file inside a document, use
POST /media/upload.
Size and Storage Limits
Plan file-size and storage limits apply to both routes. Check plan.limits.fileSizeBytes before each upload, and compare the total against your remaining storage, using GET /account. See Plan, Limits, and Usage in the Opal API: GET /account. For the per-plan numbers, see File Size and Storage Limits by Plan. Over-limit responses are listed in Errors, Rate Limits, and Plan Limits in the Opal API.
Next Steps
- Check your limits first: Plan, Limits, and Usage in the Opal API: GET /account
- Learn about files in the app: What Is a File Item? Images, PDFs, Office Files, and More and Upload Files and Images to Your Workspace (Drag and Drop)
- Write page content: Writing Page Content: BlockNote JSON and editorContent
- Set workspace icons and avatars: Profile, Icons, and OpenGraph API Endpoints
FAQ
What is the difference between items/upload and media/upload?
POST /items/upload creates a new standalone file item on a workspace. POST /media/upload returns a ready-made image, video, audio, or file block, and can insert it into an item you can edit.
What happens if my file is too big?
The route returns 413 with limit and readable fields. The limit is your plan’s max file size. See Errors, Rate Limits, and Plan Limits in the Opal API.
What happens to a Markdown file I upload?
A .md upload becomes a document item when it converts within the content limit. Its title is the first # heading, or the file name.
How do I download a file with the API?
Call GET /items/:id/download. It returns a URL for the item’s file. You need ownership or a share with canView.
