How a trunk works
A trunk belongs to a project and has a direction:
On a call, the trunk is a participant named
server:sip:<trunkId>. Its
connection reports transport sip. The people on WhatsApp always hear your
PBX, and your PBX always hears them. It never hears its own audio. Whether your
PBX and your other participants hear each other follows the session’s
conference mode: on (the default),
they hear each other; off, each hears only the people on WhatsApp. SIP trunks
carry audio only.
Manage trunks with the Platform API. Reading requires sessions:read; every
change requires sessions:manage. Use a team key with projectId, or a project
token for its own project. Client tokens cannot manage trunks.
The TypeScript SDK adds Client.sipTrunks in its next release; until then, call
the API directly. You can also manage trunks in the Console: open a project, go to Calls, and
choose Manage SIP trunks. See Calls in the Console.
Create a trunk
201 with the trunk and, for inbound and both
trunks, inboundCredentials:
maxConcurrentCalls calls at once (1 to 500, default 30).
Send WhatsApp calls to your PBX
Set these fields inoutbound:
Then route the session’s incoming calls to the trunk:
outbound
or both. Settings you omit keep their values; a later update without
sipTrunkId keeps the trunk. To stop sending calls to the trunk, send
"inboundRoute": "clients", which also clears sipTrunkId. On a Cloud API
Number, incoming calls reach the trunk only when hostCloudApiCalls is true;
see Cloud API Numbers. See
Update call settings.
When a routed call rings, Polymorfa calls targetUri. The call still rings for
your SDK participants, and they can answer it first. The request to your PBX
carries the WhatsApp call ID in the X-Polymorfa-Call-ID header.
- When your PBX answers, Polymorfa answers the WhatsApp call as
server:sip:<trunkId>. WithsipClaim: truethe answer claims the call: other participants stop ringing and cannot join. WithsipClaim: falsethe call stays shared and other participants can join. See Answer, join, or decline. - When your PBX does not answer, the WhatsApp call keeps ringing for your other participants until it is answered, declined, or the ring window ends.
- When your PBX hangs up, its connection leaves. If the trunk claimed the call, the call ends. When the WhatsApp call ends, Polymorfa hangs up your PBX.
call.connection_left event reports why its connection left:
A trunk that never answered reports only the departure, with no preceding join.
Call WhatsApp from your PBX
Configure your PBX with:- the SIP address and ports Polymorfa support gave you;
inboundCredentials.usernameandpasswordfor digest authentication; and- the WhatsApp number to call, in E.164 format, as the user part of the request
URI, for example
sip:+14155550100@SIP_ADDRESS. The leading+is optional and a00prefix is accepted.
inbound.session to the session that places the calls. Polymorfa accepts a
call only when the digest credentials are valid, the request comes from one of
allowedAddresses, the trunk is enabled, its project is active, and the
number matches one of
allowedDestinations (or the list is empty). The call counts toward the same
per-minute outbound limit as calls placed through the API from that session.
The trunk claims every call it places. Your PBX receives:
Every authentication refusal is the same
403, so the response does not
reveal which check failed. Repeated failed authentication temporarily blocks
the sending address. When your PBX cancels a ringing call, the WhatsApp call
ends. When your PBX hangs up a connected call, the call ends for everyone.
Media and security
- Codecs. PCMU and PCMA (8 kHz) and Opus (48 kHz, mono), in the order of
codecs. Your PBX must offer media in the initial INVITE; calls without an offer are refused with488. Mid-call media changes are refused. - Packets. 20 ms of audio per RTP packet.
- Encryption. With
tls, signaling is encrypted and media uses SRTP with SDES keys (AES_CM_128_HMAC_SHA1_80orAES_CM_128_HMAC_SHA1_32). Plain RTP offers over TLS and SRTP offers over UDP or TCP are refused. Usetlswhen your PBX is reachable over the internet. - Addresses. Send media from the address in your PBX’s session description or from the address your signaling comes from. Polymorfa sends media back to the address your first media packet arrives from.
- PBX behind NAT. A private address (
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10, orfc00::/7) in the session description works only when your signaling comes from a public address. Polymorfa then sends no media until your PBX’s first media packet arrives from that signaling address, so your PBX must start sending media. - Refused addresses. Session descriptions with a loopback, link-local,
multicast, unspecified, or reserved media address are refused: your PBX’s
call gets
488, and a call to your PBX ends withsip_unavailable. Trunk targets on private, loopback, or link-local addresses are rejected.
Manage trunks
PATCHchanges only the fields you send. OmitauthPasswordto keep the stored password; sendauthUsername: nullto remove both. SendexpectedRevisionto refuse the change with409 sip_trunk_revision_conflictwhen someone else changed the trunk first.- Changing
directiontooutboundremoves the inbound credentials. Changing anoutboundtrunk toinboundorbothcreates a new username; rotate its credentials to get the password. - Rotating credentials replaces the password. The old password stops working
within about a minute. Rotation returns
409 state_conflictforoutboundtrunks. - Deleting a trunk that a session still routes to returns
409 sip_trunk_in_use. Route the session back toclientsfirst. - Changes reach call routing within about a minute. Calls in progress continue.
- When a trunk’s project is deactivated, changing the trunk or rotating its
credentials returns
404 resource_not_found. Polymorfa sends no calls to the trunk and refuses its PBX’s calls within about a minute. You can still list, read, and delete the trunk.
Errors
See Errors for the full error format.