Handling Exactly Once Processing with Webhooks

Hi everyone, I’m dealing with a more advanced reliability issue in n8n around webhook-triggered workflows.
I have a workflow:
Webhook → Process → Database
The external service sometimes retries the same webhook event (e.g., due to timeouts), so n8n receives duplicate requests with the same payload.
Right now, this causes:
• Duplicate records in the database
• Duplicate downstream actions (emails, API calls, etc.)
Example payload:
{
“event_id”: “abc123”,
“user_id”: 42,
“action”: “created”
}
If the same event_id is sent twice, it gets processed twice.
What I’ve tried
• Basic checks in a Function node
• Database upserts
• Adding delays
But none of these fully guarantee exactly-once processing.
The challenge
I want to ensure:
• Each event is processed only once
• Safe handling of retries
• No duplicate side effect

Describe the problem/error/question

What is the error message (if any)?

Please share your workflow

(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)

Share the output returned by the last node

Information on your n8n setup

  • n8n version:
  • Database (default: SQLite):
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app):
  • Operating system:

Hi @Keira_Becky You’re dealing with an idempotency problem, and the only reliable solution is to track processed events there’s no built-in “exactly-once” in n8n.

Use the event_id as a unique key and let your database enforce it.

Try this

  1. Store event_id in DB with UNIQUE constraint

CREATE TABLE events (
event_id TEXT PRIMARY KEY,
payload JSONB
);

  1. Insert before processing

INSERT INTO events (event_id, payload)
VALUES ({{$json.event_id}}, {{$json}})
ON CONFLICT (event_id) DO NOTHING;

So If insert succeeds → process the event
If it conflicts → skip (already processed)

Thanks @Niffzy

I got it resolved by inserting before processing