{
    "slug": "webhook",
    "term": "Webhooks",
    "category": "architecture",
    "difficulty": "beginner",
    "short": "A reverse API — a service POSTs event notifications to a registered URL whenever something occurs, eliminating the need to poll.",
    "long": "Webhooks enable event-driven integrations: instead of your application polling an API every minute, the service POSTs a JSON payload to your endpoint the moment an event fires (payment.completed, push.received). Implementing a receiver: expose a public HTTPS endpoint, verify the payload signature (HMAC-SHA256 of the body against a shared secret, constant-time comparison with hash_equals()), respond with 200 immediately, and queue actual processing asynchronously to avoid delivery timeouts. Sending webhooks: store endpoint URLs per customer, deliver with retry and exponential backoff, record all delivery attempts, and support manual replay for debugging.",
    "aliases": [
        "HTTP callback",
        "webhook endpoint",
        "push notification HTTP"
    ],
    "tags": [
        "architecture",
        "api",
        "integration",
        "events"
    ],
    "misconception": "Webhooks are always more efficient than polling. Webhooks push events immediately but require a publicly reachable endpoint, reliable delivery handling, and signature verification. Polling is simpler and sometimes more appropriate for batch processing or when strict ordering matters.",
    "why_it_matters": "Webhooks deliver real-time event notifications via HTTP POST — they invert the polling model, but require validation, idempotency, and failure handling that polling naturally provides.",
    "common_mistakes": [
        "Not validating the webhook signature — any HTTP client can send a fake payload to your endpoint.",
        "Not returning HTTP 200 quickly and processing asynchronously — slow handlers cause the sender to retry, duplicating events.",
        "Not handling duplicate delivery — webhook senders retry on timeout or non-2xx; your handler must be idempotent.",
        "Not logging raw payloads — debugging webhook failures requires the original body."
    ],
    "when_to_use": [
        "Notifying external systems of events in real time without them needing to poll.",
        "Integrating with third-party platforms (Stripe, GitHub, Shopify) that push events on state changes.",
        "Triggering CI pipelines, cache purges, or downstream workflows in response to your own events.",
        "Replacing polling in scenarios where events are infrequent and latency matters."
    ],
    "avoid_when": [
        "The consumer cannot guarantee idempotent processing — duplicate deliveries will cause duplicate side effects.",
        "The payload contains sensitive data sent to an unverified URL — always validate signatures and use HTTPS.",
        "The receiving server is unreliable — webhooks can be lost; use a message queue for guaranteed delivery.",
        "You need request-reply semantics — webhooks are fire-and-forget; use polling or SSE for two-way flow."
    ],
    "related": [
        "event_driven",
        "hmac",
        "idempotency",
        "async_processing"
    ],
    "prerequisites": [
        "api_design",
        "message_queue_patterns",
        "event_driven"
    ],
    "refs": [
        "https://hookdeck.com/webhooks/guides/what-are-webhooks"
    ],
    "bad_code": "// No signature validation — accepts any POST as legitimate:\napp()->post('/webhook', function(Request $req): Response {\n    processEvent($req->json()); // No HMAC verification\n    return response('ok', 200);\n});\n// Add: hash_equals(hash_hmac('sha256', $req->rawBody(), $secret), $req->header('X-Signature'))",
    "good_code": "// Verify Stripe webhook signature\n$payload = file_get_contents('php://input');\n$sig = $_SERVER['HTTP_STRIPE_SIGNATURE'];\n$event = \\Stripe\\Webhook::constructEvent($payload, $sig, $secret);",
    "quick_fix": "Define webhook payloads as versioned typed events, verify HMAC signatures on every delivery, and respond 200 immediately — do processing asynchronously in a queue",
    "severity": "medium",
    "effort": "medium",
    "created": "2026-03-15",
    "updated": "2026-03-25",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/webhook",
        "html_url": "https://codeclaritylab.com/glossary/webhook",
        "json_url": "https://codeclaritylab.com/glossary/webhook.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Webhooks](https://codeclaritylab.com/glossary/webhook) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/webhook"
            }
        }
    }
}