Verification Call-back (Voice OTP)
Verification Call-back (Voice OTP)
What you build: an API call that places an outbound call to a user and speaks a one-time code, then tells you whether they picked up. Handy for fintech onboarding and step-up auth in Nigeria, where SMS OTPs are sometimes delayed or dropped by carriers.
Who it’s for: developers adding voice as a fallback (or primary) OTP channel, or confirming a sensitive action (“we’re calling to confirm your ₦50,000 transfer”).
Important: the call needs an agent, and the code is fetched at call time. POST /v1/calls takes {agent_id, from_number, to_number} — there is no per-call “greeting” or “metadata” field to inject a dynamic code. So the code can’t be baked into the request. Instead, the agent looks the code up when the call connects — via a tool call to your backend, or a BYOM agent that returns the code as its first turn. Both key off the callee’s number (to_number) or the call_id.
Prerequisites
- A
from_numberon your workspace (GET /v1/numbers). - A verification agent (below). You own the code lifecycle (generation, TTL, single-use) — Voicebip places the call and speaks what the agent returns.
- A webhook receiver for
call.completed.
Step 1 — Create the verification agent
The cleanest pattern is a tool-calling agent with one tool, get_verification_code, that your backend answers with the code for the given number. The agent’s job is to greet, call the tool, and read the digits.
Your webhook_url receives the tool.invocation (see Tool-calling) with tool.arguments.to_number, looks up the code for that number, and returns e.g. { "code": "4, 8, 2, 1" } (spacing the digits helps TTS read them individually).
Step 2 — Place the call
Store the code first, then place the call. The request is just the three required fields (plus an optional per-call webhook_url):
status is initiated when dialed synchronously, or queued when accepted onto the carrier-safe paced lane (dials when the carrier has headroom). Either way you learn the outcome from the call.* webhooks. Correlate by call_id (returned here) or to_number — there’s no free-form metadata field to round-trip.
Step 3 — Learn the outcome
call.completed tells you whether it connected. Treat the code as delivered only when the call actually completed and the caller was human:
The Call object’s status distinguishes the failure modes: completed, no_answer, busy, failed. Poll GET /v1/calls/{call_id} if you missed the webhook.
Step 4 (optional) — Confirm-an-action variant
To confirm rather than inform (“press 1 to approve this transfer”), give the agent a confirm_action tool and instruct it to call it on a spoken “yes”/“one”. You can also drive DTMF programmatically with POST /v1/calls/{call_id}/dtmf. See Tool-calling for the handler.
Test it (sandbox)
With pk_test_, no phone rings, but the lifecycle webhooks fire so you can exercise your verification flow end-to-end:
Security
Treat the code like any OTP — short TTL (e.g. 5 min), single use, rate-limited per user, never logged. Voicebip redacts message content in its own logs; the code your tool returns is yours to protect.
Troubleshooting
Next steps
- Add SMS as an automatic fallback when the call doesn’t connect → Multi-channel cascade.
- Build the code-lookup tool handler → Tool-calling.
- Run verification calls at list scale → Campaigns deep-dive.