Rule Engine
Define fraud detection rules in YAML. No code deploys. Rules hot-reload on save.
Basic Rule
Rules live in the rules/ directory. Each YAML file defines one rule with conditions, severity, and an action.
# rules/velocity-phone.yml
name: "High velocity same phone"
description: "Flag transactions when same phone number sends too frequently"
severity: high
action: review # allow | review | block
conditions:
- type: velocity
field: sender.phone
window: 1h
threshold: 5
operator: ">"
- type: amount
field: amount
threshold: 500000
currency: XOF
operator: ">="
# Both conditions must be true (AND logic)
# For OR logic, create separate rules
match: allCondition Types
velocityCount or sum transactions within a time window. Fields: sender.phone, sender.device_id, receiver.agent_id, sender.ip. Windows: 5m, 15m, 1h, 6h, 24h, 7d.
amountCheck transaction amount against a threshold. Supports currency-aware comparisons. Operators: >, >=, <, <=, ==, between.
time_of_dayMatch transactions within a time range. Useful for flagging unusual hours. Timezone-aware.
geoCheck sender/receiver location. Supports radius matching ("within 500m of known fraud hotspot") and geo-fencing (country, city, polygon).
metadataMatch on arbitrary metadata fields. Supports exact match, regex, and list membership. Example: device_type == "emulator".
historyCheck account history. "Account age < 24h", "first transaction ever", "sender has been blocked before". Queries PostgreSQL audit log.
Advanced: Agent Collusion Detection
Combine multiple condition types to catch complex fraud patterns. This rule detects when multiple distinct users cash out at the same agent during late hours.
# rules/agent-collusion.yml
name: "Agent collusion pattern"
description: "Flag when multiple users cash out at same agent within short window"
severity: critical
action: block
conditions:
- type: velocity
field: receiver.agent_id
window: 30m
threshold: 3
operator: ">"
context: "distinct sender count"
- type: time_of_day
after: "22:00"
before: "06:00"
timezone: "Africa/Abidjan"
match: all
notifications:
- channel: webhook
url: "https://your-app.com/alerts"
- channel: slack
channel: "#fraud-alerts"Testing Rules
Test rules against historical transactions before enabling them in production.
# Dry-run a rule against last 7 days of transactions
curl -X POST /v1/rules/test \
-d '{
"rule_file": "rules/agent-collusion.yml",
"lookback": "7d",
"limit": 1000
}'
# Response: how many transactions would have been flagged
# {
# "would_flag": 23,
# "total_evaluated": 1000,
# "false_positive_candidates": 4,
# "sample_matches": [...]
# }