Skip to navigation

Assets

Upload image bytes once, reference them from jobs by id, and delete them when you are done.

An Asset is the API’s handle for an uploaded input image or a generated result image. You create input Assets by uploading; the service creates result Assets when a job succeeds. Product requests refer to Asset ids, so an image is uploaded once and can be used by more than one request while it remains available.

Assets do not make an image public. Every call requires a valid API key, and an Asset belongs to the environment (live or test) whose key created it. Keep the key on your server. Do not expose Asset ids or content paths to a public client unless your own application checks that user’s access before returning them.

Upload

Send the image bytes as the whole request body, with the matching Content-Type. No multipart, no JSON wrapper, no base64. The SDKs do this in assets.upload.

curl -sS -X POST "$BASE_URL/v1/assets" \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: image/jpeg" \
--data-binary @face.jpg
{
"id": "asset_6pa8ad0ds2t7g1z6pg4tcr4zqp",
"purpose": "input",
"media_type": "image/jpeg",
"size_bytes": 482113,
"width": 1200,
"height": 1600,
"created_at": "2026-09-24T10:00:00Z",
"expires_at": "2026-09-25T10:00:00Z"
}

The answer is 201 with Location: /v1/assets/{asset_id}.

  • Accepted types are image/jpeg, image/png and image/webp. Another Content-Type, bytes that are not a recognised image, or bytes of a different type than declared are 415 UNSUPPORTED_MEDIA_TYPE. A recognised image that cannot be used — over the dimension or pixel limit, or undecodable — is 422 UNPROCESSABLE_IMAGE.
  • Too large is 413 PAYLOAD_TOO_LARGE, and it is checked before your key is: an oversized body answers 413 even with no valid key.
  • Metadata is removed. EXIF, XMP and IPTC are stripped and the pixels are rotated to their displayed orientation before the asset is stored, so width and height are what a viewer shows.

Hair Color Try-On needs one input Asset, the source image; the API finds the hair itself. A same-size grayscale hair mask from your application is an optional second Asset that replaces the API’s own segmentation. It does not create a hairstyle.

Uploads are not deduplicated

POST /v1/assets takes no Idempotency-Key. Uploading the same bytes twice creates two assets. That is deliberate: a retried upload after a timeout can leave an unreferenced asset behind, which costs nothing and expires on its own. Deduplicate at the submit, where the key is required, not at the upload.

Reading an asset

GET /v1/assets/{asset_id} returns the metadata above for any asset in your environment. An id that is not yours is 404 ASSET_NOT_FOUND, whether or not it exists elsewhere.

GET /v1/assets/{asset_id}/content returns the bytes of a result asset; the SDKs call it assets.getContent and assets.get_content. An input asset answers 404 ASSET_CONTENT_UNAVAILABLE: you already have its bytes. The bytes come from the API itself; the response is never a redirect to object storage, so your X-API-Key is the only credential involved.

Listing assets

GET /v1/assets returns your readable Assets, newest first, one page at a time. Filter by purpose (input or result), job_id, or a created_gte / created_lt window, and follow next_cursor for the next page. Listing is enabled per environment.

const page = await client.assets.list({ purpose: "input", limit: 50 });
for (const asset of page.data) console.log(asset.id, asset.expiresAt);
for await (const asset of client.assets.listAll({ purpose: "result", maxItems: 500 })) {
console.log(asset.id);
}

Deleting an asset

DELETE /v1/assets/{asset_id} deletes one of your input Assets and erases its bytes; it answers 204. A job already submitted from it keeps its own copy until that job is deleted or expires. A result Asset is deleted with its job, so deleting one directly is 409 ASSET_OWNED_BY_JOB: delete the job instead (Jobs). Deletion is enabled per environment.

await client.assets.delete("asset_6pa8ad0ds2t7g1z6pg4tcr4zqp");

Expiry

Every asset carries expires_at. Treat it as the last moment you may rely on the asset: fetch what you need before then, and store the bytes yourself if you need them longer. A submit that names an expired asset answers 410 ASSET_EXPIRED; upload again. Result Assets expire with their Job.

Keep image handling inside your retention policy

Face images and generated results can identify a person. Tell people how your application uses them, limit copies to what your product needs, and delete what you no longer need. Read Privacy and retention before storing inputs or outputs. For Job output shapes, see Job outputs.