Skip to content

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.

bash
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": [...]}'
python
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": [...]},
)
typescript
// 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: [...] }),
});
go
// 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:

  1. First request - Kanoniv acquires a lock, executes the operation, caches the response, and returns it.
  2. Duplicate while in-flight - If a second request arrives with the same key while the first is still processing, Kanoniv returns 409 Conflict.
  3. Duplicate after completion - If a second request arrives after the first completed, Kanoniv returns the cached response with the original status code and body.
  4. 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:

StrategyExampleBest for
UUID v4f47ac10b-58cc-4372-a567-0e02b2c3d479General purpose
Semanticbatch-crm-2026-03-01-001Batch operations
Content hashsha256:a1b2c3d4...Deduplicating identical payloads

The key only needs to be unique within your credential scope and the 24-hour window.

Which methods support idempotency

MethodIdempotency
GETNaturally idempotent - no header needed.
DELETENaturally idempotent - no header needed.
POSTUse Idempotency-Key for safe retries.
PUTUse Idempotency-Key for safe retries.
PATCHUse Idempotency-Key for safe retries.

Requests without an Idempotency-Key header are processed normally. The header is always optional.

Error responses

Invalid key

json
{
  "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:

json
{
  "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

ParameterValue
Key length1 - 256 characters
Cache duration24 hours
Max cached response size5 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

  1. Always use idempotency keys for batch ingestion. The POST /v1/ingest/batch endpoint creates records. If your HTTP client retries on timeout, you could ingest duplicates without a key.

  2. Use semantic keys for batch workflows. Keys like batch-crm-2026-03-01 are easier to debug than random UUIDs.

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

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

The identity and delegation layer for AI agents.