API reference
Exports
The catalogue read out for a PIM or DAM: one row per product with every asset we hold for it, signed URLs included. Read it live, ask for what changed, or freeze a selection into a file.
The ExportRow object
One product and every asset we hold for it. for product in rows: upsert product; for asset in assets: upsert asset is the whole PIM sync loop.
Attributes
productobjectShow child attributesHide child attributes
idstringCrockford-base32 ULID (26 chars).referencestringnamestringsexenum, nullablemalefemaleotherkid
deleted_attimestamp, nullableSet only in delta mode (since=), for a product deleted since then. Remove it on your side; itsassetsis empty.
assetsarray of objectsEvery asset we hold for the product, one entry each. Empty when nothing matches therolefilter, or when the product is deleted.Show child attributesHide child attributes
idstringImage id — the same idGET /v1/images/{id}accepts, so one expired URL can be re-fetched without re-running the export.roleenumsourcebeautifiertryonrecoloring
urlstring (URL)Time-limited signed URL to the bytes we hold for this asset (the active version, watermarked when your plan applies one).url_expires_attimestampRFC 3339 / ISO 8601 timestamp.mime_typeenumimage/jpegimage/pngimage/webp
widthinteger, nullableheightinteger, nullableai_generatedbooleanFalse only for your own photos. Provided for EU AI Act labelling obligations.watermarkedbooleanThe bytes behindurlcarry a watermark. This can change for a stableidwhen your plan changes; cache accordingly.derivedbooleanThe asset is an edit of a generation rather than the generation itself.created_attimestampWhen the bytes were produced (task completion for generations, upload time for your photos).task_idstring, nullableThe task that produced it, forGET /v1/tasks/{id}. Null for your photos and for generations made in the dashboard.attributesmapPer-role facts, flat and open:orientation(source),angle_shot,background,model_id(tryon),edit_number(derived). Ignore keys you do not know; new ones appear without a version bump.also_featuresarray of stringsOther products visible in this asset (tryon stylings). The asset is listed once, under the product it was made for; fan it out to these pages if your merchandising wants it there too.suggested_filenamestringA filename (never a path) built from your export naming template, with the real extension of the bytes.
The ExportRow object
{
"product": {
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"reference": "TEE-SAGE-001",
"name": "Crewneck Tee — Sage",
"sex": "male",
"deleted_at": "2026-04-27T14:32:00Z"
},
"assets": [
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"role": "source",
"url": "https://example.com/…",
"url_expires_at": "2026-04-27T14:32:00Z",
"mime_type": "image/jpeg",
"width": 1,
"height": 1,
"ai_generated": true,
"watermarked": true,
"derived": true,
"created_at": "2026-04-27T14:32:00Z",
"task_id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"attributes": {
"orientation": "front"
},
"also_features": [
"01HX5K2MZ7A3Q4FBNDC0EVDXY1"
],
"suggested_filename": "TEE-SAGE-001_front_cleaned.webp"
}
]
}The ExportJob object
Attributes
idstringCrockford-base32 ULID (26 chars).formatenummanifestzip
statusenumprocessingcompletedfailed
urlstring (URL), nullableSigned URL to the artifact oncecompleted(the JSONL manifest or the ZIP). Re-signed on every read; do not cache the URL, cache the file.url_expires_attimestamp, nullableRFC 3339 / ISO 8601 timestamp.total_assetsintegerAssets in the selection at creation time.errorobject, nullableShow child attributesHide child attributes
codestringmessagestring
created_attimestampRFC 3339 / ISO 8601 timestamp.finished_attimestamp, nullableRFC 3339 / ISO 8601 timestamp.
The ExportJob object
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXEX",
"format": "manifest",
"status": "processing",
"url": "https://example.com/…",
"url_expires_at": "2026-04-27T14:32:00Z",
"total_assets": 1,
"error": {
"code": "string",
"message": "string"
},
"created_at": "2026-04-27T14:32:00Z",
"finished_at": "2026-04-27T14:32:00Z"
}Create an export job
POST/v1/exports
Same rows as GET /v1/exports, as a stable artifact: a JSONL manifest (one ExportRow per line) or a ZIP of the image files rendered by the same export pipeline the dashboard uses, with your organization's naming template and per-workflow formats.
Select up to 500 products with product_ids, or omit it for the whole catalogue visible to the API (same cap). Narrow with role.
format: manifestis written immediately: the response is alreadycompletedwith a signedurl.format: zipis rendered asynchronously: pollGET /v1/exports/{id}(it carriesRetry-After) or subscribe toexport.completed/export.failed.
Asset URLs inside a manifest are valid one hour after it was written; the asset ids are permanent — re-fetch bytes with GET /v1/images/{id}. Send Idempotency-Key to make retries return the same job.
Parameters
formatenumoptionalmanifestzip
product_idsarray of stringsoptionalFreeze these products. Omit for the whole catalogue visible to the API (at most 500 products per job; page throughGET /v1/exportsfor more).rolearray of enumsoptionalOnly assets of these roles.
Returns
Returns a ExportJob object with status 202.
Errors
400Invalid selection
404One or more product_ids not found
500Server error
502Export pipeline unavailable
POST/v1/exports
curl https://api.veeton.com/v1/exports \
-H "Authorization: Bearer $VEETON_KEY" \
-H "Content-Type: application/json" \
-d '{
"format": "manifest",
"product_ids": [
"01HX5K2MZ7A3Q4FBNDC0EVDXY1"
],
"role": [
"source"
]
}'Response · 202
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXEX",
"format": "manifest",
"status": "processing",
"url": "https://example.com/…",
"url_expires_at": "2026-04-27T14:32:00Z",
"total_assets": 1,
"error": {
"code": "string",
"message": "string"
},
"created_at": "2026-04-27T14:32:00Z",
"finished_at": "2026-04-27T14:32:00Z"
}Export the catalogue
GET/v1/exports
The read a PIM or DAM sync wants: cursor-paginated products, each with every asset we hold for it — your own photos, beautifier renders, tryons and their edits — with a signed URL, the facts needed to file it (role, attributes, suggested_filename) and the facts needed to trust it (ai_generated, watermarked, task_id).
for row in pages:
product = upsert_by_reference(row.product)
for asset in row.assets: upsert_asset(product, asset.id, asset.url)
Each asset appears exactly once, under the product it was made for. A tryon of a top styled with pants lives on the top; the pants are listed in also_features, and whether it also goes on the pants page is your merchandising call, not our data model.
asset.id is an image id: a single expired URL is re-fetched with GET /v1/images/{id}, never by re-running the export. URLs are valid for one hour.
Products are those visible to the API (created through it). Assets are the active version of each image — the one the dashboard shows — so a crop or retouch made in the app is what you get. To freeze a selection into a file instead of paging, use POST /v1/exports.
Parameters
limitinteger· queryoptionalcursorstring· queryoptionalrolestring· queryoptionalComma-separatedExportAssetRolevalues. Products are still listed when none of their assets match; theirassetsis then empty.sincetimestamp· queryoptionalDelta mode: only products that changed at or after this instant — a field edited, a photo attached, a generation or edit completed, or the product deleted. Each returned row is the product's full current state, so upsert it whole. Deleted products appear withproduct.deleted_atset and emptyassets. Use thecreated_atof your previous run (minus a safety margin) as the nextsince.
Returns
Returns a ExportPage object with status 200.
Errors
400Invalid cursor or role
500Server error
GET/v1/exports
curl https://api.veeton.com/v1/exports \
-H "Authorization: Bearer $VEETON_KEY"Response · 200
{
"data": [
{
"product": {
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"reference": "TEE-SAGE-001",
"name": "Crewneck Tee — Sage",
"sex": "male",
"deleted_at": "2026-04-27T14:32:00Z"
},
"assets": [
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"role": "source",
"url": "https://example.com/…",
"url_expires_at": "2026-04-27T14:32:00Z",
"mime_type": "image/jpeg",
"width": 1,
"height": 1,
"ai_generated": true,
"watermarked": true,
"derived": true,
"created_at": "2026-04-27T14:32:00Z",
"task_id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"attributes": {
"orientation": "front"
},
"also_features": [],
"suggested_filename": "TEE-SAGE-001_front_cleaned.webp"
}
]
}
],
"next_cursor": "eyJpZCI6InByb2RfMDFIWCJ9",
"has_more": true
}Retrieve an export job
GET/v1/exports/{export_id}
Status and, once completed, a signed URL to the artifact (valid one hour, re-signed on every read). While processing the response carries Retry-After.
Parameters
export_idstringrequiredCrockford-base32 ULID (26 chars).
Returns
Returns a ExportJob object with status 200.
Errors
404Not found
GET/v1/exports/{export_id}
curl https://api.veeton.com/v1/exports/01HX5K2MZ7A3Q4FBNDC0EVDXY1 \
-H "Authorization: Bearer $VEETON_KEY"Response · 200
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXEX",
"format": "manifest",
"status": "processing",
"url": "https://example.com/…",
"url_expires_at": "2026-04-27T14:32:00Z",
"total_assets": 1,
"error": {
"code": "string",
"message": "string"
},
"created_at": "2026-04-27T14:32:00Z",
"finished_at": "2026-04-27T14:32:00Z"
}