Create an avatar

Upload a zip with a manifest, an image and a voice sample, and get back a fully built, ready-to-chat avatar.

Everything a person can do in Studio → Create avatar — upload a face, a voice sample, write a persona, attach knowledge files — is also one API call. The avatar that comes back is identical to one built in the UI: it shows up in Manage and can be used with POST /v1/session the same way.

Create a key with the right scope

Avatar management needs a key with the avatars:write scope; the default sessions scope is not enough. The key must be account-wide — an avatar-scoped key can update the one avatar it is scoped to, but never create or delete. See API keys.

Build the zip

snippet
my-avatar.zip
├── avatar.json          # manifest (required)
├── image/portrait.jpg   # exactly one image (jpg/png/webp ≤10 MB)
├── voice/sample.wav     # exactly one sample (mp3/wav/m4a/aac/ogg/flac/opus/webm, 10–60 s, ≤12 MB)
├── prompt/prompt.txt    # the scenario, free text (≤10,000 chars) -- or use persona.scenario
└── knowledge/           # optional, ≤20 files: pdf/docx/doc/txt/md/html/csv ≤50 MB each

The image should show the whole head with some margin around it; a face that fills the frame edge to edge fails the preview stage. The voice sample is checked server-side for clean, audible speech — not silence or a pure tone.

avatar.json:

snippet
{
  "schemaVersion": 1,
  "name": "Grand Valley Concierge",
  "description": "Friendly resort concierge",
  "language": "en",
  "visibility": "private",
  "persona": {
    "preferredAlias": "Val",
    "personality": "A warm, endlessly patient concierge who knows the resort inside out and keeps every answer practical.",
    "greeting": "Hi, I'm Val — how can I help?",
    "selfIntroduction": "I'm Val, the concierge for Grand Valley resort...",
    "guardrails": ["Never quote prices that aren't in the knowledge base"],
    "conversationalHabits": {
      "CONVERSATIONAL_HABIT_ENGAGEMENT": "CONVERSATIONAL_HABIT_SCALE_MEDIUM"
    }
  },
  "knowledge": { "descriptions": { "pricing.pdf": "2026 rate card" } },
  "previewMessage": "Hello!"
}
FieldRequiredNotes
nameyesThe project name shown in Studio (1–100 chars).
visibilitynoprivate or unlisted at create time. public requires a PATCH once the avatar is ready.
languagenoThe avatar's language: en, zh-TW or ja are honoured end to end today (other codes are accepted but not yet spoken).
previewsnotrue to also render the idle and talking preview clips (off by default for API avatars; toggle later with PATCH). Clips may be billed per plan; they are free on the standard plans today.
persona.preferredAliasnoThe name the avatar goes by in conversation.
persona.personalitynoThe main character description — who the avatar is. Up to 10,000 chars.
persona.scenarionoWhat the avatar is doing with the visitor. JSON alternative to prompt/prompt.txt — providing both is rejected.
persona.selfIntroductionno51–2000 chars, used verbatim; derived from personality when absent.
persona.greetingnoThe avatar's opening line, ≤500 chars.
persona.guardrailsnoUp to 20 entries, ≤500 chars each, used verbatim.
knowledge.descriptionsnoMaps a filename in knowledge/ to a short description. Up to 20 files.

Or point at URLs instead of zipping

If your files already live on a public host, skip the zip: send the same manifest as application/json with an assets block. The service downloads the files (https only, public hosts, same size and type limits as the zip folders) and everything after that is identical.

snippet
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": "Grand Valley Concierge",
    "language": "en",
    "persona": { "personality": "A warm, endlessly patient concierge." },
    "assets": {
      "image": { "url": "https://cdn.example.com/val/portrait.jpg" },
      "voice": { "url": "https://cdn.example.com/val/sample.wav" },
      "prompt": { "url": "https://cdn.example.com/val/scenario.txt" },
      "knowledge": [{ "url": "https://cdn.example.com/val/pricing.pdf", "description": "2026 rate card" }]
    }
  }'

assets.prompt is the scenario text (what prompt/prompt.txt is in the zip) and is mutually exclusive with persona.scenario. A URL that is not https, carries credentials or points at a private host answers 400 invalid_url; one that cannot be downloaded answers 400 fetch_failed.

Upload it

snippet
curl -X POST "https://api.atmanity.us/v1/avatars" \
  -H "X-Api-Key: $ATMEE_API_KEY" \
  -F "file=@my-avatar.zip"
snippet
{
  "avatarId": "56cc8fdd-ba78-4d48-a210-16cf80c3f5f5",
  "status": "building",
  "statusUrl": "/v1/avatars/56cc8fdd-ba78-4d48-a210-16cf80c3f5f5"
}

A 202 means the build has started. A malformed zip or manifest answers 400 with an error of invalid_zip or invalid_manifest and a message naming the problem; the full list of codes is in the Reference.

Use https://staging.atmanity.us with a staging key while you develop — keys are not interchangeable between environments, same as /v1/session.

Poll until it's ready

snippet
curl "https://api.atmanity.us/v1/avatars/56cc8fdd-ba78-4d48-a210-16cf80c3f5f5" \
  -H "X-Api-Key: $ATMEE_API_KEY"
snippet
{
  "id": "56cc8fdd-ba78-4d48-a210-16cf80c3f5f5",
  "name": "Grand Valley Concierge",
  "status": "ready",
  "readyToChat": true,
  "moderationState": "clean",
  "visibility": "private",
  "stages": {
    "appearance": { "status": "completed" },
    "voice": { "status": "completed" },
    "persona": { "status": "completed" },
    "knowledge": { "status": "completed" },
    "previews": { "status": "skipped" }
  }
}

status is buildingready (or failed / moderation_hold). Each stage under stages is independently waitingpendingprocessingcompleted (or failed, with a userFeedback message you can show verbatim). previews reads skipped unless the manifest set previews: true; with previews on, the finished clips appear as pre-signed previewUrls (idle and talking mp4, valid about an hour) on this response. A clean build typically finishes in under a minute; poll every few seconds rather than long-polling.

Once readyToChat is true, start a session with POST /v1/session using the id as avatarId, exactly as in the Quickstart.

Update it afterwards

Text changes — name, description, visibility, language, preview message and every persona field — go through PATCH /v1/avatars/{avatarId}. A new image, voice sample or knowledge set is a PUT to /appearance, /voice or /knowledge; only the affected build stages re-run, and the avatar answers building again until they finish. DELETE /v1/avatars/{avatarId} removes it.

Next steps

Create an avatar — Atmee Docs