AI Voice Agents
Answer a number's calls with an AI voice agent: a Vapi agent over a sip: address, or your own media server over wss://.
When you finish this page inbound calls to a number are answered by an AI voice agent. You need a voice-enabled number and an agent to hand the calls to. An agent is a forward destination like any other, in one of 2 shapes: a hosted platform such as Vapi takes a sip: address, and a server of your own takes a wss:// URL that the call's live, bidirectional audio streams over.
Retell and ElevenLabs are sip: destinations with guides of their own: Retell and ElevenLabs.
Step 1: Point the number at the agent
Call POST /v1/phone-numbers/{id}/voice with forwardTo set to the agent's address. This sample uses a wss:// media server of your own; a sip: address goes in the same field.
import Zernio from '@zernio/node';
const zernio = new Zernio();
const phoneNumberId = '66d4e5f6a7b8c9d0e1f2a3b4';
const { data: voice } = await zernio.voice.enableVoiceOnNumber({
path: { id: phoneNumberId },
body: { forwardTo: 'wss://your-agent.example.com/media' }
});
console.log(voice.pstnForwardTo);Response (200):
{
"enabled": true,
"phoneNumber": "+14155550100",
"pstnForwardTo": "wss://your-agent.example.com/media",
"recordingEnabled": false,
"transcriptionEnabled": false,
"voicemailEnabled": true
}How the bridge works
- Your agent exposes a WebSocket endpoint that streams call audio in both directions, or a SIP address that accepts the call.
- On an inbound call, Zernio answers and bridges the audio to that destination; the agent speaks back over the same connection. To the caller it is one continuous call.
- There is no second phone leg either way, so you pay only the inbound leg.
If the stream cannot be established (a bad URL, a failed handshake), the already-answered caller falls to voicemail when it is enabled, and the call ends otherwise.
The same wss:// destination works as a per-call forwardTo override on outbound calls, so you can hand one outbound call to an agent without changing the number's default. Turn on recording or transcription on the number when you want Zernio to capture the conversation as well.
Vapi
A Vapi assistant answers over SIP. Create the assistant's SIP username in Vapi, then point the number at sip:{username}@sip.vapi.ai. Zernio authenticates with SIP digest, so nothing else is needed on your side.
curl -X POST "https://zernio.com/api/v1/phone-numbers/66d4e5f6a7b8c9d0e1f2a3b4/voice" \
-H "Authorization: Bearer $ZERNIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"forwardTo": "sip:agent-acme@sip.vapi.ai"}'Response (200):
{
"enabled": true,
"phoneNumber": "+14155550100",
"pstnForwardTo": "sip:agent-acme@sip.vapi.ai",
"voicemailEnabled": true
}The prompt, the voice and the tools stay on Vapi's side; Zernio owns the carrier leg and bridges the audio. A sip: agent bills the same way a wss:// one does: there is no PSTN leg on the far side, so you pay only for the inbound call.
Custom agents
Point forwardTo at your own wss:// endpoint to run your own speech-to-text, LLM and text-to-speech pipeline. Zernio does not proxy or re-encode the audio: it starts the stream on the carrier leg, and Telnyx, the carrier behind Zernio voice, opens the WebSocket to your server. Your side implements Telnyx's media streaming over WebSockets, which defines the start, media, stop and error events and carries the audio as base64 RTP payloads. Zernio fixes 3 of that protocol's settings:
| Setting | Value |
|---|---|
stream_track | inbound_track, so you receive the caller's audio |
stream_bidirectional_mode | rtp, so media frames you write back on the same socket reach the caller |
stream_bidirectional_codec | PCMU, G.711 u-law at 8 kHz, in both directions |
They are not configurable per number today, so build the agent against PCMU at 8 kHz. This is the same bidirectional media bridge that WhatsApp Calling uses, so an agent you build once serves both channels.
If it fails
A 409 with code: "invalid_resource_state" on the voice call means the number is attached to a SIP trunk; detach it with DELETE /v1/phone-numbers/{id}/sip-trunk first. A call that reaches voicemail or ends right after answering usually means the handshake failed: check the wss:// URL and that the server accepts the connection, then read callErrors on the call record. Every error uses the envelope in error handling.
Related
- Setup: voicemail, business hours, IVR and the blocklist around the agent.
- ElevenLabs and Retell: SIP-based agents.
- Outbound calls: hand an outbound call to the agent.
- Enable voice on a number: every field of the request.
Setup
Set where a number's inbound calls go and turn on voicemail, business hours, an IVR menu, a caller blocklist, recording and transcription.
Integrations
Put an ElevenLabs or Retell agent on a Zernio number by SIP forwarding or a SIP trunk, while the number, its KYC, SMS and WhatsApp stay with Zernio.