Duplicate webhook processed without idempotency

This error occurs when the integration receives the same webhook more than once and runs the business logic again without checking whether the event has already been processed.

Receiving an event again does not mean the action should be executed again. The integration needs to recognize duplicates and respond with success without repeating their effects.

Unlike the Webhook received, but not persisted error, in this case the problem lies in the lack of control over events that have already been recorded or processed.

How to identify

  • the same event appears more than once in the logs;
  • the same action is executed repeatedly for a single operation;
  • notifications, releases, or updates are duplicated;
  • there are multiple processing runs with the same event identifier;
  • the system does not check whether the event has already been received;
  • there is no internal status to differentiate received and processed events;
  • two processes can execute the same event simultaneously;
  • the problem occurs after a new webhook delivery attempt.

How to fix

  1. Identify the events that were processed more than once.
  2. Confirm which actions were duplicated.
  3. Fix the unintended effects, when applicable.
  4. Persist the event identifier before starting processing.
  5. Use a unique key to control events already received.
  6. Check this key before running the business logic.
  7. When the event has already been processed, do not execute the action again.
  8. When the previous processing is pending or failed, resume it safely.
  9. Prevent concurrent processes from executing the same event.
  10. Record the result of each processing run.

Recommended flow: receive → identify the event → check the record → persist if new → process only once → update the status → respond.

Diagnostic flow

1. Identifying and handling the duplicate

%%{init: {"flowchart": {"nodeSpacing": 18,"rankSpacing": 24,"diagramPadding": 4,"padding": 7
}}}%%
flowchart TD
A["Webhook received<br/>by the endpoint"] --> B{"Is there a record with the same<br/>event identifier?"}

B --> BSim(("Yes"))
B --> BNao(("No"))

BSim --> C{"Has the event already<br/>been processed?"}

BNao --> D["Persist the event<br/>with received status"]

C --> CSim(("Yes"))
C --> CNao(("No"))

CSim --> E["Do not run the business<br/>logic again"]

CNao --> F["Continue the pending<br/>processing"]

D --> G["Process the event<br/>only once"]
F --> G

E --> H["Record the duplicate<br/>delivery"]

G --> I["Update the status<br/>to processed"]

H --> J["Respond with success without<br/>repeating the action"]
I --> J

classDef inicio fill:#DBEAFE,stroke:#2563EB,color:#1E3A8A,stroke-width:3px
classDef decisao fill:#FEF3C7,stroke:#D97706,color:#78350F,stroke-width:3px
classDef correcao fill:#FFEDD5,stroke:#EA580C,color:#7C2D12,stroke-width:2px
classDef validacao fill:#E0F2FE,stroke:#0284C7,color:#0C4A6E,stroke-width:2px
classDef sucesso fill:#DCFCE7,stroke:#16A34A,color:#14532D,stroke-width:3px
classDef analise fill:#FEE2E2,stroke:#DC2626,color:#7F1D1D,stroke-width:3px

classDef respostaSim fill:#22C55E,stroke:#15803D,color:#FFFFFF,stroke-width:3px
classDef respostaNao fill:#EF4444,stroke:#B91C1C,color:#FFFFFF,stroke-width:3px
classDef respostaDuvida fill:#8B5CF6,stroke:#6D28D9,color:#FFFFFF,stroke-width:3px

class A inicio
class B,C decisao
class D,F,G,I correcao
class H validacao
class J sucesso
class E analise

class BSim,CSim respostaSim
class BNao,CNao respostaNao

linkStyle default stroke:#94A3B8,stroke-width:2px
linkStyle 1,5 stroke:#22C55E,stroke-width:4px
linkStyle 2,6 stroke:#EF4444,stroke-width:4px

2. Processing with idempotency control

%%{init: {"flowchart": {"nodeSpacing": 18,"rankSpacing": 24,"diagramPadding": 4,"padding": 7
}}}%%
flowchart TD
A["Before processing<br/>the webhook"] --> B{"Is there a unique key<br/>for the event?"}

B --> BSim(("Yes"))
B --> BNao(("No"))

BNao --> C["Define an idempotent<br/>key"]

BSim --> D["Check the event<br/>store"]
C --> D

D --> E{"Is the key already<br/>recorded?"}

E --> ESim(("Yes"))
E --> ENao(("No"))

ESim --> F{"Is the status<br/>processed?"}

ENao --> G["Record the event<br/>with received status"]

F --> FSim(("Yes"))
F --> FNao(("No"))

FSim --> H["Respond with success without<br/>repeating the processing"]

FNao --> I["Block concurrent<br/>execution"]
G --> I

I --> J["Run the business logic<br/>only once"]

J --> K{"Was the processing<br/>completed?"}

K --> KSim(("Yes"))
K --> KNao(("No"))

KSim --> L["Update the status<br/>to processed"]

KNao --> M["Record the error and keep it<br/>for reprocessing"]

L --> N["Respond with success"]
H --> N

M --> O["Reprocess using<br/>the same key"]
O --> I

classDef inicio fill:#DBEAFE,stroke:#2563EB,color:#1E3A8A,stroke-width:3px
classDef decisao fill:#FEF3C7,stroke:#D97706,color:#78350F,stroke-width:3px
classDef correcao fill:#FFEDD5,stroke:#EA580C,color:#7C2D12,stroke-width:2px
classDef validacao fill:#E0F2FE,stroke:#0284C7,color:#0C4A6E,stroke-width:2px
classDef sucesso fill:#DCFCE7,stroke:#16A34A,color:#14532D,stroke-width:3px
classDef analise fill:#FEE2E2,stroke:#DC2626,color:#7F1D1D,stroke-width:3px

classDef respostaSim fill:#22C55E,stroke:#15803D,color:#FFFFFF,stroke-width:3px
classDef respostaNao fill:#EF4444,stroke:#B91C1C,color:#FFFFFF,stroke-width:3px
classDef respostaDuvida fill:#8B5CF6,stroke:#6D28D9,color:#FFFFFF,stroke-width:3px

class A inicio
class B,E,F,K decisao
class C,G,I,J,L,O correcao
class D validacao
class N sucesso
class H,M analise

class BSim,ESim,FSim,KSim respostaSim
class BNao,ENao,FNao,KNao respostaNao

linkStyle default stroke:#94A3B8,stroke-width:2px
linkStyle 1,7,11,18 stroke:#22C55E,stroke-width:4px
linkStyle 2,8,12,19 stroke:#EF4444,stroke-width:4px

The first flow explains how to identify and handle an event received again, while the second shows how to prevent the business logic from being executed more than once.


How to prevent

Persist all received events and use a unique key to control processing. Maintain internal statuses, such as received, processed, and error, and check this record before running the business logic.

Use uniqueness constraints, concurrency control, and idempotent operations. Test the flow by sending the same event more than once and confirm that its effects occur only once.

The same webhook may be received again. The business logic must be executed only once.



Did this page help you?