Idempotency in AI Workflows: Retry Without Double-Charging
Idempotency is what lets an AI workflow retry a failed step without sending two emails or charging a card twice. Here is how to build it in from the start.
If your AI workflow retries a failed step and the customer gets charged twice, you do not have a reliability problem. You have an idempotency problem. Idempotency means running the same step twice produces the same result as running it once. Get this right and retries become safe. Get it wrong and every retry is a coin flip between fixing the problem and creating a worse one.
I run automation across roughly twenty companies. Steps fail constantly: a network blip, a rate limit, a timeout on a slow API. The workflow has to retry, because giving up on the first failure means nothing ever finishes. But a retry that resends an invoice or double-posts a ledger entry is not help. It is damage. So idempotency is not a nice-to-have. It is the thing that makes retries allowed.
What idempotency actually means in a workflow
A step is idempotent when calling it with the same inputs any number of times leaves the system in the same state as calling it once. Reading a record is naturally idempotent. Writing is where it breaks. "Charge this card $200" run twice charges $400. "Set this invoice to paid" run twice is fine, because the second call changes nothing.
The goal is to make every write-side step behave like that second example. You do it with an idempotency key: a unique token that represents this specific unit of work. The step attaches the key to the operation. The downstream system remembers keys it has already seen and refuses to process a duplicate. Stripe, most payment rails, and any serious API support this. The ones that do not are the ones that will hurt you.
How to build idempotency into every step
Generate the key before the step runs, not inside it. If you generate it inside, a retry generates a new key and the whole point is lost. Derive it from something stable: the workflow run ID plus the step name plus the target record. Same work, same key, every time.
Store the key and the result. When a step completes, write down the key and what it returned. On a retry, check for the key first. If it exists, return the stored result and skip the side effect entirely. This turns a dangerous "do it again" into a safe "we already did this, here is what happened."
For steps that hit an external API, pass the key through. Payment processors and email providers take an idempotency header. Use it. Let their side dedupe too, so even if your own bookkeeping fails you have a second layer catching the duplicate.
For steps that write to your own database, use the key as a unique constraint. Insert the key. If the insert fails because the key exists, the work already happened. The database becomes the referee. This is how AI agents recover from failure without leaving a mess behind them.
Where teams get idempotency wrong
The most common mistake is retrying a whole workflow instead of a step. If your unit of retry is the entire run, then re-running it replays every step, including the ones that already succeeded. Now you need every step to be idempotent, which is harder than making retries granular. Retry at the step level and checkpoint what finished.
The second mistake is treating time as an input. If your key includes a timestamp, every retry gets a fresh key and dedupe never fires. Keys must be deterministic from the work, not the clock.
The third mistake is forgetting that "send" is a write. Sending an email, posting to Slack, firing a webhook: these are side effects the recipient sees. A duplicate notification is not as expensive as a duplicate charge, but it erodes trust in the automation. I treat every outbound message as an idempotent operation with a key, same as money. This connects directly to how you connect AI agents to your tools without them stepping on each other.
Why this matters more with AI in the loop
Deterministic automation fails in predictable ways. AI-driven workflows fail in stranger ones. A model call times out after the work partially completed. An agent decides to retry on its own. Steps run in parallel and race. The surface area for duplicates is larger, so the discipline has to be tighter.
This is part of what separates a real platform from a script that works on a good day. When I talk about what a complete AI automation platform requires, idempotent execution is near the top. A platform that cannot retry safely cannot run unattended, and automation you have to babysit is not automation.
We build this into the execution layer at Girard AI so every step is keyed, deduped, and safe to retry by default. You should not have to reason about double-charges on every workflow you write. The platform should make the safe path the only path. If you are evaluating tools, ask the vendor exactly how their retry logic avoids duplicate side effects. If they cannot answer in one sentence, walk. For a broader view of the same problem across an agent stack, ServoAgent treats idempotency as table stakes too, because agents that act on the world without it are a liability, not a feature.