All asset management endpoints require authentication via JWT Bearer token or API key. See Authentication for details.
Upload an asset
POST /api/v1/assets
Content-Type: multipart/form-data
Uploads a new image asset. The request must be multipart/form-data. On success, the server returns 202 Accepted and begins background processing (enrichment, derivative generation) if runEnrichment is enabled.
Request fields
The image file to upload. Must not be empty. Allowed content types are configured server-side (typically image/png, image/jpeg, image/gif, image/webp, etc.).
Optional description for the asset. Maximum 1000 characters.
Optional alt text for the asset. Maximum 1000 characters.
Whether the asset should be publicly accessible. Defaults to true.
Optional. The ID of the storage provider profile to use. If omitted, the default storage provider is used.
Whether to run AI enrichment after upload. Defaults to true.
Optional commit message for storage providers that support it (e.g. GitHub Repo).
Response — 202 Accepted
Asset type. Currently always image.
Processing status. One of: pending, processing, ready, failed.
The original filename provided at upload.
MIME type of the uploaded file.
Image width in pixels, populated after processing.
Image height in pixels, populated after processing.
The storage provider type used (e.g. local, s3, github-repo).
The storage profile ID used.
Internal storage key for the asset.
Public URL for the asset (if isPublic is true).
Whether the asset is publicly accessible.
Errors
Code Description asset_file_requiredThe file field was not included in the request. asset_file_emptyThe uploaded file has zero bytes. asset_file_too_largeThe file exceeds the configured maximum upload size. asset_content_type_not_allowedThe file’s content type is not in the allowed list. asset_description_too_longDescription exceeds 1000 characters. asset_alt_text_too_longAlt text exceeds 1000 characters.
Example
curl -X POST http://localhost:5121/api/v1/assets \
-H "Authorization: Bearer <token>" \
-F "file=@kitten.png" \
-F "description=A fluffy orange kitten" \
-F "altText=Orange cat looking at camera" \
-F "isPublic=true" \
-F "runEnrichment=true"
List assets
Returns a paginated list of all assets visible to the authenticated user.
Query parameters
Page number. Defaults to 1.
Number of results per page. Defaults to the server-configured default, capped at the configured maximum.
Free-text search. Also accepted as keyword. Matches file name, description, and alt text.
Filter by MIME type (e.g. image/jpeg).
Filter by processing status. One of: pending, processing, ready, failed.
Field to sort by. Also accepted as sortBy. Supported values: createdAtUtc (default), size.
Sort direction. Also accepted as sortDirection. One of: asc, desc (default).
Response
Array of asset list items. Each item has: id, type, status, originalFileName, contentType, size, width, height, storageProvider, storageProviderProfileId, publicUrl, isPublic, createdAtUtc, updatedAtUtc.
Total matching asset count.
Example
curl "http://localhost:5121/api/v1/assets?page=1&pageSize=20&orderBy=createdAtUtc&orderDirection=desc" \
-H "Authorization: Bearer your-api-key"
Get an asset
Returns full details for a single asset, including all derivatives and structured AI results.
Path parameters
The unique identifier of the asset.
Response
The name under which the file is stored.
SHA-256 checksum of the file content.
Public content URL (if public).
Whether the asset is publicly accessible.
Derived versions (e.g. thumbnails). Each entry includes: kind, contentType, extension, size, width, height, publicUrl, createdAtUtc.
Structured AI results attached to the asset. Each entry includes: kind, payloadJson, createdAtUtc.
Summary of the most recent skill execution on this asset. Name of the skill that ran.
How the execution was triggered.
When the execution started.
When the execution completed.
Whether the execution succeeded overall.
Individual step results. Each has: stepName, succeeded, errorMessage, startedAtUtc, completedAtUtc.
Returns 404 Not Found with error code asset_not_found if the asset does not exist.
Example
curl http://localhost:5121/api/v1/assets/01956f8d-88e4-7c6a-a8f1-5f235293db7a \
-H "Authorization: Bearer <token>"
PATCH /api/v1/assets/{id}
Updates metadata fields for an existing asset. Only fields you include in the request body are changed (patch semantics).
Path parameters
The unique identifier of the asset to update.
Request body
New description. Maximum 1000 characters. Omit to leave unchanged.
New alt text. Maximum 1000 characters. Omit to leave unchanged.
New original filename. Omit to leave unchanged.
Change public visibility. Omit to leave unchanged.
Response
Returns the full updated asset object (same shape as Get an asset ).
Example
curl -X PATCH http://localhost:5121/api/v1/assets/01956f8d-88e4-7c6a-a8f1-5f235293db7a \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"description": "Updated description", "isPublic": false}'
Delete an asset
DELETE /api/v1/assets/{id}
Permanently deletes an asset. This is a hard delete — the asset record and its file in storage are both removed.
Path parameters
The unique identifier of the asset to delete.
Request body (optional)
Optional commit message for storage backends that support versioning (e.g. GitHub Repo).
Response
Final status of the asset before deletion.
Returns 404 Not Found with error code asset_not_found if the asset does not exist.
Example
curl -X DELETE http://localhost:5121/api/v1/assets/01956f8d-88e4-7c6a-a8f1-5f235293db7a \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"commitMessage": "Remove test asset"}'
Batch delete assets
POST /api/v1/assets/batch-delete
Deletes multiple assets in one request. Assets that do not exist are silently skipped and reported in notFoundIds.
Request body
ids
array of strings (uuid)
required
An array of asset IDs to delete.
Response
Number of IDs provided in the request.
Number of assets successfully deleted.
IDs that were not found and therefore not deleted.
Example
curl -X POST http://localhost:5121/api/v1/assets/batch-delete \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '["01956f8d-88e4-7c6a-a8f1-5f235293db7a", "01956f8d-0000-0000-0000-000000000001"]'
Get asset content
GET /api/v1/assets/{id}/content
Accesses the content of an asset, with access control applied:
Public asset : Returns a 307 Temporary Redirect to the public content URL.
Private asset : Streams the content directly (requires valid JWT or API key).
Path parameters
The unique identifier of the asset.
Returns 404 Not Found with error code asset_not_found if the asset does not exist.
Example
# For a public asset — follow the redirect
curl -L http://localhost:5121/api/v1/assets/01956f8d-88e4-7c6a-a8f1-5f235293db7a/content \
-H "Authorization: Bearer <token>"
# For a private asset — save to file
curl http://localhost:5121/api/v1/assets/01956f8d-88e4-7c6a-a8f1-5f235293db7a/content \
-H "Authorization: Bearer <token>" \
-o asset.png
List available skills
GET /api/v1/assets/skills
Returns a list of AI skills available to run against assets.
Response
Unique identifier name of the skill.
Human-readable description of what the skill does.
The processing steps this skill performs.
Example
curl http://localhost:5121/api/v1/assets/skills \
-H "Authorization: Bearer your-api-key"
Run a skill on an asset
POST /api/v1/assets/{id}/skills/{skillName}/run
Triggers an AI skill to run against a specific asset. Skills can generate captions, alt text, thumbnails, and other derived outputs.
Path parameters
The unique identifier of the asset.
Request body (optional)
Optional skill-specific parameters as a JSON object. The accepted parameters depend on the skill being run.
Response
Whether the skill execution succeeded overall.
The name of the skill that ran.
Individual step results. Whether this step succeeded.
Error message if this step failed.
The updated asset object after skill execution.
Example
curl -X POST http://localhost:5121/api/v1/assets/01956f8d-88e4-7c6a-a8f1-5f235293db7a/skills/caption/run \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{}'
Get usage statistics
GET /api/v1/assets/usage-stats
Returns aggregate usage statistics across all assets.
Response
Total number of assets in the system.
Total storage used across all assets, in bytes.
Total number of asset derivatives (e.g. thumbnails).
Per-content-type usage breakdown. Number of assets with this content type.
Total bytes for assets with this content type.
The skill with the most executions, or null if no skills have been run. Number of times this skill has been executed.
Example
curl http://localhost:5121/api/v1/assets/usage-stats \
-H "Authorization: Bearer <token>"
{
"data" : {
"totalAssets" : 42 ,
"totalBytes" : 104857600 ,
"totalDerivatives" : 38 ,
"contentTypeBreakdown" : [
{ "contentType" : "image/png" , "count" : 25 , "totalBytes" : 62914560 },
{ "contentType" : "image/jpeg" , "count" : 17 , "totalBytes" : 41943040 }
],
"mostActiveSkill" : {
"skillName" : "caption" ,
"runCount" : 30
}
}
}