Files, Media, and Content Storage
The assets module stores workspace-scoped metadata for documents, images, videos, and attachments.
Status
Public capabilities includes asset registration, cursor-based listing, signed upload and download URL flows, and archive semantics.
Public capabilities
- Register an asset or upload intent with file metadata.
- Complete an upload after the object has been sent to the signed upload URL.
- Search assets by type, status, text, or linked workspace resource.
- Retrieve a signed download URL for an asset.
- Archive asset metadata without removing audit history.
- Link assets to CRM, Support, task, and CMS entry workflows.
Signed URL flow
Use the signed upload URL returned by Slab5 to upload file bytes directly, then complete the asset upload.
Create an upload intent, upload the object to the returned PUT URL, then call POST /v1/assets/{asset_id}/complete or the complete_asset_upload MCP tool. Completion moves the asset from upload_pending to available; download URLs are only returned for available assets.
The signed upload URL is used directly by the client. Send the bytes to that URL with the returned method and headers. Do not send the file body to POST /v1/assets; that route only creates the metadata record and upload intent.
Asset references are scoped to the tenant and workspace.
REST example
Create the upload intent:
curl "$SLAB5_API_BASE_URL/assets" \
-X POST \
-H "Authorization: Bearer $SLAB5_WORKSPACE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"asset_type": "document",
"name": "console-playground.txt",
"file_name": "console-playground.txt",
"mime_type": "text/plain",
"byte_size": 30
}'The response includes the asset metadata and upload instructions:
{
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"data": {
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"id": : var(--shiki-token-string)">"asset_123",
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"status": : var(--shiki-token-string)">"upload_pending",
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"asset_type": : var(--shiki-token-string)">"document",
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"file_name": : var(--shiki-token-string)">"console-playground.txt",
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"mime_type": : var(--shiki-token-string)">"text/plain",
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"byte_size": : var(--shiki-token-constant)">30,
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"storage_provider": : var(--shiki-token-string)">"managed",
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"upload_url": : var(--shiki-token-string)">"https://uploads.slab5.com/assets/...",
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"upload_method": : var(--shiki-token-string)">"PUT",
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"upload_headers": {
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"content-type": : var(--shiki-token-string)">"text/plain"
},
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"upload_expires_at": : var(--shiki-token-string)">": var(--shiki-token-constant)">2026-: var(--shiki-token-constant)">05-13T07:: var(--shiki-token-constant)">25:: var(--shiki-token-constant)">34.406Z"
},
: var(--shiki-token-string)">"color: var(--shiki-token-parameter)">: var(--shiki-token-string)">"request_id": : var(--shiki-token-string)">"req_123"
}Upload the physical file to the signed URL:
curl "$UPLOAD_URL" \
-X PUT \
-H "content-type: text/plain" \
--data-binary @console-playground.txtComplete the upload after the object has been sent successfully:
curl "$SLAB5_API_BASE_URL/assets/asset_123/complete" \
-X POST \
-H "Authorization: Bearer $SLAB5_WORKSPACE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"byte_size": 30,
"mime_type": "text/plain"
}'Fetch the available asset and use the returned download_url:
curl "$SLAB5_API_BASE_URL/assets/asset_123" \
-H "Authorization: Bearer $SLAB5_WORKSPACE_TOKEN"Playground testing
The control-plane API Playground includes an Assets category and a Storage upload panel. Select a file from the browser, create an upload intent, PUT the selected file to the returned signed URL, complete the asset, then fetch the download URL.
When a file is selected, the playground uses the file bytes for the PUT request and uses the file size and MIME type when preparing the complete call. If no file is selected, the playground can still upload a small text body for quick testing.
Billing rollups
Asset metadata records current state. Storage billing should read daily rollups from asset_storage_usage_daily, keyed by tenant, workspace, storage provider, and usage date.
The rollup separates active bytes from archived bytes and records active, archived, and total asset counts. A scheduled job can snapshot the current asset table at the end of each day, while pricing can decide how to charge active and archived storage differently.
