All documentation
Messages

Webhooks: Retries, Idempotency and HTTP Responses

Make at-least-once webhook delivery safe with delivery_id deduplication and correct receiver status codes.

Text

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

Webhooks: Retries, Idempotency and HTTP Responses
Django-style idempotency pattern
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.