How it works
Request lifecycle
- Event in. The body is validated. Unknown top-level properties, a NUL character or a full card number are refused with
400;context,userandpaymentaccept extra fields. - Enrichment. The server adds what it knows about the request: IP intelligence and geo, the device behind
context.fp_id, email and phone checks. The outcome per provider is returned inenrichment_status. - Aggregates and velocity. Counters over time windows (events per card token, accounts per IP or affiliate, and similar) and cross-account links are loaded for the rules.
- Rules. Evaluation order is fixed:
trustrules end evaluation withallowand score 0.hard_blockrules end evaluation withblock; the score is then set tomax_possible.- The declared flags (
user.trusted,user.vip,is_known_fraud,chargeback_flag,name_blocked,sanctions_match) take effect through the preset's trust and hard-block rules that read them. - Scoring rules (
high,medium,low) add or subtract points;monitoringrules only raise flags.
- Decision.
risk_score >= block_thresholdgivesblock,risk_score >= review_thresholdgivesreview, otherwiseallow. Areviewalso opens a case. - Delivery. The result is returned (sync) or stored (async), and a signed webhook is sent if the project has a webhook URL and secret.
Sync and async
| Sync (default) | Async ("async": true) | |
|---|---|---|
| Response | 200 with the full score and decision | 202 with a receipt (event_id, accepted_at) |
| Get the result | In the response | GET /v1/scores/{event_id} (pending until scored), or the decision webhook |
| Use when | You must act on the decision right now (payments, logins) | You score in the background (batch checks, post-hoc review) |
Async needs the ingestion queue; if it is down the call fails with 503 ASYNC_UNAVAILABLE. Async intake does not check ruleset availability; scoring happens afterwards.
Shadow rules
A shadow rule runs on every event and its result is recorded, but it is not shown in the response breakdown, and its points never count and its actions never fire. Use shadow to measure a new rule on live traffic, then promote it to active. Rules are drafted, simulated against recent events and published through the rules API or the dashboard.
Fail-closed behaviour
Defraudo prefers refusing to score over scoring with the wrong rules.
- On a sync request, if no compiled ruleset is reachable (
COMPILED_RULES_UNAVAILABLE) or the project's policy (thresholds) cannot be read (PROJECT_POLICY_UNAVAILABLE),POST /v1/eventsreturns503. Nothing is persisted; retry the sameevent_idand body. - If scoring succeeds but an active rule could not read a signal as written (a long-window count that overran its budget, Redis down with a failed database fallback, a regex that cannot run), the response carries a
degradedarray naming what was affected, for examplerule:<rule_id>. The decision is still returned; treat it as lower confidence. The field is absent when scoring was healthy. It is only returned on the first sync response;GET /v1/scores/{event_id}and a repeated request do not include it, so store it if you need it. - If persisting a score (including its review case) fails, the response is
500and the attempt is rolled back; retrying the sameevent_idand body is safe.