The Telephony API is how you wire the phone network into your workspace: define a trunk for a carrier, register the numbers that ride it, and assign each number to the agent, trunk, or routing flow that should answer it. These are control-plane operations — they configure routing, not live media — so they live alongside the admin console rather than in the media SDK.
Trunk and number management is a control-plane surface, driven from the admin console (agent.telequick.dev) and the control-plane API on portal.telequick.dev. There is no media-SDK method for it today — the Voice SDK Calls client originates and controls calls over a trunk you have already provisioned here (it takes a trunkId), but it 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), and each operation is tenant-checked so you can only touch trunks and numbers your workspace owns. Writes take effect live — the SIP gateway hot-reloads a changed trunk within a couple of seconds and the routing table is rebuilt, with no engine restart.

Trunks

A trunk is a named SIP/RTP profile: where your carrier’s SBC lives, how to authenticate, which codecs to offer, and what an inbound call should do. Four operations manage the set.
OperationKindInputEffect
admin.addTrunkmutation{ orgId, trunk }Create a trunk and push it live
admin.updateTrunkmutation{ orgId, trunkId, trunk }Replace a trunk’s config
admin.removeTrunkmutation{ orgId, trunkId }Delete a trunk and drop its routing rule
admin.listTrunksquery{ orgId }List the workspace’s trunks (canonical, live view)
admin.listTrunks reads from the same live routing store the engine consumes, so what you get back is exactly what is in force on the data plane — not a cache that can drift. Secret subfields are unsealed for editing on the way out and re-sealed on the next write.

The trunk object

Only trunk_id is required; everything else is optional and carries an engine-side default. The most common fields:
trunk_id
string
required
Stable identifier for the trunk, typically the carrier-supplied circuit name (for example INT-KA-SBC09-9287). This is the key numbers resolve against, so keep it stable once calls are flowing.
display_name
string
Human-readable label shown in the console.
realm
enum
default:"internal"
Trunk class, which 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 acting as the agent leg).
require_registration
boolean
When true, the gateway REGISTERs outbound to the carrier using sip_username / sip_password. When false, the trunk is matched by source IP (SBC) and no registration is sent.
sip_username
string
Auth user for a registering trunk.
sip_password
string
Auth secret for a registering trunk. Sealed at rest — see the note below.
internal_sip_ip / external_sip_ip
string
Signalling addresses. The split lets the gateway bind a private IP behind cloud NAT while advertising the public one in Contact / Via.
internal_rtp_ip / external_rtp_ip
string
Media addresses, with the same private-bind / public-advertise split as the SIP IPs.
gateway_ip / sbc_ip
string
Upstream carrier gateway / session border controller address the trunk peers with.
codec_preferences
string[]
Ordered codec offer, for example ["PCMU", "PCMA"]. G.711 µ-law/A-law are the wire codecs; see the Codec Guide.
channel_limit
integer
Maximum concurrent calls on the trunk.
media_mode
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 it on proxy unless you are explicitly testing the direct path.
compliance_headers_mode
enum
default:"full"
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. Finer control is available via enable_pai, enable_pani, and custom_pani_header.

Inbound routing target

An inbound INVITE matched to a trunk needs to know where to go. Set one of these; the routing table is regenerated on write so the target takes effect immediately:
inbound_skill_id
string
Offer the call to the oldest-idle agent holding this skill (ACD fallback).
vdn_id
string | null
Route to a VDN so its IVR/vector program runs on inbound. Mutually exclusive with an agent binding — the router emits a vector: rule when set. See PBX / ACD.
inbound_ai_quic_url
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. sip_password and api_bearer_token are field-level encrypted before storage and opened only under the trunk’s own workspace, so 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 it back in the clear except for the edit form.

Create a trunk

{
  "trunk_id": "carrier-primary",
  "display_name": "Primary PSTN carrier",
  "realm": "external",
  "require_registration": true,
  "sip_username": "acme-sip",
  "sip_password": "•••••••••",          // sealed on write
  "sbc_ip": "203.0.113.10",
  "codec_preferences": ["PCMU", "PCMA"],
  "channel_limit": 200,
  "media_mode": "proxy",
  "inbound_skill_id": "0f3c…"           // where inbound calls land
}
// admin.addTrunk — mutation
{
  "orgId": "<workspace-id>",
  "trunk": { /* the payload above */ }
}
// → { ok: true }
updateTrunk takes the same trunk object plus a trunkId and replaces the stored config wholesale; removeTrunk takes just { orgId, trunkId }.

Numbers

A phone number (DID) is a row in the workspace’s number registry that maps an E.164 address to a trunk and/or an agent. Registering a number is what makes an inbound call to it route somewhere instead of being rejected.
OperationKindInputEffect
admin.upsertPhoneNumbermutation{ orgId, number }Create or update a number and its inbound routing
admin.deletePhoneNumbermutation{ orgId, id }Remove a number and its routing entry
e164
string
required
The number in E.164, for example +14155550123.
number_type
enum
default:"local"
local, toll-free, or mobile.
country
string
default:"US"
ISO-3166 alpha-2 country code.
trunk_id
string | null
The trunk that carries this number’s calls. This is the number → trunk assignment.
agent_id
string | null
The agent that answers calls to this number. This is the number → agent assignment.
status
enum
default:"active"
active, pending, or inactive. Only an active number is published to the inbound routing index; flipping to pending/inactive withdraws it without deleting the row.
provider
string | null
Where the number came from (for example manual, or a carrier name). Numbers imported by hand use your own value.
Registering a number publishes a lookup entry the SIP gateway resolves on every inbound INVITE (by dialed number, then by trunk source IP), and rebuilds the routing table so the call reaches its assigned agent. Only active numbers are published; deletePhoneNumber (or flipping status) withdraws the entry immediately.

Assignment is the whole point

Provisioning an address and assigning 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.
Number-level assignment wins over the trunk’s default target, so you can point a whole trunk at one flow and carve out individual DIDs to specific agents.
// admin.upsertPhoneNumber — mutation
{
  "orgId": "<workspace-id>",
  "number": {
    "e164": "+14155550123",
    "number_type": "local",
    "country": "US",
    "trunk_id": "carrier-primary",   // → trunk
    "agent_id": "support-bot",       // → agent
    "status": "active"
  }
}

Managed number catalog (search-and-buy)

telephony.searchAvailableNumbers searches an upstream carrier catalog for a new DID you can allocate, paging and filtering by country and type, and annotating each offering with whether your workspace already owns it.
query
string
Substring / area-code filter.
country
string
default:"all"
ISO country filter.
type
enum
default:"all"
local, toll-free, mobile, or all.
page / perPage
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 wiring against it now is safe, but you cannot actually purchase a live number through it yet. The allocation registry and the inbound routing that reads it are real, so a number you register by hand with upsertPhoneNumber behaves exactly like a purchased one will. To go live now, bring your own DID over a trunk.

Per-tenant SIP address (no purchase)

Separately from carrier trunks, every workspace is provisioned a ready-to-use SIP address when it is created — <workspace-id>.sip.telequick.dev (with matching <workspace-id>.webrtc.telequick.dev and apex records). No Telephony API call is needed; the address exists from onboarding and the gateway derives the tenant from the leftmost DNS label of an inbound INVITE. Use it to register softphones or point a test carrier at your workspace immediately. Details 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 skill-queue routing that trunks and numbers target.

Calls API

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