Idempotent Requests
Network errors happen. When a request fails mid-flight, you can't tell whether the server processed it or not. Idempotency keys let you safely retry requests without creating duplicate records or triggering duplicate side effects.
How it works
Include an Idempotency-Key header on any POST, PUT, or PATCH request. Kanoniv stores the response for 24 hours, keyed to your credential and the idempotency key. If you send the same request again with the same key, you get the original response back.
curl -X POST https://api.kanoniv.com/v1/ingest/batch \
-H "X-API-Key: kn_..." \
-H "Idempotency-Key: batch-2026-03-01-001" \
-H "Content-Type: application/json" \
-d '{"source": "crm", "records": [...]}'import httpx
resp = httpx.post(
"https://api.kanoniv.com/v1/ingest/batch",
headers={
"X-API-Key": "kn_...",
"Idempotency-Key": "batch-2026-03-01-001",
},
json={"source": "crm", "records": [...]},
)// The SDK does not add idempotency keys automatically.
// Use native fetch with the header for idempotent requests:
const resp = await fetch('https://api.kanoniv.com/v1/ingest/batch', {
method: 'POST',
headers: {
'X-API-Key': 'kn_...',
'Idempotency-Key': 'batch-2026-03-01-001',
'Content-Type': 'application/json',
},
body: JSON.stringify({ source: 'crm', records: [...] }),
});// The SDK does not add idempotency keys automatically.
// Use net/http with the header for idempotent requests:
body, _ := json.Marshal(map[string]interface{}{"source": "crm", "records": records})
req, _ := http.NewRequest("POST", "https://api.kanoniv.com/v1/ingest/batch", bytes.NewReader(body))
req.Header.Set("X-API-Key", "kn_...")
req.Header.Set("Idempotency-Key", "batch-2026-03-01-001")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)Key scoping
Idempotency keys are scoped to your API credential (API key or JWT token). Two different API keys can use the same idempotency key value without colliding. This means you don't need to worry about key conflicts across tenants or team members.
Request lifecycle
When you send a request with an Idempotency-Key header:
- First request - Kanoniv acquires a lock, executes the operation, caches the response, and returns it.
- Duplicate while in-flight - If a second request arrives with the same key while the first is still processing, Kanoniv returns
409 Conflict. - Duplicate after completion - If a second request arrives after the first completed, Kanoniv returns the cached response with the original status code and body.
- After 24 hours - The cached response expires. A new request with the same key will be treated as a new request.
Generating keys
Use any unique string up to 256 characters. Common strategies:
| Strategy | Example | Best for |
|---|---|---|
| UUID v4 | f47ac10b-58cc-4372-a567-0e02b2c3d479 | General purpose |
| Semantic | batch-crm-2026-03-01-001 | Batch operations |
| Content hash | sha256:a1b2c3d4... | Deduplicating identical payloads |
The key only needs to be unique within your credential scope and the 24-hour window.
Which methods support idempotency
| Method | Idempotency |
|---|---|
GET | Naturally idempotent - no header needed. |
DELETE | Naturally idempotent - no header needed. |
POST | Use Idempotency-Key for safe retries. |
PUT | Use Idempotency-Key for safe retries. |
PATCH | Use Idempotency-Key for safe retries. |
Requests without an Idempotency-Key header are processed normally. The header is always optional.
Error responses
Invalid key
{
"type": "https://api.kanoniv.com/errors/bad-request",
"title": "Bad request",
"status": 400,
"detail": "Idempotency-Key must be between 1 and 256 characters"
}Concurrent duplicate
If you send two requests with the same key at the same time, the second one returns:
{
"type": "https://api.kanoniv.com/errors/conflict",
"title": "Conflict",
"status": 409,
"detail": "A request with this Idempotency-Key is already being processed"
}Wait a moment and retry. The lock is released when the first request completes.
Limits
| Parameter | Value |
|---|---|
| Key length | 1 - 256 characters |
| Cache duration | 24 hours |
| Max cached response size | 5 MB |
Responses larger than 5 MB are not cached. The request still executes, but subsequent retries with the same key will be treated as new requests.
Best practices
Always use idempotency keys for batch ingestion. The
POST /v1/ingest/batchendpoint creates records. If your HTTP client retries on timeout, you could ingest duplicates without a key.Use semantic keys for batch workflows. Keys like
batch-crm-2026-03-01are easier to debug than random UUIDs.Generate a new key for each distinct operation. Don't reuse keys across different payloads. The cached response is for the first payload, regardless of what you send the second time.
Handle 409 with a brief retry. A 409 means your first request is still processing. Wait 1-2 seconds and retry with the same key.
