Platform API · v1

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.

Base URL. All endpoints live under /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
Lifetime. Access tokens are short-lived (typically 1 hour). Refresh by re-running the token flow above, or use the refresh_token if you requested scope=offline_access.

Conventions

  • Pagination. Collection endpoints accept ?limit=N&offset=M (defaults 50 / 0; limit capped at 500). Response shape: { data, total, limit, offset, next }.
  • Errors. { error, error_description }. HTTP status: 400 bad input, 401 missing/invalid bearer, 403 bearer lacks role=admin, 404 resource not found, 405 method not allowed, 502 bridge unreachable, 503 bridge 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

POST/v1/calls
Initiate an outbound AI call. The platform whitelists the target number, places a SIP call via the configured trunk, and attaches a Live Voice AI session that follows your systemPrompt.

Request body (JSON):

FieldTypeRequiredNotes
phonestringyesE.164, e.g. +4915157610183
displayNamestringnoCaller-ID label; defaults to phone
emailstringyesWhere the transcript + outcome email is sent
systemPromptstringyesThe AI agent’s instructions. Plain text or a known prompt slug (e.g. reminder).
apiKeystringyesAI Engine API Key
modelstringnoDefaults to tenant config
voicestringnoe.g. Aoede, Charon; default Aoede
languagestringnoe.g. en-US, de-DE; default en-US
maxSecondsintno10–3600, default 300
retriesintno0–10 (default 0)
retryDelayMinutesintno1–1440 (default 5)
scheduleAtUtcstringnoISO-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": "{...}"
}
GET/v1/calls
List active + recent outbound AI calls. Includes scheduled, dialing, in-progress, completed, failed.

Query params:

NameTypeDefaultNotes
limitint50Page size, capped at 500
offsetint0Page offset
statusstring(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
}
GET/v1/calls/{id}
One call’s status, outcome, and metadata. Returned without the list envelope — the call record directly.

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-..." }
POST/v1/calls/{id}/hangup
Cancel a scheduled call or terminate an in-flight one. Idempotent — calling on an already-ended call returns 200 with "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

GET/v1/numbers
List inbound virtual numbers configured on this tenant. Each entry is a rule with the inbound DID, AI prompt (if any), routing, voice, recording flag.

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

GET/v1/transcripts
List transcripts (inbound vnum calls + outbound AI calls), newest first.

Query params:

NameTypeDefaultNotes
limitint50Page size, capped at 500
offsetint0Page offset
sourcestring(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.
qstring(none)Substring filter against caller / number / displayName / rule
sincestring(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
}
GET/v1/transcripts/{callId}
Full transcript JSON for one call — metadata header plus turn-by-turn transcript array.

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.