API
esc

Type to search.

Exporting and syncing

Read every product with every asset we hold for it, one row per SKU. Ask for what changed since your last run, or freeze a selection into a file.

The export is the read a PIM or DAM sync wants. It is deliberately not a file list: relations are the payload, so your side never parses filenames to work out which product a render belongs to.

The whole sync loop
for row in pages:
product = upsert_by_reference(row.product)
for asset in row.assets:
upsert_asset(product, asset.id, asset.url)

Read the catalogue

GET/v1/exports

Cursor-paginated products, newest first, each with every asset we hold for it.

200 OK (one row)
{
"product": {
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"reference": "TEE-SAGE-001",
"name": "Crewneck Tee — Sage",
"sex": "female",
"deleted_at": null
},
"assets": [
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXYS",
"role": "source",
"url": "https://…signed…",
"url_expires_at": "2026-09-03T15:32:00Z",
"mime_type": "image/jpeg",
"width": 2000, "height": 2667,
"ai_generated": false,
"watermarked": false,
"derived": false,
"created_at": "2026-09-01T09:12:44Z",
"task_id": null,
"attributes": { "orientation": "front" },
"also_features": [],
"suggested_filename": "TEE-SAGE-001_front.jpg"
},
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXZ9",
"role": "tryon",
"url": "https://…signed…",
"url_expires_at": "2026-09-03T15:32:00Z",
"mime_type": "image/webp",
"width": 1536, "height": 2048,
"ai_generated": true,
"watermarked": false,
"derived": false,
"created_at": "2026-09-02T16:40:03Z",
"task_id": "01HX5K2MZ7A3Q4FBNDC0EVDXY2",
"attributes": { "angle_shot": "full_body_relaxed_symmetric", "background": "park", "model_id": "01HX5K2MZ7A3Q4FBNDC0EVDXMD1" },
"also_features": ["01HX5K2MZ7A3Q4FBNDC0EVDXPN"],
"suggested_filename": "TEE-SAGE-001_tryon_park.webp"
}
]
}

What an asset tells you

Field Use it for
role What the asset is: source (your photo), beautifier, tryon, recoloring. Edits keep the role and set derived: true. New roles may appear; treat unknown values as opaque.
id An image id. One expired URL is re-fetched with GET /v1/images/{id}, never by re-running the export.
url, url_expires_at A signed URL to the active version of the image, the one the dashboard shows. Valid for one hour.
ai_generated false only for your own photos. For EU AI Act labelling.
watermarked The bytes carry a watermark. This can change for a stable id when your plan changes.
task_id The task that produced it, for GET /v1/tasks/{id}. Null for your photos and for generations made in the dashboard.
attributes Flat, open facts per role: orientation, angle_shot, background, model_id, edit_number. Ignore keys you do not know.
also_features Other products visible in this asset (tryon stylings).
suggested_filename A filename, never a path, built from your organization’s export naming template with the real extension of the bytes.

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. Whether it also goes on the pants page is your merchandising call.

Filters

  • role=beautifier,tryon limits assets to those roles. Products are still listed when nothing matches; their assets is then empty.
  • limit (1 to 100, default 25) and cursor page through, exactly like GET /v1/products.

Only what changed

Add since=<timestamp> and the same endpoint returns only the products that changed at or after that instant: a field edited, a photo attached, a generation or edit completed, or the product deleted.

Terminal window
curl "https://api.veeton.com/v1/exports?since=2026-09-02T00:00:00Z" \
-H "Authorization: Bearer $VEETON_KEY"

Each returned row is the product’s full current state, so upsert it whole; you never merge partial deltas. A deleted product appears with product.deleted_at set and an empty assets array. Remove it on your side.

Use the created_at of your previous run, minus a safety margin, as the next since. A nightly job is then a delta, not a re-export.

Freeze a selection into a file

POST/v1/exports

The same rows as a stable artifact: a JSONL manifest (one row 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.

Terminal window
curl https://api.veeton.com/v1/exports \
-H "Authorization: Bearer $VEETON_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"format":"zip","product_ids":["01HX5K2MZ7A3Q4FBNDC0EVDXY1"],"role":["beautifier","tryon"]}'
  • 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: manifest (default) is written immediately; the response is already completed with a signed url.
  • format: zip is rendered asynchronously. Poll GET /v1/exports/{id}, which carries Retry-After while processing, or subscribe to export.completed and export.failed.
202 Accepted
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXEX",
"format": "zip",
"status": "processing",
"url": null,
"url_expires_at": null,
"total_assets": 148,
"error": null,
"created_at": "2026-09-03T14:30:09Z",
"finished_at": null
}

Asset URLs inside a manifest are valid one hour after it was written; the asset ids are permanent. The artifact URL on the job is re-signed on every read, so cache the file, not the URL.