What should you do when an automation runs twice?
Stop trying to prevent the second run and make it harmless instead. Retries are normal and mostly outside your control, so the durable fix is designing every write so that running it twice produces the same result as running it once.
The instinct is to hunt for the cause, and that is worth an hour. But you will not eliminate duplicate execution, because it comes from network conditions, provider retries and human impatience, none of which you own. What you own is what happens on the second arrival.
This is the single most useful concept in automation work and it has an unfriendly name. Idempotency just means running something again changes nothing, and once you design for it, duplicate runs stop being incidents.
Why do automations run twice in the first place?
Because almost every layer between the trigger and the action will retry on uncertainty. A request that times out may well have succeeded, so the sender tries again rather than risk losing it. That is correct behaviour, and it produces duplicates.
The human causes are just as common and less discussed. Someone submits a form twice because nothing acknowledged the first submission. Someone reruns a failed job that had actually half-succeeded. Someone clicks a manual trigger, waits, and clicks again.
Then there is the version you cause yourself: two automations watching the same trigger, or one automation whose own action re-triggers it. Those are worth finding, because they are the only ones you can genuinely remove. The rest you have to absorb.
What does idempotency actually mean here?
That the outcome depends on the request rather than on how many times it arrived. Stripe's API documentation describes supporting idempotency for safely retrying requests without accidentally performing the same operation twice, which is exactly the property you want in a workflow.
Notice the framing. It is not about blocking retries, it is about making retries safe. Once a retry is safe, the whole anxious apparatus of trying to guarantee exactly-once delivery becomes unnecessary, and you can let the network be as unreliable as it actually is.
The practical translation for automation work is simple. Prefer updating a record identified by a stable key over creating a new one. An update that runs twice leaves one record in the right state. A create that runs twice leaves two records and a cleanup job.
How does an idempotency key work?
You generate a unique key per operation and send it with the request. Stripe's documentation explains that a client generates an idempotency key, which is a unique key the server uses to recognise subsequent retries of the same request. The server then remembers what it did.
The remembering is the important part, and Stripe is precise about it. Its documentation says idempotency works by saving the resulting status code and body of the first request made for any given key, regardless of whether it succeeds or fails, and that subsequent requests with the same key return the same result, including 500 errors.
That last clause is worth sitting with. A retry does not get a fresh attempt, it gets the recorded answer, error included. So an idempotency key protects your data at the cost of not retrying the underlying failure, which is the correct trade and not always what people expect. On generating keys, Stripe suggests V4 UUIDs or another random string with enough entropy to avoid collisions, notes keys are up to 255 characters long, and advises against using sensitive data such as email addresses or personal identifiers as keys.
What happens if you reuse a key with different data?
You get an error, deliberately. Stripe's documentation states that the idempotency layer compares incoming parameters to those of the original request and errors if they are not the same, to prevent accidental misuse. That check exists because key reuse is usually a bug.
This catches a mistake I have seen more than once: generating the key from something too coarse, like a customer ID or a date. Then two genuinely different operations collide on one key, and either the second is silently treated as a duplicate or it errors. Both are worse than no key at all.
So derive the key from the operation, not from the entity. One key per intended action, generated when you form the intent, reused only when retrying that exact action. If you cannot state in one sentence what a key uniquely identifies, it is the wrong key.
How long does that protection last?
Not forever, and the window matters for anything slow. Stripe's documentation says keys can be removed from the system automatically after they are at least 24 hours old, and that a new request is generated if a key is reused after the original is pruned.
So a retry arriving a week later is a fresh operation, not a deduplicated one. That is fine for the failure modes idempotency is designed for, which resolve in seconds or minutes, and it is not protection against replaying an old job by hand next Tuesday. Different problem, different control.
There is also a boundary condition worth knowing. Stripe's documentation notes results are saved only after execution of an endpoint begins, and that if incoming parameters fail validation, or the request conflicts with another executing concurrently, the idempotent result is not saved and the request can be retried. Validation failures do not consume your key.
Which operations are already safe?
Reads and deletes, generally. Stripe's documentation says all POST requests accept idempotency keys, and that keys should not be sent in GET and DELETE requests because it has no effect, those being idempotent by definition. Fetching something twice changes nothing; deleting something twice leaves it deleted.
Creates are the dangerous ones, which is why duplicate leads and duplicate deals are the classic symptom. Anything that appends rather than sets is at risk: creating a record, adding a row, sending a message, charging a card. Each of those run twice leaves visible damage.
Setting a value is usually safe and appending is usually not, which gives you a quick way to audit a workflow. Read every action and ask whether it sets or appends. The appends are your exposure, and they are where the key belongs. This pairs with deciding what an automation should do with an incomplete record, since both are about the state a half-run leaves behind.
What do you do when the tool gives you no key at all?
Build your own check in the destination. Most no-code tools do not expose idempotency keys at all, so instead you look up whether the record already exists before creating it, matching on something stable that the source system provides with every payload.
The trick is choosing the match field properly. It has to be unique, present on every record and never edited by a human. An email address fails the last test, a submission ID or an external record ID usually passes all three. Then the action becomes find-or-create rather than create, and a second run finds instead of creating.
This is slower and costs an extra API call per run, and I pay that cost willingly. The alternative is periodic deduplication, which means somebody merging records by hand while wondering which one has the right notes. I would rather spend the call. Be aware that the match field is now a dependency, which is its own reason to watch what happens when an API you automate against changes.
What should you do next?
Take your highest-volume automation and find every action that creates something. For each one, decide what happens if it runs twice today. If the answer is a duplicate record, convert it to find-or-create before you do anything else on your list.
Then check whether you would even notice. A duplicate that produces two CRM records is visible; a duplicate that sends two emails to a customer is not, unless someone complains. The invisible ones deserve attention first, which is also a good reason to version your automations so you can audit runs.
The automations I run for Ajust on Airtable with WhaleSync have delivered 25,000+ cases and helped 400,000+ people, saving 50,000+ hours, and for Kismet Health I run HubSpot work through Zapier. At that volume duplicates are not hypothetical, they are weekly, and the only thing that makes them boring is having designed for them. If your workflow creates records and you have never tested a double run, reach out and let's try it deliberately.
Get found, cited and the back office automated
Let's make your site the source AI engines quote and wire up the systems behind it.
Read more blogs
Let's get your website found and cited by AI
Tell me what you're working on, whether AI search is skipping your product, your back office is buried in manual work, or you need a build that does both.