SMS vs voice
Two channels, one number. Different latency, different transports, different operations. Use both or use one — same auth either way.
At a glance
| SMS | Voice | |
|---|---|---|
| Transport | HTTP request/response | WebSocket (binary frames) |
| Direction | Inbound + outbound | Inbound + outbound |
| Send | POST /sms/send | POST /voice/dial |
| Receive | GET /sms/receive (atomic claim; MCP receive_sms for long-poll) | /voice/presence/<phone> WS |
| Stream | — | /voice/stream/<call_id> WS |
| Latency profile | Near-instant OTP end-to-end | Low-latency PCM audio |
| Auth | Bearer token | Bearer token |
SMS
Drop in for OTPs, notifications, two-way text agents. /sms/receive
is an atomic claim — two parallel calls won't double-deliver the
same message — that returns immediately. For long-poll behavior
(the typical OTP use case), use the MCP receive_sms
tool. For history (no mutation), use
/sms/messages.
Voice
Two WebSocket channels with different jobs:
- Presence WS — long-lived listener. Open it once with your bearer, leave it open as long as you accept calls. Server pushes three event types over the call lifecycle:
{event: "ringing", call_id, caller, stream_url},{event: "answered", call_id}, and{event: "ended", call_id, missed}. - Stream WS — per-call audio channel. Open it on the
stream_urlfrom the presence event (or from a/voice/dialresponse). Send + receive 16 kHz PCM as binary frames. Close to hang up.
Same number, both channels
No separate provisioning. The phone number returned by /subscribe
is the SMS number AND the voice number. Carrier-verified caller ID,
so downstream voice services treat the call as a legitimate
consumer call.
Where to go next
- Quickstart — full SMS round-trip in four
curlcalls - Voice overview — full voice flow with both channels
- MCP — call both channels through Model Context Protocol