# Adam Calling public API > Send AI phone calls, poll for results, and read transcripts, extractions, and recordings over a REST API. This file contains all documentation content in a single document following the llmstxt.org standard. ## Overview # Adam Calling public API Drive Adam Calling programmatically: send a phone call with an org-scoped API key, poll until it completes, then read the transcript, summary, extraction, and recording. This guide gets you from zero to your first completed call; the [API reference](/api) is generated straight from the code and always current. **Base URL:** `https://api.adamcalling.com/v1` (substitute your deployment's domain; paths in this guide are relative to it). ## Start here Step 1 Get an API key Mint an org-scoped key in the app and send it in the Authorization header. Step 2 Send your first call POST /calls with a phone number and a freeform task — one curl away. Step 3 Poll for results Transcript, summary, extraction, and the recording arrive together on completion. Step 4 Use a pathway Swap the prompt for a stored, versioned conversation flow built in the app. ## Explore the API Calls Send, list, and poll calls; stream recordings, watch the event trace, stop one or all active calls. Pathways List and read stored flows — or author them: create, import, validate, simulate, version, activate. Analytics Organization call metrics, per-pathway-version analytics, and tool analytics. Identity GET /me — inspect the organization and scopes behind your key. ## When something goes wrong Every endpoint answers with one [error envelope](./errors-throttling-scopes.md), rate limits are per key (**60/min** sends, **300/min** reads), and scoped keys get a `403` outside their scopes — all detailed in [Errors, rate limits & scopes](./errors-throttling-scopes.md). --- ## Authentication An organization Admin mints keys in the app under **Settings → API keys** ("Create API key"). The key is **shown exactly once** at creation — copy it immediately; the server stores only a hash and it can never be retrieved again. If you lose it, revoke it and mint a new one. (No Admin access? Ask your account contact.) Send the key in the `Authorization` header. All three forms are accepted: ``` Authorization: Authorization: Bearer Authorization: Api-Key ``` A missing, malformed, revoked, or expired key gets a `401` — with one deliberately uniform message, so the API never reveals which of those it was. ## Scopes A key minted with no scopes has **full access**. Scoped keys carry any of `calls:write` (send), `calls:read` (list/detail/recording), and `pathways:read` (list pathways); a request outside the key's scopes gets `403`. Use scoped keys for integrations that only need to read. ## Checking who you are `GET /me` returns the organization and scopes behind the key you're holding — handy for verifying a freshly minted key before wiring it into an integration. See the [Identity reference](/api). --- ## Send your first call `POST /calls` with a phone number (E.164) and a freeform `task` describing what the agent should do: ```bash curl -X POST https://api.adamcalling.com/v1/calls \ -H "Authorization: $ADAM_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "phone_number": "+48512345678", "task": "You are calling on behalf of AutoParts sp. z o.o. to confirm the customer still wants the brake pads they asked about, and if so, arrange a pickup time this week.", "language": "pl", "name": "Jan", "metadata": {"crm_id": "42"} }' ``` ```json {"status": "success", "call_id": "8f4e…"} ``` `201` means the call was accepted and is being placed. Keep the `call_id` — it is the handle for everything that follows. `language` is `pl` (default) or `hr`; `metadata` is any JSON object you want echoed back on reads (e.g. your CRM's record id). If the number has opted out of calls you get a `403` and no call is made. Instead of a freeform `task`, you can send a `pathway_id` referencing a stored, versioned conversation flow — exactly one of the two, never both. See [Use a pathway](./pathways.md). --- ## Poll for results `GET /calls/{call_id}` returns the full call object. While the call is in flight you'll see `call_status` of `pending` or `initiated` with `null` result fields; once it completes, the transcript, summary, and extraction arrive together: ```bash curl https://api.adamcalling.com/v1/calls/$CALL_ID \ -H "Authorization: $ADAM_API_KEY" ``` ```json { "status": "success", "call_id": "8f4e…", "phone_number": "+48512345678", "call_status": "completed", "outcome": "answered", "duration_seconds": 73, "summary": "Customer confirmed the order and asked for Thursday pickup…", "transcript": "…full text…", "transcripts": [{"speaker": "assistant", "text": "Dzień dobry…", "offset_s": 1.2}], "extracted": {"pickup_day": "thursday"}, "extraction_status": "populated", "recording_url": "https://…/v1/calls/8f4e…/recording", "metadata": {"crm_id": "42"}, "cost": {"amount_usd": "0.4200"} } ``` Poll every few seconds; most calls complete within a couple of minutes. A placement that failed shows `call_status: "failed"` with `error_message`. Fetch the audio from `recording_url` (same key auth) — it streams `audio/mpeg`; `null` means no recording exists. ## Listing calls `GET /calls` lists your calls newest-first, with `status`, `outcome`, `pathway_id`, `phone_number`, `created_from`/`created_to` (dates), and `limit`/`offset` filters. See the [Calls reference](/api) for every field and filter. --- ## Use a pathway # Use a pathway instead of a prompt A pathway is a stored, versioned conversation flow built in the app. List the ones live for your organization, then send `pathway_id` **instead of** `task` (exactly one of the two — never both): ```bash curl https://api.adamcalling.com/v1/pathways -H "Authorization: $ADAM_API_KEY" # [{"id": "07a1…", "name": "Lead qualification PL", "language": "pl", "active_version_number": 3}] curl -X POST https://api.adamcalling.com/v1/calls \ -H "Authorization: $ADAM_API_KEY" -H "Content-Type: application/json" \ -d '{"phone_number": "+48512345678", "pathway_id": "07a1…"}' ``` Only pathways with an active (published) version are listed and callable; a draft-only pathway gets a `400` with an explicit message. ## Authoring pathways over the API Pathways aren't read-only: the public API also carries the full authoring surface — create a pathway, import a flow, validate and simulate it, test its webhooks, manage versions and post-call config, and activate a version. That's how `adam-cli` works with pathways. The [Pathways reference](/api) documents every endpoint. --- ## Stop calls & watch events # Stop a call See what's live, then end one call or all of them. Stopping is best-effort: the request goes to the voice provider and the call's final status arrives through the normal pipeline a moment later — poll `GET /calls/{call_id}` to confirm it wound down. List active calls (queued or in progress) — same rows and envelope as `GET /calls`: ```bash curl https://api.adamcalling.com/v1/calls/active -H "Authorization: $ADAM_API_KEY" ``` Stop one call: ```bash curl -X POST https://api.adamcalling.com/v1/calls/$CALL_ID/stop \ -H "Authorization: $ADAM_API_KEY" ``` ```json {"status": "success", "message": "Call ended successfully."} ``` Stopping a call that already ended succeeds too (the stop is idempotent). A call that isn't active — already completed or failed, or still mid-placement with no line yet — returns `400`. Stop every active call on your organization at once: ```bash curl -X POST https://api.adamcalling.com/v1/calls/active/stop \ -H "Authorization: $ADAM_API_KEY" ``` ```json {"status": "success", "message": "Stopping active calls. This may take some time...", "num_calls": 3} ``` `num_calls` is how many active calls a hangup was issued for. Stopping needs the `calls:write` scope; listing active calls needs `calls:read`. # Watch a call's events `GET /calls/{call_id}/events` returns the call's append-only event trace — a chronological log of what happened during the call (status callbacks, the websocket connecting, per-node progress, a stop request, errors), oldest first. Use it to monitor a live call or diagnose one after the fact. ```bash curl https://api.adamcalling.com/v1/calls/$CALL_ID/events \ -H "Authorization: $ADAM_API_KEY" ``` ```json { "status": "success", "count": 2, "total_count": 2, "events": [ {"type": "status_callback", "payload": {"call_status": "in-progress"}, "sequence": 0, "occurred_at": "2026-07-03T09:12:01Z"}, {"type": "ws_connected", "payload": {}, "sequence": 0, "occurred_at": "2026-07-03T09:12:03Z"} ] } ``` Each event carries a `type` (Adam Calling's own event vocabulary), an arbitrary `payload` object, a `sequence` (tie-breaker for events recorded in the same instant), and `occurred_at`. Page with `limit`/`offset` like the other lists. Needs the `calls:read` scope. --- ## Errors, rate limits & scopes # Errors Every error uses one envelope: ```json {"status": "error", "message": "Provide exactly one of 'task' or 'pathway_id'.", "errors": ["Provide exactly one of 'task' or 'pathway_id'."]} ``` `message` is the first human-readable problem; `errors` lists all of them (field errors as `"field: message"`). | Status | Meaning | | --- | --- | | `400` | Invalid request body — bad phone format, both/neither of `task`/`pathway_id`, pathway with no active version; or a stop request for a call that isn't active. | | `401` | Missing, malformed, revoked, or expired key (one message for all — the API never says which). | | `403` | Valid key without the required scope, or the phone number has opted out. | | `404` | Unknown `call_id`/`pathway_id`/`from_number_id` — including ids that belong to another organization. | | `429` | Rate limit exceeded; retry after the `Retry-After` header. | | `502` | The voice provider could not place the call; the attempt is recorded as failed. | # Rate limits Per key: **60 requests/min** on `POST /calls`, **300 requests/min** on all reads. `429` responses carry `Retry-After`. # Scopes A key minted with no scopes has **full access**. Scoped keys carry any of `calls:write` (send), `calls:read` (list/detail/recording), and `pathways:read` (list pathways); a request outside the key's scopes gets `403`. Use scoped keys for integrations that only need to read.