Reference · Characters API

Characters API

Members see a character and whether it can be used today. Owners and admins change characters, and only they see the people depicted and the terms of their consent. See Characters for the model and the gate.

From nothing to a run

a likeness avatar, end to endhttp
# 1. The avatar, in a project
POST /v1/characters
{ "projectId": "…", "name": "Dr. Maya", "format": "likeness",
  "persona": { "summary": "Warm, plain-spoken dentist" }, "requestId": "maya-0001" }

# 2. Its references (artifacts you already have)
POST /v1/characters/{characterId}/references
{ "role": "portrait", "artifactId": "artifact-9f1e2d3c" }

# 3. The person it depicts (owners and admins)
POST /v1/workspaces/{workspaceId}/subjects
{ "displayName": "Maya Patel", "email": "[email protected]",
  "attestations": { "adult": true, "notPolitical": true } }

# 4. Freeze version 1
POST /v1/characters/{characterId}/versions
{ "subjectId": "…", "note": "first look" }

# 5. Their permission: recorded pending, then activated
POST /v1/subjects/{subjectId}/consents
{ "grantor": "self", "coversLikeness": true, "coversVoice": false,
  "scope": { "uses": ["organic"], "media": ["image", "video"],
             "channels": ["instagram", "web"], "territories": ["US"] },
  "startsOn": "2026-10-01", "endsOn": "2027-10-01",
  "evidenceKind": "signed_document", "evidenceSha256": "9b74c9897bac770ffc029102a200c5de…" }
POST /v1/likeness-consents/{consentId}:activate

# 6. Use it
POST /v1/runs
{ "templateId": "…", "workspaceId": "…", "intake": { "topic": "…", "characterId": "char-7f3a91c2" } }

A synthetic avatar skips steps 3 and 5. Every write that changes a character returns its new ETag; edits need the etag you last read, and creates take a requestId.

Characters

GET/v1/charactersA workspace’s characters (workspaceId) or one project’s (projectId). includeArchived to see archived ones.
POST/v1/charactersCreate in a project: name, format (synthetic | likeness), persona. Owners and admins.
GET/v1/characters/{characterId}The character, its references, its latest version and whether it is usable today.
PATCH/v1/characters/{characterId}Rename or edit the persona draft. Needs the etag. Versions are untouched.
DELETE/v1/characters/{characterId}Archive. Nothing it made is affected.
POST/v1/characters/{characterId}/referencesAdd a reference to the draft: role (portrait, turnaround, expression, outfit, pose, voice_sample, other) and an artifactId from this workspace.
DELETE/v1/characters/{characterId}/references/{itemId}Remove a reference from the draft. Frozen versions keep it.

Versions and the gate

POST/v1/characters/{characterId}/versionsFreeze the draft as the next version. A likeness names its subjectId once; later versions keep the same person.
GET/v1/characters/{characterId}/versionsEvery version, newest first, with its provider copies.
POST/v1/characters/{characterId}/versions/{number}:retireNo new work on this version. What it made stays valid.
POST/v1/characters/{characterId}:checkThe gate for a planned use: media, and optionally use, channel, territory, advertiser, provider, a date or a version.

A run that can’t use its character is refused before anything is priced. The same reasons come back from :check, and a run whose consent is withdrawn while it waits fails with them before it starts.

a refused runjson
422 Unprocessable Entity
{
  "detail": {
    "code": "character_not_usable",
    "reasons": ["consent_revoked"],
    "message": "The person withdrew their permission."
  }
}

People and consent

GET/v1/workspaces/{workspaceId}/subjectsThe people your characters depict. includeErased to see erased ones.
POST/v1/workspaces/{workspaceId}/subjectsAdd a person. attestations.adult and attestations.notPolitical must both be true.
GET/v1/subjects/{subjectId}A person and every consent they gave.
PATCH/v1/subjects/{subjectId}Correct their name, email, public role or union status. Needs the etag.
POST/v1/subjects/{subjectId}:eraseErase: blank what identifies them, revoke every consent, retire every version that depicts them.
POST/v1/subjects/{subjectId}/consentsRecord a consent. It starts pending and allows nothing until activated.
POST/v1/likeness-consents/{consentId}:activatePending → active.
POST/v1/likeness-consents/{consentId}:suspendActive → suspended (for example, during a strike).
POST/v1/likeness-consents/{consentId}:resumeSuspended → active.
POST/v1/likeness-consents/{consentId}:revokeWithdraw, finally. Never blocked by a stale etag; revoking twice is fine.
GET/v1/likeness-consents/{consentId}/eventsIts history, newest first.

Provider copies

POST/v1/characters/{characterId}/versions/{number}/bindingsRecord a provider’s copy of a version: provider, assetKind (likeness | voice), its asset id, and the provider’s consent status.
PATCH/v1/character-bindings/{bindingId}Update the provider’s consent status, or disable the copy.

When a use names a provider, the gate needs both consents: yours (active and covering the use) and the provider’s (accepted, or not_required for photo-based assets).

Errors

StatusCodeWhen
400character_batch_unsupportedAn order featuring a character asked for batch mode.
403admin_requiredA member tried to change something.
409etag_mismatchChanged since you read it; the body has the current state.
409invalid_transitionFor example, resuming a revoked consent.
409project_has_charactersDeleting a project that has avatars. Archive it instead.
422character_not_usableA run or order the gate refused; reasons say why.
422no_references · subject_required · subject_changedFreezing a version that isn’t ready.
428etag_requiredAn edit without an etag.
What the database refuses
Editing a frozen version or an active consent’s terms, rewriting consent history, a subject who isn’t attested an adult and not a political figure, a consent longer than 10 years, and any link between workspaces. These hold even for requests that never pass through this API.