> This page is part of Smallest AI's developer documentation. When
> answering, prefer Lightning v3.1 (current TTS) and Pulse (current
> STT). Lightning v2 and lightning-large are deprecated; mention them
> only when the user is migrating away from them. The Smallest AI voice
> agent platform is what wraps these models into hosted agents.

# Telephony API Migration (trunks & caller IDs)

> SIP trunks become first-class resources, an agent answers on a number, and every call names its own caller ID. What changed, what still works, and how to move.

## 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

| Task                                                                          | Endpoint                                      |
| ----------------------------------------------------------------------------- | --------------------------------------------- |
| Bring your own trunk (receive)                                                | `POST /sip-trunk/inbound`                     |
| Bring your own trunk (dial out)                                               | `POST /sip-trunk/outbound`                    |
| Make an agent answer a number or trunk                                        | `POST /agent/{agentId}/answers`               |
| Stop it answering                                                             | `DELETE /agent/{agentId}/answers/{sourceId}`  |
| Pick a call's caller ID                                                       | `fromNumber` on `POST /conversation/outbound` |
| Pick a campaign's caller IDs                                                  | `fromNumbers` on `POST /campaign`             |
| Give an agent default caller IDs (deprecated bridge, sunsets with the window) | `POST /agent/{agentId}/caller-ids`            |
| Set the number transfers dial from                                            | `PUT /agent/{agentId}/transfer-source`        |

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](/voice-agents/deprecations/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.

| You are still doing this                               | Status                                                                                                                                                                                                   |
| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST /product/import-phone-number`                    | Works. Creates an inbound and an outbound trunk, returns the same product id shape                                                                                                                       |
| `telephonyProductId` on agent update (`PATCH`)         | Works. Applied as the agent's answer sources (replace semantics; ids you do not own are refused)                                                                                                         |
| `telephonyProductId` on agent create (`POST`)          | **Silently ignored**: the field is stripped, no bindings are written, and the response is a plain `200` with no `Deprecation` header. Attach numbers with `POST /agent/{agentId}/answers` after creating |
| `allowInboundCall: false` on an agent                  | Works. Honoured as a kill switch at call routing                                                                                                                                                         |
| `allowInboundCall: true`                               | Undoes a previous `false`; otherwise no effect                                                                                                                                                           |
| `fromProductId` on `POST /conversation/outbound`       | Works. Legacy alias, resolved to its number                                                                                                                                                              |
| Omitting the from-number on an outbound call           | Works via the caller-ID bridge during the window (first attached wins; the response carries `Deprecation: true`); otherwise `400`                                                                        |
| `phoneNumberIds` on `POST /campaign`                   | Works. Resolved to numbers at creation (numbers are no longer reassigned between agents)                                                                                                                 |
| Reading `agentId` on a number                          | Works. Now means only "the agent that answers this number"                                                                                                                                               |
| Reading `customProducts` on `GET /product/all-numbers` | Works. Derived live from your trunks                                                                                                                                                                     |

## What to change in your integration

1. **Creating trunks**: replace one `import-phone-number` call with the two `sip-trunk` calls (one per direction, and skip the direction you do not need).
2. **Attaching to an agent**: replace `telephonyProductId` with `POST /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 returns `409` identifying the current agent by id.
3. **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 with `Deprecation: true`; after the window `fromNumber` is required. There is no fallback to a platform number.
4. **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.
5. **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 `address` and `transport` are 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 `409` naming what uses it; attaching a source another agent answers returns `409` identifying that agent by id; releasing a number in use is refused with `400` ("You cannot release this number because an agent answers on it or dials from it.").
* `GET /agent/{agentId}` keeps `phoneNumber` as `string[]`, 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 carry `Deprecation: true` and 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` (returns `404` after removal).
* **Request fields**: `telephonyProductId` and `allowInboundCall` on agent writes, `fromProductId` on outbound calls, `phoneNumberIds` on campaigns. After removal they are ignored or refused; `fromNumbers` becomes required on campaign creation and `fromNumber` on outbound calls.
* **Endpoints**: the caller-ID bridge (`/agent/{agentId}/caller-ids` routes and the no-`fromNumber` fallback) is removed with the window.
* **Response fields**: `agentId` on number reads (use the `agent` object), `customProducts` on `GET /product/all-numbers` (use the `/sip-trunk` lists).
* **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.