mirror of
https://github.com/domfelipe/fraudshield.git
synced 2026-08-07 15:16:52 +00:00
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.
803 lines
19 KiB
YAML
803 lines
19 KiB
YAML
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
|