Scope: the REST API of the Canto SaaS DAM (the product formerly branded "Canto Flight") and, where relevant, the separate Product Hub (PIM) API. The legacy on-premise Cumulus product and its Canto Integration Platform (CIP) API are out of scope (Cumulus support ended January 2024 S86).
At a glance
https://{tenant}.{region}/api/v1/…; binaries: https://{tenant}.{region}/api_binary/v1/…; hashed public links: https://{tenant}.{region}/direct/…; OAuth: https://oauth.{region}/oauth/api/…; Product Hub: https://{tenant}.{region}/api/v1/pim/…. {region} ∈ canto.com, canto.global, canto.de, au.canto.com, ca.canto.com S2 S3 S12scope=admin|contributor|consumer in client-credentials mode only selects which user logs in S2Authentication
Where credentials come from S18 S20
- The first API key (App ID + App Secret) is handed over by the customer's Implementation Manager; after API activation, admins create more under User Menu → Settings → Configuration Options → API Keys → Create API Key (name, optional description, website, logo URL, redirect URL). If the menu is missing, the customer opens a support ticket; trial accounts ask their account representative. S14 S18
- Optional Support Client Credentials Mode on a key: Generate displays an access token valid one month that can be used directly (no OAuth round-trip); Revoke invalidates it. S18
OAuth host must match the tenant's domain — a .canto.global tenant must use oauth.canto.global, etc.; using the wrong host yields the error Wrong app_id value S14 S20. All five regional OAuth hosts (oauth.canto.com, oauth.canto.global, oauth.canto.de, oauth.au.canto.com, oauth.ca.canto.com) answer (405 on GET to the token URL) S16.
Token endpoint S2: POSThttps://oauth.{region}/oauth/api/oauth2/compatible/token (parameters in the query string in the docs).
| Parameter | Use |
|---|---|
app_id, app_secret | Always required |
grant_type | authorization_code | client_credentials | refresh_token |
code, redirect_uri | Authorization-code exchange (redirect_uri must equal the one on the key) |
refresh_token | Refresh; returns a new pair and invalidates the old refresh token |
user_id | Client credentials: e-mail of the Canto user to act as — recommended by Canto |
scope | Client credentials: admin (default) | contributor | consumer — logs in the alphabetically first user with that role; cannot be combined with user_id |
Response: {access_token, expires_in, token_type: "Bearer", refresh_token}; expires_in is a string S2.
Authorization-code flow S2 S14: browser to https://oauth.{region}/oauth/api/oauth2/authorize?response_type=code&app_id=…&redirect_uri=…&state=… → user enters account URL, credentials or SSO, clicks Allow → redirect with code → exchange. GEThttps://oauth.{region}/oauth/api/oauth/tenant/{refreshToken} returns the account (tenant) name bound to a refresh token, useful for multi-tenant apps S2.
Canto's guidance S14 S20: client credentials for unattended backend integrations; authorization code for web apps; do not use client credentials in browser JavaScript; use the least-privileged user/scope possible.
Sending the token: Authorization: Bearer <access_token> on /api/v1 and /api_binary/v1; 401 = missing/invalid token, 403 = the represented user lacks permission S14.
PKCE public client S3: the specs declare an authorization-code flow with PKCE and no client secret (https://oauth.canto.com/oauth/api/oauth2/universal2/authorize / /token), used by the docs portal's console. Whether third parties can register their own public clients is Unknown Unverified (not documented).
Core resources
Paths relative to https://{tenant}.{region}/api/v1 unless stated. Rate level (L1–L5) from the Postman documentation; ? = not stated S14. Endpoint inventory from the OpenAPI files S3–S12.
| Resource | Endpoints | Notes |
|---|---|---|
| Search / list assets | GET/search, GET/{scheme}, GET/folder/{id} (recursive), GET/album/{id} — L4 | Results already include full metadata; Canto asks integrators not to call detail per hit S14 |
| Asset detail | GET/{scheme}/{id} (L2); POST/batch/content (≤ 100 assets, L?) | Flags noXMPMetadata, excludeTrashed, extendedRelationInfo S3 |
| Metadata write | POST/batch/edit = read editable values (L2); PUT/batch/edit = apply (L4); PUT/{scheme}/{id}/rename (L2) | Fields addressed by name, not id S3 |
| Tags / keywords / smart tags | PUT/DELETE/{scheme}/{id}/tag/{tag}; POST/keyword; PUT/DELETE/{scheme}/{id}/keyword/{kw}; DELETE/{scheme}/{id}/smarttags/{tag} — L2 | Keywords must exist before being attached S3 |
| Metadata schema (custom fields) | GET/POST/PUT/custom/field, DELETE/custom/field/{id} — L2 | 8 types; no endpoint for built-in fields S10 |
| Terms & conditions (rights texts) | GET/POST/PUT/termAndCondition, DELETE/termAndCondition/{id} — L? | S11 |
| Folders / albums | GET/tree, GET/tree/{folderId} (L3); GET/info/folder/{id}, GET/info/album/{id} (L4); POST/folder/{parent}/{name}, POST/album/{parent}/{name} (L2); PUT/folder/{id}, PUT/album/{id}; DELETE/batch/folder[/permanent] | Tree unpaginated, depth via layer S4 |
| Album membership & relations | POST/DELETE/batch/album (L4); POST/related, POST/{scheme}/{id}/unrelated, POST/unrelated (L4) | Relations ≤ 1000 contents per call S3 |
| Collections | GET/mycollections, GET/mycollections/{id} — L1 | Per-user saved sets S4 |
| Uploads | GET/upload/setting (L1) → POST to S3 (L1) → GET/upload/status (L2); POST/uploadlink (L2); POST/uploadlink/import (L4) | See Uploads & binaries S5 |
| Metadata import | POST/import/metadata (multipart CSV), GET/import/metadata/progress | Async S5 |
| Renditions / downloads | /api_binary/v1/{scheme}/{id} (original), /preview[/{dim}], /HighJPG, /LowJPG, /PNG, /metadata (XML), /previewURI[/{dim}], /directuri[/{sec}], POST/api_binary/v1/batch/directuri — L1; /api_binary/v1/advance/image/{id}/download — L5; POST/api/v1/batch/download — L2 | S6 |
| Download & watermark presets | GET/tenant/download/presets, GET/tenant/watermark/presets — L2 | S6 |
| Metadata export | POST/export/metadata (CSV by e-mail, L4); POST/album/{id}/export/metadata, POST/folder/{id}/export/metadata; POST/file/metadata (synchronous JSON, chosen columns) | S7 |
| Sharing | PUT/share/batch/ (L2), PUT/share/batch/container | Share links with expiry and download options S3 |
| Video embed | POST/segment/video/{id} (L4) | HLS/DASH embed S3 |
| Users | GET/user (current, L1); GET/user/accounts, GET/users (L2); POST/PUT/user, DELETE/user/{userId} (L2); GET/users/fields (L1); POST/api/v2/users/$query (≤ 100 ids, L2) | S9 |
| Roles / groups | GET/roles (custom roles, L1); GET/groups (L?), GET/groups/{id} (members, L1); group CRUD and membership (L2) | No endpoint exposes a role's privilege matrix S9 |
| Reports & audit | GET/usagehistory|shareactivity|downloadactivity/{scheme}/{id} (L2); GET/tenantreport/{uploadhistory|downloadhistory|sharehistory|share3rdpartyhistory|loginhistory}; GET/tenantreport/assetview; GET/top/{keyword|tag|smart_tag|commentedFiles}; GET/operationlogs (CSV, L4) | S8 |
| Self-description | GET/api/v1 — lists the tenant's endpoints as URI templates; no auth; L1 | S8 S14 |
| Workflows / approvals | No dedicated endpoints; approval status and expiration are asset fields set through batch edit / CSV import | S3 S37. Approval Hub (proofing) has no public API — Not public |
| Rights | copyright, termsAndConditions, expirationDate, approvalStatus asset fields + T&C CRUD | Present only when DRM is enabled on the tenant S3 |
| Product Hub (PIM) | /api/v1/pim/: products (+variants, asset link/unlink), product types, custom attributes, categories, dimensions, catalogs, channel templates (+export), bulk ops, share links, imports, webhooks — 75 operations | 5 req/s S12 |
Not exposed in the documented API (checked against all specs): DAM webhook management, portal/workspace/style-guide listing and settings, folder/album permission ACLs, approval-workflow configuration, a keyword-vocabulary list endpoint, a built-in-field schema endpoint, saved searches, trash listing, SSO/password-policy configuration S3–S11. Whether the tenant self-description (GET/api/v1) reveals more is Unknown.
Data model
- Asset identity = content
id+scheme, where scheme ∈image,video,audio,document,presentation,other(case-sensitive; shown as "Smart Albums" in the UI). Every per-asset call needs both S14. - Structure: folders contain folders and albums; albums contain assets; an asset is never directly in a folder and can belong to several albums (
relatedAlbums[]withidPath) S14 S31. Folder/album ids are short codes (e.g. 5 characters) and change when the folder/album is moved S14. - Metadata buckets on an asset S3 S14:
default{}— built-in fields keyed by display label (e.g. "Uploaded by", "Date modified", "Copyright", "Author", "Dimensions", "Resolution").additional{}— custom fields keyed by field name (values: null, string or array).metadata{}— embedded EXIF/XMP/IPTC, skippable withnoXMPMetadata=true.- Top-level:
description,tag[],keyword[],smartTags[],approvalStatus,expirationDate,copyright,termsAndConditions,owner/ownerName,created,time,lastUploaded,versionHistory[],relatedFile[],url{}. - Alt-text (images) is "accessible via the Canto API" S76; its JSON key is not shown in the specs — Unknown.
- Custom field schema: GET
/custom/field→[{id, name, type, values[]}]; ids followmeta_{type}_{n}(e.g.meta_text_2,meta_multichoice_0); typesNUMBER,TEXT,DATE,SINGLECHOICE,MULTICHOICE,URL,LABEL(hex colour),RATING(1–5); create requiresname(unique, ≤ 80 chars) anddescription(≤ 800) S10. Field type cannot be changed after creation; renaming an option leaves the old value on assets S22. Sections, display order, required-on-upload and translations exist in the UI S22 S23 but are not in the API schema S10. - Taxonomy: keywords = admin-managed controlled list (flat); tags = free text; smart tags = AI-generated S24 S25. No hierarchical taxonomy object S3.
- Approval:
approvalStatus∈ Approved, Pending, Restricted (+ Expired set automatically, + up to 20 custom statuses) S26 S27. - Versions:
versionHistory[](no,versionId,ownerName,created,time,comment,currentVersion, per-version URIs) S3. - Storage class:
standardorfreeze(Cold Storage) S3. - Dates: JSON dates are strings
yyyyMMddHHmmssSSSS14; search filters take Unix seconds exceptexpirationDate(milliseconds) S3; batch-edit datesYYYY-MM-DD HH:mm(orYYYY-MM-DDfor DATE fields) S3. - Numbers as strings: many numeric values (
size,width,expires_in) are returned as strings S2 S3.
Search & querying
S3 unless noted.
| Aspect | Behaviour |
|---|---|
| Full text | keyword (UI-like search); a|b = OR, a+b = AND; restrict with searchInField=filename|description|comment|keywords|author|tags; exactMatch; operator=and|or |
| Filters | scheme, tags, tagsLiteral (case-sensitive), keywords, approval (approved|pending|restricted; expired alone), owner, fileSize=min..max, width, height, dimension, resolution, orientation, colorModel, duration, pageNumber, storageClass, created, createdTime, uploadedTime, lastModified, expirationDate, latestReviewDate, and any custom-field id (e.g. meta_text_2=…) |
| Empty / non-empty | Literal __null__ (no value) and __exists__ (any value) on filter fields |
| Negation | $filter=tagsLiteral ne 'X' and keywords ne 'Y' (OData-style, tags and keywords only) |
| Facets | Every search returns facets[]: distinct values (no counts) for categorical fields; min/max for numeric/date fields |
| Counts | found = total hits; tenant-wide frequency counts via GET/top/{keyword|tag|smart_tag|commentedFiles}, including Unkeyworded / Untagged buckets S8 S14 |
| Sorting | sortBy=name|time|scheme|owner|size, sortDirection=ascending|descending (default time, descending) |
| Page size | limit default 100, maximum 1000 S3; the docs state that over-large requests must be avoided S14 |
| Deep pagination | Maximum reachable offset is Not public Unverified — partition large crawls (e.g. by scheme and uploadedTime/lastModified ranges) rather than relying on deep offsets |
| Delta queries | Canto recommends delta requests by modification date over full re-reads S14; in search results time depends on a tenant display setting, so lastModified is the reliable delta filter S14 |
Uploads & binaries
- GET
/api/v1/upload/setting— returns an S3 POST policy (url, key, policy, signature fields); valid 5 hours, do not re-request per file..deand.ca.canto.comtenants use an AWS SigV4 variant of the fields. POSTmultipart/form-data to the returned S3 URL without the Bearer token; one file per request, file field last;x-amz-meta-scheme+x-amz-meta-idof an existing asset creates a new version;x-amz-meta-album_idtargets an album. Maximum 5 GB per API upload S14.- GET
/api/v1/upload/status?hours=…— poll no more often than every 30 s for backend applications (1 s single GUI upload, 10 s batch GUI) S14.
Other ingestion: Upload Links (POST/uploadlink, with duplicate-check mode MD5 or filename and default approval status) S5; CSV metadata import (key column File Name or Content ID + File Type; , or ; delimiter; multi-values separated by ;; UTF-8) S37. UI/desktop upload limits differ (browser 200 GB, current Canto Connect unlimited, Adobe connector 5 GB) S59.
- Token-protected: original
/api_binary/v1/{scheme}/{id}, previews/preview/{dim},HighJPG,LowJPG,PNG, embedded-metadata XML, per-version binaries. - Signed CloudFront URLs:
/directuri[/{expireSeconds}](download) and/previewURI[/{dim}], plus a batch variant; the URL is version-specific and expires S14. - Hashed "direct URLs" (
/direct/{scheme}/{id}/{hash}/original, preview sizes) need no token; returned in asset JSON S6. Preset preview widths are listed as 100, 240, 320, 500, 640, 800 and 2050 px S60. - Advanced download (resize/crop/colour mode) is L5 and must only be used for user-initiated single downloads, never batches S14.
- Bulk ZIP: POST
/api/v1/batch/download— async, e-mailed link, max 1000 files (413 above), one job at a time per tenant (429 otherwise) S6 S14. - Media Publisher / MDC (paid CDN add-on): CloudFront delivery URLs with on-the-fly operations (scale
-S, bound-B, crop-C, format/quality-F, watermark-W, crop template-T); assets > 1 GB may fail to publish S61.
Events & webhooks
- Configured by an administrator in Settings → Configuration Options → API → Webhooks; no API to create or list them S3–S11.
- 14 events: Add Album, Add Folder, Add New Version (also on "set as current"), Assign to Album, Delete Album, Delete Asset, Delete Folder, New Asset (also when moved from an Upload Link to the Main Library), Relate Files, Remove from Album, Unrelate Files, Update Album, Update Folder, Update Metadata. Fired for Main Library content.
- Per webhook: target URL, optional "Secure Token" (literal
nullif unused), content type JSON / Form / XML, checkboxes selecting which asset parameters are sent; several webhooks per event allowed. - Canto does not persist webhooks (no retry/replay); it recommends a periodic (e.g. daily) consistency check on the receiving side S20.
- Payload schema, header names and signing (HMAC) are Not public.
Product Hub webhooks S12: GET/POST/pim/webhooks, update/delete, GET/webhooks/{id}/events, DELETE/webhooks/{id}/events/purge; resources product, variant, import; events created, updated, deleted, started, completed, failed; optional secret.
Polling alternatives: lastModified search ranges; GET/operationlogs (CSV User,Operation,Location,Date,Content; both dates required in practice) S8; tenant upload/download history reports S8. The UI operation-log report covers the current and previous year S58 Unverified (retention for the API endpoint not stated).
Limits & quirks
| Topic | Detail | |
|---|---|---|
| Rate-limit scope | Whether limits count per token, per app, per user or per tenant is Not public; no Retry-After / X-RateLimit-* headers are documented | S14 Unverified |
| Rate levels | Search/list L4 (200/min), detail L2, tree L3, binaries L1, advanced download L5; several endpoints (batch content, top, tenant reports, groups list, T&C, file/metadata) have no documented level | S14 |
| Volume review | For large integrations Canto asks for a sequence diagram and expected calls/min via its developer support | S14 S20 |
| Developer support scope | Canto provides API advice in a joint meeting but does not build custom integrations or review code | S20 |
| Error bodies | Standard HTTP codes (400/401/403/404/429/500/503); body {code, message}; OAuth errors {error, error_description}; batch calls report partial failures in the body (failed[]) | S2 S3 S14 |
| 404 semantics | Also returned for arbitrary/invalid ids | S14 |
| Keywords | Attaching a keyword that does not exist fails; create it first with POST/keyword | S3 |
| Name vs id | Search filters use custom-field ids; asset JSON and batch edit use field names | S3 S10 |
| Moving folders | Folder/album ids change on move — do not key long-lived state on them alone | S14 |
| MD5 | Available via POST/file/metadata columns; the Help Center warns the MD5 value tends to change even after metadata-only edits (tags, keywords, smart tags) | S7 S28 Unverified |
| Accepted export columns | POST/file/metadata column names are only exemplified ("Content ID", "File Name", "MD5", "Description"); the full list is Not public | S7 |
| Approval filter | approval= only works when the approval workflow is enabled on the tenant | S3 |
| Forward compatibility | Canto may add response keys and give meaning to currently-undocumented parameters — avoid strict response validation | S14 |
| Access-token wording | General notes say every request "must contain an access_token parameter", while all examples use the Bearer header | S14 Unverified (query-parameter form untested) |
SDKs, tools & code samples
| Library / tool | Language | Maintainer | Status | |
|---|---|---|---|---|
| Official SDK | — | — | None published (no Canto package on npm/PyPI/NuGet; Canto states it does not provide developer resources for custom integrations) | S108 S20 |
| Universal Connector | JavaScript | Canto | Sample asset picker (OAuth login incl. SSO, smart-album and folder/album filters, global search), zip download in the Help Center; basis of Canto's Salesforce connectors | S62 |
| Canto WordPress plugin | PHP | Canto (flightbycanto) | v3.1.4, updated 2026-09-16 on WordPress.org; old GitHub mirror last pushed 2019 | S99 S100 |
jleehr/canto-saas-api | PHP | Community (continuation of an agency client) | Active: last push 2026-09-22, Packagist 3.0.1 | S101 |
TYPO3-Canto/canto-api | PHP | Community | Last push 2025-06-04 | S102 |
flownative/neos-canto | PHP | Flownative | Neos asset source; last push 2025-06-24 | S103 |
lsst-epo/canto-dam-assets | PHP | Rubin Observatory EPO | Craft CMS field/plugin; last push 2026-02-11 | S104 |
firstspirit-canto-integration-dap | Java | e-Spirit user group | FirstSpirit CMS integration; last push 2026-09-04 | S105 |
spicy-labs/canto-media-connector | TypeScript | Community | CHILI GraFx media connector; last push 2025-02-27 | S106 |
| Postman collection | — | Canto | 137 items incl. saved example responses (public Documenter) | S14 |
Changelog of the API
| Date | Change | |
|---|---|---|
| 2026-09-07 | Native Alt-Text field for images, readable via the API (key not documented) | S76 |
| 2026-08-24 | Assets API spec 1.0.1 → 1.0.2 (documentation-only changes) | S13 |
| 2026-08-03 | New docs portal api.canto.technology publishes 11 OpenAPI 3.0.1 specs (all v1.0.0) | S13 |
| 2026-07 | Product Hub MCP server (/api/v1/pim/mcp) released | S77 |
| 2025-09-09 | Product Hub (PIM) OpenAPI v1.0.1 published on SwaggerHub (Canto/Canto-PIM-Alt2) | S15 |
| 2022-03-08 | Current Postman documentation (api.canto.com) first published | S14 |
| Unknown | Legacy /oauth/api/oauth2/token endpoint deprecated in favour of /oauth2/compatible/token | S2 |
| File | Format | Size | Download |
|---|---|---|---|
canto_openapi_authorization.yaml | OpenAPI | 14 KB | Download |
canto_openapi_dam-assets.yaml | OpenAPI | 52 KB | Download |
canto_openapi_dam-download.yaml | OpenAPI | 25 KB | Download |
canto_openapi_dam-export.yaml | OpenAPI | 7.6 KB | Download |
canto_openapi_dam-fields.yaml | OpenAPI | 5.5 KB | Download |
canto_openapi_dam-librarytree.yaml | OpenAPI | 27 KB | Download |
canto_openapi_dam-reports.yaml | OpenAPI | 22 KB | Download |
canto_openapi_dam-terms.yaml | OpenAPI | 5.0 KB | Download |
canto_openapi_dam-upload.yaml | OpenAPI | 22 KB | Download |
canto_openapi_dam-user.yaml | OpenAPI | 22 KB | Download |
canto_openapi_pim-api.yaml | OpenAPI | 174 KB | Download |
canto_postman_collection.json | Postman collection | 1.5 MB | Download |
canto-producthub-pim-openapi-v1.0.1.json | OpenAPI | 158 KB | Download |