vermilion
guides

Webhooks

One event format for every PSP. Register endpoints, verify signatures, and rely on retries plus a re-drivable delivery log instead of building your own.

Endpoints

POST /v1/webhook_endpoints { "url": "https://example.com/hooks", "event_types": ["payment.captured"] }

Leave event_types empty to receive everything. The signing secret (whsec_…) is returned once — store it like a password.

Event payload

{ "id": "evt_…", "object": "event", "type": "payment.captured", "created_at": "2026-07-12T14:03:11Z", "data": { "payment": { "id": "pay_…", "status": "captured", … } } }

Event types today: payment.created, payment.authorized, payment.captured, payment.requires_action, payment.failed, payment.voided, refund.succeeded, payment_method.created.

Verifying signatures

Each delivery carries a vermilion-signature header: t=<unix>,v1=<hmac> where v1 is HMAC-SHA256 of `${t}.${rawBody}` with your endpoint secret, hex-encoded. Reject if the signature mismatches or t is more than 5 minutes old (replay protection).

// node example const [t, v1] = header.split(',').map(p => p.split('=')[1]); const expected = crypto.createHmac('sha256', secret) .update(`${t}.${rawBody}`).digest('hex'); const valid = timingSafeEqual(expected, v1) && Math.abs(now - t) < 300;

Delivery & retries