FIELD NOTE 004 / IDEMPOTENCY
How to Prevent Duplicate Workflow Executions with Idempotency
A practical idempotency pattern for preventing duplicate messages, records, charges, and other side effects when automation events are retried.
By Harrison Ndeke · Published August 14, 2026 · Updated August 14, 2026 · 12 min read
EXECUTION STATE
IDENTIFY → CLAIM → EXECUTE → RECORD
In this article
DIRECT ANSWERPrevent duplicate workflow side effects by deriving a stable idempotency key from the business event, claiming that key in durable storage before the first irreversible action, and returning the stored result when the same event arrives again. Keep states for in progress, completed, and failed work; expire them deliberately; and make every downstream write use the same key or an equivalent uniqueness constraint.
Key takeaways
- Retries are normal: webhooks, queues, schedulers, and users can deliver the same intent more than once.
- Deduplicate business events, not HTTP requests: the key must represent the operation that should happen once.
- Claim before side effects: a lookup followed later by an insert leaves a race window.
- Store execution state: distinguish work that is in progress from work already completed.
- Make the sink safe too: pass the same key to a provider that supports idempotency or enforce a unique constraint in your own database.
Why do duplicate workflow executions happen?
A sender can retry because it did not receive an acknowledgement. A queue can redeliver after a worker timeout. A person can double-submit a form. A scheduler can overlap with a slow run. None of those conditions proves that the first attempt failed before the external effect.
The dangerous window is simple: a workflow creates the invoice, sends the message, or writes the record, then crashes before it records success. A retry sees no completion marker and repeats the effect. Exactly-once delivery is rarely a safe assumption across independent systems; the practical objective is an idempotent business operation.
What should the idempotency key contain?
Prefer a stable event identifier supplied by the source, such as a webhook event ID, booking ID, order ID, or payment intent ID. If no trustworthy ID exists, derive a deterministic key from the fields that define one business operation—for example, tenant, operation type, source record, and version.
key = hash(tenantId + ":" + operation + ":" + sourceId + ":" + version)Do not use a random UUID generated inside the receiving workflow; every retry would receive a new value. Do not hash volatile fields such as receive time. Names and email addresses alone can collide or change, so use source-system identifiers where possible. Store a payload hash beside the key and reject a reused key when consequential parameters differ.
Stripe’s official idempotent-request guidance documents the same safety property at an API boundary: reusing a key with different endpoint parameters produces an idempotency error. Provider semantics differ, so verify each API rather than assuming a shared standard.
What execution states should be recorded?
| State | Meaning | Duplicate handling |
|---|---|---|
| IN_PROGRESS | One worker has claimed the operation and may be executing it. | Do not start another side effect; wait, reject, or return an accepted response. |
| COMPLETED | The operation finished and its result or external reference was stored. | Return the stored result without executing again. |
| FAILED_RETRYABLE | No irreversible effect is known to have occurred, or reconciliation proved it safe. | Allow a bounded retry under policy. |
| UNKNOWN | The worker failed after crossing a side-effect boundary and the outcome cannot yet be proved. | Reconcile with the target system or route to human review; do not guess. |
A conditional insert or unique key must claim the record atomically. A “search, then create” sequence is insufficient under concurrency because two workers can both observe absence. Store creation time, expiry, payload hash, state, attempt count, result reference, and the last verified error without storing secrets.
AWS Lambda Powertools describes an idempotency record with a key, payload hash, in-progress and completion state, expiry, and serialized response. That implementation is AWS-specific, but the state model is broadly useful.
How can this pattern be implemented in n8n?
- Validate the trigger payload and extract a stable source event ID.
- Construct the idempotency key before any email, payment, CRM write, upload, or publication.
- Atomically claim the key in durable storage. Use a database uniqueness constraint when concurrency matters.
- If the key is completed, return the stored result. If it is in progress, stop or defer.
- Execute the external effect with the same key where the destination supports it.
- Persist the provider’s external ID and mark completion.
- On ambiguous failure, reconcile against the provider before retrying.
n8n documents that Data Tables can store markers to prevent duplicate runs, and its Data Table node can retrieve, insert, update, and upsert rows. The documentation also characterises Data Tables as light-to-moderate storage. For high-concurrency or financially consequential work, confirm atomic uniqueness and locking behavior in the chosen storage layer rather than treating an ordinary lookup as a lock.
Harrison’s documented Lead Generation Subscriber Agent performs duplicate lookup before record creation. That is useful data hygiene evidence; it is not presented here as proof of atomic idempotency under concurrent delivery.
How should duplicate protection be tested?
- Send the identical event twice sequentially and verify one external effect.
- Send the same event concurrently from two workers.
- Crash after the external call but before marking completion, then retry.
- Reuse a key with different parameters and verify rejection.
- Expire an in-progress record and test the reconciliation path.
- Retry provider timeouts, 429 responses, and 5xx responses without duplicating writes.
- Confirm stored responses are returned consistently and contain no secrets.
Use a sandbox or controlled target. Count actual effects at the destination—not merely successful workflow runs. A green execution log does not prove that only one invoice, message, row, or booking was created.
Executive summary
Reliable automation assumes duplicate delivery and makes the business operation safe to repeat. Choose a stable idempotency key, atomically claim it before side effects, record in-progress and completed states, reuse the key at downstream APIs, and reconcile ambiguous outcomes. The central design question is not “did this workflow run before?” but “has this exact business effect already been committed?”
Related services and reading
- n8n workflow development for validated integrations, retries, fallbacks, and handoff-ready automation.
- Retries and fallbacks in n8n workflows for deciding what may safely run again.
- How to secure n8n webhooks for authentication, replay windows, and request validation.
- When AI agents should require human approval for consequential tool boundaries.
About the author
Harrison Ndeke is an AI automation developer in Nairobi building documented n8n workflows, agents, chatbots, RAG systems, and API integrations. His public project evidence includes duplicate lookup, entitlement checks, explicit error paths, webhook validation, and scheduled follow-up. This article explains a design pattern; it does not claim a measured production incident rate or formal reliability certification.
Sources, scope, and limitations
Primary sources: n8n Data Tables documentation, n8n Data Table node documentation, Stripe API idempotent requests, and AWS Lambda Powertools idempotency. Provider behavior, retention, storage consistency, and failure semantics differ. Verify the exact API and database used before applying this pattern to money, access, regulated data, or other high-impact operations.