API
Webhooks
Signed, retried, and idempotent — the delivery path for anything that cannot wait for a poll.
bash
curl -X POST https://api.accuren.xyz/v1/webhooks \
-H "Authorization: Bearer $ACCUREN_API_KEY" \
-d '{
"url": "https://example.com/hooks/accuren",
"events": ["risk.liquidation_window", "income.multiplier_increase"]
}'#Verifying a delivery
Every request carries Accuren-Signature and Accuren-Timestamp. Compute HMAC-SHA256 over timestamp.body with your endpoint secret and compare in constant time. Reject anything older than five minutes.
verify.ts
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(req: { body: string; headers: Record<string, string> }, secret: string) {
const ts = req.headers["accuren-timestamp"];
const sig = req.headers["accuren-signature"];
if (!ts || !sig) return false;
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = createHmac("sha256", secret).update(`${ts}.${req.body}`).digest();
const given = Buffer.from(sig, "hex");
return expected.length === given.length && timingSafeEqual(expected, given);
}Verify before you parse
An unverified webhook body is untrusted input. Check the signature first, then parse.
#Retries and duplicates
- Non-2xx or a timeout is retried with backoff for 24 hours.
- Every delivery carries an
id; the same event may arrive twice, so key your handler on it. - Respond 2xx as soon as you have stored the event. Do the work afterwards.
delivery
{
"id": "evt_9f21",
"type": "risk.liquidation_window",
"created": "2026-08-16T22:14:03Z",
"data": { "token": "NVDAx", "healthFactor": "1.08", "action": null }
}