Idempotency
Send anIdempotency-Key header on start (and on WhatsApp sends) to make retries safe. The key is 1 to 128 printable ASCII characters that you generate, scoped to your API key. Replaying it returns the original 201 unchanged, with the response header Idempotent-Replayed: true; nothing is sent or charged again. A send that fails releases the key so a retry can go through.
Your own reference
reference (up to 64 printable characters) is echoed on start and on GET /v1/verify/{id}, and you can search for it under Verify → Verifications in the console. Put your order id or session id there.
Rate limits
The three
429 codes share a status and differ in the error string. Match on the string.
- Per key. The window is the clock minute (120 requests, and 1,200 on the e-mail endpoints, counted apart), and the answer carries
Retry-After: the seconds until the next minute opens, at most 60. Wait that long before you try again. The limit fails open if the limiter itself is unavailable. - Per destination. A start and each resend count, over the last hour. The application’s “Codes per number per hour” under Verify → Settings sets it, from 1 to 10.
What to retry
The Errors page lists every code.
The protection guard
Three per-application settings stop a runaway integration or an attacker from spending your balance. All are evaluated onstart and resend, and skipped in the sandbox. Set them under Verify → Settings, in Protection.
- Daily spend cap (
daily_spend_cap_halalas, off by default, set by the owner or an admin who sees every application): today’s charges plus this send would pass the cap, so the send answers429 spend_cap_reached. Days are Riyadh calendar days. - Per-IP cap (
max_per_ip_per_hour, default 20): only checked when you passclient_iponstart. The address is never inferred from the request. Over the cap answers429 ip_rate_limited. - New-destination pumping (
max_new_destinations_per_hour, default 300): crossing it auto-pauses the application. Everystartandresendthen answers403 application_pauseduntil the owner or an admin resumes it in the console (Resume sending). The pause is audit-logged and raised under Alerts.
Pass
client_ip whenever the verification is started on behalf of an end user’s request. It is the one signal that lets Tawked tell many users apart from one abuser.Verification lifecycle
resend keeps the id and issues a new code: the old code stops working, attempts and expiry reset, and the send is charged and rate-limited like any other. A verification that is no longer pending cannot be resent (409 not_resendable): start a new one. A new start never invalidates an earlier code. Read the current state at any time with GET /v1/verify/{id} without consuming an attempt.