Media
Media assets and folders — register an upload, browse, organize, delete.
Media assets register a filename + storage path and return a signed-style upload URL the client can PUT the file body to. Folders are a simple parent/child tree.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /admin/projects/:projectId/media | Register an asset, get an upload_url. |
| GET | /admin/projects/:projectId/media | Paginated list; optional ?folder_id=. |
| DELETE | /admin/projects/:projectId/media/:assetId | Delete an asset row. |
| POST | /admin/projects/:projectId/media/folders | Create a folder. |
| GET | /admin/projects/:projectId/media/folders | List folders (optional ?parent_id=). |
| DELETE | /admin/projects/:projectId/media/folders/:folderId | Delete a folder. |
| POST | /admin/projects/:projectId/media/catalogs | Create a catalog. |
| GET | /admin/projects/:projectId/media/catalogs | List catalogs with item counts. |
| GET | /admin/projects/:projectId/media/catalogs/:catalogId | Get a catalog + its ordered items. |
| PATCH | /admin/projects/:projectId/media/catalogs/:catalogId | Update a catalog. |
| DELETE | /admin/projects/:projectId/media/catalogs/:catalogId | Delete a catalog (items cascade). |
| POST | /admin/projects/:projectId/media/catalogs/:catalogId/items | Add an asset to the catalog. |
| PATCH | /admin/projects/:projectId/media/catalogs/:catalogId/items/reorder | Reorder items. |
| DELETE | /admin/projects/:projectId/media/catalogs/:catalogId/items/:mediaId | Remove an asset. |
POST /admin/projects/:projectId/media
Register a media asset. The storage path is <projectId>/<ms>_<id>_<name>, where <name> is a URL-safe form of filename: its last path component, with accents dropped, every character other than letters, digits, ., _ and - replaced by _, and the extension kept. The asset's filename stays exactly as sent. A missing or empty filename returns 400 INVALID_INPUT.
Request (CreateMediaAssetInput)
| Field | Type | Required | Default |
|---|---|---|---|
filename | string | yes | — |
mime_type | string | yes | — |
size_bytes | number | free: yes | null |
folder_id | uuid | no | null |
alt_text | string | no | null |
metadata | object | no | {} |
Response 201
Uploading the file
PUT the raw file bytes to the returned upload_url:
- Method is
PUT; the request body is the file bytes. Content-TypeMUST match themime_typeregistered above — a mismatch is rejected.- No
Authorizationheader on the uploadPUT—upload_urlis already authorized. - Success is
200,201or204. The file is then served fromstorage_url. upload_urlis short-lived (upload_url_expires_at); if it has expired, register the asset again to get a fresh one.- Maximum file size for the
mediabucket is 50 MB. - On a free project
size_bytesis required, the upload URL accepts at most that many bytes, and the size counts toward the 250 MB media budget: an upload past it returns402 FREE_TIER_LIMIT_REACHEDwithdetails.limit: "media_storage".
The asset is registered with upload_state: "pending". It becomes
"committed" when the upload finishes, and from then on it is listed and
counts toward media storage. Until then it is left out of the list and of
media usage. A pending asset whose file never arrives is deleted by the daily
cleanup once it is 24 hours old (see
When an upload counts).
Allowed MIME types
The default media bucket accepts the types below. A mime_type outside the list returns 400 MIME_NOT_ALLOWED.
| Category | Allowed types |
|---|---|
| Image | image/jpeg, image/png, image/webp, image/gif |
| Video | video/mp4, video/webm, video/quicktime, video/x-m4v |
| Audio | audio/mpeg, audio/aac, audio/ogg, audio/mp4 |
End-to-end example
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/mediacurl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/media' \
-H 'Content-Type: application/json' \
-d '{
"filename": "logo.png",
"mime_type": "image/png",
"size_bytes": 20480
}'Curl:
GET /admin/projects/:projectId/media
Lists live assets whose upload finished (upload_state: "committed"). A
deleted asset is omitted, including every asset of a deleted user whose stored
file is still being purged
(Cascade on user delete), and
so is an asset whose upload has not finished.
Query
| Param | Default | Description |
|---|---|---|
limit | 50 | |
offset | 0 | |
folder_id | — | Restrict to a folder. |
Response 200
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/mediacurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/media'Curl:
DELETE /admin/projects/:projectId/media/:assetId
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/media/%7B%7BassetId%7D%7Dcurl -X DELETE 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/media/%7B%7BassetId%7D%7D'Curl:
POST /admin/projects/:projectId/media/folders
Request (CreateMediaFolderInput)
| Field | Type | Required |
|---|---|---|
name | string | yes |
parent_id | uuid | no |
Response 201
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/media/folderscurl -X POST 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/media/folders' \
-H 'Content-Type: application/json' \
-d '{
"name": "avatars"
}'Curl:
GET /admin/projects/:projectId/media/folders
Query
| Param | Description |
|---|---|
parent_id | If omitted, returns only root folders (parent_id IS NULL). |
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/media/folderscurl -X GET 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/media/folders'Curl:
DELETE /admin/projects/:projectId/media/folders/:folderId
Try it:
/admin/projects/%7B%7BprojectId%7D%7D/media/folders/%7B%7BfolderId%7D%7Dcurl -X DELETE 'https://api.amba.dev/v1/admin/projects/%7B%7BprojectId%7D%7D/media/folders/%7B%7BfolderId%7D%7D'Curl:
Catalogs
A catalog is a named, slug-addressable bundle of existing media assets your app
reads in one call (Amba.media.catalog(slug) →
client read). It curates assets by
reference — the item URLs are the same public asset URLs every other media read
returns, not copies.
POST /admin/projects/:projectId/media/catalogs
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
slug | string | yes | — | /^[a-z0-9][a-z0-9-]{0,63}$/, unique per project |
name | string | yes | — | |
description | string | no | null | |
is_public | boolean | no | true | Only public catalogs are readable client-side. |
metadata | object | no | {} |
A duplicate slug returns 409 SLUG_TAKEN. Response 201 echoes the catalog row.
POST /admin/projects/:projectId/media/catalogs/:catalogId/items
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
media_id | uuid | yes | — | Must be an existing media asset. |
position | number | no | 0 | Lower sorts first. |
caption | string | no | null | Per-catalog label for this asset. |
metadata | object | no | {} |
Adding the same asset to a catalog twice returns 409 ALREADY_IN_CATALOG.
PATCH /admin/projects/:projectId/media/catalogs/:catalogId/items/reorder
Body { "items": [{ "media_id": "…", "position": 0 }, …] }. Each provided pair
sets that item's position; assets not listed keep their current position.