Skip to main content
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.
Idempotency keys are required on all correction launch requests. A request without one is rejected.

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

Complete example

The following request launches correction for all copies in an exam using a well-formed idempotency key:
Launch correction with idempotency key
If the request succeeds on the first attempt, you receive a standard launch response:
First successful response
If you replay the same request after a timeout, Examino returns the same payload with alreadyRegistered: true:
Duplicate request — already registered
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:
1

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

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

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

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