Start a speech-to-speech session (audio only, no video)
Reserves a speech session with an avatar: a realtime
speech-to-speech conversation over a WebSocket, without video. The
answer carries the WebSocket url and a short-lived token:
connect to url within 60 seconds (tokenExpiresAt), presenting the
token either as Authorization: Bearer <token> or as the WebSocket
subprotocols atmee-live and atmee-token.<token>. The token only
admits the connection. The session ends at its endsAt (the
reservation plus actualDurationSeconds), whenever the connection
opened. The first connection holds the session for good: no other
connection can use it, so a client that lost its connection starts a
new session.
avatarId is required: the conversation uses that avatar's persona
(name, personality, facts, self-introduction) and always its voice;
an avatar without a voice answers 422 avatar_has_no_voice.
settings pins session.start fields from your backend: the speech
server applies them over the client's, refuses a client
session.start / session.update that sets a pinned field (naming
it), and leaves them out of the session it echoes. Unpinned fields
stay the client's to set. Pinned persona fields override the
avatar's. Appends (session.instructions.append and the like) stay
allowed: they add to the pinned instructions, never replace them.
Billing runs by the minute from the speech server's first usage report
(within 30 s of the connection) until the session ends, at the
plan's speech rate. A session nobody connects to costs nothing and is
closed (failed, no_user_joined) by the first read, or start of
the account, 30 seconds after its token expired, otherwise by the
stale-session cleanup: 120 seconds without a heartbeat (the
reservation sets the first), checked every minute.
Who pays. Always the account that owns the avatar:
- API key (
X-Api-Key) — your backend, billed to the key's account. Requires thesessionsscope; an avatar-scoped key may only start sessions on its avatar (it must name it). - The owner's login (
Authorization: Bearer <jwt>, where the deployment accepts user tokens) — billed to the account the user acts for (X-Account-Idwhen there are several). An avatar of another account answers403 avatar_not_allowed. - No credential —
401. There is no anonymous or visitor-paid speech session.
With both an API key and a bearer token, the key's start applies,
as on POST /v1/sessions. Read and end a session with either
credential of its account.
X-Api-Key<token>Secret embed API key (sk_atmee_...; keys minted before 2026-09
carry sk_live_...), held by the customer's own backend and never
sent to a browser. Format: X-Api-Key: sk_atmee_....
application/json- body
avatarId*stringAn avatar of this account, with a voice, whose persona and voice the session uses (an avatar-scoped key's own avatar).
uuidmaxDurationSeconds?integerThe session's length in seconds (default 600), counted from the reservation (this call), whenever the connection opens. Shortened when the balance or the plan allows less; the answer says what was granted (actualDurationSeconds).
60 <= value <= 3600language?stringBCP 47 language of the conversation (e.g. en, fr-FR); passed to the speech server as given.
^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$2 <= length <= 35settings?session.start fields pinned by your backend: the client cannot set them (see the operation).
The session is reserved; connect to url with token.
application/json- response
links?Absent when this deployment does not know the website's address.
sessionId*stringuuidurl*stringThe WebSocket URL to connect to (wss://...).
token*stringAdmits one connection to this session; valid for 60 seconds. Not a credential for anything else.
tokenExpiresAt*stringdate-timeactualDurationSeconds*integerThe length granted by the key's, the plan's and the balance's limits, counted from the reservation (this call), whenever the connection opens; the session ends at the reservation plus this many seconds (endsAt on GET).
requestedDurationSeconds?integerThe maxDurationSeconds you sent; omitted when you sent none.
durationCapped*booleanTrue when actualDurationSeconds is shorter than requestedDurationSeconds.
billingMode*string"reserve""metered"curl -X POST 'https://api.atmanity.us/v1/speech_sessions' \ -H 'Content-Type: application/json' \ -d '{ "avatarId": "ac572b6e-802b-4b9e-a602-0786d4441e67"}'{ "links": { "chat": "http://example.com" }, "sessionId": "f6567dd8-e069-418e-8893-7d22fcf12459", "url": "string", "token": "string", "tokenExpiresAt": "2019-08-24T14:15:22Z", "actualDurationSeconds": 0, "requestedDurationSeconds": 0, "durationCapped": true, "billingMode": "reserve"}End an avatar session (optional explicit stop) POST
Ends either kind of API session; for a conversation, POST /v1/sessions/{id}/end with an API key is the same call.
Read a speech session's state, end reason and cost GET
status is active while the session is reserved or connected, then completed or failed.

