WebhookOps

The authority on webhook operations

WebhookOps is the practice of treating webhooks as first-class production events. Guides, patterns, and open-source resources for engineering teams.

The WebhookOps Manifesto

  1. Observe everything. If you can't see the payload, headers, and timing, you can't operate it.
  2. Verify signatures. Trust but verify. Every webhook should be cryptographically validated.
  3. Be idempotent. Webhooks are delivered at-least-once. Your handlers must be safe to run twice.
  4. Test with real payloads. Mocks are fiction. Replay real events in CI.
  5. Own the failure. When a webhook breaks, diagnose it like a production incident.

Guides & Patterns

Payments

Stripe Idempotency & Signature Verification

How to safely handle Stripe webhooks, verify signatures with tolerance checks, and prevent duplicate events.

Read guide
DevTools

GitHub Signature Rotation

What to do when GitHub rotates webhook secrets. How to validate payloads and rotate without downtime.

Read guide
Architecture

At-Least-Once Delivery Patterns

Why webhooks can be lost, how to build idempotent handlers, and how to use replay budgets safely.

Read guide
Reliability

Retry Budgets & Backoff

Exponential backoff, circuit breakers, and when to stop retrying. Keep your providers happy.

Read guide
Testing

Webhook Testing in CI

Use Replay Playbooks to run real webhook payloads against your staging environment on every pull request.

Read guide
Integrations

Slack Signature Verification

How Slack signs requests and how to verify them using request timestamps to prevent replay attacks.

Read guide

Open-source libraries

HookReplay's signature verification logic is open source. Use it in your own servers.

import { verifyWebhookSignature } from "@/lib/signatures";

const result = verifyWebhookSignature({
  algorithm: "stripe",
  payload: rawBody,
  secret: process.env.STRIPE_WEBHOOK_SECRET,
  signatureHeader: req.headers["stripe-signature"],
});

if (!result.valid) {
  return res.status(400).json({ error: result.error });
}