Bring your own SIP trunk: AI voice agents on the carrier you already use
Wixzel Voice runs your AI voice agents over the SIP trunk you already own, from Twilio, Telnyx, Plivo, Vonage, Bandwidth, Exotel, Vobiz or any SIP provider. Your numbers and your carrier rates stay yours: Wixzel Voice never sells or ports numbers and never marks up minutes.
Last updated
Published . The carrier names are trademarks of their owners; Wixzel Voice integrates with them over standard SIP and is not affiliated with or endorsed by any of them.
Why bring your own SIP trunk?
Wixzel Voice is a voice AI API for building AI agents that place and answer real phone calls over your own SIP trunk, or talk to people in your web and mobile apps. One API key and one prepaid balance cover every voice engine, billed per second of actual usage.
Wixzel Voice is the voice AI API for building AI phone agents, and it stops at the voice. The phone line is yours. That is a deliberate trade: it means one more step before your first call, and it means Wixzel Voice is never between you and your telephony provider. There is no markup, no resale margin, and no reason for Wixzel Voice to care which carrier you pick.
- Your numbers stay yours. Wixzel Voice does not sell or port numbers, so leaving costs you nothing but a setting at your carrier.
- Your carrier rates stay yours. The carrier bills you directly for the line. Wixzel Voice charges only for the voice engine, billed per second of actual usage from a prepaid balance.
- Your compliance stays with your carrier. Caller ID provisioning, KYC and destination registration are between you and the carrier you already trust with them.
How does a bring-your-own SIP trunk connect?
A Wixzel Voice trunk has two halves, and they live in different parts of your carrier's console. Both addresses are returned on every trunk, so read them from the API rather than trusting a page to stay current.
Add 95.216.218.102 to your carrier's IP allowlist. Nearly every carrier authorises outbound calls by source IP, and a trunk that is correct in every other respect still fails without it.
Set your carrier's inbound destination to sip:95.216.218.102:5090. Miss it and a number simply never rings: the carrier has nowhere to deliver the call, so nothing appears in any log.
curl https://api.voice.wixzel.com/v1/sip-trunks \
-H "Authorization: Bearer $WIXZEL_API_KEY" \
| jq '.data[0] | {platform_ip, origination_uri}'Which carriers have a setup guide?
Every carrier below has its own page with what to buy, what to allowlist, where inbound calls go, the trunk request that fits it, and the gotchas that are specific to it, with a link to the full console steps in the docs. Each carrier names the two settings differently; the setting is the same.
| Carrier | Product | Allowlist is called | Inbound destination is called | Transport |
|---|---|---|---|---|
| Twilio | Elastic SIP Trunking | IP Access Control List | Origination SIP URI | udp |
| Telnyx | SIP Connections | authorised addresses on an IP-authenticated SIP Connection | Not named in the guide yet | udp |
| Plivo | Zentrunk | IP Access Control List | Primary URI | udp |
| Vonage | Programmable SIP | ACL (Access Control List) | Not named in the guide yet | udp |
| Bandwidth | - | Voice IP addresses / DNS hosts on a location | Origination | udp |
| Exotel | Virtual SIP Trunking (vSIP) | ACL mapped to the trunk | Destination URI | tcp |
| Vobiz | - | IP ACL | Not named in the guide yet | udp |
| Any SIP provider | - | IP ACL, allowlist or authorised address | Origination URI, or the carrier's name for it | udp |
What do carriers call the two settings?
| What it does | Names carriers use |
|---|---|
| Authorise calls from Wixzel Voice | IP ACL, IP Access Control List, Access Control List, allowlist, authorised address, network bridge |
| Deliver calls to Wixzel Voice | Origination URI, Primary URI, Destination URI, termination point, destination SIP URI, host |
Two more settings decide whether a correct-looking trunk works. Number format: Twilio, Telnyx and Vobiz expect the leading +; many wholesale carriers cannot parse it and time out with 408, which send_plus: false fixes. Transport: UDP is the default almost everywhere, but Exotel does not take UDP, so an Exotel trunk runs over TCP, and a mismatch looks like the carrier ignoring you.
What happens when a call does not connect?
A call the API accepted and the carrier then refused shows status: "failed" (or busy for cause 17 and no-answer for cause 19) on GET /v1/calls/{id}, with failure_code (the Q.850 cause from the carrier, the stable thing to branch on) and failure_reason (what to do, in plain language). Its cost_micros is null: a call that never connected costs nothing, and the credit reserved for it is released.
Read your carrier's own call log first. If the call is not listed, it never reached the carrier: the allowlist, the trunk host or a firewall is wrong. If it is listed with zero answered and zero cost, the carrier accepted the INVITE and then cleared it, which is a carrier permission problem far more often than a configuration one: the caller ID you present must be authorised on the account, KYC and any destination registration must be complete where the market requires them, and the destination range must be enabled, not just funded. These are the causes the API explains:
| failure_code | Carrier said | What it means | What to do |
|---|---|---|---|
| 1 | 404 | The carrier does not recognise the number. | Check the number format. If the carrier needs a technical dial prefix, set dial_prefix on the trunk. |
| 3 | - | The carrier has no route to this destination. | The destination is probably not enabled on your carrier account. Ask the carrier to open the route. |
| 16 | - | Normal clearing: the far end ended the call deliberately. | On a call that was never answered, read it as something upstream choosing to end the call, not as a completed call. |
| 17 | 486 | The number is busy. | - |
| 18 | 480 | The destination did not respond. | The phone may be switched off or unreachable. |
| 19 | - | It rang and nobody answered. | Nothing to fix: the trunk and the number both work. |
| 20 | - | The subscriber is absent or unreachable. | - |
| 21 | 403 / 407 | The carrier rejected the call outright, before it tried the number. | In order of likelihood: the carrier account is suspended, closed or out of balance; the trunk username or password is wrong; platform_ip is not allowlisted; or the caller ID is not authorised on the account. Check the carrier dashboard first. |
| 22 | - | The number has changed and is no longer in service. | - |
| 27 | - | The destination is out of order. | - |
| 28 | 484 | The carrier rejected the number format. | If the carrier needs a technical dial prefix, set dial_prefix on the trunk. |
| 34 | - | The carrier has no circuits available (congestion). | Retry shortly. |
| 38 | 500 / 501 / 502 | The carrier returned a server error. | - |
| 41 | 503 | The carrier is reachable but refusing calls. | Usually capacity, balance, or a route being down at the carrier. |
| 42 | - | The carrier's switch is congested. | Retry shortly. |
| 58 | 488 | Codec mismatch: the carrier rejected the audio codecs offered. | Offer G.711 (PCMU or PCMA) on the carrier side. G.729 is not supported. |
| 88 | 493 | The carrier rejected the call as incompatible. | - |
| 102 | 408 | The carrier did not respond in time. | Verify the trunk host and port, and that the carrier accepts SIP from platform_ip. If the carrier shows the call as received but never answered, set send_plus to false. |
These are refused by POST /v1/calls before anything is dialled, and cost nothing:
| HTTP status | code | What it means |
|---|---|---|
| 400 | no_outbound_number | No caller ID: pass from_number_id, or set outbound_phone_number_id on the agent. |
| 400 | number_not_on_trunk | The calling number has no sip_trunk_id, so there is nowhere to send the call. |
| 400 | missing_idempotency_key | The raw HTTP request had no Idempotency-Key. The SDKs send one for you. |
| 402 | insufficient_credits | The balance cannot fund the first 15 seconds. Top up and retry. |
| 429 | rate_limit_exceeded | More than 10 requests per second on this key. Wait for Retry-After; a 429 is never charged. |
| 503 | - | The voice engine is unavailable, the account already has as many calls in progress as its concurrent-call limit (5 by default), or the API key has reached its spend limit. The message says which. |
GET /v1/sip-trunks/{id}/status shows whether the trunk is reachable right now and lists its last few carrier rejections; POST /v1/sip-trunks/{id}/test probes it on demand, and GET /v1/sip-trunks/{id}/logs returns the SIP log.
curl https://api.voice.wixzel.com/v1/sip-trunks/$TRUNK_ID/status \
-H "Authorization: Bearer $WIXZEL_API_KEY"An inbound number that never rings is not a failure you can look up: if the carrier has no destination set, the call never leaves the carrier, so there is no failed call to inspect. Check the inbound destination, that the number is attached to the trunk on the carrier side, and that it has an inbound_agent_id. If calls connect but nobody hears anything, the media is not getting through: a firewall in the path has to allow the RTP range, not just the signalling port.
Frequently asked questions
- Why does Wixzel Voice need my own SIP trunk?
- Because Wixzel Voice never sells or ports phone numbers and never resells telephony. Phone calls run over a trunk from a carrier you choose, so your numbers stay yours, your carrier bills you directly at your rates with no markup, and Wixzel Voice has no reason to care which carrier you pick. The cost is one more setup step before the first call.
- Which carriers work with Wixzel Voice?
- Any carrier that offers a SIP trunk with IP allowlisting (or username and password authentication) and a configurable destination for inbound calls. There are setup guides for Twilio, Telnyx, Plivo, Vonage, Bandwidth, Exotel, Vobiz, and a general one for any other SIP provider.
- What are the two settings a trunk needs?
- The outbound half is an allowlist entry at the carrier for
platform_ip(95.216.218.102today), so the carrier accepts calls from the platform. The inbound half is the carrier's destination for inbound calls set toorigination_uri(sip:95.216.218.102:5090today), so calls to your numbers reach the platform; a carrier that takes a bare host and port, or will not send UDP, needs a different form of it, which its guide gives. Both are returned on every trunk byGET /v1/sip-trunks: read them from there rather than copying them from a page. - Can I keep my existing phone number?
- Yes, if it is on a SIP trunk you can route. Register it with
POST /v1/phone-numberson that trunk and give it aninbound_agent_id. Wixzel Voice does not port numbers, so the number stays with your carrier. - How is my carrier password stored?
- A new or updated password is encrypted at rest with AES-256-GCM under a key held only by the deployment; one saved under the older scheme is re-encrypted that way the next time it is saved. It is never returned by the API again, including to you. Store it wherever you keep the rest of your carrier credentials.
- Do I need a SIP trunk for a web or mobile app?
- No. A trunk is only for phone calls. To put an agent in a web page or an app, your server mints a realtime session with
POST /v1/realtime/sessionsand the app talks to the agent over a WebSocket, with no trunk and no number. - Do trunk changes need a restart?
- No. Creating, updating or deleting a trunk rewrites the telephony configuration and reloads it, so a change takes effect immediately.
- Do I buy phone numbers from Wixzel Voice or from my carrier?
- From your carrier. Wixzel Voice never sells or ports phone numbers and never marks up carrier minutes. You register a number you already own with
POST /v1/phone-numberson the trunk it lives on. - Why does my number never ring the agent?
- Inbound is configured separately from outbound. Your carrier has to deliver the call to the
origination_uriyour trunk returns (sip:95.216.218.102:5090today), the number has to be attached to that trunk on the carrier side, and the number needs aninbound_agent_idin Wixzel Voice. Without the first, the call never leaves the carrier and nothing appears in any log. - What does an AI call over my own SIP trunk cost?
- Two bills. Your carrier charges its own per-minute rate for the line, directly to you. Wixzel Voice charges the voice engine per second of actual usage: from $0.0466 per connected minute on gemini-live to $0.1851 on deepgram-agent, prepaid from a minimum top-up of $5, with no subscription and no free tier. A call that never connects costs nothing on the Wixzel Voice side.
Carrier guides
Elastic SIP Trunking. Allowlist the platform IP in a Termination IP ACL, and set the Origination SIP URI for inbound calls.
An IP-authenticated SIP Connection. Add the platform IP as an authorised address, pick an inbound region, and match the transport.
Zentrunk. An outbound trunk with the platform IP in its IP ACL, and a separate inbound trunk whose Primary URI points at the platform.
Programmable SIP. Add the platform IP to the trunk's ACL in the SIP Dashboard, and send inbound calls to the origination URI.
No SIP REGISTER, so a static IP is required. Add the platform IP to the location, configure Termination and Origination separately, or use network-bridge credentials.
Virtual SIP Trunking (vSIP), for Indian numbering, over TCP. The platform IP mapped to the trunk as an ACL, and the Destination URI pointed at the platform's TCP port for inbound.
A per-trunk SIP domain as the host, the platform IP in the trunk's IP ACL, and "origination URI" meaning the opposite of what it means on Twilio.
The pattern is the same everywhere: allowlist the platform IP, set the inbound destination to the origination URI, and match the number format and transport.
The four requests from nothing to a ringing phone, with what a call returns and what it costs.
