SuperAPI AI Directory

webhooks

Coding & development

View repository

Updated 10-15 21:49

About this skill

Webhooks

Webhooks are "user-defined HTTP callbacks". They are triggered by some event in a source system (e.g., Stripe, GitHub) and sent to a destination system (Your API) to notify it.

When to Use

  • Third-Party Event Notification: Consuming real-time notifications from payment providers (Stripe), Git hosts (GitHub), and SaaS platforms.
  • Asynchronous System Integration: Triggering external partner workflows when business events occur in your application.
  • Decoupled API Callback Architecture: Notifying long-running asynchronous clients when requested tasks finish without polling.
  • Event-Driven Microservice Ingestion: Ingesting incoming webhook events into queue buffers for asynchronous worker processing.

Quick Start

// Your Webhook Handler (Receiver)
app.post(
  "/webhooks/stripe",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.headers["stripe-signature"];

    try {
      // Verify the event came from Stripe and hasn't been tampered
      const event = stripe.webhooks.constructEvent(
        req.body,
        signature,
        START_SECRET,
      );

      if (event.type === "payment_intent.succeeded") {
        fulfillOrder(event.data.object);
      }

      res.json({ received: true });
    } catch (err) {
      res.status(400).send(`Webhook Error: ${err.message}`);
    }
  },
);

Core Concepts

Cryptographic Signature Verification (HMAC-SHA256)

Validates that webhook payloads originate from authentic senders and were not tampered with in transit:

import crypto from "crypto";

export function verifyWebhookSignature(
  payload: string,
  signatureHeader: string,
  secret: string,
): boolean {
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  // Constant-time comparison prevents timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader),
    Buffer.from(expectedSignature),
  );
}

Instant 200 OK Acknowledgment with Ingestion Queues

Acknowledge incoming webhooks immediately (< 500ms) and offload heavy processing to background queues:

app.post(
  "/webhooks/stripe",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const signature = req.headers["stripe-signature"] as string;
    if (
      !verifyWebhookSignature(req.body.toString(), signature, WEBHOOK_SECRET)
    ) {
      return res.status(400).send("Invalid signature");
    }

    // Push to Redis queue immediately
    await webhookQueue.add(
      "process-stripe-event",
      JSON.parse(req.body.toString()),
    );
    res.status(200).send("Received");
  },
);

Exponential Backoff Retry Strategy

Senders automatically retry failed delivery attempts with exponential delay:

Attempt 1: Immediately
Attempt 2: 15 seconds later
Attempt 3: 1 minute later
Attempt 4: 15 minutes later
Attempt 5: 1 hour later -> DLQ

Common Patterns

Idempotent Webhook Processing with Deduping

Problem: Webhook providers retry delivery on timeout, causing duplicate state changes or double processing.

Solution: Check incoming delivery event IDs against a fast cache or database before executing business logic:

import { Request, Response } from "express";

export async function handleWebhook(req: Request, res: Response) {
  const eventId = req.headers["x-webhook-id"] as string;
  const isDuplicate = await redisClient.set(`webhook:${eventId}`, "1", {
    NX: true,
    EX: 86400, // 24-hour expiration
  });

  if (!isDuplicate) {
    return res.status(200).json({ status: "already_processed" });
  }

  // Enqueue for asynchronous background worker processing
  await taskQueue.add("process-event", req.body);
  return res.status(202).json({ status: "accepted" });
}

Best Practices

Do:

  • Verify Signatures Using Raw Unparsed Request Bodies: Never verify HMAC signatures on parsed JSON objects; whitespace discrepancies break hashes.
  • Implement Idempotency Handling: Record incoming webhook event IDs in Redis or database unique indexes to discard duplicates.
  • Respond with 200 OK Immediately: Queue the event for background processing; third parties time out after 5-10 seconds.
  • Expose Webhook Delivery Logs: Provide dashboard interfaces where customers can view delivery status, response codes, and manual retries.

Don't:

  • Perform heavy business logic in the HTTP handler: Long processing times cause webhook timeouts and unnecessary retries.
  • Accept webhooks over insecure HTTP: Enforce TLS (HTTPS) on all webhook ingestion endpoints.
  • Trust unverified webhook headers: Always use cryptographic signatures rather than relying on IP whitelists alone.

Troubleshooting

Error Cause Solution
Signature Verification Failed Body parsing issue. Ensure you verify the Raw Body, not the parsed JSON object.
Timeout Processing taking too long. Move logic to a background job; return 200 immediately.

References