> ## 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.

# Safe Retries with Idempotency Keys - Examino Guide

> Learn how idempotency keys prevent duplicate corrections when retrying failed or timed-out API requests in your Examino integration.

Network hiccups, scheduler double-fires, and process crashes all produce the same problem: your code doesn't know whether the last request landed. Without a guard, retrying a correction launch could charge credits twice, trigger redundant AI processing, and produce conflicting results for the same exam. Examino solves this with idempotency keys, a client-generated string you attach to every correction launch so the server can recognise and safely absorb duplicate requests.

## How idempotency keys work

When you include an `idempotencyKey` in a `POST /exams/{examId}/corrections` request, Examino stores that key alongside the correction launch. If you replay the exact same call, whether because of a timeout, a `500` response, or a scheduler that fires twice, Examino detects the duplicate key and returns the **original result** instead of starting a new correction. No additional credits are reserved, and no second correction is created.

<Note>
  Idempotency keys are **required** on all correction launch requests. A request without one is rejected.
</Note>

## Key format and lifetime

You generate the key yourself. The only requirements are:

* **Length:** 8–128 characters
* **Uniqueness:** unique per intended correction launch (not per retry of the same launch)
* **Lifetime:** keys are valid for **24 hours**, a replay arriving after that window may be treated as a new launch

## Designing good keys

The most reliable keys are **deterministic for the attempt**: you can reconstruct them from information already in your system without storing extra state. A practical pattern is:

```text theme={null}
<your-exam-id>-<date>-<attempt-number>
```

For example: `sis-partiel-s1-2026-09-17-001`

This key encodes the exam identifier, the date of the intended run, and a sequential attempt counter. If your scheduler retries the job three times before succeeding, all three attempts carry the same key and Examino deduplicates them automatically.

<Warning>
  Do **not** reuse a key for a genuinely new correction launch, for example, a second scheduled run of the same exam on a different date. Examino will return `alreadyRegistered: true`, no new correction will be created, and your students' copies will not be re-graded.
</Warning>

## Complete example

The following request launches correction for all copies in an exam using a well-formed idempotency key:

```bash title="Launch correction with idempotency key" theme={null}
curl -X POST https://app.examino.ai/api/v1/exams/aa11bb22-cc33-44dd-88ee-ff0011223344/corrections \
  -H "Authorization: Bearer $EXAMINO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotencyKey": "sis-partiel-s1-2026-09-17-001",
    "scope": "all"
  }'
```

If the request succeeds on the first attempt, you receive a standard launch response:

```json title="First successful response" theme={null}
{
  "launchId": "c0ffee00-1111-4222-8333-444455556666",
  "launched": true,
  "alreadyRegistered": false,
  "targetCopiesCount": 1300
}
```

If you replay the same request after a timeout, Examino returns the **same payload** with `alreadyRegistered: true`:

```json title="Duplicate request - already registered" theme={null}
{
  "launchId": "c0ffee00-1111-4222-8333-444455556666",
  "launched": true,
  "alreadyRegistered": true,
  "targetCopiesCount": 1300
}
```

The `launchId` is identical in both responses, confirming you are looking at the same correction. No credits were charged a second time.

## Safe retry pattern

Follow this pattern in any scheduler or background worker that calls the correction launch endpoint:

<Steps>
  <Step title="Generate and persist the key before calling the API">
    Compute your idempotency key from deterministic inputs and write it to your job store **before** making the HTTP request. If your process crashes after sending but before receiving a response, you can still recover the key on restart.
  </Step>

  <Step title="Send the request and inspect the response">
    Make the `POST /exams/{examId}/corrections` call with the stored key.

    * **2xx response** → the launch is confirmed. Check `alreadyRegistered` to distinguish a fresh launch from a replay.
    * **4xx response** (except `429`) → do not retry with the same key. Fix the underlying problem (invalid exam state, insufficient credits, etc.) and issue a new key for the corrected request.
    * **5xx response or timeout** → proceed to the next step.
  </Step>

  <Step title="Retry with the same key on 5xx or timeout">
    Re-send the **identical request body**, same `idempotencyKey`, same `scope`, after a brief backoff. Examino will either return the original result (`alreadyRegistered: true`) or process the request fresh if the first attempt never landed.

    Continue retrying until you receive a non-5xx, non-timeout response.
  </Step>

  <Step title="Mark the job complete">
    Once you receive a `2xx` response, record the `launchId` in your job store and mark the job as complete. You can now poll `GET /exams/{examId}/corrections` to track per-copy status.
  </Step>
</Steps>

<Tip>
  Store the `launchId` from the first successful response. When you poll for results later, you can use it to confirm that the corrections you're reading belong to the launch your scheduler intended.
</Tip>
