Webhooks

Register: POST /api/v1/webhooks {"url": "https://api.yourapp.com/onetrial", "events": []} (empty = all). The signing secret (whsec_...) is returned once.
Events: decision.created, challenge.completed, challenge.failed. Body: { id, type, createdAt, data }. Header OneTrial-Signature: t=<unix>,v1=<hex hmac-sha256 of "<t>.<raw body>">. Three attempts with backoff; endpoint must be public https.
Verify (Node):
js
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(secret, rawBody, header) {
const m = /t=(\d+),v1=([0-9a-f]+)/.exec(header ?? '');
if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
const expected = createHmac('sha256', secret).update(`${m[1]}.${rawBody}`).digest();
const given = Buffer.from(m[2], 'hex');
return expected.length === given.length && timingSafeEqual(expected, given);
}
On challenge.completed with data.outcome === "passed", unlock the trial for data.decisionId. On challenge.failed, treat as deny.