The REST API — integrate calling, monitoring and transcripts into your own systems.
The CodeB platform exposes a small, focused REST surface for operators who want to integrate calling, monitoring, and transcript retrieval into their own systems. JSON in and out, bearer-token auth, no SDK required — every example here is a single curl away from a working integration.
/api.ashx/v1/ on your CodeB host. The production host is whatever domain you set as PublicBaseUrl — e.g. https://phone.codeb.io/api.ashx/v1/calls.
Authentication
Every endpoint except GET /v1 requires an OIDC access token with role=admin. Get one from the platform’s own OIDC IdP:
curl -X POST https://phone.codeb.io/oidc.ashx/token \
-d grant_type=password \
-d client_id=codeb-admin \
-d username=<your-admin-user> \
-d password=<your-admin-password> \
-d scope=openid
The response contains access_token — pass it on every API request:
curl -H "Authorization: Bearer $TOKEN" \
https://phone.codeb.io/api.ashx/v1/calls
refresh_token if you requested scope=offline_access.
Conventions
- Pagination. Collection endpoints accept
?limit=N&offset=M(defaults50/0;limitcapped at500). Response shape:{ data, total, limit, offset, next }. - Errors.
{ error, error_description }. HTTP status:400bad input,401missing/invalid bearer,403bearer lacksrole=admin,404resource not found,405method not allowed,502bridge unreachable,503bridge not configured. - Caching. Every response is
Cache-Control: no-store. Don’t cache API replies in your client. - Webhooks. Real-time events (
call.ended,transcript.saved,outbound-ai.finished) are delivered via the platform’s webhook system. REST for pull, webhooks for push.
Outbound AI calls
systemPrompt.Request body (JSON):
| Field | Type | Required | Notes |
|---|---|---|---|
phone | string | yes | E.164, e.g. +4915157610183 |
displayName | string | no | Caller-ID label; defaults to phone |
email | string | yes | Where the transcript + outcome email is sent |
systemPrompt | string | yes | The AI agent’s instructions. Plain text or a known prompt slug (e.g. reminder). |
apiKey | string | yes | AI Engine API Key |
model | string | no | Defaults to tenant config |
voice | string | no | e.g. Aoede, Charon; default Aoede |
language | string | no | e.g. en-US, de-DE; default en-US |
maxSeconds | int | no | 10–3600, default 300 |
retries | int | no | 0–10 (default 0) |
retryDelayMinutes | int | no | 1–1440 (default 5) |
scheduleAtUtc | string | no | ISO-8601 UTC; if omitted, dial immediately |
Example:
curl -X POST https://phone.codeb.io/api.ashx/v1/calls \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"phone": "+4915157610183",
"displayName": "Reminder",
"email": "ops@example.com",
"systemPrompt": "You are calling Stefan to remind him about his dental appointment tomorrow at 14:00.",
"apiKey": "AIza...",
"voice": "Aoede",
"language": "en-US",
"maxSeconds": 180
}'
Response (200):
{
"ok": true,
"callId": "oac-2f9e1b7c3a48",
"tenant": "phone.codeb.io",
"whitelistAdded": true,
"whitelistError": null,
"bridgeReply": "{...}"
}
Query params:
| Name | Type | Default | Notes |
|---|---|---|---|
limit | int | 50 | Page size, capped at 500 |
offset | int | 0 | Page offset |
status | string | (all) | Comma-separated filter, e.g. scheduled,in-flight,ended-success. Valid values: scheduled, dispatching, in-flight, ended-success, ended-failed-retry-pending, ended-failed-final, cancelled. |
Example:
curl -H "Authorization: Bearer $TOKEN" \
"https://phone.codeb.io/api.ashx/v1/calls?status=in-flight,scheduled&limit=20"
Response (200):
{
"data": [
{
"callId": "oac-2f9e1b7c3a48",
"tenant": "phone.codeb.io",
"requestedBy": "stefan",
"phone": "+4915157610183",
"displayName": "Reminder",
"email": "ops@example.com",
"voice": "Aoede",
"language": "en-US",
"model": "models/gemini-3.1-flash-live-preview",
"status": "in-flight",
"createdAtUtc": "2026-06-04T17:55:12.401Z",
"scheduledForUtc": null,
"dispatchedAtUtc": "2026-06-04T17:55:13.118Z",
"answeredAtUtc": "2026-06-04T17:55:18.622Z",
"endedAtUtc": null,
"endedReason": "",
"durationSec": 0,
"trunkId": "tr_6022a99586cdc7b0",
"transcriptPath": "",
"errorDetail": "",
"attempt": 1,
"retriesLeft": 2,
"retryDelayMinutes": 5
}
],
"total": 1,
"limit": 20,
"offset": 0,
"next": null
}
Example:
curl -H "Authorization: Bearer $TOKEN" \
https://phone.codeb.io/api.ashx/v1/calls/oac-2f9e1b7c3a48
Response (200):
{
"callId": "oac-2f9e1b7c3a48",
"tenant": "phone.codeb.io",
"requestedBy": "stefan",
"phone": "+4915157610183",
"displayName": "Reminder",
"status": "ended-success",
"createdAtUtc": "2026-06-04T17:55:12.401Z",
"dispatchedAtUtc": "2026-06-04T17:55:13.118Z",
"answeredAtUtc": "2026-06-04T17:55:18.622Z",
"endedAtUtc": "2026-06-04T17:57:44.012Z",
"endedReason": "finished",
"durationSec": 145,
"trunkId": "tr_6022a99586cdc7b0",
"transcriptPath": "outbound-ai-20260604-175513-oac-2f9e1b7c3a48.txt",
"attempt": 1,
"retriesLeft": 0
}
Response (404) — no such call:
{ "error": "not_found", "error_description": "No call with id=oac-..." }
"ok": true, "wasNoop": true.No body required — the callId comes from the path.
Example:
curl -X POST -H "Authorization: Bearer $TOKEN" \
https://phone.codeb.io/api.ashx/v1/calls/oac-2f9e1b7c3a48/hangup
Response (200):
{
"ok": true,
"callId": "oac-2f9e1b7c3a48",
"newStatus": "cancelled",
"actor": "stefan"
}
Virtual numbers
Example:
curl -H "Authorization: Bearer $TOKEN" \
https://phone.codeb.io/api.ashx/v1/numbers
Response (200):
{
"data": {
"tenant": "phone.codeb.io",
"virtualNumbers": [
{
"name": "codebdemo",
"number": "1234",
"mode": "live-voice-ai",
"voice": "Aoede",
"language": "en-US",
"saveTranscripts": true,
"maxDurationSec": 3500,
"visibility": "public"
},
{
"name": "MUSC",
"number": "24345",
"mode": "live-voice-ai",
"voice": "Charon",
"language": "en-US",
"saveTranscripts": true,
"maxDurationSec": 3500,
"visibility": "signed-in"
}
]
}
}
Transcripts
Query params:
| Name | Type | Default | Notes |
|---|---|---|---|
limit | int | 50 | Page size, capped at 500 |
offset | int | 0 | Page offset |
source | string | (both) | vnum for inbound (matches both vnum + office-tab callerSource), outbound-ai for outbound. Filter is applied client-side in api.ashx on the callerSource field. |
q | string | (none) | Substring filter against caller / number / displayName / rule |
since | string | (none) | ISO-8601 UTC; only transcripts with startedUtc ≥ since |
Example:
curl -H "Authorization: Bearer $TOKEN" \
"https://phone.codeb.io/api.ashx/v1/transcripts?source=outbound-ai&limit=10"
Response (200):
{
"data": [
{
"callId": "oac-2f9e1b7c3a48",
"callerSource": "outbound-ai",
"startedUtc": "2026-06-04T17:55:13.118Z",
"endedUtc": "2026-06-04T17:57:44.012Z",
"mtimeUtc": "2026-06-04T17:57:44.649Z",
"durationSec": 150,
"rule": "outbound: +4915157610183 (Reminder)",
"phone": "+4915157610183",
"displayName": "Reminder",
"outcome": "finished",
"voice": "Aoede",
"language": "en-US",
"model": "live-voice-ai",
"tokensTotal": 18420,
"tokensPrompt": 1240,
"turnCount": 12,
"size": 8412,
"source": "tenant",
"file": "outbound-ai-20260604-175513-oac-2f9e1b7c3a48.txt"
},
{
"callId": "vnum16ff4e0186",
"callerSource": "office-tab",
"startedUtc": "2026-06-04T17:14:03.836Z",
"endedUtc": "2026-06-04T17:15:19.500Z",
"durationSec": 75,
"rule": "vnum:Shortletsmalta",
"number": "666",
"outcome": "empty-room",
"tokensTotal": 6240,
"turnCount": 8,
"size": 3104,
"file": "vnum-666-20260604-171403-vnum16ff4e0186.txt"
}
],
"total": 47,
"limit": 10,
"offset": 0,
"next": 10
}
Example:
curl -H "Authorization: Bearer $TOKEN" \
https://phone.codeb.io/api.ashx/v1/transcripts/oac-2f9e1b7c3a48
Response (200):
{
"callId": "oac-2f9e1b7c3a48",
"callerSource": "outbound-ai",
"tenant": "phone.codeb.io",
"requestedBy": "stefan",
"phone": "+4915157610183",
"displayName": "Reminder",
"email": "ops@example.com",
"voice": "Aoede",
"language": "en-US",
"model": "models/gemini-3.1-flash-live-preview",
"trunkId": "tr_6022a99586cdc7b0",
"startedUtc": "2026-06-04T17:55:13.118Z",
"answeredUtc": "2026-06-04T17:55:18.622Z",
"endedUtc": "2026-06-04T17:57:44.012Z",
"durationSec": 150,
"answered": true,
"outcome": "finished",
"errorDetail": "",
"inputTurns": 6,
"outputTurns": 6,
"tokensTotal": 18420,
"turns": [
{ "speaker": "AI", "text": "Hi Stefan, calling about your dental appointment tomorrow at 14:00.", "ts": "2026-06-04T17:55:19.450Z" },
{ "speaker": "Caller", "text": "Hi, yes, what about it?", "ts": "2026-06-04T17:55:23.880Z" },
{ "speaker": "AI", "text": "Just confirming you'll be there. Do you need to reschedule?", "ts": "2026-06-04T17:55:28.120Z" }
]
}
Response (404) — no transcript for that callId:
{ "error": "not_found", "error_description": "No transcript for callId=oac-..." }
Live API description
The endpoint GET /api.ashx/v1 (no auth required) returns a machine-readable description of every route. Point your OpenAPI generator or scaffolding tool at it:
curl https://phone.codeb.io/api.ashx/v1
Response (200):
{
"name": "CodeB Platform REST API",
"version": "v1",
"endpoints": [
{ "method": "POST", "path": "/api.ashx/v1/calls", "description": "Initiate an outbound AI call", "area": "outbound-ai" },
{ "method": "GET", "path": "/api.ashx/v1/calls", "description": "List outbound AI calls", "area": "outbound-ai" },
{ "method": "GET", "path": "/api.ashx/v1/calls/{id}", "description": "Get one call's status + outcome", "area": "outbound-ai" },
{ "method": "POST", "path": "/api.ashx/v1/calls/{id}/hangup", "description": "Cancel/terminate a call", "area": "outbound-ai" },
{ "method": "GET", "path": "/api.ashx/v1/numbers", "description": "List virtual numbers", "area": "numbers" },
{ "method": "GET", "path": "/api.ashx/v1/transcripts", "description": "List transcripts", "area": "transcripts" },
{ "method": "GET", "path": "/api.ashx/v1/transcripts/{callId}", "description": "Get full transcript by callId", "area": "transcripts" }
],
"auth": "Authorization: Bearer <OIDC access token with role=admin>",
"pagination": "?limit=N&offset=M (defaults 50, 0)",
"errors": "{ error: <code>, error_description: <text> }"
}
Ready to integrate?
Sign in with your admin user to fetch an access token, then run any of the curl examples above against your CodeB host. Missing an endpoint that’s blocking you? Let us know.
Roadmap
v2 will add: dedicated API keys (so integrators don’t need to use admin user credentials), inbound-route CRUD, contact lists, scheduled-campaign primitives. The platform positioning sits on the self-hosted CPaaS page. If a missing endpoint is blocking you, tell us.