Errors
Every error has the same JSON shape. Switch on the code, not the HTTP status or the message.
The error body
{
"error": {
"code": "rate_limited",
"message": "This account has sent its 500 messages for today.",
"details": { "limit": "messages_per_day", "max": 500, "resetAt": "2026-11-09T00:00:00.000Z" }
}
}| Field | Description |
|---|---|
| errorrequired object | What went wrong. |
| ↳ coderequired invalid_request | pairing_invalid | pairing_expired | unauthorized | forbidden | not_found | payload_too_large | rate_limited | internal | conflict | string | Machine-readable error code. Clients switch on this, not on the HTTP status. Open enum: new codes may be added without a protocol bump; treat an unknown code by its HTTP status. |
| ↳ messagerequired string | Human-readable, English, for logs and developers, not for end users. |
| ↳ details any | Code-specific. unauthorized carries { "reason": "revoked" } when the device credential was revoked. forbidden may carry { "reason": "account_suspended" } (the account behind the credential is suspended) or a host-defined reason such as device_limit, topic_limit or images_not_enabled, often with max; rate_limited may carry { "limit", "max", "resetAt" }. |
New codes may be added. Treat a code you don't know by its HTTP status.
Codes
| Code | When | What to do |
|---|---|---|
| invalid_request 400 | A field is missing, too long or of the wrong type, or a header has a value it does not accept. | Read message for the field and fix the request. Retrying unchanged fails again. |
| pairing_invalid 400 | Only returned when pairing a phone, never to app tokens. | Ask for a new pairing code on the dashboard. |
| pairing_expired 400 | Only returned when pairing a phone, never to app tokens. | Ask for a new pairing code on the dashboard. |
| unauthorized 401 | The Authorization header is missing, or the token is not valid or has been revoked. | Check the token. A revoked token stays revoked; create a new one on the dashboard. |
| forbidden 403 | The token may not send to this topic, or the account does not allow what was asked. details.reason says which: topic_limit, images_not_enabled or account_suspended. | Use a topic the token may send to, or change the request. |
| not_found 404 | No such endpoint. Usually a typo in the path or the wrong base URL. | Check the URL against the reference. |
| payload_too_large 413 | The request body is over 16 KB. | Send less. The message body itself is limited to 2048 characters. |
| rate_limited 429 | The account reached its daily limit, or the token sent too fast. | Wait Retry-After seconds. See Limits and usage. |
| internal 500 | Something went wrong on our side. | Retry with backoff. If it keeps happening, tell us. |
| conflict 409 | The Idempotency-Key was already used with a different request, or the first send with it has not finished. | Use a new key for a new message. For a repeat of a running send, wait a moment and retry with the same key. |