Guide to Bridge API Webhooks: Features and Best Practices
Bridge API webhooks notify your systems about open-banking activity: an item (bank connection) is refreshed, an account is created or updated, a payment transaction changes. If you're building on Bridge API's aggregation platform, webhooks are how you react to banking data changes without polling.
This guide covers how Bridge API webhooks work, the events you'll handle, how to verify the BridgeApi-Signature, secret rotation, and the best practices for production.
This is Bridge API (bridgeapi.io), the open-banking aggregator by Bankin'/Bridge. It is a different company and API from bridge.xyz (the stablecoin platform), which has its own guide and a completely different (RSA) signature scheme. Make sure you're on the right one.
What are Bridge API webhooks?
Bridge API webhooks are HTTP POSTs delivered to a callback URL you configure in the dashboard, each signed with an HMAC-SHA256. The BridgeApi-Signature header carries one or more scheme-prefixed values, for example v1=E5637CDB... (hex, uppercase). You verify the v1= value over the raw body with the webhook's signing secret.
Bridge API webhook features
| Feature | Details |
|---|---|
| Configuration | Bridge dashboard (admins only): callback URL, name, events; max 10 webhooks per app |
| Signature header | BridgeApi-Signature, one or more scheme=value entries (e.g. v1=<hex>) |
| Signature scheme | HMAC-SHA256 over the raw body, hex uppercase; extract the v1= value |
| Secret | Auto-generated per webhook, shown once; 24h dual-secret window on rotation |
| Source IPs | 63.32.31.5, 52.215.247.62, 34.249.92.209 (use X-Forwarded-For behind a proxy) |
| Response | Keep response bodies under 10 KB |
| Retries | 1-2 days with exponential backoff on non-200/slow |
| SDK | None |
Common events
Bridge API event names are lowercase resource.action (some three-segment):
| Event | Fires when |
|---|---|
item.refreshed | An item (bank connection) is refreshed |
item.created | An item is created |
item.account.created | An account under an item is created |
item.account.updated | An account under an item is updated |
payment.transaction.created | A payment transaction is created |
user.deleted | A user is deleted |
Note the three-segment item.account.* form. A "Send a test" button in the dashboard emits TEST_EVENT. Consult Bridge API's reference for the full list.
Setting up Bridge API webhooks
Configure webhooks in the Bridge dashboard (admins only): callback URL, name, and selected events (max 10 webhooks per app). The signing secret is auto-generated and shown once at creation or rotation, capture it. Optionally allowlist the fixed source IPs (using X-Forwarded-For if you're behind a proxy), and keep your response bodies under 10 KB.
Securing Bridge API webhooks
The BridgeApi-Signature header can hold multiple scheme-prefixed values. Extract the v1= value specifically (ignore other schemes, this guards against downgrade attacks), compute an HMAC-SHA256 with the webhook's signing secret over the raw body, hex-encode it (uppercase), and constant-time-compare. During rotation, up to two signatures may be present; accept a match against either.
const crypto = require("crypto");
// One or two current secrets (24h overlap on rotation)
const SECRETS = (process.env.BRIDGEAPI_WEBHOOK_SECRETS || "").split(",");
function verify(rawBody, signatureHeader) {
// Extract only the v1= signatures (ignore other schemes)
const v1Sigs = (signatureHeader || "")
.split(",")
.map((s) => s.trim())
.filter((s) => s.startsWith("v1="))
.map((s) => s.slice("v1=".length));
return SECRETS.some((secret) => {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex")
.toUpperCase();
return v1Sigs.some((sig) => {
const a = Buffer.from(expected);
const b = Buffer.from(sig.toUpperCase());
return a.length === b.length && crypto.timingSafeEqual(a, b);
});
});
}
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
if (!verify(req.body, req.headers["bridgeapi-signature"])) {
return res.sendStatus(401);
}
res.sendStatus(200); // acknowledge fast
processQueue.add(JSON.parse(req.body)); // process asynchronously
});
The same check in Python:
import hashlib
import hmac
import os
SECRETS = os.environ.get("BRIDGEAPI_WEBHOOK_SECRETS", "").split(",")
def verify(raw_body: bytes, signature_header: str) -> bool:
v1_sigs = [s.strip()[3:] for s in (signature_header or "").split(",") if s.strip().startswith("v1=")]
for secret in SECRETS:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest().upper()
if any(hmac.compare_digest(expected, s.upper()) for s in v1_sigs):
return True
return False
Bridge API webhook limitations and pain points
Extract v1= to avoid downgrade
The Problem: The BridgeApi-Signature header can carry multiple scheme-prefixed values. Verifying whatever's first (or a weaker scheme) opens a downgrade attack.
Why It Happens: The header is designed to carry multiple schemes.
Workarounds:
- Extract the
v1=value specifically and verify that; ignore other schemes.
How Hookdeck Can Help: Hookdeck verifies the correct scheme at the edge, so your app isn't exposed to downgrade.
Uppercase hex and secret rotation
The Problem: The hex is uppercase, and rotation puts up to two active secrets/signatures in play for 24 hours. Lowercase comparison or checking only one secret fails valid deliveries.
Why It Happens: Bridge API uses uppercase hex and overlaps secrets on rotation.
Workarounds:
- Compare uppercase, and accept a match against either secret/signature during the 24h window.
How Hookdeck Can Help: Hookdeck handles rotation and encoding at the edge. See our guide to webhook idempotency.
Events for already-deleted users/items
The Problem: You can receive webhooks for users or items that are already deleted. A handler that assumes the referenced resource still exists errors out.
Why It Happens: Delivery and deletion race; Bridge API delivers defensively.
Workarounds:
- Handle missing resources gracefully (treat a 404 on follow-up as expected).
How Hookdeck Can Help: Hookdeck durably queues events so you can process (or skip) them at your own pace.
Source IPs and response size
The Problem: Deliveries come from fixed IPs (which you may need X-Forwarded-For to see behind a proxy), and responses must stay under 10 KB.
Why It Happens: Bridge API uses a fixed egress and caps response size.
Workarounds:
- Allowlist the IPs (via
X-Forwarded-Forbehind a proxy) and keep responses small.
How Hookdeck Can Help: Hookdeck provides a stable ingestion URL and a compact acknowledgement, so IP and response-size handling are managed for you.
Best practices
Verify the v1= uppercase-hex signature over the raw body
Extract v1=, HMAC-SHA256 with the webhook secret over the raw body, uppercase hex, and compare in constant time. Accept either secret during rotation.
Confirm you're on Bridge API, not bridge.xyz
They're different companies with different schemes (HMAC here, RSA there).
Handle deleted resources and acknowledge fast
Treat missing users/items as expected, return 200 quickly, and process off a queue. See why to process webhooks asynchronously.
Allowlist IPs and keep responses small
Restrict to the fixed source IPs and keep response bodies under 10 KB.
Make Bridge API webhooks production-ready
Hookdeck verifies BridgeApi-Signature, handles rotation, deduplicates, and durably queues every event
Conclusion
Bridge API (bridgeapi.io) webhooks are verified with a BridgeApi-Signature HMAC-SHA256 over the raw body, extracting the v1= uppercase-hex value (to prevent downgrade), with a 24-hour dual-secret rotation window and fixed source IPs. Verify the v1= value, handle rotation and already-deleted resources, and don't confuse this with bridge.xyz.
Hookdeck verifies the signature, handles rotation, deduplicates, and durably queues every event at the edge, so your app only ever processes verified, unique events.
Get started with Hookdeck for free and handle Bridge API webhooks reliably in minutes.