Verify API
One-time-password (OTP) issuance and validation across SMS, voice (TTS), email, and WhatsApp. Two patterns:- Direct — issue an OTP to a phone / email; validate the code the user enters.
- Profile-based — define a verify profile (channel, code length, expiration, locale, sender), then reference it by name from your app. Lets you tune behavior without redeploying.
/api/v1/verify
Authentication: API key (X-API-Key) or session JWT.
Send & check
Bulk send
POST /api/v1/verify/bulk fans an OTP out to up to 1000 recipients that share one
channel and (optionally) one verify profile_id / template_variables bag — built for
login surges and password-reset campaigns. Each recipient flows through the same
per-recipient pipeline as POST /verify/send (E.164 / email validation, per-recipient
rate limit, fraud velocity, wallet deduct, MessageRouter dispatch), so no new outbound path
is introduced. Recipients are de-duplicated (first-occurrence order preserved) and dispatched
with a bounded-parallel worker pool; one recipient’s failure never aborts the batch.
This endpoint is rate-limited to 2 requests / minute per tenant (each call can mint up to
1000 OTPs) — operators can raise it with a per-org override.
Request body
Response — a single envelope with batch totals plus one row per recipient (returned in
submission order):
- sent —
{ recipient, status: "sent", verification_id, channel } - failed —
{ recipient, status: "failed", error: { code, message } }
response.ok:
Verifications
GET /api/v1/verify/analytics aggregates verifications over a rolling look-back window.
Pass an optional window_days query parameter to size the window; the applied value is
echoed back in the response.
Query parameters
Response
GET /api/v1/verify/conversion-anomaly is the Verify Fraud Shield discovery signal for
artificially-inflated traffic (AIT / SMS-pumping). It scores each destination prefix by its
OTP completion rate (verified ÷ sent) over an optional window_days look-back (1–90,
default 30): destinations converting far below the tenant baseline against a material send
volume are flagged, and the response headlines an estimated carrier spend-at-risk (USD).
The payload is { window_days, total_sent, total_verified, baseline_conversion_rate, flagged_destinations, estimated_spend_at_risk_usd, destinations[] }, where each destination
carries { prefix, sent, verified, conversion_rate, score, band, anomaly, estimated_spend_at_risk_usd }. Read-only and tenant-scoped.
Profiles
Configs (alias of profiles)
/api/v1/verify/configs* is a frontend-facing alias of /profiles* — same controller,
identical payloads and behaviour. It exists because the dashboard consumes the /configs
naming; either path may be used interchangeably.
Channels
POST /api/v1/verify/send accepts channel: sms, whatsapp, voice, email, viber,
telegram, rcs, flashcall (delivery channels) plus the non-delivery / factor channels
silent, sna, totp, push, magic_link, backup_code. rcs delivers the code through
your tenant’s verified RCS Business Messaging agent (branded logo + checkmark, higher trust
than plain SMS), failing closed with 503 PROVIDER_NOT_WIRED when no RCS provider is
registered — so it slots cleanly into a fallback chain such as rcs → sms. flashcall
delivers the code as the trailing digits of a missed call’s caller ID; the recipient submits
those digits to /check. sna (Silent Network
Authentication) verifies SIM possession through the CAMARA broker when you pass a device-bound
device_token, returning status: "verified" with no OTP; without one it fails closed with
503 PROVIDER_NOT_WIRED. totp/push/backup_code short-circuit and steer you to
the matching factor endpoints below. See the Verify overview for the full
channel reference.
MFA factors
Possession-of-secret and phishing-resistant factors for end-tenant users. These factors carry no carrier delivery and no wallet deduct. Create/verify/delete require theverify:write scope.
These factors are API-only by design — there is no dashboard screen for them. TOTP, backup
codes, passkeys (WebAuthn / FIDO2), and push are enrolled, verified, and revoked from inside
your own application, where your end user is present to scan a QR code, complete a WebAuthn
ceremony, or approve a push prompt on their device. Drive them straight from the endpoints below
(or the SDK). The Verify dashboard covers OTP send/check, profiles, and analytics; the 2FA
control under Settings → Security protects your Orbit login and is separate from these end-user
factors.
TOTP (authenticator app)
Backup codes
Verify Push
Passkeys (WebAuthn / FIDO2)
Magic link
Example — send and validate an SMS OTP
200 with { data: { verification_id: "vrf_3f1c0b2a8e4d4f7a9c2b1e6d5a4c3b2a", status: "approved" } }. Failures are not 200 bodies — they surface in the error envelope, where details.attempts_remaining counts the tries left:
Codes have a small fixed number of attempts (default 3) before they’re exhausted. The check status enum is
["approved", "failed", "expired"] — failed and expired only ever appear inside the error envelope (or via GET /api/v1/verify/{id}), never as a 200 body. See Verify without an SDK for the full status model.
See also
- Channels — pick the right channel for your audience
- Webhooks → verify events