Webhooks Explained: Signed Payloads and Retry Strategies

Backend2026-11-07TryQuickToolBox

You've just built a webhook endpoint to receive events from a third-party service. Everything works in testing, but in production you notice some events never arrive, or worse, you receive duplicate events causing data inconsistencies. These are classic webhook pitfalls that stem from two critical aspects: payload authenticity and delivery reliability.

Webhooks are HTTP callbacks triggered by events, pushing data to your application in real-time. Unlike polling, they're efficient and near-instant. But they introduce challenges: how do you know the request truly came from the expected sender, and how do you handle failures when the receiver is temporarily down? This article dives into signed payloads and retry mechanisms, giving you practical patterns to build resilient webhook integrations.

Why Webhooks Need Signatures

Webhook endpoints are publicly accessible URLs. Without verification, anyone can send forged requests to your endpoint, potentially triggering unauthorized actions or data corruption. Signed payloads solve this by adding a cryptographic signature to each request, allowing you to verify the sender's identity and ensure the payload hasn't been tampered with.

Most providers use HMAC (Hash-based Message Authentication Code) with a shared secret. The sender computes a hash of the payload using the secret and includes it in a header. You recompute the hash on your side and compare. If they match, the request is legitimate.

Implementing HMAC Verification

Let's walk through a typical implementation in Node.js. Suppose the provider sends a signature in the X-Signature header, computed as HMAC-SHA256 of the raw request body using your secret.

const crypto = require('crypto');
const express = require('express');
const app = express();

// Use raw body parser to preserve exact payload
app.use(express.raw({ type: 'application/json' }));

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-signature'];
  const secret = process.env.WEBHOOK_SECRET;
  
  const expected = crypto
    .createHmac('sha256', secret)
    .update(req.body)
    .digest('hex');
  
  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process the event
  const event = JSON.parse(req.body);
  // ... handle event
  
  res.status(200).send('OK');
});

Key points:

Designing a Retry Strategy

Even with signatures, deliveries can fail due to network issues, server downtime, or timeouts. A robust retry strategy ensures eventual delivery without overwhelming your system or the sender.

1. Acknowledge Quickly, Process Asynchronously

Your webhook endpoint should respond with a 2xx status as soon as possible, ideally within a few seconds. If processing takes longer, queue the event for background processing. This prevents timeouts and allows the sender to consider the delivery successful.

2. Implement Exponential Backoff with Jitter

When a delivery fails (non-2xx response or timeout), the sender should retry with increasing delays. Exponential backoff (e.g., 1s, 2s, 4s, 8s...) prevents hammering your endpoint. Adding random jitter avoids thundering herd problems when many webhooks fail simultaneously.

Example retry schedule:

After a maximum number of retries (e.g., 5), the sender should stop and notify you (e.g., via email or dashboard).

3. Handle Duplicates with Idempotency

Retries can cause duplicate deliveries. Your endpoint must be idempotent: processing the same event multiple times should have the same effect as processing it once. Use a unique event ID from the payload and store it. Before processing, check if the ID has been seen; if so, return 200 without reprocessing.

Common approaches:

Comparison: Signed Payloads vs. Other Security Measures

Method Pros Cons
HMAC Signature Verifies authenticity and integrity; stateless Requires shared secret management
IP Allowlisting Simple to implement IPs can change; not suitable for dynamic environments
Basic Auth Easy to add Credentials sent in every request; less secure
JWT in Header Can include claims and expiry Overhead of token issuance/validation

HMAC signatures are the de facto standard because they're stateless, efficient, and provide strong security when implemented correctly.

Best Practices for Webhook Consumers

Best Practices for Webhook Producers

Testing Your Webhook Implementation

Use tools like ngrok to expose your local server and simulate webhook deliveries. Write unit tests for signature verification and idempotency logic. For load testing, consider tools that can send signed requests at scale.

When debugging, inspect the raw payload and headers. A JSON formatter can help visualize the payload structure. Try our JSON Formatter to pretty-print and validate webhook payloads quickly.

FAQ

What is a signed webhook payload?

A signed payload includes a cryptographic signature (usually HMAC) that allows the receiver to verify the sender's identity and ensure the payload hasn't been altered. It's computed using a shared secret and the request body.

How many times should a webhook be retried?

There's no universal number, but common practice is 3–5 retries with exponential backoff. After that, the event is considered failed and the consumer should be notified to investigate or replay manually.

Can I use JWT instead of HMAC for webhook signatures?

Yes, JWT can work, but it adds complexity (token issuance, expiry). HMAC is simpler and sufficient for most use cases. Choose based on your existing infrastructure and requirements.

Ready to inspect and debug webhook payloads? Use our free JSON Formatter to format and validate JSON instantly.