---
title: Idempotency and retries
description: What is actually idempotent on this platform — the Idempotency-Key header on the two intake endpoints, its 24-hour window and conflict rule, the de-duplicated report intake, which MCP tools are safe to repeat — and how to retry each error class.
locale: en
version: 0.2.0
canonical: https://enterium-developer-docs.pages.dev/developers/reference/idempotency/
---

> Documentation index: https://enterium-developer-docs.pages.dev/developers/llms.txt — every page of this portal has a Markdown twin at <route>.md; the frozen API contract is at https://enterium-developer-docs.pages.dev/developers/api/openapi.json.

Retry safety is narrower than most clients assume. This page lists exactly
what the platform guarantees today; anything not listed is **not** idempotent.

## The `Idempotency-Key` header

| Fact | Value |
| --- | --- |
| Where it is honoured | Every write under `/api/v1/` (`POST`/`PATCH`/`PUT`/`DELETE`) — a replay cache at the HTTP layer; `POST /api/v1/tasks` and `POST /api/v1/tickets` also store the key on the created record |
| Format | Any UUID, one per logical request |
| Window | 24 hours: a retry with the same key returns the **original** response instead of creating a second object |
| Same key, different body | `422` with `code: "idempotency_conflict"` and `detail: "Idempotency-Key conflict: same key used with different request body"` — the key is bound to the first body |
| What is replayed | `2xx` and `4xx` answers; a `5xx` is never cached, so a failed call can be retried with the same key. The replayed answer carries `idempotent-replayed: true` |
| In the contract | Declared as an optional header parameter on every write operation in `openapi.json` |

```sh
curl -sS -X POST https://entherium.duckdns.org:8443/api/v1/tasks \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3f1a6c62-0b5e-4d0f-9d1e-1f6a3b2c4d5e" \
  -d '{"title": "Re-check the source feeds of contract 42"}'
```

## Naturally idempotent operations

| Operation | Why repeating is safe |
| --- | --- |
| Every `GET` | Reads have no side effects |
| Defect / suggestion reports (`POST /api/v1/tickets`, MCP `entherium_report_defect`, `entherium_propose_improvement`) | Intake de-duplicates by content fingerprint: a repeated report is folded into the existing ticket, not filed twice — on top of the header |
| MCP `entherium_target_task_spec`, `entherium_reassign_task` | Setting the same target twice leaves the same state (`idempotentHint: true`) |
| `PATCH` that sets a field to the value it already has | A no-op by construction; status *transitions* are the exception below |

## Not idempotent — read the state first

| Operation | What repeating does |
| --- | --- |
| `entherium_update_task_status` / ticket `resolve` · `dismiss` · `reopen` | The second call hits a state that no longer allows the transition → `409 illegal transition`. Re-read before deciding |
| `entherium_log_task_note` | Appends a second note |
| Run launches (`POST /api/v1/runs/…`) | Starts another run; check the run list before retrying a launch whose response you lost |
| Agent writes to entities | Applied each time; a lost response does not mean a lost write — read back before repeating |

## How to retry, by error class

| You got | Retry? | How |
| --- | --- | --- |
| Network timeout / connection reset | Yes, for reads and for the two intake endpoints **with the same `Idempotency-Key`**; for other writes read back first | Exponential backoff from 1 s, at most 5 attempts |
| `429` | Yes | Wait the `Retry-After` seconds, then repeat the same request |
| `500` | Once | Short pause, then file a defect quoting `request_id` if it repeats |
| `502` / `503` / `504` | Yes, slowly | The platform is down, paused or not ready — poll [status](../status/) rather than hammering |
| Any other `4xx` | No | The request is wrong; `detail` and `doc_url` say what to fix ([Errors](../errors/)) |

## MCP: what the hints promise

Every tool declares `idempotentHint` and `readOnlyHint` ([tools reference](../../mcp/tools/)).
`readOnlyHint: true` tools can be repeated freely. A write tool with
`idempotentHint: true` may be repeated with the same arguments; one with
`idempotentHint: false` must not be repeated without reading the object first.
The hints are derived from the same code that runs the tools, so they cannot
be out of date relative to the server you are connected to.
