# Create an avatar

URL: https://www.atmee.ai/docs/v2/api/create-avatar

> Two ways to create an avatar with the API: send a JSON manifest that points at your files, or upload everything in one zip. Either builds Atmee V2 or Atmee V1.

Everything you can do in Studio's Create flow is also an API call. An avatar made through the API
shows up in Studio and starts sessions with `POST /v1/sessions` like any other.

There are two ways to create one. Both build either [model](/docs/v2/getting-started/models) and end
the same way.

|                | [With JSON](#create-with-json)                  | [With a zip](#create-with-a-zip)                        |
| -------------- | ----------------------------------------------- | ------------------------------------------------------- |
| Your files     | Public https links, or uploads you send first   | Inside the zip, next to the manifest                    |
| One request is | A JSON manifest                                 | A zip, as a multipart `file` or a raw `application/zip` |
| Best for       | Apps and agents, files already online, Atmee V2 | Files on disk, many avatars at once                     |

## Before you start

* **A key** with `avatars:write`, account-wide: an avatar-scoped key can't create avatars. Add
  `generation:write` to [generate](#no-portrait-or-voice-yet) a portrait or a voice. See
  [API keys](/docs/v2/api/api-keys).
* **A portrait:** jpg, png or webp, up to 10 MB. See
  [Avatar 101: Negative examples](/docs/v2/guides/negative-examples) for the photos that animate badly.
* **A voice sample:** 10 to 60 seconds of one person speaking clearly, up to 12 MB: mp3, wav, m4a,
  aac, ogg, flac, opus or webm. An Atmee V2 avatar's voice is cloned from it.
* **Permission:** if the portrait or the voice is a real person other than you, have that
  person's permission first.
* **The model:** set `engine` to `v2` or `v1` on every create. Without it the API builds Atmee V1.
  Atmee V2 is invite only for now (`403 v2_access_required` without access).

## Create with JSON

Send a manifest that points at your files. Atmee downloads them, checks them and starts the
build.

### Get your files to Atmee

If the portrait and the voice sample are at public https links, skip to the next step.

Otherwise, upload each one first. Start an upload, `PUT` the file to the `uploadUrl` it returns,
with its `uploadHeaders`:

```bash
curl -X POST https://api.atmanity.us/v1/uploads \
  -H "X-Api-Key: $ATMEE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "kind": "portrait" }'

curl -X PUT "<uploadUrl>" -H "Content-Type: image/jpeg" --data-binary @portrait.jpg
```

`kind` is `portrait`, `voice` or `knowledge`. Poll
[`GET /v1/uploads/{uploadId}`](/docs/v2/api/reference/avatars/getUpload) until `status` is `uploaded`,
then use `upload:<uploadId>` wherever the manifest takes a link. An upload works once and expires
an hour after you start it; a create that fails leaves it usable for a retry.

### Send the manifest

```bash
curl -X POST https://api.atmanity.us/v1/avatars \
  -H "X-Api-Key: $ATMEE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "schemaVersion": 1,
    "name": "Val",
    "engine": "v2",
    "language": "en",
    "visibility": "private",
    "previewMessage": "Hi! I am Val. What can I help you find today?",
    "assets": {
      "image": { "url": "https://cdn.example.com/val/portrait.jpg" },
      "voice": { "url": "upload:6f1c2d34-8b7a-4e19-9c5d-0a2b3c4d5e6f" }
    }
  }'
```

```json
{
  "avatarId": "56cc8fdd-ba78-4d48-a210-16cf80c3f5f5",
  "status": "building",
  "statusUrl": "/v1/avatars/56cc8fdd-ba78-4d48-a210-16cf80c3f5f5"
}
```

A `202` means the build has started. Keep the `avatarId`. Every field is in
[Manifest fields](#manifest-fields).

### Wait for the build

Poll the avatar every few seconds until it settles:

```bash
curl https://api.atmanity.us/v1/avatars/56cc8fdd-ba78-4d48-a210-16cf80c3f5f5 \
  -H "X-Api-Key: $ATMEE_API_KEY"
```

An Atmee V1 build usually finishes in under a minute; an Atmee V2 build takes a few minutes, longer
when the renderer is starting cold. What each `status` means is in [Build status](#build-status).

### Pick a take (Atmee V2)

An Atmee V2 avatar is captured as one or more **takes**: short talking clips of it saying the
preview message. By default the first take to finish goes live. To compare, list them and watch
each `previewUrl`:

```bash
curl https://api.atmanity.us/v1/avatars/56cc8fdd-ba78-4d48-a210-16cf80c3f5f5/candidates \
  -H "X-Api-Key: $ATMEE_API_KEY"
```

Capture more, or pick another, at any time. See [Takes and versions](#takes-and-versions).

### Start a conversation

Once `readyToChat` is `true`, start a session with the `avatarId`, as in the
[Quickstart](/docs/v2/api/quickstart).

## Create with a zip

Put the manifest and the files in one zip, and send it in one request.

### Lay out the zip

```
my-avatar.zip
├── avatar.json          # the manifest (required)
├── image/portrait.jpg   # exactly one image
├── voice/sample.wav     # exactly one sample; leave out for a render-only avatar
├── prompt/prompt.txt    # optional: what the avatar does with the visitor, as text
└── knowledge/           # optional: up to 20 files (pdf, docx, doc, txt, md, html, csv), 50 MB each
```

### Write avatar.json

The manifest takes the same [fields](#manifest-fields) as the JSON create, without `assets`, plus
`knowledge.descriptions` for the files in `knowledge/`:

```json
{
  "schemaVersion": 1,
  "name": "Grand Valley Concierge",
  "engine": "v1",
  "language": "en",
  "visibility": "private",
  "persona": {
    "preferredAlias": "Val",
    "whoPrompt": "A warm, endlessly patient concierge who knows the resort inside out.",
    "greeting": "Hi, I'm Val. How can I help?",
    "guardrails": ["Never quote prices that aren't in the knowledge base"]
  },
  "knowledge": { "descriptions": { "pricing.pdf": "2026 rate card" } }
}
```

`prompt/prompt.txt` and `persona.whatPrompt` say the same thing, so give only one of them.

### Upload it

```bash
curl -X POST https://api.atmanity.us/v1/avatars \
  -H "X-Api-Key: $ATMEE_API_KEY" \
  -F "file=@my-avatar.zip"
```

Or send the zip itself as the body, with `Content-Type: application/zip`. A `202` with
`status: building` and the `avatarId` means the build has started.

### Wait for the build

Poll `GET /v1/avatars/{avatarId}` as in the JSON path until it settles. See
[Build status](#build-status). For an Atmee V2 avatar, [pick a take](#takes-and-versions) the same
way.

**Render-only avatars.** A zip without a voice, or a multipart request with only an image (`file`)
and a `name`, makes a render-only avatar: `201`, ready at once, for use with your own voice agent
in [Avatar mode](/docs/v2/livekit-plugin/livekit-agents-plugin). Add a voice later with
[`PUT /v1/avatars/{avatarId}/voice`](/docs/v2/api/reference/avatars/replaceAvatarVoice) to make it talk
on its own.

## Manifest fields

| Field                                                  | Notes                                                                                                                                                                              |
| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schemaVersion`                                        | Required. `1`.                                                                                                                                                                     |
| `name`                                                 | Required. 1 to 100 characters, the name in Studio.                                                                                                                                 |
| `engine`                                               | `v2` or `v1`. Without it the API builds `v1`, so always send it.                                                                                                                   |
| `language`                                             | The avatar's language: `en`, `zh-TW`, `zh-CN`, `ja`, `ko`, `fr`, `vi`, `es`, `de`, `ru`, or `hi` (accepted, spoken in English for now).                                            |
| `visibility`                                           | `private` (default) or `unlisted`. Make it `public` with `PATCH` once it is ready.                                                                                                 |
| `assets.image.url`, `assets.voice.url`                 | JSON only. The portrait and the voice sample: a public https link or `upload:<uploadId>`.                                                                                          |
| `assets.prompt.url`, `assets.knowledge`                | JSON only. The prompt text, and the knowledge files as a list of `{ "url", "description" }`.                                                                                       |
| `previewMessage`                                       | The line the previews speak. On Atmee V2 every take speaks it, up to 60 characters (`400 preview_message_too_long`). Leave it out for a default sentence in the avatar's language. |
| `seedPreviews.count`                                   | Atmee V2. How many takes to capture now, 1 to 10. Default 1.                                                                                                                       |
| `seedSelection`                                        | Atmee V2. `auto` (default): the first finished take goes live. `manual`: the avatar waits as `awaiting_selection` until you pick one.                                              |
| `resolution`                                           | Atmee V2. The frame size, square 512 × 512 by default; portrait or landscape where your account offers it.                                                                         |
| `persona.whoPrompt`                                    | Who the avatar is. Up to 10,000 characters.                                                                                                                                        |
| `persona.whatPrompt`                                   | What it does with the visitor. Up to 10,000 characters. In a zip, this or `prompt/prompt.txt`.                                                                                     |
| `persona.greeting`                                     | Its opening line, up to 500 characters.                                                                                                                                            |
| `persona.selfIntroduction`                             | How it introduces itself, 51 to 2,000 characters. Written from `whoPrompt` when left out.                                                                                          |
| `persona.guardrails`                                   | Up to 20 rules, 500 characters each, used word for word.                                                                                                                           |
| `persona.preferredAlias`, `spokenName`, `knownAliases` | The name it goes by, how to say it, and other names people use.                                                                                                                    |
| `persona.conversationalHabits`                         | Studio's style sliders, each `CONVERSATIONAL_HABIT_SCALE_LOW`, `_MEDIUM` or `_HIGH`.                                                                                               |
| `knowledge.facts`                                      | What it knows, as text, up to 32,000 characters. Used by Atmee V1 avatars; Atmee V2 conversations don't use knowledge yet.                                                         |
| `knowledge.descriptions`                               | Zip only. A short description for each file in `knowledge/`.                                                                                                                       |
| `previews`                                             | `true` to also render idle and talking preview clips, off by default.                                                                                                              |

`personality` and `scenario` still work as older names of `whoPrompt` and `whatPrompt`; a field given
under both names is `400 invalid_manifest`.

**Start with only a name.** The portrait and the voice are optional on both paths. An avatar
created without them is a **draft** (`status: draft`, with `missingInputs` naming what it lacks).
Add them with [`PUT /appearance`](/docs/v2/api/reference/avatars/replaceAvatarAppearance) and
[`PUT /voice`](/docs/v2/api/reference/avatars/replaceAvatarVoice), in either order, and the build
carries on. An Atmee V2 draft captures one take, picked automatically.

## Build status

| `status`             | Means                                                                                                                      |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `building`           | Still working. Keep polling.                                                                                               |
| `ready`              | Start a session once `readyToChat` is `true`.                                                                              |
| `awaiting_selection` | Atmee V2 with `seedSelection: manual`: [pick a take](#takes-and-versions).                                                 |
| `draft`              | Created without its portrait or voice; `missingInputs` says which.                                                         |
| `failed`             | `latestFailure.userFeedback` says why, in words you can show your user.                                                    |
| `moderation_hold`    | Held for review. Visitors can't open it, but your account can still talk to it while `readyToChat` is `true`. Don't retry. |
| `stalled`            | No progress for a long time; `stall.reason` says what to replace. Waiting longer won't help.                               |

The answer also has `stages`: `appearance`, `voice`, `persona`, `knowledge` and `previews`, each
moving through `waiting`, `pending`, `processing` and `completed`, `skipped` when it doesn't
apply (such as `previews` when you didn't ask for them), or `failed` with a
`userFeedback` message. A clearly bad portrait fails `appearance` with a `portrait_*` reason
(`portrait_no_face`, `portrait_face_too_small`, `portrait_mouth_closed`); replace the portrait to
fix it.

If a create fails on the network, list your avatars before you send it again: a repeated create
makes a second avatar.

## Takes and versions

Atmee V2 only. Lip sync and motion vary from take to take, and the inputs don't tell you which will
be best, so watch them.

* **List** with [`GET /v1/avatars/{avatarId}/candidates`](/docs/v2/api/reference/avatars/listAvatarCandidates).
  Each item has a `status` and a `previewUrl` (an mp4, valid about an hour), and `serving` marks
  the take the avatar talks with.
* **Capture more** with
  [`POST /v1/avatars/{avatarId}/candidates`](/docs/v2/api/reference/avatars/createAvatarCandidates)
  and `{ "count": 3 }`. New takes come from the current portrait, voice and preview message, and
  never change the serving take. An account can have at most 10 captures queued or running
  (`429 rate_limited` beyond that).
* **Pick one** with
  [`PUT /v1/avatars/{avatarId}/selected_candidate`](/docs/v2/api/reference/avatars/selectAvatarCandidate)
  and its `candidateId`. Only a completed take can be picked; it serves a few seconds later.

A **version** is one set of capture inputs: the portrait, the voice sample and the preview message.
Changing any of them starts a new version with new takes, and the old take serves until a new one
goes live. Picking a take of an older version goes back to that version's inputs, with nothing
captured again. [`DELETE /v1/avatars/{avatarId}/versions/{versionId}`](/docs/v2/api/reference/avatars/deleteAvatarVersion)
removes an older version; the current one, or one holding the serving take, can't be deleted
(`version_in_use`).

## Update it afterwards

* **The character and the text fields:**
  [`PATCH /v1/avatars/{avatarId}`](/docs/v2/api/reference/avatars/updateAvatar) with only the fields to
  change, such as `persona`, `knowledge.facts`, `name`, `language`, `visibility` or
  `previewMessage`. Send an empty value (`""`, `[]`, `{}`) to clear one. Every persona edit is
  moderated again.
* **The portrait or the voice:**
  [`PUT /appearance`](/docs/v2/api/reference/avatars/replaceAvatarAppearance) or
  [`PUT /voice`](/docs/v2/api/reference/avatars/replaceAvatarVoice). Only the stages that depend on
  it run again. On Atmee V2 this starts a new version.
* **The knowledge files:**
  [`PUT /knowledge`](/docs/v2/api/reference/avatars/replaceAvatarKnowledge) replaces the whole set.
* **An agenda** (Atmee V2): [`PUT /conductor`](/docs/v2/api/reference/avatars/setAvatarConductor).
* **Delete it:** [`DELETE /v1/avatars/{avatarId}`](/docs/v2/api/reference/avatars/deleteAvatar).

## No portrait or voice yet?

Generate them. Each call needs a key with `generation:write` and `avatars:write`, and an
`Idempotency-Key` header with a new UUID, so a retry never charges twice.

| Call                                                                                    | Send                                                                           | Get back                                     |
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------- |
| [`POST /v1/generation/portraits`](/docs/v2/api/reference/generation/generatePortraits)  | A `description` of an invented character                                       | Up to four portraits                         |
| [`POST /v1/generation/enhancements`](/docs/v2/api/reference/generation/enhancePortrait) | Your photo as `image`, and an optional `prompt` to restyle it                  | Up to four portraits that keep the person    |
| [`POST /v1/generation/voices`](/docs/v2/api/reference/generation/designVoice)           | A `voiceDescription`, and optionally `language`, `avatarName` and `sampleText` | Voice previews, each with the text it speaks |

Each output comes back as `upload:<uploadId>`: pass it straight to `assets.image.url`,
`assets.voice.url`, or a `/appearance` or `/voice` replace. You're charged only when at least one
output comes back. Never generate a real person's likeness from a description; for a real person,
enhance their own photo, with their permission.

## Errors

A create that can't start answers `400` with an `error` and a message naming the problem:

| `error`                                                      | Means                                                                  |
| ------------------------------------------------------------ | ---------------------------------------------------------------------- |
| `invalid_zip`, `invalid_manifest`                            | The zip or the manifest is malformed.                                  |
| `invalid_url`                                                | A link is not https, carries credentials, or points at a private host. |
| `fetch_failed`                                               | A link couldn't be downloaded.                                         |
| `upload_not_found`, `upload_kind_mismatch`, `invalid_upload` | An `upload:` value is unknown, of the wrong kind, or not uploaded yet. |
| `preview_message_too_long`                                   | An Atmee V2 `previewMessage` over 60 characters.                       |
| `public_visibility_not_allowed_on_create`                    | Create it `private` or `unlisted`, then make it public.                |

The full list is in the [API reference](/docs/v2/api/reference/avatars/createAvatar).

- [Quickstart](/docs/v2/api/quickstart): Start a session with the avatar you just created.
- [Avatar 101: Negative examples](/docs/v2/guides/negative-examples): The portraits that animate badly, and what to use instead.
