How scoring works
Every call to POST /v1/score is evaluated against your active policy: a set of rules with weights, thresholds and actions. Scoring is deterministic: the same inputs and the same policy version always give the same result.
The decision
Section titled “The decision”| Field | Meaning |
|---|---|
score |
0–100: how anomalous the transaction looks |
risk_level |
low, medium, high or critical, from your policy’s thresholds |
confidence |
0–100: how much history backs the score. A new customer can score high with low confidence |
recommended_action |
Your policy’s action for this risk level: approve, step_up, manual_review or block |
reasons |
The rules that raised the score, and the points each added |
policy |
The exact policy version used |
Score and confidence
Section titled “Score and confidence”Only the policy’s enabled rules count:
score = round(100 × Σ(signal_i × weight_i) / Σ weight_i)confidence = round(100 × Σ(coverage_i × weight_i) / Σ weight_i)Each rule returns a signal (how anomalous, 0 to 1) and a coverage (how much reliable data it had, 0 to 1). A reason’s contribution is that rule’s share of the score in points. Rounding means contributions can differ from the score by up to 0.5.
If you leave out optional data, such as the device or beneficiary, the rules that need it contribute nothing and confidence goes down.
GET /v1/rules returns the library with defaults and validated parameters.
| Rule | Default | Parameters |
|---|---|---|
transaction_velocity |
on, weight 25 | window_minutes 10, soft_threshold 3, threshold 5 |
amount_anomaly |
on, weight 25 | baseline_multiplier 3, min_history 5 |
new_device |
on, weight 20 | lookback_days 180, min_history 3 |
new_beneficiary |
on, weight 15 | lookback_days 180, min_amount 0, min_history 3 |
rapid_activity |
on, weight 15 | interval_seconds 60 |
device_change |
off, weight 10 | lookback_days 30 |
geo_anomaly |
off, weight 10 | lookback_days 90, min_history 5 |
unusual_time |
off, weight 5 | timezone Africa/Lagos, min_history 10, min_share 0.05 |
The last three are off by default, so existing policies score as before. To use them, enable them in a new policy version, backtest it on your history, then activate it.
Behaviour rules
Section titled “Behaviour rules”device_changecompares the device with the one used in the customer’s previous device transaction, if that was withinlookback_days. A different platform, such as Android to iOS, gives a signal of 1.0. A switch to another device the customer has used before, on the same platform, gives 0.5. A device never seen before is left tonew_device. It needsdevice.id_hash, anddevice.platformif you want platform changes detected.geo_anomalycompareslocation.countryandlocation.regionwith where the customer has transacted. A country not seen withinlookback_daysgives 1.0, and a new region of a known country gives 0.5. A customer with no location history yet is not flagged. Regions are compared ignoring case and extra spaces.unusual_timelooks at the hour of day, intimezone, of the customer’s earlier transactions. If the share of them within an hour either side of this transaction’s time is belowmin_share, the signal rises, reaching 1.0 when there are none. It stays at 0 until the customer hasmin_historytransactions.
All three build on history, so confidence stays low until a customer has transacted a few times.
Why a decision came out the way it did
Section titled “Why a decision came out the way it did”Fetch GET /v1/decisions/{decision_id} for every enabled rule’s evaluation (signal, coverage, weight, contribution, message, details), the stored transaction (hashes only), the features snapshot and the policy version. Add ?expand=policy_version to embed the exact configuration used.
Idempotency
Section titled “Idempotency”Scoring is idempotent on transaction.id. Sending the same ID again returns the original decision with 200 and Idempotent-Replayed: true, and the transaction is not counted twice in behavioural history.