Skip to main content

Idempotency

Network failures happen. The safe way to retry an API call without risking duplicates is to use the Idempotency-Key header — DVS recognizes it and deduplicates within a 24-hour window.

How it works

  1. Generate a UUID client-side per logical operation (one classification, one validation).
  2. Send it in the Idempotency-Key header on every retry of that operation.
  3. If you retry and DVS already processed the original, DVS returns the original response without re-processing.
curl -X POST https://dvs-ingestion-api.sandbox.osigu.com/v1/classification-requests \
-H "Authorization: Bearer $TOKEN" \
-H "Idempotency-Key: f47ac10b-58cc-4372-a567-0e02b2c3d479" \
...

When DVS deduplicates

ScenarioDVS behavior
Same Idempotency-Key, same body, within 24hReturns cached response. No new processing. No new charges.
Same Idempotency-Key, different body, within 24hReturns 409 071-409-idempotency-mismatch.
Same Idempotency-Key, after 24hTreated as a new request. The key is reused.
Different Idempotency-Key, same bodyCreates a new request.

Best practices

Generate the key at the boundary of the operation

Generate it once when you decide "I'm going to call DVS to classify this document". Reuse it for any retry of that same call. Don't regenerate per retry — that defeats the purpose.

UUID v4 is fine

Don't overthink — uuid.uuid4() or crypto.randomUUID() is fine. Avoid sequential IDs (predictable).

Persist the key alongside your operation

Store the key in your database with the operation it represents. On crash recovery, you can retry safely by reading the persisted key.

One key per operation, not per request

If your application sends 1000 classifications, generate 1000 keys. Don't share keys across different documents.

Race conditions

If two parallel requests arrive at DVS with the same Idempotency-Key, only one is processed; the other waits for the first to complete (up to 5 seconds) and returns the same response. This is safe but adds latency — try to serialize your retries client-side.

Example: safe retry loop

import uuid, time, httpx

def classify_with_retry(token, body, max_attempts=3):
idempotency_key = str(uuid.uuid4()) # ONE key per logical operation
for attempt in range(max_attempts):
try:
return httpx.post(
"https://dvs-ingestion-api.sandbox.osigu.com/v1/classification-requests",
headers={
"Authorization": f"Bearer {token}",
"Idempotency-Key": idempotency_key, # reused on every retry
},
json=body,
timeout=30,
).raise_for_status().json()
except httpx.HTTPError:
if attempt == max_attempts - 1:
raise
time.sleep(2 ** attempt)