Error envelope
Non-2xx responses use:Actual status-code behavior
Rate-limit headers
In429, the backend sends:
X-RateLimit-GlobalX-RateLimit-LimitX-RateLimit-RemainingX-RateLimit-Retry-AfterX-RateLimit-Retry-Policy
X-RateLimit-Retry-Afteris returned in milliseconds.- The error body also includes
error.detailed.retryAfterInSeconds. - The backend does not currently add the standard
Retry-Afterheader.
Retry guidance
Retry only when the failure is likely transient:429- transient
5xx
400 INVALID_JSON_BODY401 INVALID_OR_MISSING_API_KEY403 MISSING_PERMISSION403 PROJECT_SUBSCRIPTION_REQUIRED403 PROJECT_BILLING_PAYMENT_REQUIRED403 API_KEY_MODE_MISMATCH
Idempotency
POST /messages, POST /messages/batch, and POST /otp accept Idempotency-Key.
Current backend behavior:
- Reusing the same key for the same logical operation reuses the stored result.
- OTP idempotency mismatches return
400 INVALID_JSON_BODY. - The public API does not currently expose a dedicated idempotency conflict response.
Common real-world codes
INVALID_CONTENT_TYPEINVALID_JSON_BODYINVALID_OR_MISSING_API_KEYMISSING_PERMISSIONBRAZILIAN_PHONE_NUMBER_REQUIREDPROJECT_SUBSCRIPTION_REQUIREDPROJECT_BILLING_PAYMENT_REQUIREDPROJECT_MESSAGE_QUOTA_REACHEDPROJECT_WEBHOOK_LIMIT_REACHEDWEBHOOK_ENDPOINT_ALREADY_EXISTSTEMPLATE_NAME_ALREADY_EXISTSUNKNOWN_MESSAGEUNKNOWN_TEMPLATEUNKNOWN_WEBHOOKUNKNOWN_LOGRATE_LIMITEDMESSAGE_PROVIDER_ERROROTP_PROVIDER_ERRORAPI_KEY_MODE_MISMATCHINTERNAL_SERVER_ERROR