The standard idempotency pattern is simple: the client picks a key before the first attempt, sends it in a header, and reuses it on every retry. A server that has seen the key skips the work and returns the stored result. Zuplo's implementation guide lays out the mechanics: the key is created before the first request, never during retries, and stored with the status code and response body.
Agents break the first half of that rule, and they do it in a way that is easy to miss. When a language model writes the key, three things go wrong.
Non-determinism. Ask a model for a key twice with the same prompt and you get two different strings. Even with structured outputs and a strict schema, you get schema compliance, not value stability: the structured outputs chapter puts strict-mode compliance near 99.9%, which is a statement about shape, not content. A retry path that re-asks the model produces a new key, and the server treats the retry as a fresh operation.
Partial failures. The agent may call a tool, lose the response, and re-plan from scratch. The re-planned call has different arguments (a different date format, an extra field the model decided to include) and therefore a different key. Temporal's write-up on idempotency notes that activity execution is not atomic because of timeouts, environment failures, and partial success, which is exactly the window where a re-plan happens.
Parallel tool calls. An agent that fans out to several tools at once can issue the same logical operation on two branches. If each branch mints its own key, both writes land. Shopify's idempotency guidance is blunt about the underlying error: generating a new random identifier on every call defeats idempotency entirely, because every request looks distinct.
The fix is not a better prompt. It is to take key generation away from the model and put it in deterministic code that reads the business identifiers already present in the tool call.



