Idempotency
Idempotency-Key semantics for mutating inference and governance calls.
Send Idempotency-Key on any request that must not execute twice: inference, key creation, budget writes, webhook registration, template publish, and evidence export. Eridian stores the first successful response for 24 hours per project and key.
Header
POST /v1/inference
Authorization: Bearer eridian_sk_...
Content-Type: application/json
Idempotency-Key: contract-summary-4821-v1
X-Eridian-Project: prj_legal_001
Keys must be 1 to 255 characters from [A-Za-z0-9._-]. Generate them in your application (UUID v4 or a deterministic workflow ID). Do not reuse a key across different request bodies.
Replay Behavior
| Condition | Result |
|---|---|
| Same key, same body, first request in flight | Second caller waits up to 30s then receives the original response |
| Same key, same body, complete | HTTP 200 with original eridian.inference.response and X-Eridian-Idempotent-Replay: true |
| Same key, different body | HTTP 409 idempotency_conflict |
| Key expired (>24h) | Treated as a new request |
Cached replays do not increment usage, do not re-hit GPT or Gemini, and do not emit a second inference.completed webhook. The original X-Eridian-Request-Id is returned.
Scope
Idempotency is partitioned by project_id and API key ID. Two projects may use the same string without colliding.
Mutating Endpoints
| Method | Path | Idempotent with header |
|---|---|---|
| POST | /v1/inference | Yes |
| POST | /v1/keys | Yes |
| POST | /v1/keys/{id}/rotate | Yes |
| PUT | /v1/projects/{id}/governance | Yes |
| POST | /v1/webhooks | Yes |
| POST | /v1/templates/{id}/publish | Yes |
| POST | /v1/audit/exports | Yes |
| GET | any | Not required; GET is safe |
Conflict Body
{
"error": {
"type": "eridian.error",
"code": "idempotency_conflict",
"message": "Idempotency-Key contract-summary-4821-v1 was already used with a different body.",
"request_id": "axm_req_8f3a2b1c"
}
}
Python
response = client.inference.create(
model="auto",
project_id="prj_legal_001",
messages=[{"role": "user", "content": "Summarize this MSA."}],
idempotency_key="contract-summary-4821-v1",
)
TypeScript
await client.inference.create({
model: "auto",
projectId: "prj_legal_001",
messages: [{ role: "user", content: "Summarize this MSA." }],
idempotencyKey: "contract-summary-4821-v1",
});
Production API credentials are issued with an institution workspace. Contact sales if you need access.