Skip to content
Oxtro
Documentation menu

API reference

Errors

Errors use normal HTTP status codes and one JSON shape. Branch on the code, show the message to a developer, keep the request ID.

Error format

400 Bad Request
{
  "error": {
    "code": "CONNECTION_OFFLINE",
    "message": "Cannot send message: Connection 'Support line' is in status 'DISCONNECTED'. Must be CONNECTED.",
    "request_id": "req_123"
  }
}
FieldTypeDescription
errorobject, required
error.codestring, required
error.messagestring, required
error.request_idstring, required
error.detailsobject
  • code is stable. Write your logic against it.
  • message is for people and can change. Do not parse it.
  • request_id identifies the request. It is also in the X-Request-ID response header. Include it when you ask for help.
  • details appears on some errors, for example remaining_attempts on a wrong OTP or validationErrors on a malformed body.

Error codes

CodeHTTPMeaningRetry?
INVALID_REQUEST400The body is missing a field or a value is malformed.No. Fix the request.
INVALID_PHONE400The phone number is not valid.No.
MISSING_TEMPLATE_VARIABLE400A template variable has no value.No.
CONNECTION_OFFLINE400The WhatsApp number is not connected.After the number is linked again.
OTP_INVALID400Wrong one-time code.The user can try again.
OTP_EXPIRED400The code is past its expiry.Send a new code.
OTP_MAX_ATTEMPTS400Too many wrong tries.Send a new code.
UNAUTHORIZED401The API key is missing, invalid, expired or revoked.No.
FORBIDDEN403The key lacks the scope. details.required_scope names it.No.
QUOTA_EXCEEDED403A monthly plan limit is reached.After upgrading or next month.
NOT_FOUND404The resource does not exist in this workspace.No.
IDEMPOTENCY_CONFLICT409The idempotency key was used with a different body.No. Use a new key.
CONFLICT409A request with the same idempotency key is still running, or an OTP was already verified.For a running request, shortly after.
RATE_LIMITED429Too many requests from this key, or too many codes to one phone.Yes, after a pause.
OTP_COOLDOWN_ACTIVE429A code was sent to this phone moments ago.After details.retry_after_seconds.
INTERNAL_SERVER_ERROR500Something failed on our side.Yes, with the same idempotency key.

Retrying safely

Retry on network errors, timeouts, 429 and 5xx, with a growing delay between tries. Do not retry other 4xx responses: the same request will fail the same way. When you retry a send, reuse the same Idempotency-Key so the message cannot go out twice.

A 202 from POST /v1/messages means queued, not delivered. A message that later fails does not produce an API error. You learn about it from the FAILED status or the message.failed webhook.