Reference

Every endpoint of the Atmee API — sessions, avatars and sponsoring — 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.
visitorIdstring 16–256 charsOnly meaningful when cross-visit memory is enabled for the key; see below.
metadataobject, ≤50 keysStored on the session for your own bookkeeping.
locationobject, ≤20 keysOptional location metadata.

visitorId must be an unguessable value from your server-side session, never taken from the page — whoever can name it can read that visitor's conversation history.

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 avatar's sponsor budget is empty and set to pause.
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 (plus billing:write if the manifest has a sponsor block); account-wide key only. Creates an avatar from a zip — see Create an avatar for the zip layout and manifest.

Body: multipart/form-data with one file part (the zip, ≤80 MB), or the raw zip as application/zip.

Response 202:

FieldTypeNotes
avatarIdstring (uuid)
statusbuilding
statusUrlstringPath of GET /v1/avatars/{avatarId}.
sponsorobject, optionalOnly with a sponsor block: { transferredMicrocredits, replayed, error }. A refused grant does not fail the create; error says why.
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.
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. The API never returns preview media or 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
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.

GET /v1/avatars/{avatarId}/sponsor

Scope avatars:read. The avatar's sponsor budget and settings.

snippet
{
  "balance": {
    "totalMicrocredits": 39000,
    "planSourcedMicrocredits": 39000,
    "topupSourcedMicrocredits": 0
  },
  "settings": {
    "maxPerSessionMicrocredits": 1000,
    "exhaustedBehavior": "pause",
    "autoTopupEnabled": false,
    "autoTopupThresholdMicrocredits": null,
    "autoTopupAmountMicrocredits": null,
    "anonymousAccess": false,
    "anonymousDailySecondsPerVisitor": null
  }
}

Errors: 401, 403, 404 avatar_not_found.

PATCH /v1/avatars/{avatarId}/sponsor

Scope billing:write. Changes the settings; send at least one field, null clears a cap.

FieldTypeNotes
maxPerSessionMicrocreditsinteger ≥1, nullableMost the budget spends on one session.
exhaustedBehaviorpause spend_balanceWhat happens when the budget is empty.
autoTopupEnabledboolean
autoTopupThresholdMicrocreditsinteger ≥1, nullableRefill when the budget falls below this.
autoTopupAmountMicrocreditsinteger ≥1, nullableHow much each refill moves from your balance.
anonymousAccessbooleanLet logged-out visitors use the share link (public/unlisted avatars only).
anonymousDailySecondsPerVisitorinteger ≥1, nullableBest-effort per-visitor daily cap for anonymous sessions.

Response 200: the sponsor view above. Errors: 400, 401, 403, 404 avatar_not_found.

POST /v1/avatars/{avatarId}/sponsor/grants

Scope billing:write. Moves credits from your account into the avatar's budget. Optional Idempotency-Key header (1–255 chars): repeating a request with the same key returns the original result with "replayed": true instead of transferring twice.

Body { "microcredits": 1000 } (≥1). Response 200 { "transferredMicrocredits", "replayed" }.

StatuserrorMeaning
402insufficient_balanceYour eligible balance cannot cover the amount.
402free_plan_credits_cannot_sponsorFree-plan credits cannot fund a budget; buy add-on credits first.
409idempotency_key_reusedThe same Idempotency-Key was sent with a different avatar or amount.

Plus 401, 403, 404 avatar_not_found. A monetized avatar cannot be sponsored; the grant is refused with a message saying so.

POST /v1/avatars/{avatarId}/sponsor/withdrawals

Scope billing:write. Returns unspent budget to your account. Body { "microcredits": 500 }, or {} to withdraw everything not reserved by a running session. Response 200 { "withdrawnMicrocredits" }.

StatuserrorMeaning
409insufficient_sponsored_balanceThe budget holds less than requested (credits reserved by running sessions cannot be withdrawn).

Plus 401, 403, 404 avatar_not_found.

Endpoints you will see but should not call

POST /v1/anonymous_session powers the logged-out share link described in Sponsored access. 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