error.code you can switch on
in client code; authentication failures (401) use a flat shape instead — see
the auth shape below. The human-readable message may
evolve; the codes will not.
Error shape
Most endpoints return:details is populated on payload_too_large (413) as { size, max } (the
serialized envelope size), and on the byte-length 400 errors
(message_too_long, invalid_title, invalid_url, invalid_url_title,
invalid_action) as { bytes, max } — the actual UTF-8 byte count of the
offending field and the cap it exceeded. URL errors that fail for a non-length
reason (malformed URL, non-http(s) scheme) carry no details.
Message-text changes from earlier API revisions
The errorcode values are stable wire contracts and have not changed.
The human-readable error.message strings, however, were rewritten when
validation moved to Zod schemas. Clients that match on .code are
unaffected; clients that display .message verbatim will see new wording:
.code | When | Previous .message | Current .message |
|---|---|---|---|
invalid_action | invalid action URL | must be a valid URL | is not a valid URL |
invalid_title | empty title | title must be 1–100 bytes (shared with the over-limit branch) | title must not be empty (now distinct) |
invalid_title | over-limit title | title must be 1–100 bytes (shared with the empty branch) | title must be ≤ 100 bytes (now distinct) |
invalid_message | message key omitted | message is required | Invalid input: expected string, received undefined |
invalid_action | action url missing | each action requires label + url | Schema default for the missing required field |
Auth failure shape
Auth failures use a flat shape so token problems don’t get masked behind nested structure. The full shape — both fields — is shown below:message is optional. The invalid_token variant omits it (the code alone
is actionable):
Status codes
| Status | When |
|---|---|
| 200 | Accepted and enqueued. Check warnings in the body for non-fatal notes. |
| 400 | Body malformed or a field failed validation. |
| 401 | Missing, invalid, or revoked bearer token. |
| 403 | Token exists but lacks scope (e.g. priority above priority_cap). |
| 413 | Serialized envelope > 2048 bytes. |
| 429 | Rate limit hit on one of the layers; Retry-After is set. |
| 5xx | Server error. Retry with exponential backoff. |
Codes
Validation (400)
| Code | Meaning |
|---|---|
invalid_body | Body wasn’t JSON, form-encoded, or text. |
invalid_message | message missing or not a string. |
message_too_long | message exceeds 1500 bytes (UTF-8). |
invalid_title | title is 0 bytes or > 100 bytes (UTF-8). |
invalid_priority | Not one of min, low, default, high, urgent. |
invalid_url | Not http(s), invalid URL, or > 512 bytes. |
invalid_url_title | url_title > 32 bytes. |
invalid_action | Missing label/url, oversized, or non-http(s) scheme. |
invalid_device | device parameter > 256 chars. |
Auth (401)
| Code | Meaning |
|---|---|
missing_token | No Authorization: Bearer … header sent. |
invalid_token | Token format invalid, unknown, or revoked. |
Scope (403)
| Code | Meaning |
|---|---|
priority_capped | Token’s priority_cap is below the requested priority. |
Size (413)
| Code | Meaning |
|---|---|
payload_too_large | Serialized envelope > 2048 bytes. |
Rate limit (429)
| Code | Meaning |
|---|---|
rate_limit_exceeded | One of the rate-limit layers tripped. See X-RateLimit-Resource for which one. |
Server errors (5xx)
| Code | Meaning |
|---|---|
internal_error | Catch-all for unhandled exceptions on the server (Redis unavailable, FCM dispatch failure, etc.). Retry with exponential backoff. |
Warnings vs errors
Some conditions are surfaced as warnings on a200 response rather than
errors, to keep long-running automations alive:
unknown device label: '…'— one of the names indevicedidn’t match any paired device.device fallback: all entries unknown, delivering to every paired device— every entry was unknown; the send fell back to broadcast.tags truncated to first 5 (got 7)— excess tags dropped.tag #N truncated to 32 bytes— a tag exceeded the per-tag byte cap.actions truncated to first 3 (got 5)— excess action buttons dropped.no paired devices for this token's owner— the request succeeded but produced no enqueues. Useful for cron jobs run during transient unpairing.token scope '…' dropped out-of-scope device(s): …— caller’sdeviceparameter referenced devices outside the token’sscopeDevices.