Skip to content

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" }
  • error is ready to display: it is localized to the request’s language (see below), so integrations don’t need their own code-to-message table.
  • code is a stable snake_case identifier meant for programmatic handling — for example invalid_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 on code (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

CodeWhen
400Malformed request (schema validation)
401Credential missing, invalid, revoked or expired
403Authenticated but not allowed to perform the action (e.g. non-ROOT write)
404Does not exist or is outside your scope (deliberately indistinguishable)
409Conflict: duplicate (email, part number, fiscal ID), state not valid for the action, site with devices
422Unprocessable entity: nonexistent reference, part number not in the catalog, wrong current password
429Rate 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).