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" }