One common reason for this is webhook retries.
Many services (including Facebook / Messenger APIs) use at-least-once delivery. If the platform thinks the webhook call failed (timeout, slow response, network issue), it will send the same event again.
From n8n’s perspective this looks like a new trigger, so the workflow executes twice.
A safe pattern is to add idempotency before the side effect in the workflow.
For example:
Webhook → HTTP Request (gate) → IF → your action
The idea is to generate a stable key for the event and check if it was already processed.
For example:
{{ $json.id }}
or
{{ $execution.id }}_{{ $node.name }}
If the same key appears again, you stop the workflow before executing the action.
I wrote a short explanation of the pattern here:
This usually fixes duplicate executions caused by webhook retries.