Webhooks Explained: Signed Payloads and Retries

Backend2026-10-07TryQuickToolBox

Why Webhooks Are Both Powerful and Fragile

Webhooks power real-time integrations: payment notifications, CI/CD triggers, chat messages. But they come with two big challenges: security (how do you know the request is from the expected sender?) and reliability (what if the receiver is down?). This article shows you how to address both with signed payloads and retry strategies.

What Is a Webhook?

A webhook is an HTTP POST request sent by a provider to a consumer when an event occurs. Unlike polling, webhooks push data in near real-time, reducing latency and server load. The provider must ensure the request is authentic; the consumer must process it reliably.

Securing Webhooks with Signed Payloads

A signed payload uses a secret key to create a cryptographic signature (usually HMAC-SHA256) of the request body. The receiver recomputes the signature and compares it to the one in the header. If they match, the payload is authentic and untampered.

How to Generate and Verify Signatures

Here's a typical flow:

  1. Provider and consumer share a secret key (e.g., via dashboard).
  2. Provider computes HMAC-SHA256(secret, payload) and sends it in a header like X-Signature.
  3. Consumer reads the raw body, computes the same HMAC, and compares using a constant-time function.

Example in Node.js:

const crypto = require('crypto');

function verifySignature(secret, payload, signature) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(payload);
  const digest = hmac.digest('hex');
  return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}

Always use constant-time comparison to prevent timing attacks. Never parse the body before verification—use raw body middleware.

Implementing Retries for Reliable Delivery

Even with signatures, network failures or temporary outages can cause webhook delivery to fail. A robust retry mechanism ensures eventual delivery.

Retry Strategies

Example retry logic in Python:

import time
import requests

def send_webhook(url, payload, max_retries=5):
    for attempt in range(max_retries):
        try:
            response = requests.post(url, json=payload, timeout=5)
            if response.status_code == 200:
                return True
        except requests.RequestException:
            pass
        time.sleep(2 ** attempt)  # exponential backoff
    return False

Best Practices for Webhook Consumers

Comparison: Polling vs Webhooks

AspectPollingWebhooks
LatencyHigh (interval-based)Low (real-time)
Server loadConstant requestsOnly on events
ComplexitySimpleRequires retry/security
Use caseSmall scale, infrequent updatesReal-time integrations

FAQ

What is a signed webhook payload?

A signed payload includes a cryptographic signature (e.g., HMAC) that allows the receiver to verify the request came from a trusted source and wasn't tampered with.

How many times should I retry a failed webhook?

There's no universal number, but 3–5 attempts with exponential backoff is common. After that, log the event for manual review.

Can I use JWT instead of HMAC for webhook signatures?

Yes, some providers use JWT. The principle is the same: verify the token's signature and claims before trusting the payload.

Need to inspect webhook payloads or logs? Try our JSON Formatter to pretty-print and validate JSON payloads quickly.