Phone, SMS & voice
Connect your own PBX or voice AI platform to your Pozy numbers over SIP, so it receives inbound calls directly and places outbound calls with your number.
A SIP trunk takes Pozy out of the audio path for a number. Inbound calls go straight to your SIP host, and your system can place outbound calls through the trunk presenting the number as caller ID. Use it when a PBX, a contact-centre platform or a voice AI platform should own the calls, while the number, its KYC, SMS and WhatsApp messaging stay on Pozy.
#Forwarding or a trunk
Both connect a number to a SIP destination. They differ in who controls the call.
Forwarding (forwardTo: "sip:...") |
SIP trunk | |
|---|---|---|
| Inbound calls reach your system | Yes, bridged by Pozy | Yes, delivered directly |
| Your system can place outbound calls | No | Yes, with your number as caller ID |
| Pozy voicemail, IVR, business hours, recording, browser calling | Available | Paused while the number is on the trunk |
| Call webhooks and history in Pozy | Yes | No: your platform's own call events are the source |
| WhatsApp calling on the number | Works | Not available |
| SMS and WhatsApp messaging | Unaffected | Unaffected |
If you only need inbound calls answered, forwarding is simpler. Choose a trunk when your system must also dial out.
#Create the trunk
POST /v1/phone-numbers/sip-trunks with a label and your sipHost. Set sipPort and transport (tls, tcp or udp) if your host needs them.
curl -X POST https://api.pozy.ai/v1/phone-numbers/sip-trunks \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "label": "Support PBX", "sipHost": "pbx.acme.example.com", "sipPort": 5061, "transport": "tls" }'const trunkRes = await fetch("https://api.pozy.ai/v1/phone-numbers/sip-trunks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.POZY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ label: "Support PBX", sipHost: "pbx.acme.example.com", sipPort: 5061, transport: "tls" }),
});
const trunk = await trunkRes.json();
if (!trunkRes.ok) throw new Error(trunk.error);
// Store trunk.digestPassword now. It is only returned here.
console.log(trunk.id, trunk.termination.uri, trunk.termination.username);import os, requests
resp = requests.post(
"https://api.pozy.ai/v1/phone-numbers/sip-trunks",
headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
json={"label": "Support PBX", "sipHost": "pbx.acme.example.com", "sipPort": 5061, "transport": "tls"},
timeout=30,
)
trunk = resp.json()
print(trunk["id"], trunk["termination"], trunk["digestPassword"]){
"id": "66f6a7b8c9d0e1f2a3b4c5d6",
"label": "Support PBX",
"sipHost": "pbx.acme.example.com",
"sipPort": 5061,
"transport": "tls",
"termination": { "uri": "sip.example-termination.net", "username": "pz66f6a7b8c9d0e1f2" },
"numbersAttached": 0,
"digestPassword": "rK9v...Qw2"
}digestPassword is shown only in this response. Store it in your secrets manager. If you lose it, rotate with POST /v1/phone-numbers/sip-trunks/{id}/rotate-credentials, which invalidates the old password at once.
#Configure your system
In your PBX or voice platform, point outbound calls at termination.uri and authenticate with SIP digest using termination.username and the digestPassword. No IP allowlisting is needed on either side. Register numbers in E.164 with the leading +: the trunk uses +E.164 in both directions.
#Free the number
A number on a trunk cannot also run Pozy calling. Turn calling off with DELETE /v1/phone-numbers/{id}/voice, and turn off WhatsApp calling if it is on.
#Attach the number
POST /v1/phone-numbers/{id}/sip-trunk with the trunkId. From that moment, inbound calls go to your sipHost.
curl -X POST https://api.pozy.ai/v1/phone-numbers/6650d2e1b3c4d5e6f7081923/sip-trunk \
-H "Authorization: Bearer $POZY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "trunkId": "66f6a7b8c9d0e1f2a3b4c5d6" }'Detach any time with DELETE /v1/phone-numbers/{id}/sip-trunk. Pozy routing and voice features come back immediately.
#One trunk or several
Attach as many numbers as you like to one trunk. Several trunks can point at the same host, each with its own credentials and spend cap. That keeps environments or clients apart: production and staging, or one trunk per agency client.
#Security and media
| Topic | Behaviour |
|---|---|
| Signalling | TLS by default (TLS 1.2 or later at the edge). TCP and UDP are available when your host needs them. |
| Media | SRTP is paired with TLS. G.711 (PCMU and PCMA) is native. Any transcoding happens in the carrier core, not on your side. |
| Authentication | SIP digest. Credentials are generated by Pozy, shown once, rotatable, and unique per trunk. |
| Caller ID | Outbound calls can only present numbers hosted in your workspace (or verified onto it). Other From numbers are rejected, so the trunk cannot spoof numbers you do not own. |
| Transfers | Inbound SIP REFER is honoured, so your system can transfer callers mid-call. |
| Emergency calls | Not supported |
#Caps
Each trunk carries protective limits. Contact support@pozy.ai to raise them.
| Cap | Default |
|---|---|
| Concurrent calls | 10 |
| Daily spend | A ceiling per trunk |
| Destination rate | A per-minute rate cap, which blocks premium-rate routes |
| Destinations | Countries where Pozy sells numbers |
Calls through a trunk bill per minute at the route's normal rate. Branded calling still applies to calls from an attached number.
#Errors
| Status | Code | Cause | Fix |
|---|---|---|---|
409 |
invalid_resource_state |
On attach: the number still has calling or WhatsApp calling on, is mid WhatsApp verification, or is not active | Turn those off, wait for active, then attach |
409 |
invalid_resource_state |
On voice settings or release: the number is attached to a trunk | Detach it first |
403 |
feature_not_available |
Telephony billing is not set up for the workspace | Add a payment method, or contact support@pozy.ai |
#Endpoints
| Endpoint | What it does |
|---|---|
GET /v1/phone-numbers/sip-trunks |
List SIP trunks |
POST /v1/phone-numbers/sip-trunks |
Create a SIP trunk |
GET /v1/phone-numbers/sip-trunks/{id} |
Get a SIP trunk |
DELETE /v1/phone-numbers/sip-trunks/{id} |
Delete a SIP trunk |
POST /v1/phone-numbers/sip-trunks/{id}/rotate-credentials |
Rotate a SIP trunk's password |
POST /v1/phone-numbers/{id}/sip-trunk |
Attach a number to a SIP trunk |
DELETE /v1/phone-numbers/{id}/sip-trunk |
Detach a number from its SIP trunk |
DELETE /v1/phone-numbers/{id}/voice |
Disable phone calling on a number |