Automation

Idempotency and Retries: Why Your Automations Duplicate Invoices, Orders and Emails (2026)

Jignesh Vagadiya 27/09/2026 21 min read
Illustration of a retried automation signal on the left resolving into a single created invoice on the right, representing idempotent workflow design

A Melbourne wholesaler we spoke with recently found out the hard way that their order automation was too eager. A supplier’s connection blipped for about four seconds during a Friday afternoon rush, their n8n workflow retried the failed step automatically, and by Monday the accounts team was reconciling two identical purchase orders sitting in Xero, one shipment confirmation email that went out twice, and a very confused supplier asking which order was real. Nobody had touched the workflow. Nothing was “broken” in the sense that would show up in an error log. The automation had simply done exactly what it was built to do — retry on failure — without anyone having designed it to handle what happens when the retry succeeds after the first attempt actually went through too.

This is one of the most common and least understood failure modes in business automation, and it has nothing to do with picking the wrong tool. Zapier, Make and n8n all behave this way by default, because the underlying delivery model of the internet guarantees “at least once” message delivery, not “exactly once.” Every webhook, every API call, every scheduled trigger can fire more than once for the same real-world event, and if your workflow treats every firing as a brand-new instruction, you will eventually create duplicate invoices, duplicate orders, duplicate CRM records or duplicate customer emails. The fix is a discipline called automation idempotency, and it is one of the few genuinely technical ideas in workflow automation that pays for itself the first time it prevents a $4,000 double-billed invoice from going out to a client.

We build and maintain automation for Australian businesses across trades, ecommerce, professional services and wholesale distribution, and duplication bugs are one of the most common support tickets we get on workflows we didn’t originally build. Almost always, the workflow was designed to handle the happy path and never asked what should happen if the same trigger fired twice. This guide walks through why that happens, where it actually bites in real Zapier, Make and n8n builds, and the specific patterns that stop it — without needing a computer science degree or a rebuild of your entire stack.

Why Automations Retry in the First Place

Retries aren’t a bug, they’re a deliberate design choice, and mostly a good one. Networks drop packets. APIs time out under load. A third-party service returns a 500 error for two seconds during a deploy. If your automation platform gave up the instant any of that happened, you’d spend your life manually re-running failed steps, and far more business processes would silently stop working than currently do. So Zapier, Make and n8n all build in some form of automatic retry, and most webhook providers (Stripe, Shopify, Xero) retry their own webhook deliveries too if they don’t get a fast, successful response back from your endpoint.

The problem is what “failure” actually means from the calling system’s point of view. A timeout doesn’t tell you the request failed — it tells you that you didn’t hear back in time. The request might have failed completely. It might have partially succeeded. Or it might have succeeded entirely and the response just never made it back to you because of a network issue on the way out, not the way in. From the outside, all three look identical: you sent a request and got nothing back. So the calling system does the only sensible thing it can — it retries, on the assumption that retrying a request that already succeeded is safer than not retrying one that failed. We cover the mechanics of this in more depth in our piece on webhooks versus polling for real-time data sync, because the same timeout-and-retry pattern shows up whichever transport method you’re using.

This is what “at-least-once delivery” means in practice: your workflow is guaranteed to be triggered for every real event, but it is not guaranteed to be triggered exactly once. If your workflow’s logic assumes one trigger equals one action, you have built a system that will eventually take that action twice for the same event. The question isn’t whether this will happen to you — if you’re running automations at any real volume, it’s a when, not an if.

What Idempotency Actually Means

An operation is idempotent if running it multiple times produces the same result as running it once. Setting a field to a fixed value is idempotent — setting “status” to “paid” ten times leaves the record exactly as if you’d done it once. Creating a new invoice is not idempotent by default — calling “create invoice” ten times creates ten invoices, because the API has no way of knowing the second call is a repeat of the first rather than a genuinely new invoice for the same amount.

The goal isn’t to stop your automations from retrying — retries are useful and you want to keep them. The goal is to make the operations they trigger safe to repeat, so a retry either does nothing (because the work was already done) or does exactly the same thing again with no side effect, rather than creating a second real-world outcome.

The Idempotency Key Pattern

The standard way to do this is an idempotency key: a unique identifier attached to a specific business event, not to a specific attempt at processing it. Before your workflow creates an invoice, an order or a CRM record, it checks whether it has already seen that key. If it has, it returns the result of the original action instead of doing the work again. If it hasn’t, it does the work and records the key against the result, so the next attempt with the same key finds it.

Good idempotency keys come from the source event, not from anything your workflow generates itself. A Shopify order ID, a Stripe payment intent ID, a Xero invoice reference tied to a specific sales order, or a webhook’s own event ID are all solid choices, because they identify the underlying business event rather than the automation run. A timestamp is a poor choice — two retries of the same event a few seconds apart will generate two different timestamps and defeat the entire purpose. We see this mistake constantly in workflows that were built quickly: someone uses “order date + customer email” as a makeshift key, which works until two genuine orders land from the same customer on the same day.

Where Duplication Actually Bites in Real Workflows

This isn’t a theoretical problem. These are the specific places we see it show up in Australian businesses running Zapier, Make or n8n day to day.

Duplicate Invoices in Xero and MYOB

A sales order comes through, triggers an automation that creates an invoice in Xero, and the response is slow because Xero is under load. The automation platform times out waiting for confirmation, marks the step failed, and retries. Xero, meanwhile, actually processed the first request fine — it just didn’t answer in time. The retry creates a second invoice for the same order. If nobody catches it before the customer does, you’ve got an awkward phone call and a credit note to issue. This exact failure mode is why idempotency matters even more once you’ve built out proper accounts payable automation — the more invoice volume you automate, the more retries you’ll see, and the more expensive each undetected duplicate becomes.

Duplicate Orders Flowing Into an ERP

Businesses syncing ecommerce orders, EDI documents or B2B purchase orders into an ERP system run the same risk at a bigger scale, because ERP integrations often chain several systems together — a webhook fires, that triggers an API call to a middleware layer, which then calls the ERP’s own API. A retry anywhere in that chain can fan out into a duplicate at the far end, and because ERPs are usually the system of record for stock levels and financials, a duplicate order there can throw off inventory counts and revenue reporting well beyond the one order in question. This is one of the reasons idempotency design is a standing item on the checklist for any ERP integration we scope — multi-hop, multi-system flows have more retry points than a single API call, so they need dedupe logic at more than one layer.

Duplicate Emails and SMS to Customers

Transactional messages — order confirmations, shipping notifications, appointment reminders — are especially visible failures because the customer sees them directly. A retried workflow step doesn’t just create an invisible duplicate database row, it sends a second identical text message to someone’s phone, which reads as sloppy even when the underlying cause was a four-second network blip nobody could have prevented.

Duplicate Records in a CRM

Lead capture automations that create a new contact record every time a form submits are particularly exposed, because form platforms and ad platforms both retry webhook deliveries on their own schedules. Without a dedupe check on email address or phone number before creation, the same enquiry can generate two, three or four contact records, which then pollutes your CRM automation and skews any reporting you’re running on lead volume or conversion rate.

Duplicate Payment Charges and Refunds

This is the highest-stakes version of the problem, because the duplicate isn’t just an untidy record, it’s real money leaving someone’s account twice. Payment gateways are usually well ahead of this — Stripe, for instance, has native idempotency key support built into its charge and payment intent APIs specifically because double-charging customers was a common failure mode before that existed. The risk in most Australian business workflows isn’t the payment gateway call itself, it’s the automation wrapped around it: a workflow that charges a card successfully, then times out waiting for the gateway’s response and retries the entire step, including the charge, because nobody separated “charge the card” from “record that the charge happened” as distinct, individually checkable steps.

Automation idempotency key pattern: an incoming trigger is checked against a dedupe store before deciding whether to process it as new or return the cached result
The idempotency key pattern: every incoming trigger is checked against a dedupe store before any real-world action happens.

Building Idempotency Into Zapier, Make and n8n

None of the three major platforms give you idempotency for free out of the box, but all three can support it with a bit of deliberate design. The approach differs slightly by platform, and it’s worth understanding the differences if you’re choosing between them — we go through the broader tradeoffs in our Zapier vs Make vs n8n comparison, but here’s what matters specifically for duplicate prevention.

Zapier

Zapier doesn’t have a native idempotency feature, but Storage by Zapier gives you a simple key-value store you can use as a dedupe gate. Before the “create invoice” action, add a Storage by Zapier lookup on the order ID; if a value already exists, branch to a no-op path; if it doesn’t, proceed and write the order ID to storage once the action succeeds. For calls to APIs that natively support an idempotency key header (Stripe is the best-known example), a Webhooks by Zapier custom request step lets you set that header directly using a value derived from the trigger data, which pushes the dedupe responsibility onto the API itself rather than Zapier.

Make

Make’s Data Store module works the same way as Zapier’s storage, but with slightly more structure — you can define a data structure for the store and query it with filters rather than a single key lookup. The common pattern is a router: one branch checks the data store for the incoming event ID, routes to “already processed” if found, and only the “not found” branch continues to the action and then writes the record. Make’s HTTP module also supports custom headers, so the same idempotency-key-on-the-request approach used in Zapier works here too.

n8n

Because n8n can run against your own database or Redis instance, it gives you the most flexibility for a proper dedupe table — a Postgres table keyed on event ID with a unique constraint is a clean, low-maintenance way to do this, since the database itself will reject a second insert attempt for the same key rather than relying on an application-level check that could itself race. n8n’s own execution ID identifies a specific run, not the underlying event, so don’t use it as your idempotency key — it changes on every retry, which defeats the purpose entirely. Pull the key from the trigger payload instead.

Whichever platform you use, the dedupe check has to happen before the action that has a real-world side effect, not after, and it has to be checked and written as close to atomically as your tooling allows — if two retries land close enough together, a “check, then act, then write” sequence with no locking can let both through before either writes its key, particularly in tools with parallel branches. This is a genuine edge case rather than the main failure mode, but it’s worth knowing about if you’re processing high volumes through a single workflow. Getting this structure right is exactly the kind of build we do inside workflow automation engagements, because it’s much easier to design in from the start than to retrofit once a business is relying on the workflow daily.

Designing Good Idempotency Keys

A few practical rules we’ve landed on after fixing enough of these in production:

Use an identifier that already exists in the source system rather than generating one yourself. A Shopify order ID, a Xero invoice number tied to a specific sale, a Stripe payment intent ID, or the webhook provider’s own delivery or event ID are all good because they’re assigned once per real event, before any retries happen. If you generate your own key inside the workflow (for example, hashing the payload), make sure the fields you hash don’t change between the original attempt and the retry — a timestamp field or a “received at” value in the payload will silently break this.

Decide how long you actually need to remember a key. Most business events don’t need permanent dedupe records — a rolling window of 24 to 72 hours covers the overwhelming majority of retries, which are almost always triggered within minutes or hours of the original attempt, not weeks later. Keeping every key forever just adds unbounded storage growth for no real benefit, whereas keeping keys for too short a window risks missing a genuinely delayed retry, so 72 hours is a reasonable default for most transactional workflows unless you have a specific reason to extend it.

Scope your key correctly. “Order ID” alone might not be unique if you’re pulling from two different sales channels that both number orders from one — combine the source system and the source ID (“shopify-order-48213”) rather than the raw ID alone.

Where you store the dedupe record matters more than which platform you’re on. A key-value store like Storage by Zapier or a Make Data Store is perfectly adequate for most small to mid-volume workflows, and it’s the pragmatic choice if you want this working today without standing up new infrastructure. If you’re already running a database for other parts of your stack, or you’re processing enough volume that lookup speed and concurrent writes start to matter, a proper table with a unique constraint on the key column is the more robust option, because the database enforces uniqueness for you rather than relying on your workflow logic to check first and write second without a race condition slipping through. Neither choice needs to be elaborate — the mistake is skipping this step entirely, not picking the “wrong” storage mechanism.

Retry Strategy: Backoff, Limits and Dead-Letter Queues

Idempotency makes retries safe. It doesn’t make them smart on its own — you still need a sensible retry strategy so a genuinely broken downstream system doesn’t get hammered with requests, and so failures that can’t be resolved automatically actually surface to a person instead of disappearing.

Exponential Backoff

Retrying immediately after a failure is the worst option if the failure was caused by the downstream system being overloaded — an instant retry just adds to the load that caused the problem. Exponential backoff spaces retries out (a few seconds, then longer, then longer again) so a brief outage has time to clear before the next attempt, rather than compounding it. n8n and Make both support configurable retry delays on HTTP-calling nodes; Zapier’s built-in retry behaviour is more limited, which is one reason a custom webhook step with your own retry logic is sometimes worth the extra setup for high-stakes actions.

Circuit Breakers

If a downstream system is genuinely down rather than momentarily slow, retrying every failed request for the next six hours just burns through your automation platform’s task quota and delays you noticing the real problem. A circuit breaker pattern stops attempting a call after a run of consecutive failures and instead routes straight to a failure path (or holds the item for later) until a health check confirms the system is back. This is more setup than most small workflows need, but it’s worth building for anything processing high volume against a system you don’t control, like a supplier’s API or a payment gateway.

Dead-Letter Queues and Manual Replay

Every retry strategy needs an exit condition. After a set number of attempts, a failed item should land somewhere a human can see it and manually retry or fix it, rather than either retrying forever or silently vanishing. This “dead-letter queue” can be as simple as a spreadsheet or Airtable row an ops person checks daily, or as involved as a dedicated table with a one-click replay action. What matters is that failures have a landing spot, because a workflow that just stops after its final retry attempt with no record of what happened is functionally the same as a workflow that never ran. This is a different problem to the one we cover in our guide to automation monitoring for Zapier, Make and n8n — monitoring tells you a workflow stopped firing at all, while a dead-letter queue tells you a workflow fired correctly but a specific item inside it couldn’t be processed. You want both, because they catch different failure modes.

Automation idempotency retry lifecycle showing exponential backoff, a circuit breaker branch, and unresolved items landing in a dead-letter queue for manual replay
A complete retry lifecycle: backoff between attempts, a circuit breaker for sustained outages, and a dead-letter queue so nothing just disappears.

Testing for Duplication Before It Costs You

Most duplication bugs are discovered in production because nobody deliberately tested what happens when a trigger fires twice. It’s a cheap thing to test for before it happens on a real customer record. Before a workflow that touches invoicing, orders or customer communication goes live, fire the same webhook payload at it twice in a row (most platforms let you replay a captured webhook, or you can use a tool like Postman to resend a saved request) and check that the second firing either does nothing or produces an identical, non-duplicated result. If you’re building the workflow yourself rather than through a platform’s native replay tool, a simple test harness that sends the same payload three times back-to-back will surface most dedupe gaps immediately.

It’s also worth running a periodic reconciliation check on anything financial — a scheduled job that compares invoice counts against order counts, or flags any two invoices created within a short window for the same customer and amount, catches duplicates that slipped through despite your best efforts, and it’s far cheaper to build once than to keep manually eyeballing the ledger.

If your workflow has more than one step capable of running in parallel — a common pattern in Make and n8n, where branches can execute concurrently rather than strictly in sequence — it’s worth specifically testing what happens when two near-simultaneous triggers for the same event race each other through the workflow at the same time, not just one after the other. A dedupe check that works perfectly for sequential retries can still let two truly concurrent runs both pass the “have I seen this key” check before either has written it, which is exactly the scenario a unique database constraint protects against but a simple check-then-write against a key-value store doesn’t. It’s a narrower edge case than the sequential retry problem this guide mostly focuses on, but it’s worth knowing your dedupe mechanism’s actual guarantees rather than assuming it’s airtight because it works in every test you’ve tried so far.

Common Mistakes We See

  • Assuming a trigger labelled “instant” or “real-time” can’t fire twice — speed and delivery guarantees are unrelated properties of a trigger.
  • Using a timestamp, or any field that changes between attempts, as the idempotency key.
  • Adding a dedupe check on the trigger side only, and not on the action that actually writes data, which misses duplicates introduced later in a multi-step workflow.
  • Never expiring stored idempotency keys, so the dedupe store grows indefinitely and eventually slows down every lookup.
  • Treating automation monitoring and idempotency as the same fix — alerting you that something failed doesn’t stop a retry from succeeding twice.
  • Building the happy path, shipping it, and only adding retry and dedupe logic after the first customer complaint.
  • Assuming a “queue-based” or “exactly-once” platform removes the need for idempotent design at the application level — most real-world exactly-once claims only cover the messaging layer, not the business action your workflow triggers off the back of it.

Frequently Asked Questions

Do Zapier, Make or n8n have built-in idempotency?

Not as a single toggle you can switch on. Each platform gives you the building blocks — a data store or key-value storage feature, custom HTTP headers, and configurable retry behaviour — but you have to design the dedupe check into the workflow yourself. None of them automatically prevent a retried trigger from repeating an action.

Is idempotency the same thing as deduplication?

They’re closely related but not identical. Deduplication usually refers to catching duplicate records after the fact, often by scanning for matches. Idempotency is a property you design into an operation so duplicates are prevented at the point of action, before a second record is ever created. Idempotency is the better fix where you can build it in; deduplication is a useful safety net for whatever slips through.

How long should I keep idempotency keys before expiring them?

For most transactional business workflows, 24 to 72 hours is a sensible default, since almost all retries happen within that window. If you’re integrating with a system known to queue and redeliver events after longer outages, extend the window to match, but there’s rarely a good reason to keep keys indefinitely.

Can duplication happen even with a platform’s fastest, most “real-time” trigger type?

Yes. Trigger speed and delivery guarantees are separate things. An instant webhook trigger can still be redelivered if the sending system doesn’t get a fast, successful response, regardless of how quickly your workflow itself runs.

Will adding idempotency checks slow my workflows down?

A single lookup against a data store or database table adds a small amount of time to each run, typically well under a second, which is negligible against the cost of manually unwinding a duplicate invoice or a doubled-up customer email. For very high-volume workflows it’s worth indexing the dedupe table properly so the lookup stays fast as volume grows.

How is this different from automation monitoring?

Monitoring tells you when a workflow has stopped running or is throwing errors — it’s about visibility into failures. Idempotency is about making the workflow safe to run more than once for the same event, so a retry that succeeds doesn’t create a second real-world outcome. You want both: monitoring catches the automation that silently stopped, idempotent design stops the one that’s running fine from quietly duplicating its own work.

Where to Start

If you’re not sure whether this is already happening in your workflows, the fastest way to check is to look at your last month of invoices, orders or CRM records for near-identical duplicates created within minutes of each other — that pattern is the signature of a retry-triggered duplicate rather than genuine user error. If you find a handful, it’s worth auditing the workflows behind your highest-stakes actions (anything touching invoicing, payments or customer communication first) before it happens at a scale that’s harder to unwind.

We’re happy to have a look at an existing Zapier, Make or n8n setup and flag where retries could be creating risk you haven’t noticed yet, or build the dedupe and retry logic in properly if you’re putting a new workflow together. Get in touch and we’ll talk through what your workflows actually need.

Jignesh Vagadiya

Jignesh Vagadiya is a Digital, Automation and AI Consultant at Growth Digital, an Australian agency building websites, ecommerce stores and custom software for businesses in Melbourne, Sydney, Brisbane, Perth and beyond. He helps businesses grow through digital transformation — strategic system design, development, integration, automation, cyber security and digital marketing. He writes about what actually works in search, paid media and automation, including the parts most agencies leave out.

Got a project you'd like to talk through?

Tell us what you're working on — we'll come back with a plan.