# Create an avatar (Avatar Import API)

URL: https://www.atmee.ai/docs/v2/api/reference/avatars/createAvatar

`POST /v1/avatars`

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`).

## Request body

- `application/json`: `AvatarCreateJsonRequest`
- `multipart/form-data`: object
- `application/zip`: string

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `schemaVersion` | integer (`1`) | yes |  |
| `name` | string | yes |  |
| `description` | string | no |  |
| `language` | string | no |  |
| `visibility` | string (`private`, `unlisted`) | no |  |
| `previews` | boolean | no |  |
| `previewMessage` | string | no |  |
| `engine` | string (`v1`, `v2`) | no |  |
| `seedPreviews` | object | no |  |
| `seedSelection` | string (`auto`, `manual`) | no |  |
| `blackoutRecovery` | `BlackoutRecovery` | no | 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` | `RuntimeSeed` | no | The 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. |
| `resolution` | `RenderResolution` | no | 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` | object | no |  |
| `persona` | object | no |  |
| `knowledge` | object | no |  |
| `sponsor` | object | no |  |
| `assets` | `AssetRefs` | no | 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. |

## Responses

| Status | Description | Schema |
| --- | --- | --- |
| `201` | Created and ready; a render-only avatar (image without a voice) has nothing to build. | `AvatarCreateResponse` |
| `202` | Accepted; the avatar exists and is building. | `AvatarCreateResponse` |
| `400` | invalid_zip \| invalid_manifest \| name_invalid \| public_visibility_not_allowed_on_create \| invalid_url \| fetch_failed \| invalid_upload (JSON create with asset urls) \| upload_not_found \| upload_kind_mismatch (JSON create with upload ids) \| preview_message_too_long (v2 previewMessage over 60 characters) | `ApiErrorResponse` |
| `401` | Missing or invalid API key -- or a user token that is invalid, anonymous, or of a user who may act for no account (invalid_login) | none |
| `403` | insufficient_scope \| avatar_not_allowed (avatar-scoped key) \| account_not_allowed (a user token whose X-Account-Id names an account the user may not act for) \| v2_access_required (engine v2 is invite-only and this account has not been let in yet) | `ApiErrorResponse` |
| `409` | avatar_limit_reached \| upload_not_ready \| upload_consumed \| upload_expired \| upload_in_use \| account_ambiguous (a user token of several accounts without X-Account-Id) | `ApiErrorResponse` |
| `413` | Bundle exceeds the size limit | none |
| `429` | rate_limited -- the captures do not fit the account's limit of queued or running captures. Where the database applies a v2 create or input change as a whole (the default once it is migrated), nothing the request asked for was applied (its uploads stay usable, the avatar keeps its inputs and its candidates), so the same request can be sent again once captures finish; on a database that does not yet, the uploads were used and the new input was saved without being built, and sending the request again builds it. | `RateLimitedResponse` |
| `503` | avatar_import_disabled \| upload_unavailable (an upload id on an instance without uploads) | `ApiErrorResponse` |

## `201` response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `avatarId` | string | yes |  |
| `kind` | `AvatarKind` | yes | `conversational` 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`. |
| `status` | string (`building`, `ready`, `draft`) | yes | `ready` for a render-only avatar (nothing to build), `draft` for one created without its portrait or voice, `building` otherwise. |
| `missingInputs` | array of string | no | What a `draft` was created without; absent otherwise. |
| `statusUrl` | string | yes |  |
| `engine` | `AvatarEngine` | no | Render engine. `v1` is the original renderer. `v2` is built from a bundle plus a promoted candidate; see `/candidates`. |
| `sponsor` | `SponsorGrantResponse` | no |  |

## `202` response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `avatarId` | string | yes |  |
| `kind` | `AvatarKind` | yes | `conversational` 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`. |
| `status` | string (`building`, `ready`, `draft`) | yes | `ready` for a render-only avatar (nothing to build), `draft` for one created without its portrait or voice, `building` otherwise. |
| `missingInputs` | array of string | no | What a `draft` was created without; absent otherwise. |
| `statusUrl` | string | yes |  |
| `engine` | `AvatarEngine` | no | Render engine. `v1` is the original renderer. `v2` is built from a bundle plus a promoted candidate; see `/candidates`. |
| `sponsor` | `SponsorGrantResponse` | no |  |
