> ## Documentation Index
> Fetch the complete documentation index at: https://developer.kalixo.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> A consistent error model across every endpoint.

The API uses conventional HTTP status codes and returns a consistent JSON error body.

```json theme={"dark"}
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Product 10020000213575 price mismatch: expected 10000"
}
```

<ResponseField name="statusCode" type="integer">
  The HTTP status code.
</ResponseField>

<ResponseField name="error" type="string">
  A short, machine-friendly label for the status (for example `Bad Request`, `Not Found`).
</ResponseField>

<ResponseField name="message" type="string">
  A human-readable explanation you can show to operators or log for support. When a message
  refers to a product, it uses the **`productCode` you sent** in the request.
</ResponseField>

<Note>
  Placeholders below use `{productCode}`, `{reference}`, `{min}`, `{max}`, `{expected}`,
  `{productCurrency}`, `{requested}`, and `{seconds}` for values that depend on your request.
  All other text is returned **exactly** as shown.
</Note>

## Status codes

| Code  | `error` label          | When it applies                                                                                          |
| ----- | ---------------------- | -------------------------------------------------------------------------------------------------------- |
| `400` | `Bad Request`          | Validation before or during order placement (price, currency, catalog membership, product configuration) |
| `401` | `Unauthorized`         | Missing or invalid `x-api-key`                                                                           |
| `403` | `Forbidden`            | API ordering disabled for your account                                                                   |
| `404` | `Not Found`            | Unknown catalog product or order                                                                         |
| `409` | `Conflict`             | Duplicate `externalOrderCode`                                                                            |
| `422` | `Unprocessable Entity` | Account order limit exceeded, insufficient wallet funds, or product not available for purchase           |
| `429` | `Too Many Requests`    | [Rate limit](/concepts/rate-limits) exceeded                                                             |
| `502` | `Bad Gateway`          | Order could not be created or is temporarily unavailable                                                 |
| `500` | —                      | Unexpected server error                                                                                  |

## Authentication (`401`)

| Message           |
| ----------------- |
| `Missing API key` |
| `Invalid API key` |

See [Authentication](/authentication) for examples.

## Rate limiting (`429`)

| Message                                        |
| ---------------------------------------------- |
| `Rate limit exceeded. Retry after {seconds}s.` |

The `Retry-After` response header matches `{seconds}`. See [Rate limits](/concepts/rate-limits).

## Catalog (`404`)

Returned by `GET /catalog/products/{productCode}` when the productCode is not in your active catalog.

| Message                                               |
| ----------------------------------------------------- |
| `Product {productCode} was not found in your catalog` |

## Orders - request validation (`400`)

Returned by `POST /orders` when the request body fails checks **before** the order is accepted.

| Message                                                                 |
| ----------------------------------------------------------------------- |
| `orderProducts must not be empty`                                       |
| `Each line requires a productCode and a quantity of at least 1`         |
| `Product {productCode} is priced in {productCurrency}, not {requested}` |
| `Product {productCode} is open-denomination and requires a price`       |
| `Price for product {productCode} is below the minimum allowed ({min})`  |
| `Price for product {productCode} exceeds the maximum allowed ({max})`   |
| `Product {productCode} price mismatch: expected {expected}`             |
| `Order price mismatch: expected total {expected}`                       |

<Info>
  Prices in mismatch and denomination messages are in **minor units** (same as the catalog), e.g. `10000` = 100.00.
</Info>

## Orders - rejected at placement (`400`)

Returned by `POST /orders` when the order cannot be accepted after validation.

| Typical situation                          | Message                                                                                      |
| ------------------------------------------ | -------------------------------------------------------------------------------------------- |
| Product not orderable for country/currency | `Product {productCode} is not available for ordering with the supplied country or currency.` |
| Product inactive                           | `Product {productCode} is not currently available.`                                          |
| Wrong order currency                       | `Product {productCode} cannot be ordered in the requested currency.`                         |
| Open denomination, price missing           | `Product {productCode} is open-denomination and requires a price.`                           |
| Order data invalid                         | `The order could not be validated. Check product codes, prices, and currency.`               |
| Order already placed                       | `This order has already been placed.`                                                        |
| Other rejection                            | `The order could not be placed. Check your request and try again.`                           |

<Warning>
  Order each product for the market shown in the catalog (`countryCode`). Mismatched country or
  currency often surfaces as “not available for ordering with the supplied country or currency”.
</Warning>

## Orders - forbidden (`403`)

| Message                                                                      |
| ---------------------------------------------------------------------------- |
| `API ordering is disabled for your account. Contact support for assistance.` |

## Orders - conflict (`409`)

| Message                                                  |
| -------------------------------------------------------- |
| `An order with this external order code already exists.` |

## Orders - unprocessable (`422`)

| Message                                                                       |
| ----------------------------------------------------------------------------- |
| `Your account order limit has been exceeded. Contact support for assistance.` |
| `Insufficient funds. Please top up your wallet and try again.`                |
| `Product {productCode} is not available for purchase.`                        |

## Orders - not found (`404`)

Returned by `GET /orders/{reference}`.

| Message                       |
| ----------------------------- |
| `Order {reference} not found` |

`{reference}` is the path value you sent (the UUID `orderId` or your `externalOrderCode`).

## Orders - bad gateway (`502`)

Returned when the order cannot be created or processing is temporarily unavailable.

| Message                                                                |
| ---------------------------------------------------------------------- |
| `Order processing is temporarily unavailable. Please try again later.` |
| `Order could not be created. Please try again later.`                  |

## Server errors (`500`)

Returned when an unexpected error occurs. Retry with backoff; contact support if the problem persists.

| Message                 |
| ----------------------- |
| `Internal server error` |

The `error` field is omitted on some `500` responses.

## Handling errors in your integration

<Steps>
  <Step title="Log statusCode and message">
    Persist both fields for support and retries. Treat `message` as human-readable text.
  </Step>

  <Step title="Map by productCode">
    When `message` starts with `Product {productCode}`, attach the error to that line in your cart or order payload.
  </Step>

  <Step title="Retry only when appropriate">
    Retry `502` and `429` with backoff. Do not retry `400`, `403`, `404`, `409`, or `422` without fixing the request.
  </Step>

  <Step title="Refresh catalog on configuration errors">
    If you see “not available for ordering with the supplied country or currency”, re-fetch the product from
    `GET /catalog/products` and verify `countryCode`, `price`, and open-denomination bounds.
  </Step>
</Steps>
