Guides

Handling Webhooks

Receive and verify webhook events from MUDBASE in your application.

1. Configure an endpoint

From Console → Webhooks, set the URL you want events delivered to and check the events you want to receive. Then generate a signing secret — copy it immediately, it's shown only once.

2. Verify every request

Read the raw request body (don't let a body-parsing middleware transform it first) and compare the HMAC against X-MUDBASE-Signature:

javascript
import express from "express";
import crypto from "crypto";

const app = express();

// express.raw (not express.json) so req.body is the exact bytes MUDBASE signed
app.post(
  "/webhooks/mudbase",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.header("X-MUDBASE-Signature");
    const expected = crypto
      .createHmac("sha256", process.env.MUDBASE_WEBHOOK_SECRET)
      .update(req.body)
      .digest("hex");

    const valid =
      signature &&
      crypto.timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));

    if (!valid) {
      return res.status(401).json({ error: "Invalid signature" });
    }

    const event = JSON.parse(req.body.toString("utf8"));

    // Respond fast — do the real work after acknowledging.
    res.status(200).json({ received: true });

    handleEvent(event).catch((err) => console.error("Webhook handler failed", err));
  }
);

async function handleEvent(event) {
  switch (event.event) {
    case "collection.insert":
      // ...
      break;
    case "kyc.completed":
      // ...
      break;
    default:
      break;
  }
}
import express from "express";
import crypto from "crypto";

const app = express();

// express.raw (not express.json) so req.body is the exact bytes MUDBASE signed
app.post(
  "/webhooks/mudbase",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.header("X-MUDBASE-Signature");
    const expected = crypto
      .createHmac("sha256", process.env.MUDBASE_WEBHOOK_SECRET)
      .update(req.body)
      .digest("hex");

    const valid =
      signature &&
      crypto.timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));

    if (!valid) {
      return res.status(401).json({ error: "Invalid signature" });
    }

    const event = JSON.parse(req.body.toString("utf8"));

    // Respond fast — do the real work after acknowledging.
    res.status(200).json({ received: true });

    handleEvent(event).catch((err) => console.error("Webhook handler failed", err));
  }
);

async function handleEvent(event) {
  switch (event.event) {
    case "collection.insert":
      // ...
      break;
    case "kyc.completed":
      // ...
      break;
    default:
      break;
  }
}

3. Respond quickly, process asynchronously

Acknowledge with a 2xx status as soon as the signature checks out — before you've finished handling the event. If your endpoint takes too long to respond, MUDBASE times out and retries the delivery, and you'll end up handling the same event twice.

Because retries can (and will) redeliver the same event, make your handler idempotent — key any side effect off X-MUDBASE-Delivery-ID or the event's own document ID so processing the same delivery twice is harmless.

Tip
Test your endpoint from the Console's webhook log before going live — it shows the exact payload, headers, response status, and latency for every delivery, so you can confirm signature verification and event handling without waiting for a real event to occur.

See Webhooks (Concepts) for the full event list, payload format, and retry policy.