API Reference

Complete documentation for the Fraud Detection API endpoints.

Base URL

http://localhost:8000

Authentication

Currently, the API does not require authentication for local development. Production deployments should implement API key or OAuth authentication.


Endpoints

POST /decide

Make a fraud decision for a transaction.

Request:

curl -X POST http://localhost:8000/decide \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_id": "txn_abc123",
    "idempotency_key": "idem_abc123",
    "amount_cents": 9999,
    "currency": "USD",
    "service_id": "mobile_prepaid_001",
    "service_type": "mobile",
    "event_subtype": "sim_activation",
    "card_token": "card_xyz",
    "user_id": "subscriber_789",
    "phone_number": "15551234567",
    "imei": "353456789012345",
    "device_fingerprint": "device_abc",
    "ip_address": "192.168.1.100",
    "timestamp": "2026-01-04T15:30:00Z"
  }'

Request Body:

FieldTypeRequiredDescription
transaction_idstringYesUnique transaction identifier
idempotency_keystringYesKey for duplicate detection
amount_centsintYesTransaction amount in cents
currencystringYesISO 4217 currency code
service_idstringYesTelco service identifier
service_typestringYesmobile or broadband
event_subtypestringNosim_activation, topup, device_upgrade, sim_swap, international_enable, equipment_purchase
card_tokenstringYesTokenized card reference
user_idstringYesSubscriber identifier
phone_numberstringNoSubscriber phone number (telco)
imeistringNoDevice IMEI (telco mobile)
sim_iccidstringNoSIM card ICCID (telco mobile)
device_fingerprintstringYesDevice identifier
ip_addressstringYesSubscriber IP address
timestampstringYesISO 8601 timestamp
device_emulatorboolNoTrue if device is emulated (SIM farm indicator)
device_rootedboolNoTrue if device is rooted/jailbroken
ip_datacenterboolNoTrue if IP is datacenter/cloud
ip_torboolNoTrue if IP is Tor exit node
ip_vpnboolNoTrue if IP is known VPN
card_countrystringNoCard issuing country (ISO 3166)
ip_countrystringNoIP geolocation country

Response:

{
  "transaction_id": "txn_abc123",
  "decision": "ALLOW",
  "scores": {
    "overall_risk": 0.15,
    "criminal_score": 0.10,
    "friendly_fraud_score": 0.08,
    "bot_score": 0.02
  },
  "signals": [],
  "latency_ms": 7.8,
  "policy_version": "1.0",
  "evidence_id": "evt_550e8400-e29b-41d4-a716-446655440000"
}

Response Fields:

FieldTypeDescription
transaction_idstringEcho of request transaction ID
decisionstringALLOW, FRICTION, REVIEW, or BLOCK
scores.overall_riskfloatCombined risk score (0-1)
scores.criminal_scorefloatCriminal fraud probability (0-1)
scores.friendly_fraud_scorefloatFriendly fraud probability (0-1)
scores.bot_scorefloatBot/automation probability (0-1)
signalsarrayList of triggered detection signals
latency_msfloatProcessing time in milliseconds
policy_versionstringPolicy version used for decision
evidence_idstringUUID of stored evidence record

Decision Values:

DecisionDescriptionRecommended Action
ALLOWLow risk, approve transactionProcess normally
FRICTIONMedium risk, needs verificationRequest 3DS/OTP
REVIEWHigh risk, needs manual reviewQueue for analyst
BLOCKVery high risk, declineReject transaction

GET /health

Check system health and component status.

Request:

curl http://localhost:8000/health

Response:

{
  "status": "healthy",
  "redis": "connected",
  "postgres": "connected",
  "policy_loaded": true,
  "policy_version": "1.0",
  "uptime_seconds": 3600
}
FieldDescription
statusOverall health: healthy, degraded, unhealthy
redisRedis connection status
postgresPostgreSQL connection status
policy_loadedWhether policy is loaded
policy_versionCurrent policy version
uptime_secondsSeconds since API started

GET /policy/version

Get current policy version and metadata.

Request:

curl http://localhost:8000/policy/version

Response:

{
  "version": "1.0",
  "loaded_at": "2026-01-04T10:00:00Z",
  "rules_count": 5,
  "thresholds": {
    "block": 80,
    "review": 60,
    "friction": 40
  }
}

POST /policy/reload

Hot-reload policy configuration without restart.

Request:

curl -X POST http://localhost:8000/policy/reload

Response:

{
  "success": true,
  "previous_version": "1.0",
  "new_version": "1.1",
  "loaded_at": "2026-01-04T15:45:00Z"
}

GET /metrics

Prometheus metrics endpoint.

Request:

curl http://localhost:8000/metrics

Response (text/plain):

# HELP fraud_decisions_total Total fraud decisions by type
# TYPE fraud_decisions_total counter
fraud_decisions_total{decision="ALLOW"} 1234
fraud_decisions_total{decision="FRICTION"} 56
fraud_decisions_total{decision="REVIEW"} 23
fraud_decisions_total{decision="BLOCK"} 12

# HELP fraud_decision_latency_seconds Decision latency in seconds
# TYPE fraud_decision_latency_seconds histogram
fraud_decision_latency_seconds_bucket{le="0.005"} 800
fraud_decision_latency_seconds_bucket{le="0.01"} 1200
fraud_decision_latency_seconds_bucket{le="0.05"} 1320

# HELP fraud_detector_triggered_total Detector trigger counts
# TYPE fraud_detector_triggered_total counter
fraud_detector_triggered_total{detector="card_testing"} 45
fraud_detector_triggered_total{detector="velocity"} 78
fraud_detector_triggered_total{detector="geo_anomaly"} 23
fraud_detector_triggered_total{detector="bot"} 12
fraud_detector_triggered_total{detector="friendly_fraud"} 34

Error Responses

400 Bad Request

Invalid request payload.

{
  "error": "validation_error",
  "message": "transaction_id is required",
  "details": {
    "field": "transaction_id",
    "issue": "missing"
  }
}

500 Internal Server Error

Server-side error.

{
  "error": "internal_error",
  "message": "Redis connection failed",
  "request_id": "req_abc123"
}

Rate Limits

EnvironmentLimit
DevelopmentUnlimited
Production10,000 req/min (configurable)

Idempotency

The /decide endpoint is idempotent. Sending the same transaction_id multiple times returns the cached result without re-processing:

# First call - processes transaction
curl -X POST http://localhost:8000/decide -d '{"transaction_id": "txn_001", ...}'
# Response: {"decision": "ALLOW", "latency_ms": 8.2, ...}

# Second call - returns cached result
curl -X POST http://localhost:8000/decide -d '{"transaction_id": "txn_001", ...}'
# Response: {"decision": "ALLOW", "latency_ms": 0.3, "cached": true, ...}

This prevents duplicate charges or inconsistent decisions on network retries.