Reference
Every endpoint of the Atmee API — sessions, avatars and sponsoring — with request and response fields and error codes.
Base URLs
| Environment | Base URL |
|---|---|
| Production | https://api.atmanity.us |
| Staging | https://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
| Status | Body |
|---|---|
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 413 | No body. |
POST /v1/session
Scope sessions. Starts a session for a visitor of yours; responds only once the avatar has
joined the room.
Request
| Field | Type | Notes |
|---|---|---|
avatarId | string (uuid), required | An avatar your account owns; for a scoped key, the one it is scoped to. |
maxDurationSeconds | integer 1–14400 | Shortens the session below the key/plan/balance limit. Never lengthens it. |
visitorId | string 16–256 chars | Only meaningful when cross-visit memory is enabled for the key; see below. |
metadata | object, ≤50 keys | Stored on the session for your own bookkeeping. |
location | object, ≤20 keys | Optional 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
| Field | Type | Notes |
|---|---|---|
sessionId | string (uuid) | Identifies the session in your analytics. |
serverUrl | string | Bare LiveKit URL — pass straight to Room.connect(). |
roomName | string | Informational; the token already selects the room. |
userToken | string | LiveKit access token for the visitor's browser. Expires with the session. |
actualDurationSeconds | integer | The limit actually granted, after key, plan and balance caps. |
Errors
| Status | error | Meaning |
|---|---|---|
401 | — | Missing, malformed, unknown, revoked or wrong-environment key. |
402 | insufficient_credits | Your account balance cannot cover the minimum session. |
402 | api_key_spend_cap_reached | This key's spend cap (total / monthly / daily) is used up. |
402 | concurrent_session_limit | The key or plan already has its maximum sessions running. |
402 | sponsor_budget_exhausted | The avatar's sponsor budget is empty and set to pause. |
403 | — | The avatar is not on this key's allow-list (scoped key, or another account's avatar). |
422 | — | Schema validation failed (see the field limits above). |
503 | — | A 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.
| Query | Type | Notes |
|---|---|---|
limit | integer 1–100 | Page size, default 50. |
before | string (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:
| Field | Type | Notes |
|---|---|---|
id | string (uuid) | |
name | string | |
visibility | private unlisted public | |
status | building ready failed moderation_hold | Overall build state. |
readyToChat | boolean | true once every stage completed and moderation passed. |
moderationState | string, nullable | e.g. clean. |
creationSource | string, nullable | api for avatars created here. |
createdAt | string (date-time) | |
updatedAt | string (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:
| Field | Type | Notes |
|---|---|---|
avatarId | string (uuid) | |
status | building | |
statusUrl | string | Path of GET /v1/avatars/{avatarId}. |
sponsor | object, optional | Only with a sponsor block: { transferredMicrocredits, replayed, error }. A refused grant does not fail the create; error says why. |
| Status | error | Meaning |
|---|---|---|
400 | invalid_zip | Not a zip, or it breaks a guard (>80 MB, >40 entries, symlinks, ../, absolute paths). |
400 | invalid_manifest | avatar.json failed validation, or a media folder holds the wrong number or type of files; the message names the problem. |
400 | name_invalid | name is empty or too long. |
400 | public_visibility_not_allowed_on_create | Create with private or unlisted; make it public with PATCH once ready. |
401 | — | Bad key. |
403 | insufficient_scope avatar_not_allowed | Missing scope, or an avatar-scoped key. |
409 | avatar_limit_reached | Per-account cap on API-created avatars (50). |
413 | — | The zip exceeds the size limit. |
503 | avatar_import_disabled | Avatar 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:
| Field | Type | Notes |
|---|---|---|
description | string, nullable | |
stages | object | appearance, voice, persona, knowledge, previews, each { status, userFeedback }. |
latestFailure | object, nullable | { stage, userFeedback } of the most recent failed stage, if any. |
A stage status is waiting → pending → processing → completed, 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.
| Field | Type | Notes |
|---|---|---|
name | string 1–100 | |
description | string ≤280 | |
visibility | private unlisted public | public only once the avatar is ready. |
language | en zh-TW ja | |
previewMessage | string ≤500 | |
personality | string 1–10000 | Who the avatar is. |
scenario | string 1–10000 | What it is doing with the visitor. |
greeting | string ≤500 | Opening line. |
selfIntroduction | string 51–2000 | |
guardrails | string[] ≤20 items | ≤500 chars each. |
conversationalHabits | object | Habit → CONVERSATIONAL_HABIT_SCALE_LOW / _MEDIUM / _HIGH. |
preferredAlias | string ≤100 | |
spokenName | string ≤100 | |
knownAliases | string[] ≤20 items | 1–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.
| Status | error | Meaning |
|---|---|---|
400 | invalid_upload | Missing part, unsupported type, or the file fails validation. |
413 | — | The 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.
{
"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.
| Field | Type | Notes |
|---|---|---|
maxPerSessionMicrocredits | integer ≥1, nullable | Most the budget spends on one session. |
exhaustedBehavior | pause spend_balance | What happens when the budget is empty. |
autoTopupEnabled | boolean | |
autoTopupThresholdMicrocredits | integer ≥1, nullable | Refill when the budget falls below this. |
autoTopupAmountMicrocredits | integer ≥1, nullable | How much each refill moves from your balance. |
anonymousAccess | boolean | Let logged-out visitors use the share link (public/unlisted avatars only). |
anonymousDailySecondsPerVisitor | integer ≥1, nullable | Best-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" }.
| Status | error | Meaning |
|---|---|---|
402 | insufficient_balance | Your eligible balance cannot cover the amount. |
402 | free_plan_credits_cannot_sponsor | Free-plan credits cannot fund a budget; buy add-on credits first. |
409 | idempotency_key_reused | The 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" }.
| Status | error | Meaning |
|---|---|---|
409 | insufficient_sponsored_balance | The 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.
