Overview
Webhooks use at-least-once delivery. A temporary receiver/network failure can cause PulseGrid to retry the same delivery.
The same delivery_id is reused for retries. The receiver must make processing idempotent so retries cannot create duplicate orders, payments, emails, SMS messages or workflow starts.
Setup
Recommended receiver order:
1. Verify the signature.
2. Begin a database transaction.
3. Insert delivery_id into a table with a unique constraint.
4. If it already exists, return HTTP 200 without repeating business work.
5. Save or perform the required local database changes.
6. For slow external work, queue it locally.
7. Commit.
8. Return 2xx quickly.
Status guidance:
- 200/202/204: accepted successfully.
- 401: verification/authentication rejected.
- 404: route is wrong/missing.
- 429: receiver is overloaded; retry can help.
- 500-599: temporary receiver failure; retry can help.
Code example
from django.db import IntegrityError, transaction
def process_once(event):
delivery_id = event["delivery_id"]
try:
with transaction.atomic():
ProcessedWebhook.objects.create(delivery_id=delivery_id)
handle_business_event(event)
except IntegrityError:
return "duplicate"
return "processed"
# Node/database implementations should use the same rule:
# UNIQUE(delivery_id) + transactional claim before irreversible work.
Notes
Do not deduplicate by event name. Many legitimate events share the same name.
Store the original event id as well when you want traceability across several endpoint deliveries, but use delivery_id as the delivery retry key.