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

# Errors

> Paylead application errors carry a problem-details envelope (a machine-readable code, a human title, and field-level errors), while some responses are raised at the edge without it.

Paylead uses standard HTTP status codes for protocol-level outcomes and a JSON error envelope for application-level detail.

Errors come from two layers:

* **The application** handles most calls and, on failure, returns the structured [error envelope](#error-envelope) below; parse it for the precise cause.
* **The infrastructure in front of the API** (gateway, authentication, throttling) can answer first, and those responses may not carry the envelope.

Always branch on the HTTP status first, then enrich with the envelope when it is present.

**Why this matters.** A `400` from malformed JSON and a `400` from a field that fails validation require different fixes. The envelope's `code` and `errors` disambiguate them.

## Error envelope

Errors raised by the application share the same JSON shape: a problem-details-style object (the Paylead error profile). Three fields are always present (`code`, `title`, `status`); `errors` and `instance` are added when relevant. Errors raised [at the edge](#raised-at-the-edge) may not include it.

<Note>
  **Code examples coming soon.** The Paylead API (v2) is still under construction. Request and response examples for this section will be published once the contract is finalized. In the meantime, contact your Paylead account manager for early-access details.
</Note>

| Field      | Type             | Always present | Description                                                                                                                                                                               |
| ---------- | ---------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `code`     | string           | Yes            | Stable, machine-readable Paylead error code. Switch your logic on this, never on `title`.                                                                                                 |
| `title`    | string           | Yes            | Human-readable explanation. The wording may change between versions.                                                                                                                      |
| `status`   | integer          | Yes            | The HTTP status code, repeated in the body for convenience.                                                                                                                               |
| `errors`   | array of objects | No             | Present on validation failures. Each item describes one problem; its fields are endpoint-specific (offending location, expected format, …). `null` when there are no field-level details. |
| `instance` | string           | No             | Reference to this specific occurrence of the failure, when the platform sets one. Include it in support tickets if present. May be `null`.                                                |

```json Example 400 response theme={null}
{
  "code": "VALIDATION_ERROR",
  "title": "The request payload is invalid.",
  "status": 400,
  "instance": "req_01J9Z3K8QY",
  "errors": [
    {
      "loc": ["body", "id"],
      "msg": "String should match pattern '^[a-zA-Z0-9_-]+$'",
      "type": "string_pattern_mismatch"
    }
  ]
}
```

<Warning>
  Log the full error envelope (`code`, `title`, `status`). When the response carries an `instance`, include it in any bug report you open; it is the fastest way for Paylead support to find the failure in our logs.
</Warning>

## HTTP status codes

### Returned by the application

On failure, these carry the [error envelope](#error-envelope) above.

| Code                        | Meaning      | When you see it                                                                                                      |
| --------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------- |
| `200 OK`                    | Success      | Read or update succeeded.                                                                                            |
| `201 Created`               | Success      | Resource was created (Consumer, payment account, …).                                                                 |
| `202 Accepted`              | Success      | Request accepted for asynchronous processing, e.g. a payout is initiated synchronously but settled later by the PSP. |
| `204 No Content`            | Success      | Delete or update succeeded; no body.                                                                                 |
| `400 Bad Request`           | Client error | Payload invalid, missing field, or malformed JSON. The `errors` array carries the field-level detail.                |
| `401 Unauthorized`          | Auth error   | Missing, expired, or invalid credentials/token.                                                                      |
| `403 Forbidden`             | Auth error   | Authenticated, but not allowed: wrong scope, [Program](/glossary#program), or environment.                           |
| `404 Not Found`             | Client error | Resource ID does not exist.                                                                                          |
| `409 Conflict`              | Client error | Resource already exists, or the state transition is illegal.                                                         |
| `500 Internal Server Error` | Server error | Unexpected failure inside the API. Retry with backoff.                                                               |

### Raised at the edge

Some requests never reach the application: the API gateway and upstream infrastructure answer first (throttling and gateway failures). **These responses may not carry the error envelope**: the body can be empty, plain text, or a different JSON shape, so branch on the HTTP status and never assume a parseable `code` / `title`.

| Code                    | Meaning    | Typical cause                                                                      |
| ----------------------- | ---------- | ---------------------------------------------------------------------------------- |
| `429 Too Many Requests` | Throttling | Program rate limit exceeded. See [Rate limits](/program/user-journey/rate-limits). |
| `502 Bad Gateway`       | Server     | A gateway or upstream dependency was unavailable. Retry.                           |
| `504 Gateway Timeout`   | Server     | A gateway or upstream dependency timed out. Retry idempotent calls only.           |

Authentication is also enforced here: `401` and `403` (listed above) are returned by the application **with** the envelope, but an edge rejection may arrive **without** it. Handle both.

## Handling common errors

<AccordionGroup>
  <Accordion title="400 Bad Request">
    The payload is malformed JSON, or a field fails validation.

    **Fix.** Read the `errors` array: each entry points at the offending field and the rule it broke. Coerce the value on the client and resend. If `errors` is empty, the body itself was not valid JSON.
  </Accordion>

  <Accordion title="401 Unauthorized">
    The credentials or bearer token are missing, malformed, or expired.

    **Fix.** Verify the `Authorization` header. For OAuth2, request a fresh token from `/tokens` (client-credentials flow) and check the API key in [Shift](/glossary#shift). See [Authentication](/program/user-journey/authentication).
  </Accordion>

  <Accordion title="403 Forbidden">
    The token is valid but is not allowed to access this resource, for example it was issued for a different [Program](/glossary#program) or environment, or it lacks the required scope (`LOYALTIES`, `PERKS`, `TX_INJECTION`, or `ALL`).

    **Fix.** Confirm you are using the token issued for this Program and this environment (sandbox vs production), and that it carries the scope the endpoint needs. See [Authentication](/program/user-journey/authentication).
  </Accordion>

  <Accordion title="404 Not Found">
    No resource matches the given ID, for example no [Consumer](/glossary#consumer) with that `consumer_id`.

    **Fix.** Verify the ID and confirm the resource was created against the same environment. Create it if needed.
  </Accordion>

  <Accordion title="409 Conflict">
    The resource already exists, or the requested state transition is illegal, for example creating a [Consumer](/glossary#consumer) whose `id` is already in your [Program](/glossary#program).

    **Fix.** Read the resource first, then update it instead of recreating it, or pick a new identifier. Never reuse an ID for a different resource.
  </Accordion>

  <Accordion title="500 / 502 Server errors">
    Something failed inside Paylead (`500`) or in an upstream dependency (`502`). Not your fault, but you still need to handle it.

    **Fix.** Retry idempotent calls with exponential backoff. If the issue persists, share the response body (including the `instance` when present) with Paylead support.
  </Accordion>
</AccordionGroup>

## Retry strategy

| Status code          | Retry?               | Strategy                                                                                                    |
| -------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------- |
| `4xx` (except `429`) | No                   | Fix the client. Retrying gives the same error.                                                              |
| `429`                | Yes                  | Honor `Retry-After`. Exponential backoff with jitter. See [Rate limits](/program/user-journey/rate-limits). |
| `500`, `502`         | Yes                  | Exponential backoff. Cap at 5 attempts.                                                                     |
| `504`                | Yes, idempotent only | `GET` and `DELETE` are safe to retry. Retrying a timed-out or `POST` may create a duplicate.                |

<Warning>
  Paylead does **not** support an `Idempotency-Key` request header. When a `POST` times out, you cannot ask the server to deduplicate the retry.
  Mitigate by re-reading the resource via `GET` before retrying, or by using deterministic identifiers (for example, your own Consumer ID) so a re-creation surfaces as `409` instead of a duplicate.
</Warning>

## What's next

<CardGroup cols={2}>
  <Card title="FAQ" icon="circle-question-mark" href="/program/user-journey/faq">
    Common integration questions, answered.
  </Card>

  <Card title="Security" icon="shield-half" href="/program/user-journey/security">
    Data protection and regulatory compliance for your integration.
  </Card>

  <Card title="Support" icon="life-buoy" href="mailto:support@paylead.fr">
    Contact Paylead support. Share the failing response, including its `instance` when present.
  </Card>
</CardGroup>
