Errors

Errors are JSON: {"error": {"code": "country_not_allowed", "message": "…"}}. Branch on code, show message to developers.

HTTP code Meaning / what to do
400 bad_request Invalid JSON or an unknown field.
401 unauthorized Missing, wrong, revoked key, or the project / organisation is suspended.
403 forbidden The key lacks the scope for this endpoint.
404 not_found No such message / template / webhook in this project.
409 idempotency_conflict Same idempotency key, different content.
409 not_cancelable The message already left the queue.
422 invalid_request A field is wrong; message says which.
422 country_not_allowed Only Mauritian mobiles (+230 5xxx xxxx).
422 recipient_suppressed The number opted out (opt-out link or STOP) or is on your do-not-contact list.
429 quota_exceeded Project quota reached (see /v1/usage); Retry-After header.
429 recipient_rate_limited Too many messages / codes to one number; wait.
429 otp_cooldown Wait 30 s before sending another code to that number.
429 rate_limited More than 50 requests per second with one key.
500 internal Our side. Safe to retry with the same idempotency key.

Message failure codes (error_code on a failed message)

NO_SERVICE, RADIO_OFF (no signal / airplane mode), GENERIC_FAILURE (often: SIM out of credit or SMS bundle), SIM_UNAVAILABLE, INVALID_NUMBER, DELIVERY_FAILED (carrier could not deliver), LIMIT_EXCEEDED (Android's own rate limit), DISPATCH_TIMEOUT (the phone did not answer), EXPIRED_ON_DEVICE, APP_ERROR. Phone-level failures are retried; 5 in a row pause the phone for 30 minutes and alert the organisation's admins.