Telephony API Migration (trunks & caller IDs)
Telephony API Migration (trunks & caller IDs)
The model change in one paragraph
Previously, one attachment did two jobs. A number attached to an agent decided both who answers it and what the agent dials from. These are now separate. Who answers a number is durable configuration. Attach a number or inbound trunk to the agent that answers it. What a call dials from is a property of the call. The request, campaign, or transfer names its caller ID at call time. The agent no longer owns outbound numbers. A temporary caller-ID bridge (/agent/{agentId}/caller-ids) lets calls without an explicit fromNumber dial from attached defaults, but it is deprecated at introduction and sunsets with the window; treat fromNumber as the real surface.
The new surface
Each inbound trunk carries its own numbers, credentials, media-encryption policy, Krisp toggle, and ringing timeout, and can restrict which peer addresses may send it calls.
What still works during the migration window
The old API keeps working, translated to the new model underneath, for 45 days after the rework’s release. After that, everything in the deprecated list is removed in one change. The concrete sunset date is published at release on the Deprecation Notices page and in the changelog. Responses on deprecated paths carry a Deprecation: true header, so the affected code paths in your integration are discoverable from your own logs.
What to change in your integration
- Creating trunks: replace one
import-phone-numbercall with the twosip-trunkcalls (one per direction, and skip the direction you do not need). - Attaching to an agent: replace
telephonyProductIdwithPOST /agent/{agentId}/answers({ "sourceKind": "phoneNumber" | "sipTrunk", "sourceId": "..." }), one call per source. An agent can answer on any number of numbers and trunks; a source is answered by exactly one agent, and attaching a taken one returns409identifying the current agent by id. - Outbound calls: send
fromNumber(E.164, exact match against numbers you own; no normalization is applied). During the window, calls without it dial from the agent’s attached default caller IDs (POST /agent/{agentId}/caller-ids, first attached wins), and such calls respond withDeprecation: true; after the windowfromNumberis required. There is no fallback to a platform number. - Campaigns: send
fromNumbers. Several numbers rotate; retries reuse the number the recipient already saw; the list freezes at creation. It is effectively required already, since a campaign that resolves to zero numbers is refused. - Transfers: if an agent transfers calls, set its transfer caller ID once via
PUT /agent/{agentId}/transfer-source. Migrated setups get this backfilled automatically. When unset, transfers on outbound calls use the call’s own line; transfers on inbound calls are refused.
Behavior changes worth knowing
- Inbound phone numbers are unique across the platform; outbound caller IDs are unique within your organization, and one outbound trunk exists per carrier address.
- An outbound trunk’s
addressandtransportare immutable. Changing carriers means delete and recreate. cpsLimit(1-50, default 1) paces outbound calls per carrier address, shared by every trunk on that address.- Deleting or releasing telephony resources is guarded, instead of silently breaking things. Deleting a trunk in use returns
409naming what uses it; attaching a source another agent answers returns409identifying that agent by id; releasing a number in use is refused with400(“You cannot release this number because an agent answers on it or dials from it.”). GET /agent/{agentId}keepsphoneNumberasstring[], now meaning what the agent answers on. An agent answering on an extension-only trunk returns[]and is still reachable.- Default caller IDs (
/agent/{agentId}/caller-ids) are the one shared attachment: many agents may present the same number. Answering sources stay exclusive. A trunk-backed caller ID must name which of the trunk’s numbers it dials from. The whole surface is a deprecated bridge: its responses carryDeprecation: trueand it is removed with the window.
After the window: what gets removed
Everything below exists only to keep old integrations running and is deleted together when the window closes.
- Endpoint:
POST /product/import-phone-number(returns404after removal). - Request fields:
telephonyProductIdandallowInboundCallon agent writes,fromProductIdon outbound calls,phoneNumberIdson campaigns. After removal they are ignored or refused;fromNumbersbecomes required on campaign creation andfromNumberon outbound calls. - Endpoints: the caller-ID bridge (
/agent/{agentId}/caller-idsroutes and the no-fromNumberfallback) is removed with the window. - Response fields:
agentIdon number reads (use theagentobject),customProductsonGET /product/all-numbers(use the/sip-trunklists). - Internals: the
productType: "custom"pointer records.
Campaigns are the one change that forces API integrators to ship code: after the window, fromNumbers is required at creation. If your release cycle is slow, start there.
Dashboard equivalents
SIP trunks moved from the Add Number dialog’s “Import SIP” tab to a dedicated SIP Trunks page (sidebar, under Deploy, next to Phone Numbers). An agent’s answering sources and transfer number live on the agent’s Telephony tab. Campaign creation has a Call from selector. Test calls have a Call from picker with a shared platform test number as the labelled default.

