Skip to main content
POST
Mint a signed upload URL for one character reference image
POST /v1/characters/uploads/init mints an upload ticket: a short-lived signed PUT URL plus an opaque upload_id. Upload your image bytes to that URL, then spend the ticket on one of two routes:
  • PUT /v1/characters/{id}/refs/{view} — the builder. The photo becomes the reference for one named view, used as-is, after a fit check. Pay only for the views you do not fill.
  • POST /v1/characters — the one-shot create. The photos are the input to synthesis; the platform renders all eight views from them and charges for all eight.
POST /v1/characters/uploads/classify can label a ticket’s pose first, but it is optional on either path. You typically call this endpoint once per reference image. A one-shot create accepts 1–6 photos; those photos are the input to synthesis, and the references the character will carry are the eight named views the platform renders from them — see List character views. Screen the file locally first. GET /v1/characters/views returns a constraints object — min_short_side_px, max_aspect_ratio, max_dimension_px, content_types — and those are exactly the values the builder enforces on PUT /v1/characters/{id}/refs/{view}. Checking them client-side turns a wasted mint-upload-reject round-trip into a local comparison.

When to use

  • You already have ref images on disk or in your own storage and want to attach them to a new character without round-tripping bytes through your API.
  • You are filling a draft’s views one at a time with the builder — mint one ticket per photo.
  • You want to capture pose metadata before committing the character (use upload-classify).
If instead you want the platform to generate refs from a text description, skip the upload flow and pass generate: true + attributes to POST /v1/characters (the synthesize flow).

Lifecycle

  1. POST /v1/characters/uploads/init → { upload_id, upload_url, expires_at }.
  2. PUT <upload_url> with the image bytes (any well-formed PUT, no extra headers required).
  3. Optional: POST /v1/characters/uploads/classify with the same upload_id to detect the pose.
  4. Spend it — either PUT /v1/characters/{id}/refs/{view} with { "upload_id": "upl_..." } to file the photo into one view of a character you already have, or POST /v1/characters with { upload_ids: [upl_..., ...] } to create a character from those photos in one shot.

Example

Limits

  • Rate limit: bucket characters_post — 30 requests/min sustained, 60 burst per team.
  • Upload URL TTL: 15 minutes — this is what expires_at in the response reports. Mint a fresh ticket if the PUT would land later.
  • Upload ticket TTL: 24 hours, and expires_at does not report it. After that the bytes are evicted and upload_id is no longer accepted on POST /v1/characters or PUT /v1/characters/{id}/refs/{view}.
  • Tickets are not consumed. Within its 24 hours a ticket stays usable by every route that takes one: classify keeps answering for it, every create that lists it produces a separate character and a separate charge, and a PUT that is refused leaves it fully intact — that is what lets you re-send a wrong_view photo to the view it actually shows without re-uploading the bytes. Guard create retries with an Idempotency-Key rather than relying on the ticket to be spent.

Common pitfalls

  • The upload_url is not authenticated — the signature is in the query string. Do not add X-Api-Key to the PUT.
  • One ticket = one image. Mint multiple tickets in parallel for multi-ref characters.
  • Browsers may need a CORS-aware proxy in front of the PUT — the V1 surface targets server-side integrators and does not advertise CORS headers on the upload host.

Authorizations

X-Api-Key
string
header
required

Your team API key (starts with al_live_).

Headers

Aurous-Version
string

Optional API version pin (YYYY-MM-DD). Omit the header to receive the platform default, currently 2026-08-26.

Pattern: ^\d{4}-\d{2}-\d{2}$
Example:

"2026-08-26"

Body

application/json
filename
string
required

Original filename (used for Content-Disposition; not stored as the storage key).

Required string length: 1 - 120
Example:

"front-portrait.jpg"

extension
enum<string>
required

File extension. Must match content_type; mismatched combinations return 400.

Available options:
jpeg,
jpg,
png,
webp
Example:

"jpg"

content_type
enum<string>
required

MIME type. Must match extension; mismatched combinations return 400.

Available options:
image/jpeg,
image/png,
image/webp
Example:

"image/jpeg"

Response

Upload ticket minted

upload_id
string
required

Upload ticket ID. Use as upload_id on POST /v1/characters/uploads/classify and POST /v1/characters.

Example:

"upl_01HXMQ7Z3K8Y2NABCDEFGHJKMR"

upload_url
string
required

Pre-signed PUT URL — upload bytes via HTTP PUT with the headers from upload_headers. The URL expires at expires_at.

Example:

"https://aurous.storage/.../upload?signature=…"

upload_headers
object
required

HTTP headers to include on the PUT request (Content-Type matches the request payload).

Example:
expires_at
string
required

Upload URL expiration (ISO 8601). Re-mint via /uploads/init if you need to retry past this point.

Example:

"2026-05-08T10:15:00Z"