Every error response uses the same JSON envelope, with a status code that follows standard HTTP semantics.
{
"code": "BAD_REQUEST",
"message": "invalid_type in 'customer.email': Required",
"requestId": "37a04f8f-e791-491c-81e1-86cd304649bb",
"docs": "https://docs.nomos.energy/api-references/errors/BAD_REQUEST"
}
| Field | Description |
|---|
code | Machine-readable error code. Switch on this in your handler. |
message | Human-readable description of what went wrong. |
requestId | Unique request identifier. Include it when contacting support. |
docs | Link to the reference page for this code. |
errors | Optional. Per-field breakdown of a validation or business-rule failure (see Structured errors). |
Always log the requestId alongside the request that caused the error. It’s
the fastest way for support to find the corresponding trace.
Structured errors
When a request fails validation or a business rule, the envelope carries an additional errors array with a per-field breakdown. Switch on each entry’s code to map the failure back to a specific input, instead of parsing the human-readable message.
The errors array is available from version 2026-05-27.curie onwards. On
earlier versions the envelope omits it and only code, message,
requestId, and docs are returned.
{
"code": "BAD_REQUEST",
"message": "We don't currently serve this postal code.",
"requestId": "37a04f8f-e791-491c-81e1-86cd304649bb",
"docs": "https://docs.nomos.energy/api-references/errors/BAD_REQUEST",
"errors": [
{
"code": "unserviceable_zip",
"field": "address.zip",
"message": "We don't currently serve this postal code."
}
]
}
Each entry has these fields:
| Field | Description |
|---|
code | Machine-readable reason for this field. Either a standard validation code (invalid_type, too_small, …) or a Nomos-specific business code (unserviceable_zip). |
field | Dot-path to the offending field (address.zip). Empty for a top-level issue. |
message | Human-readable description of this specific issue. |
The top-level code stays the coarse HTTP class (for example BAD_REQUEST); the errors[].code values are the granular reasons you switch on.
Validation codes
These cover schema validation failures and map to the underlying validator’s issue codes.
| Code | Description |
|---|
invalid_type | Wrong type for the field, or the field is missing entirely. |
too_big | Value exceeds its maximum (number too large, string or array too long). |
too_small | Value is below its minimum (number too small, string or array too short). |
invalid_format | String doesn’t match the expected format (email, UUID, ISO date, …). |
not_multiple_of | Number isn’t a multiple of the required step. |
unrecognized_keys | The object contains keys that aren’t allowed. |
invalid_union | Not surfaced directly in errors[]; unions are flattened into their branch issues. |
invalid_key | A key in a record or map is invalid. |
invalid_element | An element of a set or map is invalid. |
invalid_value | Value isn’t one of the allowed options (enum or literal mismatch). |
custom | A custom validation rule failed with no more specific code. |
Business error codes
Beyond the validation codes above, errors[].code can carry a Nomos-specific business code:
| Code | Description |
|---|
unserviceable_zip | The postal code in address.zip is outside the area Nomos serves. |
unsupported_product | A requested entry in product_orders isn’t offered by the subscription’s plan. |
duplicate_customer_email | A customer with this email already exists under a different type, name, or company. Reconcile before retrying. |
invalid_iban | The IBAN in payment_method.sepa_debit.iban failed checksum validation. |
unsupported_meter | The plan doesn’t support the capacity (RLM) meter named in meter.type. |
unsupported_meter_order | The subscription’s plan offers no smart meter product, so a meter order can’t be placed. |
duplicate_meter_order | An active meter order of this product already exists for the subscription. |
ended_subscription | The subscription has ended, so a grid fee reduction can’t be created for it. |
duplicate_grid_reduction | An active §14a EnWG grid fee reduction already exists for this module on the subscription. |
missing_smart_meter | §14a EnWG module 3 requires a smart meter, which the subscription doesn’t have. |
missing_module_1 | §14a EnWG module 3 can only be ordered together with module 1. |
out_of_period_meter_reading | The reading timestamp falls on or after the subscription’s end date. |
unsupported_meter_reading | Meter readings can only be reported for analog meters; this subscription’s meter is smart. |
upgrade_api_version | The request targets a feature (such as feed-in subscriptions) not available on the requested API version. Upgrade to a newer version. |
Retrying
Retry only when retrying could plausibly succeed. Use exponential backoff with jitter and cap the attempts.
| Status | Retry |
|---|
4xx (except 429) | No. Fix the request before retrying. |
429 TOO_MANY_REQUESTS | Yes, after backing off. |
5xx | Yes, with exponential backoff. |
Error reference
| Status | Code | Description |
|---|
400 | BAD_REQUEST | Schema validation failed: missing fields, wrong types, or unparseable JSON. Check message and errors[] for the field path. |
401 | UNAUTHORIZED | No credentials, or the access token is invalid or expired. Refresh and retry. See Authentication. |
403 | FORBIDDEN | Valid token, but the caller doesn’t have permission for this resource. Confirm the resource belongs to your organization and your Auth Client’s scope. |
404 | NOT_FOUND | The resource doesn’t exist, or your token can’t see it. Double-check the ID and your organization. |
405 | METHOD_NOT_ALLOWED | The endpoint doesn’t support the HTTP method you used. |
409 | CONFLICT | Another resource already uses this value (for example, a globally unique email). Reuse it or change the value before retrying. |
422 | UNPROCESSABLE_ENTITY | Well-formed request that breaks a business rule (for example, cancelling an already-cancelled subscription). Read message for the specific rule. |
429 | TOO_MANY_REQUESTS | Rate limit exceeded. Back off and retry with exponential delays. |
500 | INTERNAL_SERVER_ERROR | Unexpected error on Nomos’s side. Retry with backoff. If it persists, email support@nomos.energy with the requestId. |