Athos Developer Docs
Reference

Error codes

The REST error envelope and the full code-to-status table.

Every error from the REST API uses one envelope. Branch on code; message is human-readable and may change.

{
  "error": {
    "code": "CALL_NOT_FOUND",
    "message": "Call not found",
    "requestId": "req_2f8c1e94-3b6a-4d20-9a7e-1c5f0b8e2d44"
  }
}

The requestId matches the X-Athos-Request-Id response header — quote it in support tickets.

A few errors also carry a details object with machine-readable extras. It is present only on the codes that document one (today AGENT_ALREADY_EXISTS); treat it as absent otherwise.

Codes

codeHTTPMeaning
INVALID_REQUEST400The request body or query was invalid (validation, bad cursor).
DRILL_NOT_FOUND400The drillKey doesn't match a known drill.
INVALID_API_KEY401Missing or invalid API key.
API_KEY_REVOKED401The API key was revoked.
INVALID_TOKEN401The session token was malformed or rejected.
TOKEN_EXPIRED401The session token expired before it was redeemed.
TOKEN_ALREADY_USED401The single-use session token was already redeemed.
IP_NOT_ALLOWED403The caller's IP is not on the allowlist (if one is configured).
TENANT_INACTIVE403Your tenant is inactive.
TENANT_QUOTA_EXCEEDED403Your tenant's monthly usage allowance is spent. Not retryable: it clears at the start of the next calendar month, or when Athos raises the allowance.
CALL_NOT_FOUND404No such call for your tenant (also returned for another tenant's call).
AGENT_NOT_FOUND404No agent of yours is registered under that id — the id you minted with, or the agentId you filtered GET /v1/calls by. Register the rep first, or resolve an NPN or email to an id with GET /v1/agents.
AGENCY_NOT_FOUND404No such agency for your tenant (also returned for another tenant's agency) — the agencyId / platformAgencyId on POST /v1/agents, GET /v1/agents?agencyId= and GET /v1/calls?agencyId=. Create the agency with POST /v1/agencies first, or check the id you stored.
AGENT_ALREADY_EXISTS409A rep is already registered under one of the handles you sent. details carries { agentId, agencyId, matchedOn } — use those ids instead of retrying.
INTERNAL_ERROR500An unexpected error. Retry; if it persists, contact Athos with the requestId.
SERVICE_UNAVAILABLE503No persona is available for the drill right now. Transient, but the attempt spent the session token — see below.

Which codes you'll see where

  • POST /v1/agencies, POST /v1/agents and GET /v1/agents (roster): INVALID_REQUEST (validation — including a registration that names no agency, or names it both ways; more than one lookup handle on GET /v1/agents; a limit outside 1–100; a bad cursor), INVALID_API_KEY, API_KEY_REVOKED, TENANT_INACTIVE, IP_NOT_ALLOWED, AGENCY_NOT_FOUND (an agencyId or platformAgencyId that is not one of your agencies — on POST /v1/agents and GET /v1/agents?agencyId=; POST /v1/agencies never answers it, an unknown id is a create), and AGENT_ALREADY_EXISTS (register only). A lookup that matches nobody is a 200 with an empty data, never an error.
  • POST /v1/session (mint): INVALID_REQUEST, INVALID_API_KEY, API_KEY_REVOKED, TENANT_INACTIVE, IP_NOT_ALLOWED, TENANT_QUOTA_EXCEEDED (no token is issued; not retryable until the allowance resets), AGENT_NOT_FOUND (the id is not a registered rep's platformAgentId or Athos agentId — an NPN or an email address lands here too).
  • POST /v1/roleplay/session (SDK redeem): INVALID_REQUEST, DRILL_NOT_FOUND, INVALID_TOKEN, TOKEN_EXPIRED, TOKEN_ALREADY_USED, SERVICE_UNAVAILABLE (no persona is available for the drill right now), INTERNAL_ERROR. These surface through the SDK's error event. Most of them spend the session token — see below.
  • GET /v1/calls and GET /v1/calls/:callId (reads): INVALID_REQUEST (list only — a missing or unknown source, a limit outside 1–100, a bad cursor, or the same rep / agency named both ways), INVALID_API_KEY, API_KEY_REVOKED, TENANT_INACTIVE, IP_NOT_ALLOWED, AGENCY_NOT_FOUND / AGENT_NOT_FOUND (list only — an agencyId / agentId that is not yours), and CALL_NOT_FOUND (detail only). The platformAgencyId / platformAgentId filters never error: an unknown value is an empty page.

Every endpoint can also answer 500 INTERNAL_ERROR.

A failed redemption usually spends the token

The session token is single-use, and it is consumed as the request is redeemed — before the call is set up. So any failure raised after that point (DRILL_NOT_FOUND, SERVICE_UNAVAILABLE, INTERNAL_ERROR) leaves the token spent: replaying the identical request returns TOKEN_ALREADY_USED, even once the underlying condition has cleared. Recover by minting a new session token and starting a new call.

The SDK gates on the microphone before it redeems, so a refused or missing microphone usually fails without ever reaching this endpoint and leaves the token unspent. The same codes can also come from later in the call setup, though, so treat a microphone failure as "retry this token once, mint a new one if it comes back TOKEN_ALREADY_USED".

Of the codes this endpoint itself returns, only three are raised before redemption and leave the token unspent: INVALID_TOKEN, TOKEN_EXPIRED and INVALID_REQUEST. Of those, only INVALID_REQUEST is worth retrying with the same token — a rejected or expired token will not be accepted on a second attempt either. Treat every other failure as having spent the token.

On this page