Accurendocs
Start free

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 }
}
⌘I