Errors
Authevo uses standard HTTP status codes. Successful responses are wrapped in a data object; failures return an error object with a machine-readable code and a human-readable message. The tables below cover every code the public send/verify/TOTP endpoints can return — always branch on the HTTP status and error.code, not the message, since messages may change.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "body/phone must match pattern \"^\\+[1-9]\\d{6,14}$\""
}
}General & authentication
| Status | Code | Meaning |
|---|---|---|
400 | VALIDATION_ERROR | The request body was malformed, missing a required field, or the phone number isn't a valid E.164 number. |
401 | INVALID_API_KEY | The Authorization header is missing or the secret key is invalid. |
404 | NOT_FOUND | The requested resource (e.g. an OTP request id) doesn't exist or doesn't belong to your account. |
429 | RATE_LIMIT_EXCEEDED | Too many requests. Slow down and retry after a short delay. |
500 | INTERNAL_ERROR | Something failed on Authevo's side. Safe to retry — these are logged and monitored. |
Sending & verification
| Status | Code | Meaning |
|---|---|---|
400 | INVALID_PHONE | The phone number is not a valid, reachable number for this endpoint. |
400 | OTP_NOT_FOUND | The code has expired, was already used, or was never sent for this phone number. |
409 | IDEMPOTENCY_KEY_IN_PROGRESS | A request with this Idempotency-Key is still being processed — wait for it to finish instead of sending a third attempt. |
409 | TEMPLATE_NOT_READY | The WhatsApp message template isn't approved yet on the sending number. Falls back to Telegram automatically if the recipient has linked it. |
409 | CHANNEL_NOT_LINKED | WhatsApp delivery failed and this recipient hasn't linked Telegram yet — nothing was delivered. Call the Telegram-link endpoint for this phone first. |
429 | TOO_MANY_ATTEMPTS | Too many failed verification attempts for this phone number. Wait before trying again. |
500 | DELIVERY_FAILED | The message could not be delivered on any channel. Safe to retry. |
503 | TELEGRAM_UNAVAILABLE | Telegram fallback isn't available right now. |
Two-Factor (TOTP)
| Status | Code | Meaning |
|---|---|---|
400 | TOTP_NOT_ENROLLED | This phone number has no active TOTP enrollment to verify or disable. |
409 | ALREADY_ENROLLED | This phone number already has a confirmed TOTP enrollment. Pass replace: true to reissue one. |
429 | TOO_MANY_ATTEMPTS | Too many failed verification attempts for this phone number. Wait before trying again. |
503 | TOTP_NOT_CONFIGURED | TOTP is temporarily unavailable on Authevo's side. |
Billing & credit
| Status | Code | Meaning |
|---|---|---|
402 | INSUFFICIENT_CREDITS | Your account balance is below the amount required to send — add prepaid credit to continue (a $2 minimum balance applies). |
402 | DEPOSIT_REQUIRED | Your account has never had a deposit — add prepaid credit before your first send. |
402 | FREE_TRIAL_EXHAUSTED | You've used all of your free verifications. Add credit to keep sending. |
429 | SPEND_CAP_EXCEEDED | Your hourly spend cap was reached. It resets automatically; raise it from your dashboard if you're hitting it often. |
500 | BILLING_ERROR | The verification itself succeeded, but billing it failed — Authevo's side, not yours. Logged and reconciled automatically; the verified result stands. |
503 | FREE_TRIAL_PAUSED | The free trial is temporarily paused platform-wide. Retry shortly, or add credit to send immediately. |
Rate limits
Requests are rate limited per phone number and per source IP. When you exceed a limit, the API responds with 429 RATE_LIMIT_EXCEEDED — back off and retry after a short delay. These are the real, current numbers, not illustrative ones.
| Scope | Limit | Window |
|---|---|---|
| Same recipient phone number (send) | 3 requests | 10 minutes |
| Same source IP address | 10 requests | 1 hour |
| Failed verify attempts, same phone number | 5 attempts | 15-minute block |
Safety Floor
Idempotency
Send an Idempotency-Key header on POST /v1/otp/send to make network retries safe — a retry with the same key replays the original response instead of sending (and charging for) a second message.
Idempotency-Key: 5b6b4c9e-6e0a-4b7a-9b7a-2f2b6a7c9e10Generate a fresh key per logical send attempt (a UUID works well) and reuse it only when retrying that exact same attempt. Keys are scoped to your account and remembered for 24 hours — a retry with the same key while the original request is still in flight gets back a 409 IDEMPOTENCY_KEY_IN_PROGRESS immediately, rather than racing it.
The header is entirely optional — requests without one behave exactly as before. It's worth adding wherever your client-side retry logic could plausibly re-send the same request after a timeout.