Core resource
Webhooks
Register an HTTPS endpoint to receive event notifications. Every delivery is signed with HMAC-SHA256 in a Holdyn-Signature header, in the Stripe-compatible t=<unix-seconds>,v1=<hex> format.
Register an endpoint
/api/v1/b2b/webhook-endpointsDashboard session required. The signing_secret is returned once at creation — store it before you leave the response.
{
"url": "https://example.com/webhooks/holdyn-b2b",
"description": "Prod listener",
"subscribed_events": ["*"]
}Signature verification
Compute HMAC-SHA256 over ${t}.${rawBody} with your endpoint's signing secret and compare to the v1 component of the header.
const crypto = require('crypto');
function verifyHoldynSignature(rawBody, signatureHeader, signingSecret) {
const parts = Object.fromEntries(
signatureHeader.split(',').map((p) => p.split('='))
);
const expected = crypto
.createHmac('sha256', signingSecret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(parts.v1 || ''),
);
}=== string check is vulnerable to timing attacks. crypto.timingSafeEqual in Node and equivalents in other runtimes prevent leakage of the expected HMAC byte-by-byte.Retry policy
| Attempt | Timing | Outcome on failure |
|---|---|---|
| 1 | Immediate, synchronous with the event | Schedule attempt 2 at +60 s |
| 2 | +60 s after attempt 1 | Schedule attempt 3 at +5 min |
| 3 | +5 min after attempt 2 | Mark delivery exhausted, no further retries |
Receivers must respond with a 2XX within 10 seconds. Any other status code or timeout counts as a failure. Acknowledge quickly and defer work to a background queue on your side.
Inspect deliveries
/api/v1/b2b/webhook-deliveriesCursor pagination; filter by status and/or webhook_endpoint_id. Each delivery row carries the request body, response status, response body (truncated), and timing.