Idempotency
Agents and networks retry. An idempotency key is how you make a retried write land once, so a dropped connection mid-request doesn't leave you with two identical schedules. This is the request-side companion to the at-least-once delivery described in Webhooks & signing.
Why you need idempotency keys
A mutating call can fail in the one place you can't observe: after AIGears created the resource but before the response reached you. Your agent, seeing no response, retries — and without a key that retry is a brand-new create. You get a duplicate schedule, a duplicate deadline, a duplicate approval request to a human.
An idempotency key closes that gap. You attach the same key to the original call and every retry of it; AIGears remembers the first outcome and replays it instead of doing the work again.
How it works
Idempotency is scoped to the Team and the
key you choose — (Team, key) — with a 24-hour window. Scoping to the Team
(not the API key) means rotating or revoking a key never breaks an in-flight
retry.
Within that window:
- Same key + same request → the original result is replayed. No second resource is created; you get back the first outcome.
- Same key + a different request body → the call is rejected with
422 Unprocessable Entityand{"error": "idempotency_key_collision"}. The original is untouched and its result is not returned — a reused key must mean the same operation. Use a fresh key. - Same key after 24 hours → treated as a new request.
Idempotency covers every mutating operation — create, cancel,
complete, snooze, pause, resume — because the crash-then-retry failure
mode is identical for all of them. Reads (list, get, query, status,
history) don't use it. Sending a key is recommended, not required: omit it
and you get at-most-once-per-call semantics, where retries create duplicates.
Passing the key on each surface
Idempotency keys are an API/MCP concept — you won't set one in the Dashboard. The form is the natural dedupe: a human clicking Create once produces one resource, and a double-submit is guarded in the browser. The concept exists for the surfaces where an unattended agent retries on your behalf.
Send the key in the Idempotency-Key request header on any mutating call:
curl -X POST https://<host>/v1/schedules/ \
-H "Authorization: Bearer $AIGEARS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: renew-cert-acme-2026-06" \
-d '{ "fire_at": "2026-06-01T09:00:00Z", "webhook_url": "https://example.com/hook" }'
On a replayed request the original response is returned with an
Idempotency-Replayed: true header so you can tell a replay from a fresh create.
MCP tools don't expose HTTP headers, so the key is a tool argument,
idempotency_key — the body-field alias of the header, with identical semantics.
It's accepted on every mutating tool:
schedule_create(
webhook_url="https://example.com/hook",
fire_at="2026-06-01T09:00:00Z",
idempotency_key="renew-cert-acme-2026-06",
)
A retried tool call carrying the same idempotency_key replays the first tool
result instead of creating a second schedule.
Choosing good keys
A good key is derived from the agent's task, not generated fresh per attempt — because the whole point is that the retry carries the same key. A random UUID minted inside the retry loop defeats the mechanism: each attempt looks like a new request.
Derive the key from something stable about the intent — the task id, or a natural
composite like renew-cert-acme-2026-06. Reuse it across every retry of that one
logical operation, and change it only when the operation itself is genuinely
different (a different body under the same key is exactly the 422 collision
above).