# Error handling

Understand HTTP status codes and error responses from the FiscalRail API.

FiscalRail uses standard HTTP response codes. `2xx` responses indicate success, `4xx` responses indicate a problem with the request or account, and `5xx` responses indicate an error on our side.

A `5xx` response does not necessarily mean that the operation had no effect. Record the response's `Request-Id` header before retrying or contacting support so we can trace the request.

## Error responses

API errors are returned as JSON under a top-level `error` property:

```json
{
  "error": {
    "code": "invalid_request",
    "message": "limit must be between 1 and 100"
  }
}
```

Every error includes a stable machine-readable `code` and a human-readable `message`. Validation errors may also include a `details` array identifying individual fields and violations. Integrations should branch on the HTTP status and `code`, not the text of `message`.

Validation details use dotted field paths where necessary:

```json
{
  "error": {
    "code": "invalid_customer",
    "message": "The customer is invalid",
    "details": [
      {
        "code": "required_value",
        "field": "tax_id.country",
        "message": "can't be blank",
        "metadata": {}
      }
    ]
  }
}
```

## Common errors

| HTTP status | Code | Meaning |
| --- | --- | --- |
| `400` | `invalid_request` | The request could not be parsed or contains an invalid parameter. |
| `401` | `authentication_required` | The API key is missing, malformed or unknown. |
| `402` | `balance_exhausted` | The live account does not have enough balance for a billable operation. |
| `404` | `resource_not_found` | The resource does not exist or belongs to another account. |
| `409` | `idempotency_key_in_use` | Another request is currently using the same idempotency key. |
| `409` | `idempotency_key_mismatch` | The idempotency key was reused for another operation or different parameters. |

## Resource and operation errors

These codes apply only when the corresponding resource or operation is involved:

| HTTP status | Code | Meaning |
| --- | --- | --- |
| `404` | `customer_not_found` | The customer supplied when issuing an invoice does not exist in the account. |
| `409` | `pdf_render_in_progress` | Another request is already rendering the invoice PDF. Retry after a short delay. |
| `422` | `invalid_customer` | Customer data is invalid, or the requested customer operation is not allowed. Check `details`. |
| `422` | `invalid_invoice` | Invoice data is invalid. Check `details`. |
| `422` | `account_not_configured` | The account is missing configuration required by the operation. |
| `503` | `pdf_rendering_unavailable` | PDF rendering is temporarily unavailable. Retry later. |

## Safe retries

Retry connection failures, timeouts, `408 Request Timeout`, `429 Too Many Requests`, and transient `5xx` responses with capped exponential backoff and jitter. Honor `Retry-After` when it is present. Do not automatically retry other `4xx` responses.

Retry a request that changes state only when the operation is inherently idempotent or you supplied an `Idempotency-Key`. Reuse the same key and exactly the same parameters on every attempt. A `409 idempotency_key_in_use` response is safe to retry after a delay; `idempotency_key_mismatch` is not. See [Idempotency](/en/api/idempotency).

The Python SDK applies this policy in its pooled transport and raises typed `FiscalRailError` subclasses after retries are exhausted. See [Python SDK retries and errors](/en/python-sdk#retries-and-errors).
