Use the Telephony API to connect the phone network to your workspace. Define a trunk for a carrier. Register the numbers that ride the trunk. Assign each number to the agent, trunk, or routing flow that answers it. These are control-plane operations. They configure routing, not live media. For that reason, they live with the admin console, not in the media SDK.
Trunk and number management is a control-plane surface. Drive it from the admin console (agent.clutchcall.dev) and the control-plane API on portal.clutchcall.dev. There is no media-SDK method for it today. The Voice SDK Calls client originates and controls calls over a trunk that you already provisioned here (it takes a trunkId). The Calls client does not create trunks or numbers. For the task-oriented walkthroughs, see SIP Trunking and Number Provisioning. This page is the operation-and-schema reference.
Every operation is workspace-scoped. The caller must hold a valid session for the target workspace (orgId). Each operation is tenant-checked, so you can touch only the trunks and numbers that your workspace owns. Writes take effect live: the SIP gateway hot-reloads a changed trunk within a couple of seconds, and the engine rebuilds the routing table, with no engine restart.

Trunks

A trunk is a named SIP/RTP profile. The profile defines where your carrier’s SBC lives, how to authenticate, which codecs to offer, and what an inbound call does. Four operations manage the set.
admin.listTrunks reads from the same live routing store that the engine consumes. The response is exactly what is in force on the data plane, not a cache that can drift. The API unseals secret subfields for the edit form on the way out. The API re-seals them on the next write.

The trunk object

Only trunk_id is required. Every other field is optional and carries an engine-side default. The most common fields:
string
required
The stable identifier for the trunk, typically the carrier-supplied circuit name (for example INT-KA-SBC09-9287). Numbers resolve against this key. Keep the key stable after calls start to flow.
string
The human-readable label that the console shows.
enum
default:"internal"
The trunk class. The class drives billing categorisation and which destinations a tool call may transfer to. One of internal (your own PBX/extensions), external (a PSTN/SIP carrier — per-minute billing applies), or ai_vendor (an external media vendor such as LiveKit, Vapi, or Twilio that acts as the agent leg).
boolean
When true, the gateway REGISTERs outbound to the carrier with sip_username / sip_password. When false, the gateway matches the trunk by source IP (SBC) and sends no registration.
string
The auth user for a registering trunk.
string
The auth secret for a registering trunk. Sealed at rest — see the note below.
string
The signalling addresses. The split lets the gateway bind a private IP behind cloud NAT while it advertises the public IP in Contact / Via.
string
The media addresses, with the same private-bind / public-advertise split as the SIP IPs.
string
The address of the upstream carrier gateway / session border controller that the trunk peers with.
string[]
The ordered codec offer, for example ["PCMU", "PCMA"]. G.711 µ-law/A-law are the wire codecs. See the Codec Guide.
integer
The maximum number of concurrent calls on the trunk.
enum
default:"proxy"
proxy routes RTP through the engine’s media plane (the production default). direct is signalling-only carrier→runtime media.
direct is partial today. The live runtime degrades a direct trunk back to proxy, because direct-media open is not yet implemented end to end. Leave the mode on proxy unless you explicitly test the direct path.
enum
default:"full"
The identity-header policy on outbound INVITEs. full sends regulator-friendly caller-identity headers (P-Asserted-Identity, Remote-Party-ID, P-Access-Network-Info). minimal suppresses them, for carriers whose CLI screening silently drops calls that assert identity. For finer control, use enable_pai, enable_pani, and custom_pani_header.

Inbound routing target

An inbound INVITE matched to a trunk needs a destination. Set one of these fields. The engine regenerates the routing table on write, so the target takes effect immediately:
string
Offer the call to the oldest-idle agent who holds this skill (ACD fallback).
string | null
Route to a VDN, so its IVR/vector program runs on inbound. This field is mutually exclusive with an agent binding. The router emits a vector: rule when the field is set. See PBX / ACD.
string
Stream the answered call to an AI agent endpoint over QUIC. inbound_ai_websocket_url is the WebSocket-fallback equivalent for bespoke WS audio receivers.
Secrets are sealed at rest. The API applies field-level encryption to sip_password and api_bearer_token before storage. The API opens them only under the trunk’s own workspace. The rest of the trunk (IPs, usernames, routing) stays readable to the engine without any decrypt. Deployments that require this — HIPAA / SOC 2 — get it by default. You send the plaintext secret. You never receive the secret back in the clear, except in the edit form.

Create a trunk

updateTrunk takes the same trunk object plus a trunkId. It replaces the stored config wholesale. removeTrunk takes just { orgId, trunkId }.

Numbers

A phone number (DID) is a row in the workspace’s number registry. The row maps an E.164 address to a trunk and/or an agent. Register a number to route an inbound call to it. The gateway rejects a call to an unregistered number.
string
required
The number in E.164, for example +14155550123.
enum
default:"local"
local, toll-free, or mobile.
string
default:"US"
The ISO-3166 alpha-2 country code.
string | null
The trunk that carries this number’s calls. This is the number → trunk assignment.
string | null
The agent that answers calls to this number. This is the number → agent assignment.
enum
default:"active"
active, pending, or inactive. Only an active number is published to the inbound routing index. A flip to pending/inactive withdraws the number, but does not delete the row.
string | null
Where the number came from (for example manual, or a carrier name). For numbers that you import by hand, use your own value.
When you register a number, the API publishes a lookup entry. The SIP gateway resolves this entry on every inbound INVITE (by dialed number, then by trunk source IP). The API also rebuilds the routing table, so the call reaches its assigned agent. Only active numbers are published. deletePhoneNumber (or a status flip) withdraws the entry immediately.

Assignment is the whole point

To provision an address and to assign it are two steps. A number with neither a trunk_id nor an agent_id still parks a call. The assignment model:
  • Number → trunk (trunk_id) — which carrier profile carries the call.
  • Number → agent (agent_id) — which agent answers it.
  • Trunk → target (inbound_skill_id / vdn_id / inbound_ai_quic_url) — the fallback destination for any inbound call on the trunk that a specific number does not override.
A number-level assignment wins over the trunk’s default target. You can point a whole trunk at one flow, and carve out individual DIDs to specific agents.

Managed number catalog (search-and-buy)

telephony.searchAvailableNumbers searches an upstream carrier catalog for a new DID that you can allocate. The search pages and filters by country and type. The search also annotates each offer to show whether your workspace already owns the number.
string
A substring / area-code filter.
string
default:"all"
An ISO country filter.
enum
default:"all"
local, toll-free, mobile, or all.
integer
Pagination; perPage clamps to 50.
The managed catalog is a stub today. Search returns a small demo set (India-only, served by a placeholder catalog provider), pending live upstream carrier integration. The response shape is final, so you can wire against it now safely. However, you cannot purchase a live number through it yet. The allocation registry and the inbound routing that reads it are real. A number that you register by hand with upsertPhoneNumber behaves exactly like a purchased number will. To go live now, bring your own DID over a trunk.

Per-tenant SIP address (no purchase)

Separately from carrier trunks, every workspace gets a ready-to-use SIP address at creation — <workspace-id>.sip.clutchcall.dev (with matching <workspace-id>.webrtc.clutchcall.dev and apex records). No Telephony API call is necessary. The address exists from onboarding. The gateway derives the tenant from the leftmost DNS label of an inbound INVITE. Use the address to register softphones, or to point a test carrier at your workspace immediately. Details are in Number Provisioning.

SIP Trunking

Task walkthrough: connect a carrier, bring your own trunk, or use a managed one.

Number Provisioning

The three routes to a reachable address, and how to assign it.

PBX / ACD

VDNs, vectors, and the skill-queue routing that trunks and numbers target.

Calls API

Originate and control live calls over a provisioned trunk from the SDK.