mirror of
https://github.com/domfelipe/fraudshield.git
synced 2026-08-07 13:16:53 +00:00
plan: add implementation plan for fraud detection engine
Stack: Python 3.12/FastAPI + React 19/TypeScript + PostgreSQL + Redis. 6 artifacts: plan.md, research.md, data-model.md, api.yaml (OpenAPI), quickstart.md, updated AGENTS.md. Constitution check: 6/6 PASS.
This commit is contained in:
parent
e31cba2847
commit
7f9832bf4b
6 changed files with 1487 additions and 1 deletions
803
specs/001-fraud-detection-engine/contracts/api.yaml
Normal file
803
specs/001-fraud-detection-engine/contracts/api.yaml
Normal file
|
|
@ -0,0 +1,803 @@
|
|||
openapi: "3.0.3"
|
||||
info:
|
||||
title: FraudShield API
|
||||
version: "1.0.0"
|
||||
description: API do motor de detecção de fraudes bancárias.
|
||||
servers:
|
||||
- url: http://localhost:8000/api/v1
|
||||
description: Local development
|
||||
|
||||
paths:
|
||||
/transactions/ingest:
|
||||
post:
|
||||
summary: Ingest transaction for fraud evaluation
|
||||
operationId: ingestTransaction
|
||||
tags: [Transactions]
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/TransactionIngestRequest"
|
||||
responses:
|
||||
"202":
|
||||
description: Transaction accepted for processing
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/TransactionAccepted"
|
||||
"400":
|
||||
$ref: "#/components/responses/ValidationError"
|
||||
"429":
|
||||
description: Rate limit exceeded
|
||||
"503":
|
||||
description: Service unavailable (backpressure)
|
||||
|
||||
/transactions/{transaction_id}:
|
||||
get:
|
||||
summary: Get transaction details with evaluation result
|
||||
operationId: getTransaction
|
||||
tags: [Transactions]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
parameters:
|
||||
- name: transaction_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: Transaction details
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/TransactionDetail"
|
||||
"404":
|
||||
description: Transaction not found
|
||||
|
||||
/alerts:
|
||||
get:
|
||||
summary: List fraud alerts with filters
|
||||
operationId: listAlerts
|
||||
tags: [Alerts]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
parameters:
|
||||
- name: status
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
enum: [pending, confirmed, false_positive, escalated]
|
||||
- name: min_score
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
- name: from_date
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: to_date
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- name: triggered_rule
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
- name: limit
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 50
|
||||
maximum: 200
|
||||
- name: offset
|
||||
in: query
|
||||
schema:
|
||||
type: integer
|
||||
default: 0
|
||||
responses:
|
||||
"200":
|
||||
description: Paginated alert list
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AlertListResponse"
|
||||
|
||||
/alerts/{alert_id}:
|
||||
get:
|
||||
summary: Get alert details
|
||||
operationId: getAlert
|
||||
tags: [Alerts]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
parameters:
|
||||
- name: alert_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: Alert details with full explanation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AlertDetail"
|
||||
"404":
|
||||
description: Alert not found
|
||||
|
||||
/alerts/{alert_id}/decide:
|
||||
post:
|
||||
summary: Submit investigation decision
|
||||
operationId: decideAlert
|
||||
tags: [Alerts]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
parameters:
|
||||
- name: alert_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AlertDecisionRequest"
|
||||
responses:
|
||||
"200":
|
||||
description: Decision recorded
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AlertDetail"
|
||||
"400":
|
||||
$ref: "#/components/responses/ValidationError"
|
||||
"409":
|
||||
description: Alert already decided
|
||||
|
||||
/rules:
|
||||
get:
|
||||
summary: List detection rules
|
||||
operationId: listRules
|
||||
tags: [Rules]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
- AdminAuth: []
|
||||
parameters:
|
||||
- name: is_active
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: Rule list
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/DetectionRule"
|
||||
|
||||
post:
|
||||
summary: Create new detection rule
|
||||
operationId: createRule
|
||||
tags: [Rules]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
- AdminAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/CreateRuleRequest"
|
||||
responses:
|
||||
"201":
|
||||
description: Rule created (inactive by default)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DetectionRule"
|
||||
"400":
|
||||
$ref: "#/components/responses/ValidationError"
|
||||
|
||||
/rules/{rule_id}:
|
||||
get:
|
||||
summary: Get rule details
|
||||
operationId: getRule
|
||||
tags: [Rules]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
- AdminAuth: []
|
||||
parameters:
|
||||
- name: rule_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: Rule details
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DetectionRule"
|
||||
|
||||
put:
|
||||
summary: Update detection rule
|
||||
operationId: updateRule
|
||||
tags: [Rules]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
- AdminAuth: []
|
||||
parameters:
|
||||
- name: rule_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UpdateRuleRequest"
|
||||
responses:
|
||||
"200":
|
||||
description: Rule updated
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DetectionRule"
|
||||
|
||||
/rules/{rule_id}/activate:
|
||||
post:
|
||||
summary: Activate a rule
|
||||
operationId: activateRule
|
||||
tags: [Rules]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
- AdminAuth: []
|
||||
parameters:
|
||||
- name: rule_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: Rule activated
|
||||
"400":
|
||||
description: Rule has no weight or threshold set
|
||||
|
||||
/rules/{rule_id}/deactivate:
|
||||
post:
|
||||
summary: Deactivate a rule
|
||||
operationId: deactivateRule
|
||||
tags: [Rules]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
- AdminAuth: []
|
||||
parameters:
|
||||
- name: rule_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: Rule deactivated
|
||||
|
||||
/rules/{rule_id}/test:
|
||||
post:
|
||||
summary: Test rule against historical data
|
||||
operationId: testRule
|
||||
tags: [Rules]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
- AdminAuth: []
|
||||
parameters:
|
||||
- name: rule_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/TestRuleRequest"
|
||||
responses:
|
||||
"200":
|
||||
description: Test results
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/TestRuleResponse"
|
||||
|
||||
/dashboard/metrics:
|
||||
get:
|
||||
summary: Get real-time dashboard metrics
|
||||
operationId: getDashboardMetrics
|
||||
tags: [Dashboard]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
parameters:
|
||||
- name: period
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
enum: [24h, 7d, 30d, 90d]
|
||||
default: 24h
|
||||
responses:
|
||||
"200":
|
||||
description: Dashboard metrics
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DashboardMetrics"
|
||||
|
||||
/dashboard/report:
|
||||
get:
|
||||
summary: Export compliance report
|
||||
operationId: exportReport
|
||||
tags: [Dashboard]
|
||||
security:
|
||||
- BearerAuth: []
|
||||
parameters:
|
||||
- name: from_date
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: date
|
||||
- name: to_date
|
||||
in: query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: date
|
||||
- name: format
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
enum: [pdf, csv]
|
||||
default: pdf
|
||||
responses:
|
||||
"200":
|
||||
description: Report file
|
||||
content:
|
||||
application/pdf:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
text/csv:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
|
||||
/health:
|
||||
get:
|
||||
summary: Health check
|
||||
operationId: healthCheck
|
||||
tags: [System]
|
||||
responses:
|
||||
"200":
|
||||
description: Service healthy
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
example: healthy
|
||||
version:
|
||||
type: string
|
||||
example: "1.0.0"
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
BearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
ApiKeyAuth:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-API-Key
|
||||
AdminAuth: {}
|
||||
|
||||
schemas:
|
||||
TransactionIngestRequest:
|
||||
type: object
|
||||
required: [external_id, amount, merchant_name, merchant_category, customer_id, channel, transaction_at]
|
||||
properties:
|
||||
external_id:
|
||||
type: string
|
||||
description: ID original do sistema bancário
|
||||
amount:
|
||||
type: number
|
||||
format: decimal
|
||||
minimum: 0.01
|
||||
example: 5000.00
|
||||
merchant_name:
|
||||
type: string
|
||||
example: "Loja Exemplo Ltda"
|
||||
merchant_category:
|
||||
type: string
|
||||
example: "eletrônicos"
|
||||
latitude:
|
||||
type: number
|
||||
format: float
|
||||
nullable: true
|
||||
longitude:
|
||||
type: number
|
||||
format: float
|
||||
nullable: true
|
||||
customer_id:
|
||||
type: string
|
||||
description: Token do cliente
|
||||
example: "tok_abc123xyz"
|
||||
channel:
|
||||
type: string
|
||||
enum: [web, mobile, pos]
|
||||
transaction_at:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
TransactionAccepted:
|
||||
type: object
|
||||
properties:
|
||||
transaction_id:
|
||||
type: string
|
||||
format: uuid
|
||||
status:
|
||||
type: string
|
||||
example: accepted
|
||||
trace_id:
|
||||
type: string
|
||||
format: uuid
|
||||
|
||||
TransactionDetail:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
external_id:
|
||||
type: string
|
||||
amount:
|
||||
type: number
|
||||
merchant_name:
|
||||
type: string
|
||||
merchant_category:
|
||||
type: string
|
||||
latitude:
|
||||
type: number
|
||||
nullable: true
|
||||
longitude:
|
||||
type: number
|
||||
nullable: true
|
||||
customer_id:
|
||||
type: string
|
||||
channel:
|
||||
type: string
|
||||
transaction_at:
|
||||
type: string
|
||||
format: date-time
|
||||
ingested_at:
|
||||
type: string
|
||||
format: date-time
|
||||
alert:
|
||||
$ref: "#/components/schemas/AlertDetail"
|
||||
nullable: true
|
||||
|
||||
AlertListResponse:
|
||||
type: object
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/AlertSummary"
|
||||
total:
|
||||
type: integer
|
||||
limit:
|
||||
type: integer
|
||||
offset:
|
||||
type: integer
|
||||
|
||||
AlertSummary:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
score:
|
||||
type: integer
|
||||
status:
|
||||
type: string
|
||||
amount:
|
||||
type: number
|
||||
merchant_name:
|
||||
type: string
|
||||
top_rule:
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
AlertDetail:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
transaction_id:
|
||||
type: string
|
||||
format: uuid
|
||||
score:
|
||||
type: integer
|
||||
triggered_rules:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
rule_id:
|
||||
type: string
|
||||
format: uuid
|
||||
rule_name:
|
||||
type: string
|
||||
score:
|
||||
type: integer
|
||||
explanation:
|
||||
type: string
|
||||
explanation:
|
||||
type: string
|
||||
description: Explicação composta em linguagem natural
|
||||
status:
|
||||
type: string
|
||||
enum: [pending, confirmed, false_positive, escalated]
|
||||
assigned_to:
|
||||
type: string
|
||||
nullable: true
|
||||
decision:
|
||||
type: string
|
||||
nullable: true
|
||||
decided_by:
|
||||
type: string
|
||||
nullable: true
|
||||
decided_at:
|
||||
type: string
|
||||
format: date-time
|
||||
nullable: true
|
||||
justification:
|
||||
type: string
|
||||
nullable: true
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
AlertDecisionRequest:
|
||||
type: object
|
||||
required: [decision]
|
||||
properties:
|
||||
decision:
|
||||
type: string
|
||||
enum: [confirmed, false_positive, escalated]
|
||||
justification:
|
||||
type: string
|
||||
description: Required for false_positive and escalated
|
||||
notes:
|
||||
type: string
|
||||
|
||||
DetectionRule:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
condition:
|
||||
type: string
|
||||
weight:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
threshold:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
is_active:
|
||||
type: boolean
|
||||
version:
|
||||
type: integer
|
||||
created_by:
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
CreateRuleRequest:
|
||||
type: object
|
||||
required: [name, description, condition]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
example: "Valor atípico"
|
||||
description:
|
||||
type: string
|
||||
example: "Detecta transações com valor muito acima da média do cliente"
|
||||
condition:
|
||||
type: string
|
||||
example: "amount > customer.avg_amount_30d * 3"
|
||||
weight:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
default: 0
|
||||
threshold:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
default: 0
|
||||
|
||||
UpdateRuleRequest:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
condition:
|
||||
type: string
|
||||
weight:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
threshold:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
|
||||
TestRuleRequest:
|
||||
type: object
|
||||
properties:
|
||||
days_back:
|
||||
type: integer
|
||||
default: 90
|
||||
maximum: 90
|
||||
|
||||
TestRuleResponse:
|
||||
type: object
|
||||
properties:
|
||||
rule_id:
|
||||
type: string
|
||||
format: uuid
|
||||
total_transactions_evaluated:
|
||||
type: integer
|
||||
example: 50000
|
||||
would_trigger_count:
|
||||
type: integer
|
||||
example: 1250
|
||||
would_trigger_pct:
|
||||
type: number
|
||||
example: 2.5
|
||||
score_distribution:
|
||||
type: object
|
||||
properties:
|
||||
p50:
|
||||
type: number
|
||||
p90:
|
||||
type: number
|
||||
p95:
|
||||
type: number
|
||||
max:
|
||||
type: number
|
||||
estimated_false_positives:
|
||||
type: integer
|
||||
description: Based on analyst decisions on similar patterns
|
||||
sample_alerts:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/AlertSummary"
|
||||
|
||||
DashboardMetrics:
|
||||
type: object
|
||||
properties:
|
||||
period:
|
||||
type: string
|
||||
total_transactions:
|
||||
type: integer
|
||||
total_alerts:
|
||||
type: integer
|
||||
fraud_rate_pct:
|
||||
type: number
|
||||
description: Alertas confirmados / total de transações
|
||||
false_positive_rate_pct:
|
||||
type: number
|
||||
avg_decision_time_seconds:
|
||||
type: number
|
||||
alerts_by_status:
|
||||
type: object
|
||||
properties:
|
||||
pending:
|
||||
type: integer
|
||||
confirmed:
|
||||
type: integer
|
||||
false_positive:
|
||||
type: integer
|
||||
escalated:
|
||||
type: integer
|
||||
alerts_by_hour:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
hour:
|
||||
type: integer
|
||||
count:
|
||||
type: integer
|
||||
top_triggering_rules:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
rule_name:
|
||||
type: string
|
||||
alert_count:
|
||||
type: integer
|
||||
score_distribution:
|
||||
type: object
|
||||
properties:
|
||||
low:
|
||||
type: integer
|
||||
medium:
|
||||
type: integer
|
||||
high:
|
||||
type: integer
|
||||
critical:
|
||||
type: integer
|
||||
|
||||
responses:
|
||||
ValidationError:
|
||||
description: Validation error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
detail:
|
||||
type: string
|
||||
errors:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
field:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
Loading…
Add table
Add a link
Reference in a new issue