Errors and limits
Error format
Business errors (401, 403, 404, 409, 422) return a human-readable error
message plus a stable code:
{ "error": "An account with that email already exists.", "code": "email_exists" }erroris ready to display: it is localized to the request’s language (see below), so integrations don’t need their own code-to-message table.codeis a stable snake_case identifier meant for programmatic handling — for exampleinvalid_credentials,email_exists,not_found,forbidden,current_password_invalid,client_details_required. Codes will not change meaning or spelling within/v1; new ones may be added as endpoints grow. Branch oncode(never on the message text), and do not parse either field for data.
Message language
The message language is resolved as: explicit Accept-Language header (if
supported) → the authenticated account’s language preference (set in the
app under Settings) → English. Supported languages today: en, es.
Two cases use Fastify’s default envelope, with statusCode, error and
message: schema-validation 400s and rate-limit 429s (the 429 of a full
command queue, commands_queue_full, is a
business error with error and code):
{ "statusCode": 400, "error": "Bad Request", "message": "querystring/limit must be <= 200" }Common status codes
| Code | When |
|---|---|
400 | Malformed request (schema validation) |
401 | Credential missing, invalid, revoked or expired |
403 | Authenticated but not allowed to perform the action (e.g. non-ROOT write) |
404 | Does not exist or is outside your scope (deliberately indistinguishable) |
409 | Conflict: duplicate (email, part number, fiscal ID), state not valid for the action, site with devices |
422 | Unprocessable entity: nonexistent reference, part number not in the catalog, wrong current password |
429 | Rate limit exceeded, or too many queued commands for a device |
Rate limiting
200 requests/minute per origin. 429 responses include the standard
x-ratelimit-* headers with the state of the window.
Pagination
Listings accept limit and offset and return:
{ "items": [], "total": 128, "limit": 50, "offset": 0 }The general maximum limit is 100–200 depending on the resource, with two
exceptions: /v1/devices accepts up to 500 (intended for the map) and
/v1/telemetry up to 2000 (historical series).