Skip to main content
If your numbers already live at a carrier, you can connect them to your agents without porting anything. Point the carrier’s SIP trunk at Fish Audio and import the number: it stays with your carrier, who keeps billing you for the telephone-network legs, and on Fish Audio the calls bill as ordinary agent sessions. Imported numbers carry no monthly rental and no telephony charges of any kind: no phone surcharge, no transfer fees; the call bills like a web session. This works with any carrier or PBX that speaks SIP trunking: Twilio Elastic SIP Trunking, Asterisk or FreePBX, and most SIP providers. It is also the only way to use non-US/CA numbers, which the purchasable inventory does not cover.

How it works

A SIP trunk has two directions, and carriers name them from the carrier’s point of view. Origination is traffic your carrier originates towards Fish Audio: an inbound call. Termination is traffic your carrier terminates for Fish Audio: an outbound call the platform hands to your carrier or PBX to complete. Twilio’s trunk configuration uses the same two words, and so do the fields below.
  • Inbound (origination): your carrier routes calls for the number over its trunk to Fish Audio’s SIP endpoint. The platform matches the dialed number and hands the call to the agent bound to it. From there it is a normal inbound call.
  • Outbound (termination), optional: give the import a termination host and the platform can also place calls from the number. Outbound calls and warm-transfer consult legs dial out through your trunk, with the imported number as the caller ID. You can also attach custom SIP headers that every call through the termination carries, for routing hints or tenant identification on your PBX.
The SIP endpoint to point your trunk at:
Origination URI

Prerequisites

  • A number at your carrier and access to its SIP trunk configuration.
  • Team owner or admin role (console), or an API key.

Import the number

In the console

On the workspace Phone numbers page, choose Import number. Enter the number in E.164 format, set at least one inbound authentication factor, and optionally fill in the Outbound calling (termination) section, including any Custom SIP headers (one X-Name: value per line). The same form is available later from the number’s row menu as Edit configuration.

Through the API

Import with the same endpoint that purchases numbers, using the sip provider variant:
Request
Returns 201 with the number object. Imported numbers land in your default workspace, like purchases. At least one inbound factor (digest credentials and/or allowed addresses) is required; a 422 reports what is missing. A 409 means the number is already on the platform. A 502 means trunk provisioning failed; the number stays visible with status error and is safe to release and retry.
Digest passwords are stored only in the underlying trunk objects and are never echoed back by the API.

Inbound authentication

The SIP endpoint is shared, so an import must prove that calls really come from your trunk:
  • Digest credentials: the platform challenges your trunk and verifies the username and password. Use this whenever your carrier or PBX answers digest challenges (Asterisk, FreePBX, most SIP providers).
  • Allowed source addresses: calls are only accepted from the listed IPs or CIDR ranges. Use this for carriers that do not authenticate their origination traffic; Twilio Elastic SIP Trunking is one, so for Twilio this is the required factor.
Set both when your carrier supports it.

Carrier walkthroughs

Twilio Elastic SIP Trunking

1

Create a trunk

In the Twilio console, under Elastic SIP Trunking, create a trunk (or reuse an existing one).
2

Point origination at Fish Audio

Add an origination URI: sip:1pv316az391.sip.livekit.cloud;transport=tcp.
3

Attach your number

On the trunk’s Numbers tab, add the phone number. Twilio routes its calls through the trunk from then on.
4

Import on Fish Audio

Twilio’s origination does not answer digest challenges, so authenticate by source address: allow Twilio’s published signaling IP ranges for the regions you use (see Twilio’s IP address list). In the console, the Twilio Elastic SIP Trunking preset button fills the ranges and sets the transport for you.
5

Enable outbound (optional)

On the trunk’s Termination tab, note the termination SIP URI (yourprefix.pstn.twilio.com) and attach a Credential List. Pass the host as termination_uri and the credentials as termination_auth_username and termination_auth_password. Credentials are required here: Fish Audio’s outbound traffic does not come from fixed IPs, so Twilio IP access control lists cannot authorize it.
6

Allow transfers (optional)

For cold transfers, enable Call Transfer (SIP REFER) in the trunk’s settings so Twilio honors the handoff.

Asterisk, FreePBX, and other SIP platforms

  • Route the number’s inbound calls to sip:1pv316az391.sip.livekit.cloud;transport=tcp.
  • Configure digest credentials on the trunk and pass the same pair as inbound_auth_username and inbound_auth_password; add your PBX’s public IPs to inbound_allowed_addresses for defense in depth.
  • For outbound, expose a termination host reachable from the internet and pass it as termination_uri, with digest credentials if your PBX requires registration or authentication.

Outbound calls and transfers

What an imported number can do depends on whether you configured a termination: The number object reports this as supports_outbound. Place calls with the same API as purchased numbers; see Outbound calls.

Custom SIP headers

termination_headers lets you tag every INVITE the platform sends through your termination, for example to identify the tenant or pick a route on your PBX:
The headers ride along on outbound calls and on warm-transfer consult legs, since both dial through the termination. Cold transfers hand the call off on the inbound leg and do not carry them. Names must be X- tokens or User-to-User, so the request’s own fields (From, To, Via) cannot be overridden, and X-Fish- is reserved for headers the platform sets itself. Values are 1 to 1024 printable ASCII characters (128 for User-to-User). Header names are case-insensitive, so a map with two spellings of the same name is rejected. Values that change per call, such as a campaign or account id, go on the call instead: sip_headers on the outbound call request is merged over these number-level headers. For matching calls to sessions you do not need a header of your own; see Match calls with your SIP records.

Match calls with your SIP records

Your carrier, SBC or PBX keeps its own record of every call: a CDR, a log line, a recording file. Two identifiers let you line those records up with Fish Audio sessions, one in each direction, and neither needs any configuration. Which one you use depends on where you start:
  • From a Fish Audio webhook to your records: read sip_call_id from the webhook’s session object and look it up in your CDR. This works without any change on your side, because every SIP system already records Call-ID.
  • From your records to Fish Audio: take the Call-ID from your CDR and list sessions with GET /v1/agent/sessions?sip_call_id=…, which returns the matching session. If your SBC logs custom headers, X-Fish-Session-Id from the INVITE gives you the session id directly.
When each identifier is present: The X-Fish-Session-Id header is only sent through your own trunk. Calls from platform-purchased numbers terminate on the platform’s carrier and carry no such header, but sip_call_id is still exposed for them. For your own business keys, such as a campaign or tenant id, add custom SIP headers on the number or per call. They complement the two identifiers above rather than replace them: a business key tells you which campaign a call belonged to, the identifiers tell you which call it was. A phone_call.dial_finished payload carries everything needed for the webhook-to-CDR direction:

Update the configuration

Change any part of an imported number’s trunk configuration in place: rotate digest credentials, adjust the allowed addresses, or add, change, and remove the termination. In the console, open Edit configuration from the number’s row menu; over the API, PUT the full desired configuration:
Request
The body is the import payload without phone_number, label, and agent_id, and it replaces the whole configuration. Three things to know:
  • An empty password next to a set username keeps the stored password, so you can edit other fields without re-entering secrets. Setting a username for the first time requires a password.
  • An empty termination_uri removes the termination and makes the number inbound-only again.
  • termination_headers replaces the stored map; omit it or send {} to remove all custom headers.
Updates apply in place: routing is never interrupted, and calls already in progress are unaffected. The response is the updated number object; for imported numbers it carries the non-secret configuration (inbound_auth_username, inbound_allowed_addresses, termination_uri, termination_transport, termination_auth_username, termination_headers) alongside supports_outbound.

Billing

Imported numbers are free on Fish Audio: no monthly rental, no phone surcharge, no transfer fees. Calls on them bill like web sessions (see Pricing). Your carrier continues to bill you directly for its side of the traffic.

Release

Releasing an imported number (DELETE /v1/agent/phone-numbers/$PHONE_NUMBER_ID) disconnects it from Fish Audio and returns 204. The number itself stays yours at the carrier; you can import it again later.

Going further

Inbound calls

What happens when someone dials a bound number.

Call transfers

Cold and warm handoffs to a human.

Phone numbers

The shared lifecycle: list, bind, label, release.

API introduction

API keys and workspace scoping for every request.