Twilio SendGrid Event Webhooks and a Custom Status Consumer: Reconciling Duplicate and Out-of-Order Events
0 reputation · 09 Sept 2021, 13:45 UTC
0 reputation · 09 Sept 2021, 13:45 UTC
A service consumes Twilio SendGrid Event Webhook payloads to track per-message delivery status across events such as processed, delivered, opened, and clicked. SendGrid does not guarantee exactly-once delivery: the same event can be POSTed more than once, and status events may arrive out of order or delayed. The endpoint must return a 2xx within a short documented timeout, and failed deliveries are retried only a bounded number of times. No API exists to request retransmission of events missed while an endpoint is unavailable.
The unresolved decision sits at the boundary between the webhook producer and the consumer's state model. Payload event and message identifiers could support deduplication, but the consumer still needs a reconciliation rule for cases such as an opened event being processed before its corresponding delivered event.
What deduplication strategy prevents duplicate POSTs from advancing state incorrectly? How should status precedence be modeled so out-of-order events converge to a consistent final state per message? Given the absence of a retransmission mechanism, what fallback, if any, is appropriate for events lost during endpoint downtime?
35275 reputation · 09 Sept 2021, 18:41 UTC
To prevent duplicate POSTs from advancing state incorrectly, use the sg_id (the SendGrid event ID) as a unique idempotency key.
sg_id.2xx OK immediately without re-processing the logic. This prevents SendGrid from retrying the request indefinitely.To handle events arriving out of order (e.g., an opened event arriving before delivered), implement a state-based priority model rather than a simple sequential update. Assign a numerical weight to each status:
| Status | Weight |
|---|---|
| processed | 1 |
| delivered | 2 |
| opened | 3 |
| clicked | 4 |
The Reconciliation Rule: When an event arrives, compare its weight against the current state of the message in your database. Only update the record if the incoming event's weight is greater than the existing weight. If the incoming event is lower (e.g., a delivered event arriving after an opened event), discard the state change but return a 2xx to acknowledge the webhook.
Since there is no retransmission mechanism for events missed during downtime, a proactive fallback is required.
<Assumption: This strategy assumes your system tracks the message_id provided by SendGrid to link multiple sg_id events together.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.