Skip to content

Errors

How the API reports errors as RFC 9457 Problem Details with stable codes and a request id.

Every error response has a 4xx or 5xx status and a body of type application/problem+json (RFC 9457 Problem Details). Your code decides by the HTTP status and the stable code; the texts are for people.

Members of a problem

typestringrequired

Problem type. about:blank unless a specific type is documented.

titlestringrequired

Short summary; for about:blank the HTTP status phrase, e.g. Bad Request.

statusintegerrequired

The HTTP status code, repeated in the body.

codestringrequired

Stable, machine-readable code in snake_case, e.g. validation_error. See Codes.

detailstring

Explanation of this occurrence, in English. Show it in logs, do not parse it.

instancestring

URI reference of the request that caused the problem, when present.

request_idstring

The id of the request, equal to the X-Request-Id response header. Quote it when you contact support.

errorsobject[]

Field-level details for validation_error, and for conflict when a value must be unique.

Field error codes

CodeMeaning
requiredA required field, parameter or header is missing.
too_longA text is longer than allowed.
too_shortA text is shorter than allowed.
out_of_rangeA number, date or time lies outside the allowed range, e.g. a measured_at in the future.
invalid_formatThe value has the wrong format (date, UUID, pattern), or the body is not valid JSON or not application/json.
invalid_check_digitThe check digit of a GTIN or GLN is wrong.
invalid_valueThe value is not allowed here: an unknown enum value, an invalid parameter or cursor, a failing cross-field check.
unknown_fieldThe field is not known here, e.g. a field that is not condition data of the unit, or an unknown body property.
already_existsA value that must be unique is already taken (with conflict).

Examples

A condition update with a measurement time in the future (updateCondition) is refused as a whole:

400
{
  "type": "about:blank",
  "title": "Bad Request",
  "status": 400,
  "code": "validation_error",
  "detail": "The request failed validation.",
  "request_id": "4f9c2a7d1e0b5c8a3f6e2d19",
  "errors": [
    {
      "field": "values[0].measured_at",
      "code": "out_of_range",
      "message": "measured_at lies in the future"
    }
  ]
}

Too many requests: the Retry-After header says how many seconds to wait.

429
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 17
X-Request-Id: 8b1e6f03c2d94a7e5f0a1b2c

{
  "type": "about:blank",
  "title": "Too Many Requests",
  "status": 429,
  "code": "rate_limited",
  "detail": "Too many attempts. Try again later.",
  "request_id": "8b1e6f03c2d94a7e5f0a1b2c"
}

Codes

The ProblemCode values of the published contract. Codes marked as portal only do not occur with an API key; they are listed because the contract contains them.

CodeHTTP statusMeaning
validation_error400The request is invalid. errors[] names each affected field, query parameter or header with a stable error code.
invalid_token400Portal only: an email or invitation link is unknown, used or expired.
unauthenticated401No valid API key: the header is missing, the key is unknown or revoked, or the request also carries a session cookie.
invalid_credentials401Portal only: the email address or password of a sign-in is wrong.
forbidden403The key lacks the scope the operation needs, or a viewer's rights are not enough.
email_not_verified403Portal only: the email address is not verified yet.
not_found404The resource does not exist, or it belongs to another organisation.
method_not_allowed405The path exists, but not with this HTTP method.
conflict409The request contradicts the current state, for example the unit's battery model is archived: its data can be read but no longer changed.
identifiers_locked409Portal only: the identifiers of a published unit can no longer change.
payload_too_large413The request body is larger than allowed (1 MiB by default).
publish_blocked422Portal only: publishing fails on open required data; blockers[] lists it.
rate_limited429Too many requests. Wait the seconds given in the Retry-After header, then try again.
request_closed410Supplier and service-partner links only: the link expired, was cancelled or has been used.
internal_error500An error on our side. Retry later; quote the request_id when you contact support.
address_lookup_unavailable503Portal only: the address search is not reachable right now; the address can be entered by hand.

Request id

Every response, successful or not, carries an X-Request-Id header. In a problem, request_id has the same value. The server creates the id itself and ignores an id you send. Log it with each failed request and quote it when you contact support.

Handling errors

  • Decide by status and code, never by title, detail or message: those texts can change.
  • Expect new codes over time. Treat an unknown code by its HTTP status class.
  • Fix 400, 401, 403, 404 and 409 instead of repeating the request unchanged.
  • Retry 429 after Retry-After, and 5xx or network errors with exponential backoff. Before you repeat a write, read Retries and idempotency.
  • Responses never contain SQL, stack traces or upstream errors; an unexpected failure is 500 internal_error with a request id.

The guide Handle errors shows complete error handling in TypeScript, Python and Go.

On this page