Idempotency
The gateway accepts an Idempotency-Key header on mutating routes
(POST, PUT, PATCH, DELETE). The same key with the same request
body, within the retention window, returns the stored response from the
first call instead of running the operation again.
When to send one
- Retrying a request that may have completed. A client that times out after the server finished the work cannot tell. With a key, the retry returns the stored result; without one, a liveness upload of the same frames is refused as a replay (see Liveness checks).
- App-level retries. The user taps "Submit" twice; a job re-runs. Pin a key tied to your domain entity.
curl -X POST https://api.drukverify.com/v1/liveness/check \
-H "Authorization: Bearer dv_tok_live_..." \
-H "Drukverify-Version: 2026-05-12" \
-H "Idempotency-Key: attempt-7f3c…" \
-F "mode=challenge" -F "challenge_token=lct_…" \
-F "image_1=@frame1.jpg" -F "image_2=@frame2.jpg" -F "image_3=@frame3.jpg"
// Flutter: the liveness widget sends a key per attempt on its own.
// For direct calls, pass one:
await client.livenessCheckChallenge(
frames,
challengeToken: token,
idempotencyKey: 'attempt-$attemptId',
);
client.Liveness.Check(ctx, req,
ekyc.WithIdempotencyKey("verification-"+verificationID),
)
Rules
- Format: any string of 1 to 255 characters. UUIDs or stable domain ids both work.
- Scope: per tenant and mode. The same key from two tenants does not collide.
- Reusing a key with a different body is a
409 idempotency_conflict. A retry that arrives while the first attempt is still running is a409 idempotency_in_progress; retry shortly. GETrequests ignore the header.
None of the SDKs generates keys for you, except the Flutter liveness widget, which sends one per upload attempt since 2.5.0.