Webhooks & events
Hbridge delivers signed, at-least-once webhooks to your endpoint as things happen. Set your endpoint URL and get your signing secret in the dashboard under Webhooks. Then verify every request and dedupe on the event id.
Event types#
Your endpoint receives these event types:
| Field | Type | Required | Description |
|---|---|---|---|
| address.provisioned | event | no | A deposit address was created for a user on a chain. |
| deposit.pending | event | no | A deposit was detected but has not yet reached finality. |
| deposit.confirmed | event | no | A deposit reached finality and counts toward your balance. |
| deposit.reorged | event | no | A previously reported deposit was dropped by a chain reorg. |
| payout.completed | event | no | A payout settled on-chain (net sent to your address). |
| payout.failed | event | no | A payout could not be completed. |
Payload#
Every event is a JSON envelope with a stable type, a unique id (dedupe on this), a created_at timestamp, and event-specific fields.
{
"type": "deposit.confirmed",
"id": "evt_9f2c...",
"user": { "id": "6a44...", "external_ref": "user_42" },
"chain": "ETH",
"token": "USDC",
"amount": "2",
"txid": "0x3646...98f9a",
"created_at": "2026-07-02T10:07:00.000Z"
}Verify signatures#
Each request carries an X-Hbridge-Signature header of the form t=<timestamp>,v1=<hmac>. Compute the HMAC-SHA256 of "<t>.<rawBody>" using your signing secret and compare it to v1 with a constant-time comparison. Use the raw request body (not re-serialized JSON).
X-Hbridge-Signature: t=1719914820,v1=9c8f...e3a1import crypto from "node:crypto";
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(
header.split(",").map((kv) => kv.split("=")),
);
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(parts.v1),
);
}Event API#
Every outbound event is also stored, so you can audit what was sent and replay anything your endpoint missed (e.g. during downtime).
List outbound events, newest first. Paginated.
| Field | Type | Required | Description |
|---|---|---|---|
| type | string | no | Filter by event type, e.g. deposit.confirmed. |
| status | string | no | One of pending, delivered, failed. |
| limit | number | no | Page size, 1–200, default 50. |
| cursor | string | no | Previous next_cursor. |
{
"events": [
{
"id": "6e01...",
"event_id": "evt_9f2c...",
"type": "deposit.confirmed",
"delivery_status": "delivered",
"attempts": 1,
"last_attempt_at": "2026-07-02T10:07:03.000Z",
"delivered_at": "2026-07-02T10:07:03.000Z",
"created_at": "2026-07-02T10:07:00.000Z"
}
],
"next_cursor": "6e01...",
"has_more": true
}Fetch a single event, including the full payload Hbridge sent.
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | yes | The event id (not event_id). |
Re-send an event to your endpoint (even one already delivered). Returns 202; 422 no_webhook_configured if you have no endpoint set.
{ "id": "6e01...", "event_id": "evt_9f2c...", "status": "pending" }