verification.approved / verification.failed, then layer on a fallback chain and an optional fraud-score pre-check.
Before you start
- An Orbit API key (
dv_live_sk_…ordv_test_sk_…). Key-level rate limits (20/sendcalls and 60/checkcalls per minute per key) are ample for this tutorial. - The channels you plan to deliver on connected under Settings → Channels if you use WhatsApp, voice, or email.
1. Create a verification profile
A profile pins the code length, TTL, attempt cap, hourly rate, and (optionally) a channel fallback chain so every send inherits those rules. Without a profile you get platform defaults: 6-digit code, 600-second TTL, 3 attempts, single channel.id — you pass it as profile_id on every send that should follow these rules.
2. Send the OTP
The user taps “send code” in your app; your backend posts the send:verification_id against your user session — you’ll need it for the check call. expires_at is the row TTL; after it, checks fail with EXPIRED_TOKEN (410).
3. Check the code
Your UI collects the digits, then your backend validates them:422 VALIDATION_ERROR with attempts_remaining in the details block — surface that to the user. The full status and error matrix is in Verify integration without our SDK.
4. Subscribe to verification events (optional)
Instead of (or in addition to) the synchronous check response, subscribe to webhooks so post-verification side effects (mint a session, unlock an account) run on an idempotent worker. Configure the endpoint under Settings → Webhooks and pick theverification.* events — primarily verification.approved, verification.failed, and verification.fallback_exhausted.
A minimal receiver (Node, no framework):
5. Add a fallback chain
SMS alone fails for users in poor coverage or with full-connectivity blockers. Make the profile’schannels array ordered: try each in turn.
Update your profile:
templates.voice script containing {{code}}. Fallback advances asynchronously on provider rejection or timeout, re-delivering the same code on each channel — so a recipient who reads the SMS and then hears the voice call gets one identical code. Watch verification.fallback_triggered for each hop or verification.fallback_exhausted for a hard failure. Full profile mechanics: Verify profiles and fallback chains.
6. Optional: fraud-score gate
Before dispatching an OTP you can dip the number for risk.POST /verify/fraud-score returns a 0–100 score with low/medium/high banding and an advisory allow/step_up/block. POST /verify/fraud-gate is the composite operator fusion (SIM-swap, port-event, silent-network-auth when available) returning allow/review/deny. Both are rate-limited to 20 dips per minute per tenant.
Use the gate to refuse-or-step-up risky numbers before you spend an OTP; when a dip fails in flight the verification lands in the review bucket rather than a fabricated clean verdict. The same fraud surface is visible in the dashboard console along with code-attempt logs and fallback timelines — see the Verify console guide and the full API at Verify API reference.
7. Common errors
Next steps
- Verify overview — channel catalog and factor suite (TOTP, push, passkeys).
- Verify API reference — every endpoint shape.