Reference

Every endpoint of the Atmee API — sessions and avatars — with request and response fields and error codes.

Base URLs

EnvironmentBase URL
Productionhttps://api.atmanity.us
Staginghttps://staging.atmanity.us

Keys belong to one environment; a staging key is rejected in production and vice versa.

Authentication and scopes

Send your secret in the X-Api-Key header (sk_atmee_…; keys created before September 2026 start with sk_live_… and keep working). Requests are server-to-server only: browsers are blocked by CORS by design, so the header can never be sent from a page.

Each endpoint below names the scope it needs. A key without it answers 403 insufficient_scope. An avatar-scoped key can only address the avatar it is scoped to; any other avatarId answers 403 avatar_not_allowed (or a bare 403 on /v1/session).

Amounts are in microcredits: 1 credit = 100 microcredits.

Error shapes

StatusBody
400 403 404 409 503{ "error": "<code>", "message": "..." } — the error codes are listed per endpoint.
402{ "error": "<code>", "message": "...", "resetsAt": "<date-time>" }resetsAt only for windowed caps.
422{ "code": 422, "message": "..." } — the body failed schema validation; the message names the field.
401 413No body.

POST /v1/session

Scope sessions. Starts a session for a visitor of yours; responds only once the avatar has joined the room.

Request

FieldTypeNotes
avatarIdstring (uuid), requiredAn avatar your account owns; for a scoped key, the one it is scoped to.
maxDurationSecondsinteger 1–14400Shortens the session below the key/plan/balance limit. Never lengthens it.
languagestring, BCP-47 (≤35 chars)Per-session language, e.g. en, zh-TW, ja. Omit to use the avatar's own language.
metadataobject, ≤50 keysStored on the session for your own bookkeeping.
locationobject, ≤20 keysOptional location metadata.

language is honoured end to end for en, zh-TW and ja today. Other well-formed tags are accepted and forwarded; speech recognition then falls back to auto-detection.

Response 201

FieldTypeNotes
sessionIdstring (uuid)Identifies the session in your analytics.
serverUrlstringBare LiveKit URL — pass straight to Room.connect().
roomNamestringInformational; the token already selects the room.
userTokenstringLiveKit access token for the visitor's browser. Expires with the session.
actualDurationSecondsintegerThe limit actually granted, after key, plan and balance caps.

Errors

StatuserrorMeaning
401Missing, malformed, unknown, revoked or wrong-environment key.
402insufficient_creditsYour account balance cannot cover the minimum session.
402api_key_spend_cap_reachedThis key's spend cap (total / monthly / daily) is used up.
402concurrent_session_limitThe key or plan already has its maximum sessions running.
402sponsor_budget_exhaustedThe owner funds this avatar's visitor chats from Studio and that budget is empty.
403The avatar is not on this key's allow-list (scoped key, or another account's avatar).
422Schema validation failed (see the field limits above).
503A transient error occurred or the service is out of capacity. Retry with backoff.

There is no key-authenticated "end" call: a session ends when the visitor's participant leaves the room (room.disconnect()), or when actualDurationSeconds elapses. Billing stops at that moment.

GET /v1/avatars

Scope avatars:read. Lists your account's avatars, newest first.

QueryTypeNotes
limitinteger 1–100Page size, default 50.
beforestring (date-time)Cursor: return avatars created before this instant.

Response 200: { "items": [Avatar…], "nextBefore": "<date-time>" | null }. Pass nextBefore as the next before until it is null. Each item:

FieldTypeNotes
idstring (uuid)
namestring
visibilityprivate unlisted public
statusbuilding ready failed moderation_holdOverall build state.
readyToChatbooleantrue once every stage completed and moderation passed.
moderationStatestring, nullablee.g. clean.
creationSourcestring, nullableapi for avatars created here.
createdAtstring (date-time)
updatedAtstring (date-time)

Errors: 401, 403 insufficient_scope.

POST /v1/avatars

Scope avatars:write; account-wide key only. Creates an avatar from a zip or from asset URLs — see Create an avatar for the zip layout and manifest.

Body, one of:

  • multipart/form-data with one file part (the zip, ≤80 MB), or the raw zip as application/zip.
  • application/json: the manifest object plus an assets block naming the files by public https URL — image: {url}, voice: {url}, optional prompt: {url} (the scenario text, mutually exclusive with persona.scenario) and knowledge: [{url, description?}] (≤20). The service downloads them (same size and type rules as the zip folders) and continues identically. Private, loopback and link-local hosts are refused.

Response 202:

FieldTypeNotes
avatarIdstring (uuid)
statusbuilding
statusUrlstringPath of GET /v1/avatars/{avatarId}.
StatuserrorMeaning
400invalid_zipNot a zip, or it breaks a guard (>80 MB, >40 entries, symlinks, ../, absolute paths).
400invalid_manifestavatar.json failed validation, or a media folder holds the wrong number or type of files; the message names the problem.
400name_invalidname is empty or too long.
400public_visibility_not_allowed_on_createCreate with private or unlisted; make it public with PATCH once ready.
400invalid_urlJSON create: an asset URL is not https, carries credentials, or points at a non-public host.
400fetch_failedJSON create: an asset URL could not be downloaded (unreachable, non-2xx, too many redirects).
400invalid_uploadJSON create: a downloaded file has the wrong type or exceeds its size limit.
401Bad key.
403insufficient_scope avatar_not_allowedMissing scope, or an avatar-scoped key.
409avatar_limit_reachedPer-account cap on API-created avatars (50).
413The zip exceeds the size limit.
503avatar_import_disabledAvatar creation through the API is switched off. Retry later.

GET /v1/avatars/{avatarId}

Scope avatars:read. The avatar with its per-stage build status — poll this after a create or a part replacement.

Response 200: every field of a list item plus:

FieldTypeNotes
descriptionstring, nullable
stagesobjectappearance, voice, persona, knowledge, previews, each { status, userFeedback }.
latestFailureobject, nullable{ stage, userFeedback } of the most recent failed stage, if any.

A stage status is waitingpendingprocessingcompleted, or failed with a userFeedback text you can show to the person who supplied the file. stages.previews reads skipped for avatars created without preview clips (the default; see previews below).

FieldTypeNotes
previewUrlsobject, optional{ idle, talking, expiresAt } — pre-signed mp4 downloads of the finished preview clips, valid about one hour. Present on GET only, once at least one clip is completed; re-GET to refresh. Absent while building, when previews are skipped, and on list/PATCH responses.

The API does not return share URLs.

Errors: 401, 403 insufficient_scope / avatar_not_allowed, 404 avatar_not_found.

PATCH /v1/avatars/{avatarId}

Scope avatars:write. Updates text fields; send at least one, unknown fields are rejected.

FieldTypeNotes
namestring 1–100
descriptionstring ≤280
visibilityprivate unlisted publicpublic only once the avatar is ready.
languageen zh-TW ja
previewMessagestring ≤500
previewsbooleantrue turns preview clip generation on (the preview stages build, previewUrls appear); false turns it off. Each generated clip is billed at the plan's preview price (free on the standard plans today).
personalitystring 1–10000Who the avatar is.
scenariostring 1–10000What it is doing with the visitor.
greetingstring ≤500Opening line.
selfIntroductionstring 51–2000
guardrailsstring[] ≤20 items≤500 chars each.
conversationalHabitsobjectHabit → CONVERSATIONAL_HABIT_SCALE_LOW / _MEDIUM / _HIGH.
preferredAliasstring ≤100
spokenNamestring ≤100
knownAliasesstring[] ≤20 items1–100 chars each.

Response 200: the same object as GET /v1/avatars/{avatarId}. Persona changes re-run the persona stage. Errors: 400 (validation failure), 401, 403, 404 avatar_not_found.

DELETE /v1/avatars/{avatarId}

Scope avatars:write; account-wide key only. Soft-deletes the avatar; running sessions end normally. Response 204. Errors: 401, 403, 404 avatar_not_found.

PUT /v1/avatars/{avatarId}/appearance

Scope avatars:write. Replaces the portrait. Body: multipart/form-data with one file part (jpg/png/webp, ≤10 MB, whole head with margin). Response 200 { "avatarId", "status": "building" }; the appearance and preview stages re-run.

StatuserrorMeaning
400invalid_uploadMissing part, unsupported type, or the file fails validation.
413The upload exceeds the size limit.

Plus 401, 403, 404 avatar_not_found as above.

PUT /v1/avatars/{avatarId}/voice

Scope avatars:write. Replaces the voice sample. Body: one file part (mp3/wav/m4a/aac/ogg/ flac/opus/webm, 10–60 s of clean speech, ≤12 MB). Response and errors as for /appearance; the voice and preview stages re-run.

PUT /v1/avatars/{avatarId}/knowledge

Scope avatars:write. Replaces the whole knowledge set. Body: repeated files parts (pdf/docx/doc/txt/md/html/csv, ≤50 MB each, ≤20 files) and an optional descriptions part — a JSON object mapping filename → description (≤500 chars). Sending no files clears the knowledge base. Response and errors as for /appearance; the knowledge stage re-runs.

Endpoints you will see but should not call

POST /v1/anonymous_session powers the logged-out share link of avatars whose owner funds visitor chats from Studio. It is called by Atmee's own pages, is rate-limited per IP, and is not an integration surface — use /v1/session with a key instead.

Reference — Atmee Docs