Webhooks
Magnum delivers database events to your server through a persistent queue. Sandbox events never confirm a real payment.
Register an endpoint
POST /v1/webhook-endpoints requires webhooks:write.
{ "url": "https://store.example/hooks/magnum" }
The response includes id, url, and a one-time signingSecret. URLs require public HTTPS on port 443. Literal IPs, credentials, fragments, and private destinations are rejected. The worker resolves and validates DNS before every attempt, then pins the connection IP; redirects are not followed.
Verify signatures
The magnum-signature header uses t=TIMESTAMP,v1=HEX. Compute HMAC-SHA256 over TIMESTAMP + "." + rawBody with the signing secret. Compare in constant time, allow at most five minutes of clock skew, and deduplicate id in your database.
import { createHmac, timingSafeEqual } from 'node:crypto';
const expected = createHmac('sha256', signingSecret).update(`${timestamp}.${rawBody}`).digest();
const supplied = Buffer.from(signatureHex, 'hex');
const valid = supplied.length === expected.length && timingSafeEqual(supplied, expected);
Validate format and age before comparing. Do not parse and reserialize JSON for signature verification: use the original request bytes.
Delivery and retries
Events have a stable ID, type, createdAt, livemode, and data. The worker uses leases and row locking. Only 2xx succeeds; it retries up to eight times with exponential delay capped at one hour. Each attempt times out after eight seconds. Delivery is at least once.
Inspect GET /v1/webhook-deliveries. Disable endpoints with DELETE /v1/webhook-endpoints/:id. The available sandbox event is sandbox.payment.completed. Check livemode before any financial action.