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
- Generate a UUID client-side per logical operation (one classification, one validation).
- Send it in the
Idempotency-Keyheader on every retry of that operation. - 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
| Scenario | DVS behavior |
|---|---|
Same Idempotency-Key, same body, within 24h | Returns cached response. No new processing. No new charges. |
Same Idempotency-Key, different body, within 24h | Returns 409 071-409-idempotency-mismatch. |
Same Idempotency-Key, after 24h | Treated as a new request. The key is reused. |
Different Idempotency-Key, same body | Creates 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)