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

# Error Handling - Examino Codes, Structure & Retries

> Understand the Examino error envelope, every error code and its HTTP status, the requestId field, and when to retry a failed request.

When something goes wrong, the Examino API always returns a structured JSON body. Every error, regardless of its HTTP status code, uses the same envelope, so you can build a single error-handling layer in your client code.

## Error Envelope

Here is a representative error response:

```json title="Error response body" theme={null}
{
  "error": {
    "code": "invalid_request",
    "message": "Corps de requête invalide.",
    "details": [
      {
        "code": "too_big",
        "maximum": 300,
        "path": ["copies", 0, "studentName"],
        "message": "String must contain at most 300 character(s)"
      }
    ]
  },
  "requestId": "7f1c4a9e-0f8b-4c2a-9d31-5c2b7ad0f0e1"
}
```

<ResponseField name="error" type="object" required>
  The top-level error container.

  <Expandable title="error fields">
    <ResponseField name="error.code" type="string" required>
      A stable machine-readable identifier for the error type. **Always key
      your application logic on this field**, see the [Error Codes](#error-codes) table.
    </ResponseField>

    <ResponseField name="error.message" type="string" required>
      A human-readable description of the error, intended for logs and
      debugging. Messages may change between API versions; do not parse them
      programmatically.
    </ResponseField>

    <ResponseField name="error.details" type="array | object">
      Optional. Present for `invalid_request` (validation errors) and
      `unprocessable` (business-rule rejections). See [The details Field](#the-details-field).
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="requestId" type="string (UUID)" required>
  A unique identifier for this specific API call. Present on **every** response, both successes and errors. Log it alongside every request you make; it is
  the only reference Examino support can use to trace a call on the server side.
</ResponseField>

<Warning>
  Always key your error-handling logic on `error.code`, **not** on
  `error.message`. Error messages are for human readers and may be updated
  without notice. Error codes are stable across API versions.
</Warning>

## Error Codes

| Code               | HTTP Status | Meaning                                                                    |
| ------------------ | ----------- | -------------------------------------------------------------------------- |
| `unauthorized`     | 401         | API key is missing, malformed, unknown, revoked, or expired                |
| `forbidden`        | 403         | Key is valid but lacks the required scope, or the operation is not allowed |
| `feature_disabled` | 403         | The `rest_api` feature is not enabled on the team                          |
| `invalid_request`  | 400         | Unreadable JSON or schema validation failure                               |
| `not_found`        | 404         | Resource doesn't exist or is outside the key's team scope                  |
| `conflict`         | 409         | The current resource state prevents the operation                          |
| `unprocessable`    | 422         | Well-formed request rejected by a business rule                            |
| `rate_limited`     | 429         | Call quota exceeded, slow down                                             |
| `internal_error`   | 500         | Unexpected incident on Examino's side                                      |

## The `details` Field

The `details` field is optional and its shape depends on the error code.

<AccordionGroup>
  <Accordion title="invalid_request - schema validation errors">
    When the request body fails schema validation, `details` is an **array** of
    objects. Each object describes one schema violation:

    ```json title="invalid_request details" theme={null}
    "details": [
      {
        "code": "too_big",
        "maximum": 300,
        "path": ["copies", 0, "studentName"],
        "message": "String must contain at most 300 character(s)"
      }
    ]
    ```

    * `path` is an array of keys and array indices that locates the invalid
      field in your request body.
    * `code` is the validation rule that failed (e.g., `too_big`, `invalid_type`, `required`).
    * `maximum` and similar fields are present when they are relevant to the
      violated rule.

    A single request can fail multiple validation rules; fix all reported paths
    before retrying.
  </Accordion>

  <Accordion title="unprocessable - business rule rejections">
    When a correction launch is rejected because of a business rule, `details`
    is an **object** with a `reason` field:

    ```json title="unprocessable details" theme={null}
    "details": {
      "reason": "INSUFFICIENT_CREDITS"
    }
    ```

    Refer to the Corrections reference for the full list of `reason` values and
    how to resolve each one.
  </Accordion>

  <Accordion title="All other codes">
    For all other error codes, `details` is **absent** from the response body.
    The `error.code` and `error.message` fields contain everything you need.
  </Accordion>
</AccordionGroup>

## The `requestId` Field

Every response, success or error, carries a request ID in two places:

* **Response body:** the `requestId` top-level field.
* **Response header:** the `x-request-id` header.

```http title="Response headers (excerpt)" theme={null}
x-request-id: 7f1c4a9e-0f8b-4c2a-9d31-5c2b7ad0f0e1
```

<Tip>
  Log the `requestId` (or capture `x-request-id`) for every API call in your
  application logs. When you contact Examino support about an unexpected
  response, sharing the request ID allows the team to pull the exact server-side
  trace immediately.
</Tip>

## Retry Guidance

Not all errors are worth retrying. Use this table to decide:

| Code               | Retry?        | Notes                                                                  |
| ------------------ | ------------- | ---------------------------------------------------------------------- |
| `rate_limited`     | ✅ Yes         | Back off exponentially: 1 s, 2 s, 4 s… with random jitter              |
| `internal_error`   | ⚠️ Cautiously | Use an idempotency key on write operations to avoid duplicates         |
| `conflict`         | ❌ No          | The resource state must change first before a retry can succeed        |
| `invalid_request`  | ❌ No          | Fix the request body before retrying                                   |
| `unprocessable`    | ❌ No          | Fix the business-rule violation (e.g., top up credits) before retrying |
| `unauthorized`     | ❌ No          | Check your API key, it may be revoked or expired                       |
| `forbidden`        | ❌ No          | The key's scopes need updating                                         |
| `feature_disabled` | ❌ No          | Contact Examino support to enable the `rest_api` feature               |

<Note>
  For `internal_error` retries on mutating endpoints (POST, PATCH, DELETE),
  always include an idempotency key in your request so that a successful
  server-side operation that returned a 500 due to a network hiccup is not
  executed a second time. Refer to the individual endpoint docs for idempotency
  key support.
</Note>
