Error reference

idempotency_expired

HTTP 409 retryable: false REST and MCP

The Idempotency-Key and the payload both match a previous request, but the window in which the stored response can be replayed has passed.

Why it happens

An idempotency record carries two timestamps. replay_until is set 30 days out and is how long the stored response snapshot can be returned again. tombstone_until is set 365 days out and is how long the key stays claimed so it cannot be reused for something else. Between those two dates the key is known, the payload matches, and the response is gone, which is this error. It is not a duplicate-send error: the original send happened and its record still exists, you simply cannot retrieve the answer through this route any more.

Every condition below was read in the implementation at the file and line given. They are the branches that raise this code, not a summary of what the name suggests.

How to fix it

  1. Look up the message instead of replaying. The original send succeeded, so the message exists and is readable through the inbox's message listing.
  2. Do not reuse a key after 30 days to mean the same logical send. Mint a new one, and expect a genuinely new message.
  3. Do not treat this as a failed send. Nothing rolled back; only the cached response is gone.
  4. Shorten the retry horizon in your client. A queue that can hold an item for a month is a queue that will produce this error.

Why it works this way

The two windows are separate on purpose. Keeping the key claimed for a year after the response snapshot expires is what stops a key from silently becoming reusable for a different message once the cache falls away.

Messages you will see

The message field is written for a human reading a log and is not a contract value. Branch on code instead. These are the exact strings this code is sent with today, listed so that a search for one of them lands here.

Where it appears

These are the documented surfaces that raise it. The REST base is https://api.emailforagents.ai/v1, and the remote MCP endpoint is https://mcp.emailforagents.ai/mcp.

The response

Read request_id off the body or the x-request-id header before doing anything else. It is the only handle on one specific attempt, and for a 503 it is the only handle at all, because the underlying detail stays server-side.

HTTP/1.1 409
content-type: application/json
x-request-id: req_...

{
  "error": {
    "code": "idempotency_expired",
    "message": "The idempotency replay period has ended.",
    "request_id": "req_...",
    "retryable": false
  }
}

Verified against packages/domain/src/services.ts:1617. The status comes from ERROR_STATUS in packages/contracts/src/errors.ts, which is the one place a code is mapped to a status.

All error codes · Documentation · Quickstart