Core
Webhooks
Webhooks let your backend receive asynchronous updates from Scorift. When an event is too slow for the synchronous POST /v1/score response — such as a case disposition, an escalation, or a rule outcome — we push a signed JSON payload to an HTTPS endpoint you control. Every webhook is HMAC-signed and delivered with at-least-once semantics, so you can keep your own systems, audit trails, and queues in sync with the decisions Scorift makes.
Why use webhooks
- Case lifecycle — receive updates when a flagged transaction is approved or rejected by an analyst.
- Async deep checks — get notified when a deferred or long-range signal (e.g., a SIM swap report or sanctions update) changes a decision.
- Rule audit — log every rule trigger in your own data warehouse for compliance and reporting.
Register an endpoint
POST /v1/webhooks
{
"url": "https://api.acme.com/hooks/scorift",
"events": ["case.dispositioned", "score.async", "rule.triggered"],
"description": "Prod case pipeline"
}Event catalog
| Event | When it fires |
|---|---|
| score.async | A deferred score completes (>50ms deep-check). |
| case.created | A new case is opened, either by rule or analyst. |
| case.dispositioned | An analyst sets fraud, safe, or chargeback outcome. |
| rule.triggered | A rule fires in shadow or enforce mode. |
| signal.updated | A signal changes state (e.g. sim_swap detected). |
Verifying signatures
Each request carries an X-Scorift-Signature header of the form t=<timestamp>,v1=<hmac_sha256>. Recompute the HMAC over {timestamp}.{raw_body} with your webhook secret and compare in constant time.
import { createHmac, timingSafeEqual } from "crypto";
function verify(body: string, header: string, secret: string) {
const [tPart, sigPart] = header.split(",");
const t = tPart.split("=")[1];
const sig = sigPart.split("=")[1];
const expected = createHmac("sha256", secret)
.update(`${t}.${body}`).digest("hex");
return timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}Retries & delivery
- Retries follow exponential backoff up to 24 hours (max 12 attempts).
- Respond with a 2xx status within 5 seconds — otherwise the delivery is retried.
- Failed deliveries are surfaced in Settings → Webhooks with full payload replay.
