# Audelo API > Audelo is an AI voice-agent platform. This REST API lets you manage AI phone > agents, place and control outbound calls, read call transcripts and recordings, > query analytics, store per-call context, and subscribe to webhook events. > All requests and responses are JSON over HTTPS. - **Base URL:** `https://audelo.ai/api/v1` - **Auth:** API key — `Authorization: Bearer cgk_...` - **Plan requirement:** any paid plan (Starter or higher). Free/Explorer accounts cannot use the API. - **Format:** JSON request and response bodies; UTF-8; timestamps are ISO-8601. --- ## Authentication Every request must include an API key as a Bearer token: ``` Authorization: Bearer cgk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` - Create keys in the dashboard under **API Keys** (account **admins** only). - The full key (`cgk_` followed by 48 hex characters, 52 chars total) is shown **once** at creation. Store it securely — afterward only a `cgk_xxxxxxxx` prefix is displayed. - API access requires a **paid plan**. Requests from Free/Explorer tenants return `403 plan_required`. - A maximum of **20 active keys** per account. - **Revoke** a key by deleting it, or **disable** it by setting it inactive. ### Scopes Each key is restricted to the scopes you assign at creation. A key created with **no scopes** has access to everything. Endpoints require these scopes: | Scope | Grants access to | |-------------------|-------------------------------------------------------------------------| | `agents:read` | List and read agents | | `calls:read` | List/read calls, transcripts, recordings, and read context | | `calls:write` | Initiate and end calls; write/delete context (**Pro plan required**) | | `analytics:read` | Read call and agent analytics | | `webhooks:manage` | Create, update, delete, and test webhook endpoints | A request lacking the required scope returns `403 forbidden`. --- ## Plan requirements | Capability | Minimum plan | |-----------------------------------------------------------------------------|--------------| | Any API access; read agents/calls/analytics; manage webhooks | Starter | | `POST /calls/initiate`, `POST /calls/{id}/end`, all `/context/*` endpoints | **Pro** | Requests below the required plan return `403` with `error: "plan_required"`. --- ## Errors Error responses use a consistent JSON envelope: ```json { "error": "validation_error", "message": "The given data was invalid.", "errors": { "phone_number": ["The phone number format is invalid."] }, "request_id": "f1c2a4e8-..." } ``` - `errors` is present only for `422` validation failures. - `request_id` is a unique id for every request — include it when contacting support. | Status | `error` code | Meaning | |--------|-----------------------|------------------------------------------------------| | 401 | `unauthorized` | Missing, malformed, inactive, or expired API key | | 403 | `forbidden` / `plan_required` | Key lacks the scope, or plan too low | | 404 | `not_found` | Resource doesn't exist or belongs to another tenant | | 422 | `validation_error` | Invalid request body or parameters | | 429 | `too_many_requests` | Rate limit exceeded | | 5xx | `internal_error` | Server error (the detail is logged, not returned) | --- ## Rate limits **300 requests per minute, per API key.** Exceeding the limit returns `429 too_many_requests`. --- ## Pagination List endpoints (`/agents`, `/calls`) return a standard length-aware paginator. Page size is fixed at **50**; navigate with `?page=N`. ```json { "data": [ /* rows */ ], "current_page": 1, "last_page": 3, "per_page": 50, "total": 142, "next_page_url": "https://audelo.ai/api/v1/calls?page=2", "prev_page_url": null } ``` Single-resource, transcript, recording, analytics, and context endpoints are not paginated. --- ## Endpoints ### Agents #### `GET /api/v1/agents` — scope `agents:read` List your agents (paginated, 50/page, ordered by name). Each row: `id, name, status, voice, language, created_at, updated_at`. #### `GET /api/v1/agents/{id}` — scope `agents:read` Get one agent. `404` if it isn't in your account. ```json { "id": 12, "name": "Reception Bot", "status": "active", "voice": "aria", "language": "en-AU", "humour_level": 10, "empathy_level": 80, "created_at": "2026-06-25T10:30:00.000000Z", "updated_at": "2026-06-25T10:30:00.000000Z" } ``` ### Calls #### `GET /api/v1/calls` — scope `calls:read` List calls (paginated, newest first). Row fields: `id, agent_id, from_e164, to_e164, direction, status, duration_sec, created_at`. Query filters: `agent_id` (int), `status` (string), `from` (date), `to` (date). ``` GET /api/v1/calls?status=completed&agent_id=12&from=2026-06-01 ``` #### `GET /api/v1/calls/{id}` — scope `calls:read` Get one call, including the AI summary and recording link. ```json { "id": 456, "agent": { "id": 12, "name": "Reception Bot" }, "from_e164": "+61412345678", "to_e164": "+61253009003", "direction": "inbound", "status": "completed", "duration_sec": 92, "summary": "Caller booked a consultation for Tuesday...", "recording_url": "https://...", "created_at": "2026-06-25T10:30:00.000000Z" } ``` #### `GET /api/v1/calls/{id}/transcript` — scope `calls:read` Ordered transcript turns. `timestamp` is the **offset in milliseconds** from the start of the call. `speaker` is `"caller"` or `"agent"`. ```json { "call_id": 456, "transcript": [ { "timestamp": 0, "speaker": "agent", "text": "Hi, thanks for calling..." }, { "timestamp": 3200, "speaker": "caller", "text": "I'd like to book an appointment" } ] } ``` #### `GET /api/v1/calls/{id}/recording` — scope `calls:read` Returns the stored recording link. `404` if the call has no recording. ```json { "call_id": 456, "recording_url": "https://...", "duration_sec": 92 } ``` #### `POST /api/v1/calls/initiate` — scope `calls:write` · **Pro plan** Place an outbound call. The call runs through your DNC list, calling-hours, and max-concurrent caps. Returns `202 Accepted`. Body: | Field | Type | Required | Notes | |-------------------|---------|----------|---------------------------------------------------| | `agent_id` | int | yes | An agent in your account, with a number assigned | | `phone_number` | string | yes | E.164, e.g. `+61412345678` | | `caller_name` | string | no | Display name for the contact | | `callback_url` | url | no | Optional per-call callback | | `custom_data` | object | no | Arbitrary data echoed back to your integration | | `idempotency_key` | string | no | ≤64 chars; de-dupes retries for 24h | ```json { "call_id": "cgt_789", "agent_id": 12, "status": "queued", "created_at": "2026-06-25T10:30:00+00:00" } ``` > **Note:** `call_id` is a `cgt_...` placeholder for the queued call — the call hasn't > been dialed yet. Use it with `/end`, or poll `GET /calls` for the materialized call > record once it connects. A repeated `idempotency_key` returns the original response with status `202`. The agent must belong to your account and have a phone number assigned, or you get `422`. #### `POST /api/v1/calls/{id}/end` — scope `calls:write` · **Pro plan** Hang up an in-progress call. `id` may be a numeric call id or a `cgt_...` placeholder. Idempotent for already-ended calls. ```json { "call_id": "cgt_789", "status": "in-progress", "hangup_requested_at": "2026-06-25T10:31:00+00:00" } ``` The final status is set by the carrier callback — poll `GET /calls/{id}` to confirm. ### Context store A per-account key/value store for data your agents use across calls. Keys match `[A-Za-z0-9._:-]+` (≤191 chars). Values are JSON. All context endpoints require **Pro**. #### `POST /api/v1/context/{key}` — scope `calls:write` · **Pro** Body: `{ "value": , "ttl_seconds": }`. TTL defaults to **86400** (24h), max **2592000** (30 days). Returns `201`. ```json { "key": "customer:42", "expires_at": "2026-06-26T10:30:00+00:00", "ttl_seconds": 86400 } ``` #### `GET /api/v1/context/{key}` — scope `calls:read` · **Pro** Returns `{ "key", "value", "expires_at" }`. Expired or missing keys return `404`. #### `DELETE /api/v1/context/{key}` — scope `calls:write` · **Pro** Deletes the key. Idempotent — returns `204 No Content` whether or not it existed. ### Analytics #### `GET /api/v1/analytics/calls` — scope `analytics:read` Aggregate call stats for a window (`from`, `to` dates; default last 30 days). ```json { "period": { "from": "2026-05-26", "to": "2026-06-25" }, "calls": { "total_calls": 412, "total_duration_sec": 38400, "avg_duration_sec": 93, "completed": 388 } } ``` #### `GET /api/v1/analytics/agents` — scope `analytics:read` Per-agent breakdown for the window (`from`, `to`; default last 30 days). ```json { "period": { "from": "2026-05-26", "to": "2026-06-25" }, "agents": [ { "id": 12, "name": "Reception Bot", "total_calls": 250, "avg_duration_sec": 88 } ] } ``` ### Webhook endpoints Manage webhook subscriptions via the API. All require scope `webhooks:manage`. Max **20 endpoints** per account. #### `GET /api/v1/webhooks` List endpoints: `id, url, description, events, is_active, failure_count, created_at`. #### `POST /api/v1/webhooks` Create an endpoint. Body: `url` (required, public HTTPS), `description` (optional), `events` (required array; see Events below). Returns `201` including the signing **`secret` (`whsec_...`), shown once**. ```json { "id": 5, "url": "https://example.com/hooks/audelo", "events": ["call.ended"], "secret": "whsec_...", "is_active": true, "created_at": "..." } ``` #### `PUT /api/v1/webhooks/{id}` Update `url`, `description`, `events`, or `is_active`. Re-activating resets the failure count. #### `DELETE /api/v1/webhooks/{id}` Delete the endpoint. #### `POST /api/v1/webhooks/{id}/test` Fire a synchronous test delivery. Returns `{ delivery_id, status, response_code, delivered_at }`. --- ## Webhooks (events & verification) When a subscribed event occurs, Audelo sends an HTTP `POST` to your endpoint URL. ### Events | Event | Fires when | `data` fields | |-------------------|-----------------------------------------------------|--------------------------------------------------------------------------| | `call.started` | A call connects | `call_id, agent_id, from, to, direction, started_at` | | `call.ended` | A call completes | `call_id, agent_id, from, to, direction, status, duration_sec, started_at, ended_at` | | `call.transcript` | A transcript segment is finalized during a call | `call_id, speaker, text` | ### Payload ```json { "id": "wh_a1b2c3d4e5f6a7b8", "event": "call.ended", "timestamp": "2026-06-25T10:31:32.000000Z", "data": { "call_id": 456, "agent_id": 12, "from": "+61412345678", "to": "+61253009003", "direction": "inbound", "status": "completed", "duration_sec": 92, "started_at": "2026-06-25T10:30:00.000000Z", "ended_at": "2026-06-25T10:31:32.000000Z" } } ``` ### Delivery headers | Header | Value | |---------------------------|--------------------------------------------------| | `X-Audelo-Signature` | `sha256=` — HMAC-SHA256 of the raw body | | `X-Audelo-Event` | The event name | | `X-Audelo-Delivery` | Unique delivery id (use to dedupe retries) | | `User-Agent` | `Audelo-Webhook/1.0` | ### Verifying the signature Compute `HMAC-SHA256` over the **raw request body** using your endpoint's `whsec_` secret, and compare (constant-time) against the hex in `X-Audelo-Signature` (after the `sha256=` prefix). ```python import hmac, hashlib def verify(raw_body: bytes, header: str, secret: str) -> bool: expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, header) ``` ### Delivery & retries - Delivery is asynchronous; a `2xx` response marks it delivered. - Failures retry up to **5 times** with backoff: 5s, 30s, 5m, 30m, 2h. - An endpoint is **auto-disabled after 10 consecutive failures**. - Use the `X-Audelo-Delivery` id (or the payload `id`) to dedupe — the same event may be delivered more than once. --- ## Limits summary - 300 requests/minute per API key. - 20 active API keys per account. - 20 webhook endpoints per account. - Context: key ≤191 chars; value TTL default 24h, max 30 days. - Call list / agent list pages: 50 items each.