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:
| Field | Type | Required | Description |
|---|---|---|---|
| transaction_id | string | Yes | Unique transaction identifier |
| idempotency_key | string | Yes | Key for duplicate detection |
| amount_cents | int | Yes | Transaction amount in cents |
| currency | string | Yes | ISO 4217 currency code |
| service_id | string | Yes | Telco service identifier |
| service_type | string | Yes | mobile or broadband |
| event_subtype | string | No | sim_activation, topup, device_upgrade, sim_swap, international_enable, equipment_purchase |
| card_token | string | Yes | Tokenized card reference |
| user_id | string | Yes | Subscriber identifier |
| phone_number | string | No | Subscriber phone number (telco) |
| imei | string | No | Device IMEI (telco mobile) |
| sim_iccid | string | No | SIM card ICCID (telco mobile) |
| device_fingerprint | string | Yes | Device identifier |
| ip_address | string | Yes | Subscriber IP address |
| timestamp | string | Yes | ISO 8601 timestamp |
| device_emulator | bool | No | True if device is emulated (SIM farm indicator) |
| device_rooted | bool | No | True if device is rooted/jailbroken |
| ip_datacenter | bool | No | True if IP is datacenter/cloud |
| ip_tor | bool | No | True if IP is Tor exit node |
| ip_vpn | bool | No | True if IP is known VPN |
| card_country | string | No | Card issuing country (ISO 3166) |
| ip_country | string | No | IP 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:
| Field | Type | Description |
|---|---|---|
| transaction_id | string | Echo of request transaction ID |
| decision | string | ALLOW, FRICTION, REVIEW, or BLOCK |
| scores.overall_risk | float | Combined risk score (0-1) |
| scores.criminal_score | float | Criminal fraud probability (0-1) |
| scores.friendly_fraud_score | float | Friendly fraud probability (0-1) |
| scores.bot_score | float | Bot/automation probability (0-1) |
| signals | array | List of triggered detection signals |
| latency_ms | float | Processing time in milliseconds |
| policy_version | string | Policy version used for decision |
| evidence_id | string | UUID of stored evidence record |
Decision Values:
| Decision | Description | Recommended Action |
|---|---|---|
| ALLOW | Low risk, approve transaction | Process normally |
| FRICTION | Medium risk, needs verification | Request 3DS/OTP |
| REVIEW | High risk, needs manual review | Queue for analyst |
| BLOCK | Very high risk, decline | Reject 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
}| Field | Description |
|---|---|
| status | Overall health: healthy, degraded, unhealthy |
| redis | Redis connection status |
| postgres | PostgreSQL connection status |
| policy_loaded | Whether policy is loaded |
| policy_version | Current policy version |
| uptime_seconds | Seconds 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"} 34Error 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
| Environment | Limit |
|---|---|
| Development | Unlimited |
| Production | 10,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.