Webhooks quickstart

Five steps from nothing to a verified delivery. Register an endpoint, stand up a receiver, send a test, inspect it, subscribe.

Every request carries Authorization, X-Tenant and X-Environment, like every other Bridge request.

1. Register an endpoint

curl -X POST "https://api.bridgemoni.com/webhook/endpoint" \
  -H "Authorization: Bearer $BRIDGEMONI_TOKEN" \
  -H "X-Tenant: acme" \
  -H "X-Environment: test" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.merchant.example.com/bridge/events",
    "label": "Ledger sync",
    "deliveryMode": "UNORDERED",
    "eventTypes": [{ "name": "transfer.completed" }, { "name": "virtual_account.credited" }]
  }'

The secret in the response is shown once and never again. Put it in your secret store before you close the terminal; if you lose it, rotate.

2. Stand up a receiver

const express = require("express");
const app = express();

app.post("/bridge/events",
  express.raw({ type: "application/json" }),          // RAW. Not express.json().
  async (req, res) => {
    const raw = req.body.toString("utf8");
    if (!verify(req.get("Bridge-Signature"), raw, process.env.BRIDGE_WEBHOOK_SECRET)) {
      return res.sendStatus(401);
    }
    if (!(await seen.addIfAbsent(req.get("Bridge-Event-Id")))) {
      return res.sendStatus(200);                      // at least once: this is the dedupe
    }
    res.sendStatus(200);                               // acknowledge first...
    queue.push(JSON.parse(raw));                       // ...work after
  });

verify is on Verify a signature. Four things carry the whole contract: the raw body, the timestamp tolerance, checking every v1= value, and the constant-time compare.

3. Send yourself a test

curl -X POST "https://api.bridgemoni.com/webhook/endpoint/$ENDPOINT/test" \
  -H "Authorization: Bearer $BRIDGEMONI_TOKEN" -H "X-Tenant: acme" -H "X-Environment: test"

This sends a real webhook.ping through the real pipeline with a real signature. Every endpoint receives it, subscribed or not.

4. Check what happened

curl "https://api.bridgemoni.com/webhook/delivery?endpointReference=$ENDPOINT" \
  -H "Authorization: Bearer $BRIDGEMONI_TOKEN" -H "X-Tenant: acme" -H "X-Environment: test"

Every attempt records your status code, how long it took and up to 2 KB of your response body, so “why is my endpoint failing” is answerable from the API.

5. Subscribe to the rest

curl -X PUT "https://api.bridgemoni.com/webhook/endpoint/$ENDPOINT/subscription" \
  -H "Authorization: Bearer $BRIDGEMONI_TOKEN" -H "X-Tenant: acme" -H "X-Environment: test" \
  -H "Content-Type: application/json" \
  -d '{ "eventTypes": [
        { "name": "transfer.completed" }, { "name": "withdrawal.completed" },
        { "name": "withdrawal.failed" }, { "name": "escrow.*" } ] }'

PUT is a genuine replace, so the list in your repository is the list in production.