This is an outbound, API-triggered flow (Wave calls the user). It is distinct
from IVR Menus (a caller dials you). Voice Authentication is
gated — ask your Wave contact to enable it for your account.
Authentication & tiers
Every/v1/voice-auth endpoint needs a production API key (sk_live_) with
the voice_auth:write scope (or voice_auth:read for status). Sandbox keys
can’t place real authentication calls.
Create a challenge
201 returns the challenge id and (for number_match) the digits to display:
The verdict — fail closed
A challenge resolves to exactly one terminalstate:
Answering-machine detection (AMD)
Before any prompt, Wave classifies who answered. A machine or voicemail answer resolves tomachine_detected and the approval prompt is never played — so a
voicemail can never capture a keypress or leave a stray approval. This is enforced,
not best-effort.
The classification is returned as amd_result (human or machine) on both the
status API and the webhook. It errs on the safe side: an ambiguous answer is
treated as a machine and blocked. As a result, a real person who gives a long
spoken greeting can occasionally be misread as a machine and safely declined.
High-assurance number matching
For high-value actions (payments, admin changes), setassurance: "number_match".
The 201 response includes match_digits (e.g. "47") — display them to your
user. The call then asks the callee to key those exact digits to approve.
There is no “press 1” shortcut in this mode, and a wrong or partial entry
resolves to timeout (a non-grant) — so it defeats “just press 1” MFA-fatigue and
accidental approvals, and the short code can’t be guessed (a wrong entry ends the
attempt).
Caller brand
By default the call says “This is Wave calling…”. Your organization can make it say your own name (a caller brand) instead, in the call language: “This is Acme calling to verify a login on Acme App…”. A caller brand is a spoken name only — the caller ID stays Wave’s verified number. Get a brand approved (portal). In the developer portal, open Caller brands and request a brand with an English name and an Arabic name. It needs two approvals before a call can use it: an owner or admin of your organization (a request by an owner or admin skips this step), then Wave. Names are 2–40 characters (letters, digits, spaces and. & ' -; no web
addresses; no run of 4 or more digits); the Arabic name must use Arabic letters.
A brand’s names never change — to use a new name, request a new brand.
Use it on a challenge. Pass brand_id on create. The call speaks:
- the
brand_idyou send, if any; else - your project’s default brand (set on the Caller brands page); else
- “Wave” — so an integration that never sends
brand_idkeeps working.
brand_id must be an approved brand of your organization; anything else
(unknown, pending, rejected, retired, another organization’s) returns
422 VOICE_AUTH_BRAND_NOT_APPROVED and no call is placed. The challenge
keeps the name it started with, so a brand retired mid-call never changes what
the user hears. The 201, the status API and the webhook carry brand_id
(null = “Wave”).
List your brands from your backend.
voice_auth:read. Returns every brand of your
organization with its status (pending_tenant, pending_wave, approved,
rejected, retired), the reject reason, and is_default for the key’s
project. Paginate with cursor = the previous next_cursor.
If Wave’s text-to-speech is unavailable, Wave can fall back to pre-recorded
prompts. In that mode the standard prompt does not name a caller (neither
“Wave” nor your brand); the number-match prompt still says your brand.
brand_id is still stored and returned.The result webhook
On a terminal state Wave sends a signedvoice_auth.completed event to your
project’s webhook endpoint:
X-Wave-Signature—sha256=…, an HMAC-SHA256 of the raw body with your endpoint’s signing secret. Reject a mismatch.X-Wave-Timestamp— unix seconds; reject deliveries older than your window (e.g. 5 minutes).X-Wave-Event-Id— stable across retries; dedupe on it.
Poll status
state, attempts, amd_result, brand_id, timestamps,
and (once terminal) the decision. Scoped to your organization only.
Testing each verdict
There is no sandbox simulation for Voice-Auth — testing needs a production key and a real phone you control. Each terminal state is reproducible by how you answer the call:
Confirm each on the status API (
state, amd_result, attempts). Space out
repeated tests to the same number, or you’ll hit VOICE_AUTH_RATE_LIMITED.
Idempotency, rate limits & abuse controls
- Idempotency — send an
Idempotency-Key; a retry with the same key returns the same challenge and never places a second call. - Per-number & per-originator limits — Wave caps challenges to a single number
and per API key, and blocks known-abusive numbers/prefixes, to prevent
voice-bombing and toll fraud. Over a limit →
429.

