Skip to main content
Verify is a messaging-based one-time code primitive. Your backend asks Wave to send a verification code to a phone number over WhatsApp; the user reads and submits it; you check the code and get a trustworthy, machine-readable verdict over a signed webhook (and a status API). It’s a phone-possession factor for signup, step-up auth, and password reset — a security primitive, not a menu-tree IVR. Because the outcome gates access, it is fail-closed by design: only an exact, in-time, in-attempt-budget code grants, and a wrong or expired code never does.
This is an API-triggered, outbound flow (Wave sends the user a code). It is distinct from Voice Authentication (a call-to-approve primitive). Verify is gated — ask your Wave contact to enable it for your account.

Authentication & tiers

Every /v1/verify endpoint needs a production API key (sk_live_) with the verify:write scope (or verify:read for status). Sandbox keys can’t send real verification codes.

Create a verification

201 returns the verification id and its expiry:

The verdict — fail closed

A verification resolves to exactly one terminal state:
Only verified grants access. Treat every other state as a non-grant and fail closed. Always confirm the verdict via the status API (below) before honoring an approval — defense in depth.

Check the code

200 returns the verification state and a boolean verdict:
On a wrong code, the response carries state: "awaiting_check" and verified: false; the attempt counter increments. After five wrong attempts, state becomes max_attempts and further checks fail with state: "max_attempts".

The result webhook

On a terminal state Wave sends a signed verify.completed event to your project’s webhook endpoint:
Verify every delivery:
  • X-Wave-Signaturesha256=…, 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.
The webhook is a notification — reconfirm via the status API before granting.

Poll status

Returns the current state, attempts, channel, timestamps, and (once terminal) the decision. Scoped to your organization only.

One code per number, code expiry & idempotency

  • One live code per number — a new POST /v1/verify to the same number supersedes the previous code and cancels the old verification.
  • Code expiry — a code expires at expires_at. A check after expiry returns state: "expired" and does not increment attempts.
  • Idempotency — send an Idempotency-Key; a retry with the same key returns the same verification and never mints a second code or sends a second message.

Rate limits & abuse controls

  • Per-number limits — Wave caps verifications to a single number per API key, and blocks known-abusive numbers/prefixes, to prevent code-bombing and toll fraud. Over a limit → 429.
  • Per-verification attempt cap — five wrong attempts → max_attempts. No further checks are accepted.
  • Global volume limits — Wave tracks overall verification volume per API key. Sustained excess → 429.

Errors