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.