Athos Developer Docs

Quickstart

From your credentials to a live, scored roleplay call in about 15 minutes.

This walks through the entire loop: create an agency, register a rep into it, mint a token, start a live call from the browser, receive the call.scored webhook, and read the score. Every REST call is server-to-server — keep your API key on your backend.

Base URL: https://app.useathos.ai/api/external/v1. Paths in these docs are written /v1/… — POST /v1/session means POST https://app.useathos.ai/api/external/v1/session. You'll get an API key from your Athos contact — see Authentication.

Get your credentials

There's no self-serve signup — setup is a quick coordination with the Athos team. You give us one thing, and we send you two:

You provide: your HTTPS webhook URL (where call.scored events should be delivered).

Athos sends you (each shown once — store both as backend secrets):

  • an API key (ath_live_…) → ATHOS_API_KEY
  • a webhook signing secret (whsec_…) → ATHOS_WHSEC

We register your webhook URL on our side and start delivering events to it. Keep both secrets server-side — the API key never goes near a browser.

Developing on a laptop? Expose your local handler with a tunnel (ngrok, cloudflared) and give us that URL — you can change it later. No URL yet? Skip the webhook for now and read the score with GET /v1/calls/:callId in the last step; events are not queued for later delivery. See Registering your endpoint.

Your key is a live key: every call runs against the real service and is billed. Keep test data apart by creating a dedicated test agency and registering test reps into it.

Create the agency (backend, once per customer)

An agency is one of your customers — a group of reps. Create it under your own id for it before you register its first rep. The same call renames it later (200 instead of 201), so it is safe to repeat.

curl -X POST https://app.useathos.ai/api/external/v1/agencies \
  -H "Authorization: Bearer $ATHOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"platformAgencyId":"acme-7731","name":"Acme Senior Solutions"}'
# → 201 { "id": "athos_agency_…", "name": "Acme Senior Solutions", "platformAgencyId": "acme-7731", … }

Register the rep (backend, once per person)

A session token is only minted for a registered agent, so register each rep once — with your own id for them, into the agency you just created — before their first practice call. Registration never creates an agency: a platformAgencyId you have not created is 404 AGENCY_NOT_FOUND.

curl -X POST https://app.useathos.ai/api/external/v1/agents \
  -H "Authorization: Bearer $ATHOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"firstName":"Dana","lastName":"Reyes","platformAgentId":"rep_8842","platformAgencyId":"acme-7731"}'
# → 201 { "agent": { "id": "athos_agent_…", … }, "agency": { "id": "athos_agency_…", … } }

Registering twice is a 409 AGENT_ALREADY_EXISTS whose error.details carries the existing ids. Details on Managing agencies and agents.

Mint a session token (backend)

Your backend exchanges its API key for a short-lived, single-use session token scoped to one registered rep — minted by the rep's platformAgentId or Athos agentId. Expose this as your own endpoint (e.g. POST /athos/token) that your frontend can call.

curl -X POST https://app.useathos.ai/api/external/v1/session \
  -H "Authorization: Bearer $ATHOS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"platformAgentId":"rep_8842"}'
# → { "token": "eyJhbGci…", "expiresAt": "2026-06-05T18:05:00.000Z" }

Node and Python versions of that endpoint are on Minting session tokens.

Mint the token at the moment the rep clicks "start practice", not on page load. It's single-use and lives ~5 minutes.

Install the SDK

npm install @useathos/sdk

Start the call (frontend)

Roleplay calls run on desktop Chromium-based browsers (Chrome, Edge, Brave, Opera, …) and desktop Firefox only — create() throws BROWSER_NOT_SUPPORTED on anything else, so call it on the client and check first. The page must be served over HTTPS (or localhost), or the browser withholds the microphone. See Requirements.

drillKey picks the scenario — ma-full-sale here is a full Medicare Advantage enrollment; the catalog covers Medicare Advantage, Medicare Supplement, Final Expense, under-65 health, Hospital Indemnity and Critical Illness, see Drills. create() is synchronous, so register handlers before connect() — nothing is missed.

import { AthosRoleplay } from '@useathos/sdk';

const r = await fetch('/athos/token', { method: 'POST' });
if (!r.ok) throw new Error((await r.json()).error.code); // e.g. TENANT_QUOTA_EXCEEDED
const { token } = await r.json();

const session = AthosRoleplay.create({ token, drillKey: 'ma-full-sale' });

session.on('ready', ({ persona }) => console.log(`${persona.name} is ready`));
session.on('ended', ({ callId, durationSec }) => console.log('ended', callId, durationSec));
session.on('error', ({ code, message }) => console.error(code, message));

await session.connect(); // joins the call; the AI persona starts speaking

endCallButton.onclick = () => session.disconnect();

The persona never hangs up — build an "End call" button. A call runs until you call disconnect(), until the two-hour ceiling, or until the network drops, so an abandoned tab runs (and bills) the full two hours. See How a call ends.

Talk to the persona for a minute or two before you hang up — a call on which the rep never speaks is not scored (the webhook arrives with success: false).

That's the whole frontend. See the SDK guide for events, microphone control, and browser detection.

Receive the call.scored webhook (backend)

call.scored typically arrives within a minute or two of the call ending. Athos POSTs a small, signed payload to your registered URL:

{
  "event": "call.scored",
  "success": true,
  "callId": "athos_call_GZ1DjOkZv_VEPoE-CJ8mp",
  "source": "roleplay",
  "agentId": "athos_agent_bR4kQ2wZ9Lp0nH3xVq1Yt",
  "agencyId": "athos_agency_V1StGXR8Z5jdHi6B-myT",
  "platformAgentId": "rep_8842",
  "platformAgencyId": "acme-7731",
  "occurredAt": "2026-06-05T18:09:14.000Z",
  "requestId": "req_b1f0…"
}

Your handler does three things: verify the signature over the raw request body (parsing and re-serializing changes the bytes and breaks the check), answer 2xx fast, then fetch the score afterwards. Copy a verifier — Node or Python — from Webhooks.

success is false when the call could not be scored — there is no score to fetch for it. Retries reuse the same X-Athos-Webhook-Id, so treat that header as your idempotency key. If nothing has arrived 30 minutes after the call ended, read GET /v1/calls/:callId directly.

Read the score (backend)

curl https://app.useathos.ai/api/external/v1/calls/athos_call_GZ1DjOkZv_VEPoE-CJ8mp \
  -H "Authorization: Bearer $ATHOS_API_KEY"

The response includes the transcript (a list of speaker-labelled turns) and the score object (overallScore, summary, and feedback). See Reading calls.

You're done

You've run the full loop. Next:

On this page