Media API
Public API · Enterprise Edition
The Media library exposes a public, key-gated surface (/api/media/v1/) for external integrations; the workspace-scoped management routes are part of the internal application API. Both are detailed below. Media is an Enterprise-Edition feature.
The Media API manages binary assets (images, videos, documents) stored in Cloudflare R2 object storage. Assets are processed with Sharp for optimization and variant generation.
There are two surfaces:
- Application API (this page's main sections) — project-scoped, session-authenticated, used by the Studio SPA:
/api/workspaces/:workspaceId/projects/:projectId/media/... - Public Media API — key-gated external surface (see Public Media API below):
/api/media/v1/:projectId/assets/...
Enterprise Feature
Media upload and management require the enterprise bridge (ee/) to be loaded and R2 credentials to be configured. The media provider is null in the Community Edition, so every media route returns 503 there. On the managed service the media library is available on Starter plans and above.
Upload Media
Upload a file to the project media library.
POST /api/workspaces/:workspaceId/projects/:projectId/mediaRequest
Multipart form data:
| Field | Type | Required | Description |
|---|---|---|---|
file | File | Yes | Binary file to upload |
alt | string | No | Alt text for accessibility |
tags | string | No | Comma-separated tags |
Response
{
"id": "uuid",
"filename": "hero-banner.webp",
"contentType": "image/webp",
"size": 125000,
"width": 1920,
"height": 1080,
"format": "webp",
"blurhash": "LEHV6nWB2yk8",
"alt": "Hero banner image",
"tags": ["hero", "banner"],
"originalPath": "media/original/abc123.webp",
"variants": {
"thumbnail": {
"path": "media/thumbnail/abc123.webp",
"width": 300,
"height": 169,
"format": "webp",
"size": 12000
}
},
"createdAt": "2026-01-01T00:00:00Z"
}Plan Limits
| Plan | Max File Size | Storage | Variants per Field |
|---|---|---|---|
| Free | — | — | — |
| Starter | 5 MB | 1 GB | 4 |
| Pro | 50 MB | 15 GB | 10 |
| Enterprise | 100 MB | 100 GB | Unlimited |
Upload from URL
Import a media asset from an external URL.
POST /api/workspaces/:workspaceId/projects/:projectId/media/upload-urlRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Source URL to fetch |
alt | string | No | Alt text |
tags | string[] | No | Tags for organization |
List Media
List media assets with filtering and pagination.
GET /api/workspaces/:workspaceId/projects/:projectId/mediaQuery Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number |
limit | number | 20 | Items per page |
search | string | -- | Search by filename, alt text |
tags | string | -- | Filter by tags (comma-separated) |
contentType | string | -- | Filter by MIME type prefix (e.g., image) |
sort | string | newest | Sort order: newest, oldest, name, size |
Response
{
"assets": [ ... ],
"total": 42
}Get Asset
Get full metadata for a specific media asset.
GET /api/workspaces/:workspaceId/projects/:projectId/media/:assetIdPreview Asset
Get a binary preview of a media asset (serves the file).
GET /api/workspaces/:workspaceId/projects/:projectId/media/:assetId/previewUpdate Asset Metadata
Update alt text, tags, or focal point for an asset.
PATCH /api/workspaces/:workspaceId/projects/:projectId/media/:assetIdRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
alt | string | No | New alt text |
tags | string[] | No | New tags |
focalPoint | { x: number, y: number } | No | Focal point coordinates (0-1 range) |
Delete Asset
Delete an asset and all its variants from storage and database.
DELETE /api/workspaces/:workspaceId/projects/:projectId/media/:assetIdBulk Operations
Perform bulk operations on multiple assets.
POST /api/workspaces/:workspaceId/projects/:projectId/media/bulkRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
action | 'delete' | Yes | Bulk action type |
assetIds | string[] | Yes | Asset IDs to process |
Public Media API (Key-Gated)
The public media surface lets agents and CI authenticate with a CDN/media API key instead of a session. It is gated by CDN keys carrying the right scope:
Authorization: Bearer crn_live_your-api-keyAuth model (per request): Bearer key validation → the key's project must match :projectId → scope check → per-key hourly rate limit → plan feature → media provider. The project must have CDN enabled (cdn_enabled); read routes require the media:read scope and the media.library feature, write routes require media:write and media.upload.
| Method | Path | Scope |
|---|---|---|
GET | /api/media/v1/:projectId/assets | media:read |
POST | /api/media/v1/:projectId/assets | media:write |
GET | /api/media/v1/:projectId/assets/:assetId | media:read |
PATCH | /api/media/v1/:projectId/assets/:assetId | media:write |
DELETE | /api/media/v1/:projectId/assets/:assetId | media:write |
POST | /api/media/v1/:projectId/assets/bulk | media:write |
List (GET /assets)
Query parameters: search, tags (comma-separated), type (MIME prefix), limit (default 50, max 100), page, sort (newest, oldest, name, size). Returns { assets, total }.
Upload (POST /assets)
Accepts either multipart/form-data with a file part (plus optional alt, tags, variants) or a JSON body { url, alt?, tags?, variants? } for URL import (SSRF-guarded). Returns 201 with the created asset.
Get / Update / Delete (/assets/:assetId)
GET returns the asset. PATCH updates alt, tags, and/or focalPoint. DELETE removes the asset and its variants, returning { "deleted": true }.
Bulk (POST /assets/bulk)
{
"action": "delete",
"assetIds": ["uuid1", "uuid2"]
}action is delete or tag (the latter also requires a tags array). Capped at 50 asset IDs per call; assets outside the key's project are silently skipped. Returns { action, affected }.
Related Pages
- Chat API -- agent tools for media operations
- Field Types --
image,video, andfilefield types - CDN API -- media delivery via CDN