API reference
Images
Every image we hold: your photos, uploaded as bytes, and the renders made from them. Addressed by id, typed by content, and every URL you get back is a short-lived signed one. The list is the catalog read a PIM or DAM sync wants.
The Image object
Attributes
idstringStable image id;GET /v1/images/{id}re-signs it.mime_typeenumimage/jpegimage/pngimage/webp
widthinteger, nullablePixel width, null when not yet known.heightinteger, nullableurlstring (URL)Time-limited signed URL to the bytes. Fetch a fresh one fromGET /v1/images/{id}when it expires.url_expires_attimestampWhenurlstops working.created_attimestampUpload time for your photos, task completion for generations.
The Image object
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXYS",
"mime_type": "image/jpeg",
"width": 1,
"height": 1,
"url": "https://example.com/…",
"url_expires_at": "2026-04-27T14:32:00Z",
"created_at": "2026-04-27T14:32:00Z"
}The ImageListItem object
An image as GET /v1/images lists it: the Image object plus what it is and where it belongs. Also the line format of a JSONL export manifest.
Attributes
idstringStable image id;GET /v1/images/{id}re-signs it.mime_typeenumimage/jpegimage/pngimage/webp
widthinteger, nullablePixel width, null when not yet known.heightinteger, nullableurlstring (URL)Time-limited signed URL to the bytes. Fetch a fresh one fromGET /v1/images/{id}when it expires.url_expires_attimestampWhenurlstops working.created_attimestampUpload time for your photos, task completion for generations.product_idstringThe product this image belongs to — the one it was made for. A tryon styled with other products lists those inalso_features.roleenumsourcebeautifiertryonrecoloring
ai_generatedbooleanFalse only for your own photos (role: source). Provided for EU AI Act labelling obligations.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, and recoloring: the view it was made from),angle_shot,background,model_id(tryon),hex_colorand, for a several-colour result,hex_colors(recoloring),edit_number(edits). Ignore keys you do not know; new ones appear without a version bump.edited_from_image_idstring, nullableFor an edit made throughPOST /v1/tasks/edit: the image it was made from. Null otherwise. Every version is listed as its own image, so an image nothing was edited from is the end of a chain.archivedbooleanTrue when this result, or the result this edit was made from, is archived in the dashboard. Archived results are left out of ZIP exports.also_featuresarray of stringsOther products visible in this image (a tryon's outfit). The image 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 ImageListItem object
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXYS",
"mime_type": "image/jpeg",
"width": 1,
"height": 1,
"url": "https://example.com/…",
"url_expires_at": "2026-04-27T14:32:00Z",
"created_at": "2026-04-27T14:32:00Z",
"product_id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"role": "source",
"ai_generated": true,
"task_id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"attributes": {
"orientation": "front"
},
"edited_from_image_id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"archived": true,
"also_features": [
"01HX5K2MZ7A3Q4FBNDC0EVDXY1"
],
"suggested_filename": "TEE-SAGE-001_front_cleaned.webp"
}Upload an image
POST/v1/images
Single-shot upload via multipart/form-data. The bytes flow through this endpoint to Supabase Storage, and the image is only reported as created once they are on disk — so the returned id is immediately referenceable from POST /v1/products. If any step fails the upload is rolled back whole; a partially-stored image is never returned.
This is the single-product path, paired with POST /v1/products. For a catalogue, skip the upload entirely: POST /v1/imports takes each photo as an https:// URL and fetches it for you.
Limits: body must be ≤ 10 MB. Accepted formats: JPEG, PNG, WebP — detected from the file's bytes; the multipart Content-Type you declare is ignored.
Form fields:
file(required): the image bytes.
Example:
curl -X POST https://api.veeton.com/v1/images \
-H "Authorization: Bearer ak_..." \
-F "file=@./crewneck-front.jpg"
Parameters
filestringrequiredThe image bytes, as amultipart/form-datafield. JPEG, PNG or WebP, up to 10 MB; typed from the bytes.
Returns
Returns a Image object with status 201.
Errors
400Invalid request
413Payload too large
415Unsupported media type
500Server error
POST/v1/images
curl https://api.veeton.com/v1/images \
-H "Authorization: Bearer $VEETON_KEY" \
-F "file=@./photo.jpg"Response · 201
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXYS",
"mime_type": "image/jpeg",
"width": 1,
"height": 1,
"url": "https://example.com/…",
"url_expires_at": "2026-04-27T14:32:00Z",
"created_at": "2026-04-27T14:32:00Z"
}List images
GET/v1/images
Every image we hold for your products — your own photos, beautifier renders, tryons and their edits — each with a signed URL, what it is (role, attributes) and where it belongs (product_id, also_features). This is the read a PIM or DAM sync wants; suggested_filename and ai_generated are there for filing and labelling.
Each image 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.
Pages are cut on product boundaries: a page holds every image of up to limit products, newest product first, so the count of images per page varies. Narrow with product_id or role; since= returns only the images of products that changed (pair it with GET /v1/products?since= for deletions).
Every version made through the API is its own image with its own id: a task's output, and each edit, which names its source in edited_from_image_id. A crop or retouch made in the dashboard is not listed. To freeze a selection into a file instead of paging, use POST /v1/exports.
Parameters
limitinteger· queryoptionalcursorstring· queryoptionalproduct_idstring· queryoptionalOnly the images of this product.rolestring· queryoptionalComma-separatedImageRolevalues.sincetimestamp· queryoptionalDelta mode: only the images of products that changed at or after this instant (a photo attached or removed, a generation or edit completed, a result archived or deleted, a field edited). Each product comes back whole, so replace its images with these rows; a product left with no images has none here. Pair withGET /v1/products?since=for deletions.
Returns
Returns a ImageList object with status 200.
Errors
400Invalid cursor or role
500Server error
GET/v1/images
curl https://api.veeton.com/v1/images \
-H "Authorization: Bearer $VEETON_KEY"Response · 200
{
"data": [
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXYS",
"mime_type": "image/jpeg",
"width": 1,
"height": 1,
"url": "https://example.com/…",
"url_expires_at": "2026-04-27T14:32:00Z",
"created_at": "2026-04-27T14:32:00Z",
"product_id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"role": "source",
"ai_generated": true,
"task_id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"attributes": {
"orientation": "front"
},
"edited_from_image_id": "01HX5K2MZ7A3Q4FBNDC0EVDXY1",
"archived": true,
"also_features": [
"01HX5K2MZ7A3Q4FBNDC0EVDXY1"
],
"suggested_filename": "TEE-SAGE-001_front_cleaned.webp"
}
],
"next_cursor": "eyJpZCI6InByb2RfMDFIWCJ9",
"has_more": true
}Retrieve an image
GET/v1/images/{image_id}
Returns the image metadata and a short-lived signed URL pointing at the original bytes. Useful for re-fetching expired URLs or inspecting reference images attached to a Product. The URL TTL is one hour — re-fetch on expiry, do not cache.
Parameters
image_idstringrequiredCrockford-base32 ULID (26 chars).
Returns
Returns a Image object with status 200.
Errors
404Not found
500Server error
GET/v1/images/{image_id}
curl https://api.veeton.com/v1/images/01HX5K2MZ7A3Q4FBNDC0EVDXY1 \
-H "Authorization: Bearer $VEETON_KEY"Response · 200
{
"id": "01HX5K2MZ7A3Q4FBNDC0EVDXYS",
"mime_type": "image/jpeg",
"width": 1,
"height": 1,
"url": "https://example.com/…",
"url_expires_at": "2026-04-27T14:32:00Z",
"created_at": "2026-04-27T14:32:00Z"
}