Create an avatar (Avatar Import API)
Three body shapes, one result.
An image. multipart/form-data with the portrait (jpg, png or webp,
<= 10 MB) as the file part and the avatar name as a second field
(description optional). Creates a render-only avatar: no voice, no
persona, nothing to build -- the response is 201 with status: ready
and the avatar can be rendered for an external agent at once. Add a voice
later with PUT /v1/avatars/{avatarId}/voice to make it conversational.
A zip. avatar.json plus image/, voice/, prompt/prompt.txt,
knowledge/, as the file part or a raw application/zip body. voice/ is
optional; without it the result is render-only as above. With a voice the
avatar is created exactly as the studio would: the row, the files under
<account>/<avatar>/, and one configs.latest write; the creator-studio
pipeline builds it asynchronously (202, status: building), poll
GET /v1/avatars/{avatarId}.
The manifest's persona block carries the character: whoPrompt (who the
avatar is: the main character description), whatPrompt (what the avatar is
doing with the visitor; JSON alternative to prompt/prompt.txt -- providing
both is rejected), preferredAlias, spokenName, knownAliases, greeting,
selfIntroduction (derived from whoPrompt when absent), guardrails,
conversationalHabits. personality and scenario are older names of
whoPrompt and whatPrompt and still accepted; a field given under both
names is 400 invalid_manifest.
JSON. The manifest as application/json with an assets block naming
the image, the voice sample, prompt text and knowledge files by https URL;
the service downloads them (public hosts only, same size and type rules as
the zip) and continues identically.
A draft. The image and the voice are both optional on the zip and JSON
bodies (and assets itself on JSON), on either engine. An avatar created
without its portrait, or without its voice (other than a v1 avatar with a
portrait, which is render-only), is a draft: status: draft with
missingInputs naming what it lacks. Its persona and knowledge build;
nothing that needs the missing input does. Give them with
PUT /v1/avatars/{avatarId}/appearance and PUT /v1/avatars/{avatarId}/voice,
in either order; the build continues once both are there (on v2, the input
that completes it captures the first candidate). seedPreviews and
seedSelection need both inputs at create: a v2 draft takes one candidate,
selected automatically.
Where assets takes a URL for the image, the voice sample or a knowledge
file, upload:<uploadId> names a file sent with POST /v1/uploads instead
(the prompt stays a URL). The upload must be this account's, of the matching
kind (portrait, voice, knowledge) and uploaded; its bytes decide its
type, and the same size and type rules apply. A create that succeeds
consumes its uploads; one that fails leaves them usable for a retry.
The portrait (every body shape) decides whether the avatar can be
animated. Use a front-facing photo of one person, head and shoulders with
some margin around the head, the face at least about a third of the frame,
in even light, with the lips slightly parted as if about to speak. A v2
avatar animates its first moments from the portrait itself, so a closed
mouth usually makes the first second of speech fail (the lips stay still);
lips slightly parted work far more often. The build refuses a clearly bad
portrait at the appearance stage: stages.appearance and latestFailure
carry a portrait_* reason in userFeedback (portrait_no_face,
portrait_face_too_small, portrait_mouth_closed, with a message you can
show verbatim). Replace it with PUT /v1/avatars/{avatarId}/appearance.
Preview clips are not generated unless the manifest sets previews: true
(toggle later with PATCH); stages.previews then reads skipped.
Requires the avatars:write scope, plus billing:write when the manifest has a
sponsor block. Behind the avatar_import_api feature flag.
engine is v1 (the default) or v2. A v2 avatar's voice
is cloned from its sample; a v2 create with the portrait and the voice
captures seedPreviews.count candidates (default 1, at most 10). seedSelection is auto (the default:
the first completed candidate is promoted) or manual. Both are v2 only.
A portrait, voice or preview message changed outside the API (Creator
Studio) captures one candidate of the new inputs by itself.
Captures count against the account's limit of 10 queued or running; a v2
create answers with X-RateLimit-Limit and X-RateLimit-Remaining, and
with 429 rate_limited (and Retry-After) when the captures do not fit.
previewMessage is the line the preview clips speak; omit it for a default
sentence in the avatar's language. On v2 it is the ~6 s prime clip every
candidate is captured from, so at most 60 Unicode code points
(400 preview_message_too_long).
knowledge.descriptions maps a knowledge file name to a short description.
knowledge.facts is the avatar's knowledge as text, at most 32,000
characters (400 invalid_manifest otherwise), on either engine: it is saved
as Creator Studio saves its "Facts with context" box (shown there) and as a
knowledge file the knowledge base reads; change it with PATCH.
knowledge.questionnaire answers Creator Studio's knowledge questionnaire
(question to answer, the questions of GET /v1/avatars/{avatarId}/knowledge;
each answer non-blank, at most 2,000 characters), saved as Studio saves it;
change it with PATCH. Knowledge
(files and facts) is used in conversations by v1 avatars; v2 conversations
do not use it yet.
blackoutRecovery tunes how v2 sessions recover from frames that
fade to black; it is on by default, so omit it unless a setting
should differ. It is ignored for v1 avatars (the setting exists only
on the v2 renderer).
runtimeSeed (v2) is the seed of a session's motion; omit it to keep
the renderer default, 42. Ignored for v1 avatars, like
blackoutRecovery.
resolution (v2) is the frame size the avatar renders at, e.g.
{"width": 384, "height": 672} for portrait; default 512 x 512 (see
RenderResolution). Unlike blackoutRecovery it is refused for v1
avatars (400 invalid_input).
X-Api-Key<token>Secret embed API key (sk_atmee_...; keys minted before 2026-09
carry sk_live_...), held by the customer's own backend and never
sent to a browser. Format: X-Api-Key: sk_atmee_....
- body
avatar.json plus an assets block. Field rules match the zip
manifest and are enforced by the service (400 invalid_manifest,
naming the field); only the shape is checked here.
schemaVersion*11name*string1 <= length <= 100description?stringlanguage?stringvisibility?string"private""unlisted"previews?booleanpreviewMessage?stringengine?string"v1""v2"seedPreviews?seedSelection?string"auto""manual"blackoutRecovery?Blackout recovery of the v2 renderer. While the avatar listens, it screens each generated chunk for a fade to black (or fully black frames) and shows a healthy listening clip in place of a failed one, returning to live frames on the first healthy chunk. On by default. Send only the settings to change; an absent or null one keeps its current value (on create: its default). Ignored for v1 avatars (the setting exists only on the v2 renderer): nothing is stored and the rest of the request behaves as without it. It changes how sessions render, never the avatar's build: no candidate is captured or cancelled. A change applies from the next session.
runtimeSeed?RuntimeSeedThe v2 renderer's runtime seed: it seeds a session's noise and motion after the first chunk, so another seed gives the same avatar different movement; the serving candidate stays the same. Default 42 (the renderer default; send 42 to go back to it). Absent or null keeps the current value (on create: the default). Ignored for v1 avatars: nothing is stored and the rest of the request behaves as without it. It is no build input: nothing is captured or rebuilt, and a change applies from the next session.
0 <= value <= 2147483647resolution?A frame size in pixels for the v2 renderer: width and height are each
a multiple of 32 from 256 to 1024, and width x height is at most
262,144 (512 x 512); a larger frame is 400 invalid_manifest on an
avatar, 400 invalid_input on a clip. Default 512 x 512 (square).
Common shapes: square 512 x 512, portrait 384 x 672, landscape
672 x 384. A deployment offers only some sizes (512 x 512 always;
GET /v1/account pricing.generation.clipResolutions lists them
where clips are offered): another is 400 invalid_input ("resolution
WxH is not offered; offered: ..."). On an avatar it is a build input of v2 avatars only
(400 invalid_input for a v1 avatar): the portrait is preprocessed
at that size, so a change on PATCH builds a new version with new
candidates, like a new portrait; sending the current size again
builds nothing. Absent or null keeps the current size (on create:
the default).
voice?persona?knowledge?sponsor?assets?The files of a JSON create, by URL. Same limits as the zip folders (image jpg/png/webp <=10 MB, voice 10-60 s <=12 MB, knowledge <=20 files). The image is the portrait; front-facing, face at least about a third of the frame, lips slightly parted (see the operation). With an image and no voice a v1 avatar is render-only; without the image (or a v2 one without the voice) it is a draft.
Created and ready; a render-only avatar (image without a voice) has nothing to build.
application/json- response
avatarId*stringuuidkind*AvatarKindconversational avatars have a voice and a persona and can run a full
Atmanity session once built. render_only avatars have a portrait only:
they are ready immediately and render video for an external agent, but
readyToChat stays false until a voice is added via PUT /voice.
"conversational""render_only"status*stringready for a render-only avatar (nothing to build), draft for one created without its portrait or voice, building otherwise.
"building""ready""draft"missingInputs?array<>What a draft was created without; absent otherwise.
statusUrl*stringengine?AvatarEngineRender engine. v1 is the original renderer. v2 is
built from a bundle plus a promoted candidate; see /candidates.
"v1""v2"sponsor?curl -X POST 'https://api.atmanity.us/v1/avatars' \ -H 'Content-Type: application/json' \ -d '{ "schemaVersion": 1, "name": "string"}'{ "avatarId": "ac572b6e-802b-4b9e-a602-0786d4441e67", "kind": "conversational", "status": "building", "missingInputs": [ "image" ], "statusUrl": "string", "engine": "v1", "sponsor": { "transferredMicrocredits": 0, "replayed": true, "error": "string" }}
