API Endpoints
All v1 endpoints are mounted under https://your-freedam-instance.com/api/v1 and require an API bearer token. Every request is rate limited (60/minute per token) and audited.
For authentication and token scopes see API Authentication. For rate-limit headers and the back-off contract see Rate Limiting. For the cookieless image transformation URLs (/i/, /is/, /ip/) and other asset delivery URLs that live outside /api/v1, see Image Transformations.
Conventions
- Base URL -
https://your-freedam-instance.com/api/v1 - Auth header -
Authorization: Bearer <your-token> - JSON body - send
Content-Type: application/jsononPOST/PUT, except file uploads, which usemultipart/form-data. - Asset identifier - assets are addressed by their GAID (Global Asset ID), a stable public id such as
IM-20260309-UDXZH8PP6VPW. Every field that names an asset takes GAIDs, and responses return them:asset_gaid,asset_gaids,cover_asset_gaid. - Numeric routes -
{collection},{webhook},{batch}and{share}take numeric ids. - Errors - validation errors return
422with amessageand anerrorsobject keyed by field. A missing ability returns403withrequired_abilities. See Authentication.
Pagination
List endpoints accept page and per_page. Two response shapes exist:
| Endpoints | Default per_page |
Shape |
|---|---|---|
| Assets, collections | 24 (max 100) | data, links, meta with current_page, last_page, per_page, total |
| Shares, operations | 20 | data, links, meta |
| Webhooks, webhook deliveries | 20 | Flat: data, current_page, last_page, per_page, total, next_page_url at the top level |
Endpoint catalogue
User
| Method | Path | Ability | Description |
|---|---|---|---|
GET |
/v1/user |
any | Authenticated user: id, name, email. |
Tokens
Self-service token management for the user who owns the token. See Authentication.
| Method | Path | Description |
|---|---|---|
GET |
/v1/tokens |
List your tokens with abilities, last_used_at, last_used_ip and expires_at. |
POST |
/v1/tokens |
Create a token. The plain-text token is returned once. |
DELETE |
/v1/tokens/{token} |
Revoke a token immediately. |
POST /v1/tokens body:
| Field | Rules |
|---|---|
name |
Required string. |
abilities |
Required array of ability names. You can't grant abilities your own role doesn't allow. |
description |
Optional string. |
expires_at |
Optional date. |
Assets
| Method | Path | Ability | Description |
|---|---|---|---|
GET |
/v1/assets |
assets:read |
Search and list assets. |
GET |
/v1/assets/{gaid} |
assets:read |
One asset with its technical, descriptive and AI fields. |
PUT |
/v1/assets/{gaid} |
assets:write |
Update an asset. Fields you omit are left unchanged. |
DELETE |
/v1/assets/{gaid} |
assets:delete |
Move an asset to the Trash. |
Results only include assets the token's user is allowed to see.
Search parameters for GET /v1/assets
| Parameter | Description |
|---|---|
q |
Full-text and semantic search, up to 500 characters. |
sort_by |
created_at (default), updated_at, title, original_filename, size, or relevance (default when q is set). |
sort_direction |
asc or desc (default). |
asset_class_id |
Only assets of this asset class. |
mime_type |
MIME type or part of one, such as image/jpeg or video. |
ingestion_batch_id |
Only assets from this upload batch. |
filters |
Advanced rules as a JSON string, see below. |
page, per_page |
Pagination, per_page up to 100. |
filters accepts a single rule or nested and/or groups:
{
"type": "group",
"operator": "and",
"rules": [
{ "type": "filter", "field": "asset.file_category", "operator": "equals", "value": "Image" },
{ "type": "filter", "field": "asset.width", "operator": "greater_than", "value": 1920 },
{ "type": "filter", "field": "metadata.campaign", "operator": "contains", "value": "Spring" }
]
}
- Operators:
equals,not_equals,contains,not_contains,starts_with,ends_with,greater_than,greater_than_or_equal,less_than,less_than_or_equal,in,not_in,between([min, max]),is_empty,is_not_empty,color_equals,color_near. - Common fields:
asset.title,asset.original_filename,asset.mime_type,asset.file_category(Image,Video,Audio,Document),asset.extension,asset.width,asset.height,asset.file_size,asset.created_at,asset.updated_at,asset.capture_date,asset.creator,asset.copyright,asset.keywords,asset.ai_caption,asset.ai_tags,asset.ocr_text,asset.workflow_status,asset.collections(within/not_in), andmetadata.{key}for custom fields.
Invalid JSON or an invalid rule tree returns 422.
Body for PUT /v1/assets/{gaid}
| Field | Rules |
|---|---|
title |
String, up to 255 characters. |
description |
String up to 2,000 characters, or null. |
keywords |
Array of strings, each up to 100 characters. Replaces the existing keywords. |
ai_caption, ocr_text |
String up to 5,000 characters, or null. |
ai_tags |
Array of strings. |
metadata |
Object of custom field values keyed by the field's key, for example {"campaign": "Spring 2026", "product_line": "outdoor"}. |
workflow_status |
The status to move the asset to, when its workflow allows that transition. |
asset_class_id |
Move the asset to another asset class. |
curl -X PUT https://your-freedam-instance.com/api/v1/assets/IM-20260309-UDXZH8PP6VPW \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Hero shot Q3", "keywords": ["outdoor", "summer"], "metadata": {"campaign": "Spring 2026"}}'
The response is the updated asset in data. Every successful update sends an asset.updated webhook.
Video Embeds
Generate iframe embed codes for video assets. Each call mints (or reuses) a long-lived public share dedicated to embedding so you can drop the returned iframe_html straight into a CMS, blog post, or third-party site. Embed shares are isolated from regular /v1/shares and cannot be reached through that endpoint.
Required abilities: shares:read for GET, shares:manage for POST and DELETE.
| Method | Path | Ability | Description |
|---|---|---|---|
GET |
/v1/assets/{gaid}/embed |
shares:read |
Return the existing embed code, or 404 if none. |
POST |
/v1/assets/{gaid}/embed |
shares:manage |
Generate or refresh an embed code. |
DELETE |
/v1/assets/{gaid}/embed |
shares:manage |
Revoke the embed code. |
Only assets with a video/* MIME type can be embedded; calling these endpoints on any other asset returns 422.
Request body for POST (all fields optional)
| Field | Type | Default | Notes |
|---|---|---|---|
width |
integer | 640 |
Player width in pixels for the non-responsive iframe (160–3840). |
height |
integer | 360 |
Player height in pixels (90–2160). |
responsive |
boolean | true |
If true, the response also includes a responsive_html snippet. |
autoplay |
boolean | false |
Browsers force muted when autoplay is on. |
muted |
boolean | false |
Start muted. |
loop |
boolean | false |
Restart playback on end. |
controls |
boolean | true |
Show the player chrome. |
start |
integer | 0 |
Start offset in seconds. |
allowed_domains |
array of string | null |
Restrict the iframe to these origins via CSP frame-ancestors. |
force_new |
boolean | false |
Revoke the existing embed share and mint a fresh one. |
Response
{
"data": {
"token": "shr_…",
"asset_gaid": "A_01HXYZ…",
"embed_url": "https://your-freedam-instance.com/embed/shr_…/A_01HXYZ…?autoplay=1",
"iframe_html": "<iframe src=\"…\" width=\"640\" height=\"360\" …></iframe>",
"responsive_html": "<div style=\"position:relative;padding-bottom:56.25%…\">…</div>",
"width": 640,
"height": 360,
"expires_at": "2027-05-06T12:00:00+00:00",
"allowed_domains": null,
"options": {
"autoplay": false,
"muted": false,
"loop": false,
"controls": true,
"start": 0
}
}
}
A repeated POST for the same asset returns 200 with the existing share (and refreshes its expiry when it is within 30 days of expiring); a fresh share returns 201.
Collections
| Method | Path | Ability | Description |
|---|---|---|---|
GET |
/v1/collections |
collections:read |
List collections you can view. |
GET |
/v1/collections/{collection} |
collections:read |
One collection. |
POST |
/v1/collections |
collections:write |
Create a collection. |
PUT |
/v1/collections/{collection} |
collections:write |
Update a collection. |
DELETE |
/v1/collections/{collection} |
collections:delete |
Delete a collection. |
POST |
/v1/collections/{collection}/assets |
collections:write |
Add assets. |
DELETE |
/v1/collections/{collection}/assets |
collections:write |
Remove assets. |
- List filters:
collection_type_id,parent_id(children of that collection),root_only=1(top-level collections only),is_active,per_page. - Create body:
name(required),collection_type_id(required),description,parent_id,is_active,is_public,cover_asset_gaid(an asset you can view). - Update body: any of
name,description,parent_id,is_active,is_public,cover_asset_gaid. Send"cover_asset_gaid": nullto remove the cover. - Add assets:
POSTa JSON body{"asset_gaids": ["IM-…", "VI-…"]}. - Remove assets:
DELETEwith the GAIDs in the query string:?asset_gaids[]=IM-…&asset_gaids[]=VI-….
Adding and removing report partial results: the response contains a message such as "2 asset(s) added to collection.", the number of assets that succeeded, and failed and errors entries describing the assets that could not be added or removed. Check them rather than assuming every GAID was processed.
Uploads
Ability: uploads:manage. Uploading is a three-step flow.
| Method | Path | Description |
|---|---|---|
POST |
/v1/uploads/batches |
Create an upload batch. |
POST |
/v1/uploads/batches/{batch}/files |
Upload one file into the batch. |
GET |
/v1/uploads/batches/{batch} |
Read the batch status and per-file progress. |
- Create a batch. All fields are optional defaults for the files in the batch:
batch_name,asset_class_id,collection_id,target_workflow_status,title,description,keywords(array),creator,copyright,location,metadata(object keyed by field key). The response contains the batchidandbatch_uuid. - Upload each file as
multipart/form-data, one file per request, with the binary infile. You can override the batch defaults per file withtitle,description,creator,copyright,location,asset_class_idandcollection_id. Because multipart forms can't nest values, sendkeywordsas a JSON array string (["landscape","sunset"]) andmetadataas a JSON object string. - Poll the batch until it has finished. Each file moves through
uploading,scanning,storing,processing_metadata,processing_ai,finalizingandcompleted, or ends infailed. The batch reportstotal_files,processed_files,successful_files,failed_filesandis_completed.
curl -X POST https://your-freedam-instance.com/api/v1/uploads/batches/42/files \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-F "[email protected]" \
-F "title=Hero shot Q3" \
-F 'keywords=["outdoor","summer"]'
Instead of polling, subscribe to the upload.batch_completed and upload.batch_failed webhooks. To find the assets a batch created, list assets with ingestion_batch_id.
Metadata
Ability: metadata:read. A read-only catalogue of field definitions, vocabularies and terms, to build your own forms without hard-coding workspace-specific values.
| Method | Path | Description |
|---|---|---|
GET |
/v1/metadata/definitions |
Field definitions. Filter with asset_class_id. |
GET |
/v1/metadata/vocabularies |
Vocabularies with id, name, code, is_tree. |
GET |
/v1/metadata/vocabularies/{vocabulary} |
One vocabulary with its terms. |
GET |
/v1/metadata/vocabularies/{vocabulary}/terms |
Terms of a vocabulary. For tree vocabularies, pass parent_id to list children. |
GET |
/v1/metadata/vocabularies/{vocabulary}/terms/{term} |
One term. |
Use the definition key as the property name in metadata objects when updating assets or uploading.
Shares
| Method | Path | Ability | Description |
|---|---|---|---|
GET |
/v1/shares |
shares:read |
Shares created by the token's user. Filter with shareable_type. |
GET |
/v1/shares/{share} |
shares:read |
One share. |
POST |
/v1/shares |
shares:manage |
Create a share link. |
PUT |
/v1/shares/{share} |
shares:manage |
Update access, permissions, dates or is_active. |
DELETE |
/v1/shares/{share} |
shares:manage |
Delete a share. |
POST |
/v1/shares/{share}/revoke |
shares:manage |
Stop a share immediately. |
POST /v1/shares body:
| Field | Rules |
|---|---|
shareable_type |
Required: collection, asset or selection. |
shareable_id |
For collection: the collection's numeric id. |
asset_gaid |
For asset: the asset's GAID. |
asset_gaids |
For selection: an array of asset GAIDs. |
access_type |
Required: public, email_restricted, password_protected or private. |
allowed_emails, allowed_domains |
Arrays. At least one of them is required for email_restricted. |
password |
At least 6 characters, for password_protected. |
permissions |
{"view": true, "download": false} |
download_resolution |
original, w_5120, w_4096, w_2048, w_1024, w_512, w_256 or w_128. |
watermark_id |
A watermark id, when your plan includes watermarks. |
starts_at, expires_at |
Dates; expires_at must be after starts_at. |
acknowledge_rights_warnings |
true to create the share even when rights policies block some assets. |
A share created through the API behaves exactly like one created in Freedam:
- You need the right to share what you send:
assets.shareand access to (and download of) every asset, or for a collection,collections.shareon a collection you created. An unknown GAID is refused with the same403as an asset you may not share. - Rights policies are checked: a
publicshare needs thepublishaction on every asset, other access typesdistribute. A blocked share returns403with"error": "rights_blocked"and theblocked_assets(with theirasset_gaid). Resend withacknowledge_rights_warnings: trueto create it anyway; blocked assets stay hidden from recipients. - If your workspace requires accepting share terms of use, the request returns
409with"error": "consent_required"until the token's user has accepted them in Freedam. - Each address in
allowed_emailsreceives an invitation email, and theshare.createdwebhook is sent. Adding addresses withPUTinvites the new ones and tells existing recipients what changed.
Share responses identify what is shared with shareable_type plus shareable_id (collections), asset_gaid (a single asset) or asset_gaids (a selection). See Share Links for how each option behaves for recipients.
Operations
Long-running asynchronous operations. POST /v1/operations requires an Idempotency-Key header - replaying the same key returns the original operation instead of creating a new one. The body is {"type": "...", "params": {...}}. Any other type is rejected with 422.
type |
What it does | params |
Ability |
|---|---|---|---|
asset_upload_batch |
Creates an upload batch to send files into. | Optional: batch_name, asset_class_id (integer), collection_id, target_workflow_status |
uploads:manage |
asset_bulk_update |
Applies the same metadata updates to many assets. | Required: asset_gaids (array of GAIDs), updates (object) |
assets:write |
| Method | Path | Description |
|---|---|---|
GET |
/v1/operations |
Your operations. Filter with type and status. |
POST |
/v1/operations |
Start an operation. |
GET |
/v1/operations/{operation} |
Status, progress_percentage, current_stage, result and error. |
POST |
/v1/operations/{operation}/cancel |
Cancel a pending or processing operation. |
Operation status is one of pending, processing, completed, partially_completed, failed or cancelled.
asset_bulk_update only changes the assets the token's user can view and edit. The operation's result lists every other GAID in errors as {"asset_gaid": "…", "error": "Asset not found or not updatable."}, whether the asset does not exist or you may not edit it; the operation then ends as partially_completed or failed.
Webhooks
No ability is needed: each token manages the webhook endpoints of its own user. See Webhooks for bodies, events and signatures.
| Method | Path |
|---|---|
GET |
/v1/webhooks |
POST |
/v1/webhooks |
GET |
/v1/webhooks/{webhook} |
PUT |
/v1/webhooks/{webhook} |
DELETE |
/v1/webhooks/{webhook} |
POST |
/v1/webhooks/{webhook}/test |
GET |
/v1/webhooks/{webhook}/deliveries |
POST |
/v1/webhooks/{webhook}/rotate-secret |
Consents
Compliance and audit endpoints. Ability: consents:read.
| Method | Path | Description |
|---|---|---|
GET |
/v1/consents/me |
Terms of use you have accepted and those still pending. |
GET |
/v1/consents/users/{user} |
The same for another user, by numeric id. |
GET |
/v1/consents/users/{user}/history |
The append-only acceptance history for a user, paginated. |
OpenAPI specification
A machine-readable OpenAPI 3.1 spec for every endpoint above is available at /docs/api.json. It is the source of truth used to generate the official SDKs. Generate a client in any language with openapi-generator or Kiota.