The standard transactional outbox exists because a database and an external system do not share a transaction coordinator. If you update your own state and then call an external API, a crash between the two leaves you inconsistent. Fail after the commit and the external effect never happens; fail after the external call and your database never records it. This is the dual-write problem, and the outbox pattern is the usual answer: write the message to a table inside the same local transaction, then publish from that table later (the transactional outbox analysis, CodeToByte's outbox guide).
Agents add three multipliers to this problem. They retry aggressively because persistence is what they are rewarded for, so a tool call that times out is often reissued without any check on whether the first call landed (Tool Calling Has a Tax). They run concurrently, so two agents can act on the same record at once; race conditions in multi-agent orchestration are the expected case rather than the edge case, and the classic pattern is two actors both passing a check before either acts, leaving shared state corrupted (Handling Race Conditions in Multi-Agent Orchestration, OWASP on race conditions). And they are non-deterministic, so the same intent can be expressed with slightly different arguments on a second attempt.
The practical consequence: you cannot ask the model to produce the idempotency key. A freshly sampled value identifies the retry as a brand new operation and defeats deduplication entirely. The orchestration layer must generate and persist the key outside model-controlled arguments, derived from a durable execution step (Agent Tool Call Retries). An outbox row is exactly that durable step.



