KEEP LEARNING
Build the bigger picture.
The Workflow Engineer connects individual n8n concepts to testing, deployment and running a complete workflow.
Tips > Building Workflows
An idempotent workflow produces the same result whether it runs once or multiple times with the same input.
An idempotent workflow returns the same result whether it runs once or many times with the same input, which matters because n8n may re-execute on retries, duplicate webhooks, or manual re-runs. Achieve it with database upserts, check-then-act lookups, or an idempotency key: hash the defining fields, check whether that key was already processed, and skip if so.
An idempotent workflow produces the same result whether it runs once or multiple times with the same input. This is critical because n8n may re-execute workflows due to retries, duplicate webhook deliveries, or manual re-runs during debugging. Without idempotency, you get duplicate records, double charges, or repeated notifications.
Real-world example: A workflow processes incoming orders. Without idempotency, a duplicate webhook from Shopify creates two records in the database and sends two confirmation emails.
Idempotency patterns:
Pattern 1: Upsert instead of Insert
Use ON CONFLICT (unique_key) DO UPDATE instead of INSERT.
If the record exists, it gets updated (no duplicate).
Pattern 2: Check-then-act
Before creating a record, query whether it already exists.
Skip creation if found.
Pattern 3: Idempotency key
Store a hash of the input and check it before processing.
If the same input was already processed, skip entirely.Implementing Pattern 3 in a Code node at the start of every workflow:
const crypto = require('crypto');
const inputData = $json;
// Generate a deterministic hash of the input
const idempotencyKey = crypto
.createHash('sha256')
.update(JSON.stringify({
order_id: inputData.order_id,
// Include only fields that define uniqueness
updated_at: inputData.updated_at
}))
.digest('hex');
return [{
json: {
...inputData,
_idempotency_key: idempotencyKey
}
}];Follow with a database check:
-- Postgres node: Check if already processed
SELECT idempotency_key
FROM processed_events
WHERE idempotency_key = '{{ $json._idempotency_key }}'
LIMIT 1;Then an IF node:
{{ $json.idempotency_key ? true : false }}
→ true (already exists): Skip processing, return 200 OK
→ false (new): Continue to processing pipelineAt the end of the workflow, record the key:
-- Postgres node: Mark as processed
INSERT INTO processed_events (idempotency_key, workflow_name, processed_at)
VALUES ('{{ $json._idempotency_key }}', '{{ $workflow.name }}', NOW())
ON CONFLICT (idempotency_key) DO NOTHING;Tip: Database Upserts Are Your Friend Most n8n database nodes support upsert operations. In the Postgres node, use Operation: Upsert and define the conflict column. This is the simplest path to idempotent database writes without separate check-then-act logic.
Idempotency is not optional for production workflows. It is the single most important reliability pattern.
Related: Always Set an Error Workflow on Every Production Workflow · Use "Pin Data" to Freeze Node Output
KEEP LEARNING
The Workflow Engineer connects individual n8n concepts to testing, deployment and running a complete workflow.
APPLY IT TO YOUR SYSTEM
Bring the process, the tools involved and an example of where the current workflow gets stuck.