Skip to main content
POST
Create a character (upload OR synthesize flow) — async
POST /v1/characters creates a character — a reusable identity asset you can attach to an image or video generation via character_id (see Create an image or Create a video). There are two mutually exclusive flows:
  • Upload flow: pass upload_ids (1–6) collected from POST /v1/characters/uploads/init. Those photos are the INPUT; the platform renders the character’s eight named views from them. The response returns once the request is validated and priced (typically a few seconds — allow at least 30 s of client timeout) with status: synthesizing and refs: [] — not a usable character yet. Poll GET /v1/characters/{id} (or subscribe to the character.completed webhook) until status: ready; the character advances there on its own, with no save step.
  • Synthesize flow: pass generate: true plus an attributes object describing who the character is. The platform renders the eight named views (head_front, upper_front, lower_front, upper_back, lower_back, full_left, full_front, full_right). The response returns once the request is validated and priced (typically a few seconds — allow at least 30 s of client timeout) with status: synthesizing and refs: []. Poll GET /v1/characters/{id} (or subscribe to the character.completed webhook) until status: ready; the character advances there on its own, with no save step.
Send upload_ids or generate: true — never both, never neither. Sending both or neither returns 400 parameter_invalid_combination.
There is a third way in. POST /v1/characters/drafts creates a free character in draft, you file your own photo into each view with PUT /v1/characters/{id}/refs/{view}, and POST /v1/characters/{id}/build renders only what is still missing — so you pay per rendered view instead of for all eight. This route stays the one-shot path: it charges for eight views up front and renders them immediately. See Views and lifecycle.
These changes ship on the existing 2026-08-26 contract. No Aurous-Version pin isolates them: the version catalogue carries image and video pricing pointers only, not character pricing or the size of the reference set. Pinning an earlier Aurous-Version restores neither the smaller reference set nor the previous price. A character created before 2026-09-14 keeps the references it already has — add either of the two newer views on demand with POST /v1/characters/{id}/refs/{view}/regenerate; every character created on or after that date carries eight.

Upload flow

Use this when you already have ref images. Mint one upload_id per file via POST /v1/characters/uploads/init, PUT the bytes, then create the character.

Synthesize flow

Use this when you want the platform to generate the refs from a description. The attributes object is locked at v1.0 to 7 typed fields plus a free-text additional_details catch-all; on this synthesize flow every field drives generation (on the upload flow only additional_details is used, and only as a best-effort hint — see Upload flow). Synthesize burns credits at create time — eight renders, priced at build.per_view_credits each — so read build.per_view_credits off any character rather than hard-coding a price.

Nudity

nudity on create decides what the whole reference set is rendered as — nude or clothed. Omit it and the character takes the team default: nude unless your team’s default is clothed. On teams that always render clothed, an explicit nudity: "nude" returns 400 nudity_not_allowed. The choice is fixed once the references are built. It is echoed back as nudity on every character read, and a per-view regenerate re-renders at the character’s own nudity — to change it, create a new character.

Status transitions

A character created here never passes through draft — that status belongs to the builder. And a one-shot character is not builder lineage: its source photos are still upload tickets, so POST /v1/characters/{id}/build refuses it with 400 character_status_invalid and points at resynthesize instead. Neither flow stops at reviewing, so POST /v1/characters/{id}/save is not a step in either one. It exists for a character left in reviewing by Resynthesize or by a build without auto_save: true.

Limits

  • Rate limit: bucket characters_synthesize — 15 requests/min sustained, 30 burst per team. Both flows ride this bucket since the synthesize discriminator is decided server-side after the request lands. Shared across POST /v1/characters, POST /v1/characters/{id}/refs/regenerate, POST /v1/characters/{id}/refs/{view}/regenerate, POST /v1/characters/{id}/resynthesize and POST /v1/characters/{id}/build — five routes, one bucket.
  • Idempotency: pass Idempotency-Key (any opaque value, 1–256 chars). Same key + same body within 24h replays the cached response with Aurous-Idempotent-Replayed: true. Same key + different body returns 409 idempotency_key_in_use. See Idempotency for details.
Always send Idempotency-Key on create — either flow dispatches eight paid renders, so a network retry without a key can double-charge.

Response fields added in this release

Five fields are additive — nothing was removed or renamed, and every existing field keeps its meaning. They appear on every character response (create, retrieve, list, and the action endpoints). See List character views for the view vocabulary and the local pre-check constraints.

Errors

Common pitfalls

  • Both flows return 201 with status: "synthesizing", but the character is not usable until it reaches ready, which both flows do on their own — there is no save step on create. Calling POST /v1/images or POST /v1/videos with a synthesizing character_id returns 400 character_not_ready.
  • Upload tickets are not consumed by a create. A ticket stays usable until its expires_at; every create that lists it is a separate character and a separate charge. If you retry a create, either reuse the ticket deliberately or guard the retry with an Idempotency-Key.
  • An upload flow with a single ref still works. On the upload flow your reference images define identity, so the 7 typed attributes (gender, age, etc.) are stored and echoed back but do not shape the generated refs — only additional_details is applied there, and only as a best-effort hint (your reference images are the primary signal). The synthesize flow (generate: true) is the opposite: all attributes drive generation, so don’t send empty attributes there.
  • The attributes schema is locked for v1.0; new attributes go into additional_details until a v1.1 bump introduces them as typed fields.

Authorizations

X-Api-Key
string
header
required

Your team API key (starts with al_live_).

Headers

Idempotency-Key
string

Stripe-style idempotency key (1-256 chars). Same key + same canonical-JSON body returns the cached response with Aurous-Idempotent-Replayed: true. Same key against a different route (e.g. previously used on /v1/images) returns 409 invalid_request / idempotency_key_in_use. Replay window is 24 hours. Absent header is treated as non-idempotent (each call processes anew).

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
name
string
required

Display name for the character (1-80 chars).

Required string length: 1 - 80
Example:

"Aurora the Adventurer"

generate
boolean

Selects the synthesize flow when true (the platform renders the eight named views head_front, upper_front, lower_front, upper_back, lower_back, full_left, full_front, full_right from attributes). When omitted/false, upload_ids must be provided to attach customer-supplied refs. Mutually exclusive with upload_ids.

Example:

false

upload_ids
string[]

Upload tickets from POST /v1/characters/uploads/init (1-6). When set, the upload flow is used and the server moves bytes from upload storage to character storage on create (uploads are consumed). Mutually exclusive with generate: true.

Required array length: 1 - 6 elements
Example:
attributes
object

Character attributes. Required when generate: true (synthesize flow), where ALL fields drive generation. Optional on the upload flow, where the 7 typed fields (gender, age, ethnicity, hair_color, hair_style, eye_color, body_type) are stored and echoed back but do NOT shape the generated refs — your uploaded images define identity. additional_details is the exception: it is also applied on the upload flow as a free-text styling hint, but treat it as best-effort — your uploaded reference images are the primary signal, so supply a reference image for any trait you need to guarantee.

nudity
enum<string>

What the reference set is rendered as. An explicit value always wins; omit it for your team's default, which is nude unless your team's default is clothed. On teams that always render clothed an explicit nude returns 400 nudity_not_allowed. Fixed once the references are built.

Available options:
nude,
clothed
Example:

"clothed"

client_reference_id
string

Optional caller-supplied reference, echoed back on GET /v1/characters/{id} and in every character webhook for this character. Use it to correlate the webhook with the originating record in your system (Stripe-style). Immutable after create. Max 256 chars.

Maximum string length: 256
Example:

"bot_8472"

Response

Character accepted; synthesis dispatched (status: synthesizing, refs: []).

id
string
required

Opaque character ID.

Example:

"char_01HXMQ7Z3K8Y2NABCDEFGHJKMR"

object
enum<string>
required

Discriminator

Available options:
character
Example:

"character"

name
string
required

Display name.

Example:

"Aurora the Adventurer"

status
enum<string>
required

Lifecycle state. draft: references are being assembled; nothing has been charged and the character cannot be used for generation. New lifecycle states may be added in future — treat any status other than ready as "not yet usable". synthesizing: references are being built. ready: usable on POST /v1/images — both create flows advance here on their own. reviewing: reached only after POST /:id/resynthesize or POST /:id/build; call POST /:id/save to return to ready. failed: the build failed; error_message carries the reason and POST /:id/build (builder characters) or POST /:id/resynthesize retries. deleted: soft-deleted (filtered out of the list endpoint).

Available options:
draft,
synthesizing,
reviewing,
ready,
failed,
deleted
Example:

"ready"

nudity
enum<string>
required

What the reference set is rendered as — nude unless you chose clothed, or — when you omitted nudity — your team's default is clothed. An explicit value always wins. Fixed once the references are built.

Available options:
nude,
clothed
Example:

"clothed"

refs
object[]
required

Reference images, one per view the character has. A character built today has eight; an older one can have fewer than eight (typically six, some of the oldest four). Do not assume a count — read views[], the canonical read model for new integrations, or build.missing_views. refs[] lists only the references that exist.

views
object[]
required

One entry per named view, in canonical order — every reference that maps to a named view, keyed by view, plus what is missing or was rejected. A reference from before named views existed (refs[].view is other) appears only in refs[].

build
object
required

What a build would render and cost right now. Always present.

created_at
string
required

Creation timestamp (ISO 8601).

Example:

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

updated_at
string
required

Last-update timestamp (ISO 8601).

Example:

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

client_reference_id
string | null

Caller-supplied reference echoed back (the value sent on create). Null if unset.

Example:

"bot_8472"

attributes
object | null

Character attributes. Null when unset.

error_message
string | null

Machine-readable failure code from the most recent synthesize/resynthesize attempt. One of synthesis_failed, synthesis_timeout, provider_unavailable — a closed set to switch on, not customer-facing prose. Null when the most recent attempt succeeded (or none has run yet). Set on failed, but NOT failed-exclusive: a failed resynthesize restores the character to its prior working status (reviewing) rather than overwriting a good generation, so a reviewing row can carry a non-null code here — check this field for failure, not status. Cleared on the next resynthesize attempt and on success.

Example:

null

aurous_version
string

API contract version applied at the time this row was minted (D25 — frozen for replay across future version bumps).

Example:

"2026-08-26"