Transaction Service
The Transaction Service (transaction_service) manages financial transfers across the STR (Sistema de Transferencia de Reservas), LPI (Liquidacao por Prioridade Intradiaria), TED (Transferencia Eletronica Disponivel), and DOC (Documento de Credito) settlement systems.
Port: 4005
Responsibilities
- Process STR real-time gross settlement transfers
- Handle LPI intraday priority-based settlement
- Manage TED same-day electronic funds transfers
- Process DOC next-business-day credit transfers
- Enforce settlement window rules and operating hours
- Maintain transaction state machines with full audit trails
- Perform balance checks and liquidity validation
Transaction State Machine
Every transaction follows a well-defined state machine:
+----------+
| created |
+----+-----+
|
+-----v------+
| validating |
+-----+------+
|
+-------+--------+
| |
+-----v-----+ +-----v------+
| rejected | | submitted |
+-----------+ +-----+------+
|
+-----v------+
| processing |
+-----+------+
|
+---------+---------+
| |
+-----v-----+ +-----v------+
| settled | | failed |
+-----------+ +-----+------+
|
+-----v------+
| retrying |
+------------+STR Transfers
STR is BACEN's RTGS system for high-value, time-critical transfers. Each STR transfer is settled individually in real time against the institution's reserve account at BACEN.
Creating an STR Transfer
POST /api/v1/transactions/str
Authorization: Bearer <token>
Content-Type: application/json
{
"sender_ispb": "12345678",
"receiver_ispb": "87654321",
"amount": "5000000.00",
"purpose_code": "1",
"reference": "PAGTO-2026-001",
"scheduled_time": null
}STR Operating Rules
| Rule | Value |
|---|---|
| Operating hours | 06:30 - 17:30 BRT |
| Minimum amount | R$ 0.01 |
| Maximum amount | No limit (subject to balance) |
| Settlement | Real-time (LBTR) |
| Finality | Irrevocable upon R3 confirmation |
defmodule TransactionService.STR do
@operating_start ~T[06:30:00]
@operating_end ~T[17:30:00]
def create_transfer(params) do
with :ok <- validate_operating_hours(),
:ok <- validate_ispb(params.sender_ispb),
:ok <- validate_ispb(params.receiver_ispb),
:ok <- validate_amount(params.amount),
{:ok, tx} <- persist_transaction(params),
{:ok, _} <- submit_to_bacen(tx) do
{:ok, tx}
end
end
defp validate_operating_hours do
now = DateTime.now!("America/Sao_Paulo") |> DateTime.to_time()
if Time.compare(now, @operating_start) in [:gt, :eq] and
Time.compare(now, @operating_end) in [:lt, :eq] do
:ok
else
{:error, :outside_operating_hours}
end
end
endTED Transfers
TED enables same-day electronic funds transfers between different financial institutions. TED transfers are settled through STR in real time.
Creating a TED Transfer
POST /api/v1/transactions/ted
Authorization: Bearer <token>
Content-Type: application/json
{
"sender_ispb": "12345678",
"receiver_ispb": "87654321",
"amount": "15000.00",
"sender_account": "00012345",
"sender_branch": "0001",
"sender_name": "Empresa ABC Ltda",
"sender_document": "12345678000190",
"receiver_account": "00098765",
"receiver_branch": "0002",
"receiver_name": "Empresa XYZ S.A.",
"receiver_document": "98765432000110",
"purpose": "payment"
}TED Response
{
"data": {
"id": "tx_01HQXYZ123456",
"type": "TED",
"status": "submitted",
"amount": "15000.00",
"sender_ispb": "12345678",
"receiver_ispb": "87654321",
"nuop": "123456782026011500042",
"created_at": "2026-01-15T10:30:00-03:00",
"bacen_status": "awaiting_r2",
"estimated_settlement": "2026-01-15T10:30:05-03:00"
}
}DOC Transfers
DOC processes credit transfers settled on the next business day (D+1) through the LDL deferred net settlement system.
POST /api/v1/transactions/doc
Authorization: Bearer <token>
Content-Type: application/json
{
"sender_ispb": "12345678",
"receiver_ispb": "87654321",
"amount": "4500.00",
"sender_account": "00012345",
"receiver_account": "00098765",
"settlement_date": "2026-01-16"
}Balance and Statement Queries
STR Balance
GET /api/v1/str/balance
Authorization: Bearer <token>
# Response
{
"data": {
"ispb": "12345678",
"available_balance": "125000000.00",
"blocked_balance": "5000000.00",
"total_balance": "130000000.00",
"currency": "BRL",
"as_of": "2026-01-15T10:30:00-03:00"
}
}Transaction Statement
GET /api/v1/str/statement?date=2026-01-15&page=1&page_size=50
Authorization: Bearer <token>
# Response
{
"data": {
"date": "2026-01-15",
"opening_balance": "120000000.00",
"closing_balance": "125000000.00",
"entries": [
{
"nuop": "123456782026011500001",
"type": "STR",
"direction": "credit",
"amount": "10000000.00",
"counterparty_ispb": "11111111",
"settled_at": "2026-01-15T08:15:00-03:00"
}
],
"pagination": {
"page": 1,
"page_size": 50,
"total_entries": 127,
"total_pages": 3
}
}
}Transaction Cancellation
Transactions may be cancelled before final settlement (before R3 is received):
POST /api/v1/transactions/tx_01HQXYZ123456/cancel
Authorization: Bearer <token>
Content-Type: application/json
{
"reason": "Duplicate transaction",
"reason_code": "DUP"
}Cancellation is subject to the transaction's current state and BACEN's cancellation rules for each system.
Database Schema
CREATE TABLE transactions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
type VARCHAR(3) NOT NULL CHECK (type IN ('STR', 'LPI', 'TED', 'DOC')),
status VARCHAR(20) NOT NULL DEFAULT 'created',
nuop VARCHAR(20) UNIQUE,
sender_ispb VARCHAR(8) NOT NULL,
receiver_ispb VARCHAR(8) NOT NULL,
amount DECIMAL(18, 2) NOT NULL CHECK (amount > 0),
purpose_code VARCHAR(4),
reference VARCHAR(100),
bacen_status VARCHAR(20),
r1_received_at TIMESTAMPTZ,
r2_received_at TIMESTAMPTZ,
r3_received_at TIMESTAMPTZ,
settled_at TIMESTAMPTZ,
failed_at TIMESTAMPTZ,
failure_reason TEXT,
tenant_id UUID NOT NULL,
created_by UUID NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_transactions_tenant ON transactions (tenant_id, created_at DESC);
CREATE INDEX idx_transactions_nuop ON transactions (nuop) WHERE nuop IS NOT NULL;
CREATE INDEX idx_transactions_status ON transactions (status) WHERE status NOT IN ('settled', 'failed');Health Check
curl http://localhost:4005/health
# {"status": "ok", "database": "connected", "pending_transactions": 12}