Triple P Bank API
Triple P Bank API Reference

Visão geral

A API do Triple P Bank é uma API REST que fala JSON. Ela cobre o banco digital (clientes, conta corrente, depósito, saque, transferência, extrato) e um módulo de investimentos simulado: perfil do investidor, carteira sugerida, aplicação e resgate em títulos do Tesouro, no CDB e na LCI do banco e no fundo FGV Quant Capital, posição e rentabilidade. A operação do banco tem rotas próprias, em /internal, para bloquear contas, fechar o dia e ver a receita.

Três garantias valem para toda a API:

  • Nenhum centavo é criado ou some. Toda movimentação grava o pedido e as linhas do extrato num único commit, com as contas travadas. A tarifa sai do cliente e entra na conta do banco.
  • Um pedido repetido não move o dinheiro de novo (ver Idempotência).
  • Um cliente nunca vê o recurso de outro. O recurso de outro cliente responde 404, igual a um que não existe.

Como começar

  1. Suba o ambiente: docker compose up -d (API, banco e o simulador das fontes de preço).
  2. Cadastre um cliente: POST /customer.
  3. Abra a conta: POST /customer/{customer_key}/account.
  4. Deposite: POST .../transaction com type = DEPOSIT e uma Idempotency-Key.
  5. Para investir: responda o perfil (POST .../suitability), veja o catálogo (GET /products) e aplique (POST .../investment_order).

Ambiente e host

A API roda localmente com Docker Compose. Não há ambiente público.

ServiçoHostObservação
APIhttp://localhost:3000A porta vem da variável API_PORT do .env (padrão 3000).
Simulador das fontes de preço (MockServer)http://localhost:1080Imita o Tesouro, a administradora do fundo e o Banco Central (MOCK_PORT).
PostgreSQLlocalhost:5432DB_PORT. Schema e dados iniciais em database/database.sql.

Mudou o database.sql? Ele só roda quando o banco nasce: docker compose down -v e depois docker compose up -d --build.

Autenticação

Toda rota exige o cabeçalho INTERNAL-TOKEN, menos GET / e GET /health_check. O valor é o da variável INTERNAL_TOKEN do ambiente. Sem ele, ou com valor errado: 403 (PPP000002).

As rotas em /internal são da operação do banco e exigem também o cabeçalho OPERATOR-TOKEN (variável OPERATOR_TOKEN). Sem ele: 403 (PPP000007). O cliente da API só tem o primeiro token; é isso que impede, por exemplo, um cliente bloqueado de se desbloquear.

CabeçalhoOndeValor
INTERNAL-TOKENTodas as rotas, menos / e /health_checkVariável INTERNAL_TOKEN
OPERATOR-TOKENRotas /internal/...Variável OPERATOR_TOKEN
Idempotency-KeyPOST .../transaction e POST .../investment_orderUm UUID novo por pedido
Content-TypeRotas com corpoapplication/json

Idempotência

As rotas que movem dinheiro (POST .../transaction e POST .../investment_order) exigem o cabeçalho Idempotency-Key: um UUID gerado por quem chama a API, um por pedido. Se a resposta se perder (a internet caiu, deu timeout), reenvie o mesmo pedido com a mesma chave: o banco devolve a resposta original e não move o dinheiro de novo.

A chave é única no banco inteiro. Quando ela chega de novo, o banco compara o pedido com o que já gravou:

Situação (mesma chave)Resposta
Reenvio do mesmo pedido, pela mesma conta200 com a resposta original. Nada se move.
Mesma conta, pedido diferente (outro valor, tipo, destino ou produto)409 (PPP000004)
Chave já usada por outra conta409 (PPP000004), sem mostrar nada do outro pedido
Chave de um depósito mandada numa aplicação (ou o contrário)409 (PPP000004)
Sem o cabeçalho, ou fora do formato UUID400 (PPP000003)

Um pedido recusado (saldo insuficiente, por exemplo) não grava nada, então a mesma chave pode ser usada de novo depois.

Dinheiro, datas e decimais

O quêFormatoExemplo
DinheiroInteiro em centavos. Campos terminam em _amount (e balance).R$ 10,50 = 1050
PercentualInteiro em pontos-base. Campos terminam em _bps.10000 = 100%, 150 = 1,5%
Cotas e preço unitárioTexto com 8 casas decimais, para não virar float."1.25000000"
DataAAAA-MM-DD"2026-10-09"
InstanteUTC, AAAA-MM-DDTHH:MM:SSZ"2026-10-09T14:12:00Z"
IdentificadorA ..._key (UUID). O id interno nunca sai."9c2f..."

Data de movimento. O "hoje" do banco (GET /internal/business_date) é separado do relógio. Ele só avança quando a operação fecha o dia (POST /internal/day_close), que também busca os preços dos produtos. Aplicações e resgates usam o preço gravado para a data de movimento. Idade mínima e validade do perfil usam o relógio real.

Paginação

Listas longas (extrato e histórico de ordens) aceitam page (a partir de 1, padrão 1) e limit (1 a 100, padrão 20) na query, e respondem:

{"items": [ ... ], "page": 1, "limit": 20, "total": 57}

total é a quantidade de itens que passam pelos filtros, somando todas as páginas.

Formato de erro

Todo erro responde com o mesmo corpo, com o status HTTP correspondente:

{
  "title": "Insufficient Balance",
  "description": "The account does not have enough balance for amount plus fee.",
  "translation": "Saldo insuficiente para o valor mais a tarifa.",
  "code": "PPP003003"
}
CampoO que é
titleNome curto do erro, em inglês.
descriptionExplicação em inglês.
translationExplicação em português, pronta para mostrar ao usuário.
codeCódigo estável do erro (PPP + 6 dígitos). Use ele para decidir o que fazer, não o texto.

O corpo pode ganhar campos novos no futuro: não trate um campo a mais como erro. A lista completa está em Catálogo de erros.

Catálogo de erros

Todos os códigos que a API devolve. Os três primeiros dígitos depois de PPP dizem o módulo: 000 geral, 001 clientes, 002 contas, 003 movimentações, 004 perfil, 005 produtos, 006 carteira, 007 ordens.

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
400PPP000003Missing Idempotency KeyThis route moves money and requires the Idempotency-Key header with a UUID.Esta operação exige o cabeçalho Idempotency-Key com um UUID.
409PPP000004Idempotency Key ReusedThis Idempotency-Key was already used by another request.Esta Idempotency-Key já foi usada em outro pedido.
400PPP000005Invalid Parameter(varia: diz o que está errado)Parâmetros inválidos foram fornecidos na requisição.
409PPP000006Business Date MismatchThe close_date is not the current business date.A data a fechar não é a data de movimento atual.
403PPP000007Operator OnlyThis route belongs to the bank operation and requires the OPERATOR-TOKEN header.Esta rota é da operação do banco e exige o token de operador.
404PPP000404Resource not FoundThe requested resource could not be found but may be available in the future. Subsequent requests by the client are permissible.O resource solicitado não pode ser encontrado, mas pode estar disponível no futuro. Requests subsequentes do cliente são permitidos.
405PPP000405Method not allowedThe requested method is forbidden for this resource.O método desejado não foi encontrado para esse recurso.
500PPP000500Internal ErrorAn internal error has occurred and its being investigated.Um erro interno aconteceu e está sendo investigado.
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
409PPP001002Document Already RegisteredThere is already a customer with this document_number.Já existe um cliente cadastrado com este CPF.
422PPP001003Invalid Document NumberThe document_number check digits are invalid.CPF inválido.
422PPP001004Underage CustomerThe customer must be at least 18 years old and born in the past.O cliente precisa ter pelo menos 18 anos.
404PPP002001Account Not FoundThe account_key does not exist for this customer.Conta não encontrada.
409PPP002002Account Already ExistsThe customer already has an account that is not closed.O cliente já tem uma conta não encerrada.
409PPP002003Invalid Status TransitionThe account cannot move from its current status to the requested one.A conta não pode passar do status atual para o status pedido.
409PPP002004Account Not EmptyThe account must have zero balance and no investment position to be closed.Só é possível encerrar uma conta sem saldo e sem investimentos.
409PPP002005Account Not ActiveThe account is blocked or closed and cannot move money.A conta está bloqueada ou encerrada e não pode movimentar dinheiro.
422PPP002006Status Reason RequiredBlocking or closing an account requires a reason.Bloquear ou encerrar a conta exige um motivo.
403PPP002007Status Change Reserved To BankCustomers can only close their own account; blocking and unblocking are bank decisions.O cliente só pode encerrar a própria conta; bloquear e desbloquear são decisões do banco.
404PPP003001Transaction Not FoundThe transaction_key does not exist or does not involve this account.Transação não encontrada.
404PPP003002Destination Account Not FoundThe destination_account_key does not exist.Conta de destino não encontrada.
409PPP003003Insufficient BalanceThe account does not have enough balance for amount plus fee.Saldo insuficiente para o valor mais a tarifa.
422PPP003004Invalid AmountThe amount must be an integer greater than zero, in cents.O valor precisa ser maior que zero, em centavos.
422PPP003005Amount Above LimitThe amount is above the limit for this type of operation.O valor está acima do limite para este tipo de operação.
422PPP003006Same Account TransferThe destination account must be different from the origin account.A conta de destino precisa ser diferente da conta de origem.
409PPP003007Destination Account Not ActiveThe destination account is blocked or closed and cannot receive money.A conta de destino está bloqueada ou encerrada e não pode receber dinheiro.
404PPP004001Suitability Not FoundThe customer has never answered the suitability questionnaire.O cliente ainda não respondeu o questionário de perfil de investidor.
422PPP004002Incomplete AnswersEvery question must be answered exactly once, and only existing questions can be answered.Cada pergunta do questionário precisa de exatamente uma resposta.
422PPP004003Invalid OptionThe option_code does not belong to the question.A opção escolhida não pertence a esta pergunta.
409PPP004004Suitability ExpiredThe investor profile is more than 24 months old and must be answered again.O perfil de investidor tem mais de 24 meses e precisa ser refeito.
409PPP004005Suitability RequiredThe customer needs an investor profile before this operation.O cliente precisa responder o questionário de perfil de investidor antes desta operação.
404PPP005001Product Not FoundThe product_key does not exist.Produto não encontrado.
502PPP005006Price Source UnavailableAn external price or rate source is down, timed out or answered an error.Uma fonte externa de preço ou taxa está fora do ar ou respondeu erro. Nada foi gravado.
502PPP005007Invalid Price Source ResponseAn external price or rate source answered something that makes no sense.Uma fonte externa de preço ou taxa respondeu algo sem sentido. Nada foi gravado.
422PPP006001Suggestion Amount Too LowThe amount for a portfolio suggestion must be at least 100000 cents (R$ 1,000.00).O valor mínimo para pedir uma sugestão de carteira é R$ 1.000,00.
404PPP007001Order Not FoundThe investment_order_key does not exist for this customer.Ordem não encontrada.
422PPP007002Below Minimum ApplicationThe amount is below the product's minimum application.O valor está abaixo da aplicação mínima do produto.
422PPP007003Product Not SuitableThe product's risk level is above what the investor profile allows.O risco do produto está acima do permitido para o seu perfil de investidor.
409PPP007004Insufficient PositionThe redemption is larger than the customer's position in this product.O resgate é maior do que o que você tem aplicado neste produto.
422PPP007005Redemption Not AllowedThis product can only be redeemed at maturity.Este produto só pode ser resgatado no vencimento.

Clientes

Cadastrar cliente

Cadastra um cliente pessoa física. O CPF pode vir com ou sem pontuação e é guardado só com os dígitos; o cliente precisa ter 18 anos ou mais. A resposta traz a customer_key, que identifica o cliente em todas as outras rotas, e o CPF mascarado.

Endpoint/customer
MétodoPOST

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).
Content-Typesimapplication/json

Corpo da requisição

CampoTipoDescriçãoValores aceitos
name*stringNome. No cadastro: nome completo do cliente, de 3 a 120 caracteres, sem espaço nas pontas.mínimo 3 caracteres; até 120 caracteres; sem espaço nas pontas
document_number*stringCPF. Entra com ou sem pontuação (123.456.789-09 ou 12345678909) e sai mascarado (*.456.789-).000.000.000-00 ou 11 dígitos
birth_date*stringData de nascimento (AAAA-MM-DD). O cliente precisa ter 18 anos ou mais.data AAAA-MM-DD

* obrigatório.

Exemplo de requisição

curl -X POST 'http://localhost:3000/customer' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"name": "Ana Souza", "document_number": "529.982.247-25", "birth_date": "1992-04-17"}'

Respostas

201 Cliente cadastrado.

STATUS 201Cliente cadastrado
{
  "customer_key": "4f9a35d6-e269-4e84-a9f6-b50552c5ce56",
  "name": "Ana Souza",
  "document_number": "***.982.247-**",
  "birth_date": "1992-04-17",
  "created_at": "2026-10-11T19:43:59Z"
}
STATUS 409CPF já cadastrado
{
  "title": "Document Already Registered",
  "description": "There is already a customer with this document_number.",
  "translation": "Já existe um cliente cadastrado com este CPF.",
  "code": "PPP001002"
}

Campos da resposta

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....
nameNome. No cadastro: nome completo do cliente, de 3 a 120 caracteres, sem espaço nas pontas.
document_numberCPF. Entra com ou sem pontuação (123.456.789-09 ou 12345678909) e sai mascarado (*.456.789-).
birth_dateData de nascimento (AAAA-MM-DD). O cliente precisa ter 18 anos ou mais.
created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
409PPP001002Document Already RegisteredThere is already a customer with this document_number.Já existe um cliente cadastrado com este CPF.
422PPP001003Invalid Document NumberThe document_number check digits are invalid.CPF inválido.
422PPP001004Underage CustomerThe customer must be at least 18 years old and born in the past.O cliente precisa ter pelo menos 18 anos.

Clientes

Consultar cliente

Devolve os dados do cliente. O CPF nunca sai completo: aparece como *.456.789-.

Endpoint/customer/{customer_key}
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....

Exemplo de requisição

curl -X GET 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Dados do cliente.

STATUS 200Dados do cliente
{
  "customer_key": "4f9a35d6-e269-4e84-a9f6-b50552c5ce56",
  "name": "Ana Souza",
  "document_number": "***.982.247-**",
  "birth_date": "1992-04-17",
  "created_at": "2026-10-11T19:43:59Z"
}

Campos da resposta

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....
nameNome. No cadastro: nome completo do cliente, de 3 a 120 caracteres, sem espaço nas pontas.
document_numberCPF. Entra com ou sem pontuação (123.456.789-09 ou 12345678909) e sai mascarado (*.456.789-).
birth_dateData de nascimento (AAAA-MM-DD). O cliente precisa ter 18 anos ou mais.
created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.

Contas

Abrir conta

Abre a conta corrente do cliente, que nasce ACTIVE e com saldo zero. Cada cliente tem no máximo uma conta não encerrada; para abrir outra, a anterior precisa estar CLOSED.

Endpoint/customer/{customer_key}/account
MétodoPOST

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....

Exemplo de requisição

curl -X POST 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/account' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

201 Conta aberta.

STATUS 201Conta aberta
{
  "account_key": "b043541b-19bc-4c23-a079-b18cbff9354c",
  "status": "ACTIVE",
  "balance": 0,
  "investments_amount": 0,
  "net_worth_amount": 0,
  "created_at": "2026-10-11T19:43:59Z",
  "updated_at": "2026-10-11T19:43:59Z"
}

Campos da resposta

CampoDescrição
account_keyIdentificador da conta (UUID).
statusStatus da conta: ACTIVE (movimenta), BLOCKED (não movimenta, só consulta) ou CLOSED (encerrada).
balanceSaldo da conta (só o dinheiro parado nela, sem os investimentos), em centavos. Na resposta de uma movimentação, é o saldo logo depois dela.
investments_amountValor de todas as posições ao preço do dia, em centavos. Fica fora do balance: o que foi aplicado saiu do saldo.
net_worth_amountCapital total do cliente no banco: caixa + investimentos ao preço do dia, em centavos.
created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).
updated_atQuando o registro mudou pela última vez, em UTC.

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
409PPP002002Account Already ExistsThe customer already has an account that is not closed.O cliente já tem uma conta não encerrada.

Contas

Consultar conta

Devolve o status, o saldo (dinheiro na conta), o valor investido ao preço do dia e o capital total (saldo + investimentos), em centavos. Os dois últimos são calculados na hora e batem com o total de posições. Conta de outro cliente responde 404, igual a uma inexistente.

Endpoint/customer/{customer_key}/account/{account_key}
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....
account_keyIdentificador da conta (UUID).

Exemplo de requisição

curl -X GET 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/account/b043541b-19bc-4c23-a079-b18cbff9354c' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Dados da conta.

STATUS 200Dados da conta
{
  "account_key": "b043541b-19bc-4c23-a079-b18cbff9354c",
  "status": "ACTIVE",
  "balance": 0,
  "investments_amount": 0,
  "net_worth_amount": 0,
  "created_at": "2026-10-11T19:43:59Z",
  "updated_at": "2026-10-11T19:43:59Z"
}
STATUS 200Conta com investimentos
{
  "account_key": "b043541b-19bc-4c23-a079-b18cbff9354c",
  "status": "ACTIVE",
  "balance": 99800,
  "investments_amount": 79998,
  "net_worth_amount": 179798,
  "created_at": "2026-10-11T19:43:59Z",
  "updated_at": "2026-10-11T19:43:59Z"
}

Campos da resposta

CampoDescrição
account_keyIdentificador da conta (UUID).
statusStatus da conta: ACTIVE (movimenta), BLOCKED (não movimenta, só consulta) ou CLOSED (encerrada).
balanceSaldo da conta (só o dinheiro parado nela, sem os investimentos), em centavos. Na resposta de uma movimentação, é o saldo logo depois dela.
investments_amountValor de todas as posições ao preço do dia, em centavos. Fica fora do balance: o que foi aplicado saiu do saldo.
net_worth_amountCapital total do cliente no banco: caixa + investimentos ao preço do dia, em centavos.
created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).
updated_atQuando o registro mudou pela última vez, em UTC.

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
404PPP002001Account Not FoundThe account_key does not exist for this customer.Conta não encontrada.

Contas

Encerrar conta (cliente)

O cliente encerra a própria conta, informando o motivo. Só encerra com saldo zero e sem investimentos. Bloquear e desbloquear são exclusivos do banco (rota interna); pedir isso aqui responde 403.

Endpoint/customer/{customer_key}/account/{account_key}
MétodoPATCH

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).
Content-Typesimapplication/json

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....
account_keyIdentificador da conta (UUID).

Corpo da requisição

CampoTipoDescriçãoValores aceitos
status*stringNovo status. O cliente só pode pedir CLOSED; BLOCKED e ACTIVE são do banco (rota interna) e respondem 403.ACTIVE · BLOCKED · CLOSED
reasonstringMotivo da mudança de status, até 255 caracteres. Obrigatório para bloquear e encerrar.até 255 caracteres

* obrigatório.

Exemplo de requisição

curl -X PATCH 'http://localhost:3000/customer/117b4144-e22a-456f-8975-6760acfc0481/account/829a2669-8375-43b1-bbf8-19e21609e92d' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"status": "CLOSED", "reason": "Não vou mais usar"}'

Respostas

200 Conta encerrada; um evento de status é gravado.

STATUS 200Cliente encerra a conta vazia
{
  "account_key": "829a2669-8375-43b1-bbf8-19e21609e92d",
  "status": "CLOSED",
  "balance": 0,
  "investments_amount": 0,
  "net_worth_amount": 0,
  "created_at": "2026-10-11T19:44:00Z",
  "updated_at": "2026-10-11T19:44:00Z"
}
STATUS 403Cliente tenta bloquear
{
  "title": "Status Change Reserved To Bank",
  "description": "Customers can only close their own account; blocking and unblocking are bank decisions.",
  "translation": "O cliente só pode encerrar a própria conta; bloquear e desbloquear são decisões do banco.",
  "code": "PPP002007"
}

Campos da resposta

CampoDescrição
account_keyIdentificador da conta (UUID).
statusStatus da conta: ACTIVE (movimenta), BLOCKED (não movimenta, só consulta) ou CLOSED (encerrada).
balanceSaldo da conta (só o dinheiro parado nela, sem os investimentos), em centavos. Na resposta de uma movimentação, é o saldo logo depois dela.
investments_amountValor de todas as posições ao preço do dia, em centavos. Fica fora do balance: o que foi aplicado saiu do saldo.
net_worth_amountCapital total do cliente no banco: caixa + investimentos ao preço do dia, em centavos.
created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).
updated_atQuando o registro mudou pela última vez, em UTC.

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
403PPP002007Status Change Reserved To BankCustomers can only close their own account; blocking and unblocking are bank decisions.O cliente só pode encerrar a própria conta; bloquear e desbloquear são decisões do banco.
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
404PPP002001Account Not FoundThe account_key does not exist for this customer.Conta não encontrada.
409PPP002003Invalid Status TransitionThe account cannot move from its current status to the requested one.A conta não pode passar do status atual para o status pedido.
409PPP002004Account Not EmptyThe account must have zero balance and no investment position to be closed.Só é possível encerrar uma conta sem saldo e sem investimentos.
422PPP002006Status Reason RequiredBlocking or closing an account requires a reason.Bloquear ou encerrar a conta exige um motivo.

Contas

Histórico de status da conta

Toda mudança de status da conta, da abertura em diante, com o motivo e quem fez (CUSTOMER ou BANK). Nada é apagado.

Endpoint/customer/{customer_key}/account/{account_key}/status_events
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....
account_keyIdentificador da conta (UUID).

Exemplo de requisição

curl -X GET 'http://localhost:3000/customer/117b4144-e22a-456f-8975-6760acfc0481/account/829a2669-8375-43b1-bbf8-19e21609e92d/status_events' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Eventos, do mais antigo para o mais novo.

STATUS 200Abertura e encerramento
{
  "items": [
    {
      "from_status": null,
      "to_status": "ACTIVE",
      "reason": null,
      "actor": "CUSTOMER",
      "created_at": "2026-10-11T19:44:00Z"
    },
    {
      "from_status": "ACTIVE",
      "to_status": "CLOSED",
      "reason": "Não vou mais usar",
      "actor": "CUSTOMER",
      "created_at": "2026-10-11T19:44:00Z"
    }
  ]
}

Campos da resposta

CampoDescrição
itemsA lista de resultados.
items[].from_statusStatus anterior. Nulo no evento de abertura da conta.
items[].to_statusStatus depois da mudança.
items[].reasonMotivo da mudança de status, até 255 caracteres. Obrigatório para bloquear e encerrar.
items[].actorQuem fez a mudança: CUSTOMER (rota do cliente) ou BANK (rota interna).
items[].created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
404PPP002001Account Not FoundThe account_key does not exist for this customer.Conta não encontrada.

Movimentações e extrato

Depositar, sacar ou transferir

Cria uma movimentação de dinheiro na conta. Na transferência, a tarifa de R$ 2,00 sai da conta de origem e entra na conta do banco; o destino recebe o valor inteiro. Limites por operação: depósito até R$ 1.000.000,00, saque até R$ 5.000,00, transferência até R$ 50.000,00. A resposta traz o saldo da conta logo depois da operação.

Endpoint/customer/{customer_key}/account/{account_key}/transaction
MétodoPOST

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).
Idempotency-KeysimUUID novo por pedido; reenviar com a mesma chave não repete o efeito. Ver Idempotência.
Content-Typesimapplication/json

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....
account_keyIdentificador da conta (UUID).

Corpo da requisição

CampoTipoDescriçãoValores aceitos
type*stringTipo da movimentação: DEPOSIT (depósito), WITHDRAWAL (saque) ou TRANSFER (transferência). Aplicação e resgate vão pela rota de ordens.DEPOSIT · WITHDRAWAL · TRANSFER
amount*integerValor em centavos, inteiro e maior que zero (R$ 10,50 = 1050).
destination_account_keystringConta que recebe a transferência. Obrigatório só no TRANSFER; pode ser de outro cliente, mas não a própria conta.até 36 caracteres
descriptionstringTexto livre que aparece no extrato e no comprovante, até 140 caracteres.até 140 caracteres

* obrigatório.

Exemplo de requisição

curl -X POST 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/account/b043541b-19bc-4c23-a079-b18cbff9354c/transaction' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>' \
  -H 'Idempotency-Key: cbb910f5-6214-48fb-bdba-3d2aa80ffe86' \
  -H 'Content-Type: application/json' \
  -d '{"type": "DEPOSIT", "amount": 200000, "description": "Salário"}'

Respostas

201 Movimentação feita.

200 Repetição do mesmo pedido (mesma chave, mesma conta, mesmo corpo): devolve a resposta original e não move o dinheiro de novo.

STATUS 201Depósito de R$ 2.000,00
{
  "transaction_key": "97d971c5-10dc-4350-bdb1-ff8dad5de744",
  "type": "DEPOSIT",
  "amount": 200000,
  "fee_amount": 0,
  "description": "Salário",
  "balance": 200000,
  "created_at": "2026-10-11T19:43:59Z"
}
STATUS 201Transferência de R$ 200,00 (tarifa de R$ 2,00)
{
  "transaction_key": "42ff2a44-cb70-41d9-9d9e-8c25bc377dd6",
  "type": "TRANSFER",
  "amount": 20000,
  "fee_amount": 200,
  "description": "Aluguel",
  "balance": 179800,
  "created_at": "2026-10-11T19:43:59Z"
}
STATUS 200Reenvio do mesmo depósito (mesma chave)
{
  "transaction_key": "97d971c5-10dc-4350-bdb1-ff8dad5de744",
  "type": "DEPOSIT",
  "amount": 200000,
  "fee_amount": 0,
  "description": "Salário",
  "balance": 200000,
  "created_at": "2026-10-11T19:43:59Z"
}
STATUS 409Mesma chave com outro valor
{
  "title": "Idempotency Key Reused",
  "description": "This Idempotency-Key was already used by another request.",
  "translation": "Esta Idempotency-Key já foi usada em outro pedido.",
  "code": "PPP000004"
}
STATUS 409Saldo insuficiente
{
  "title": "Insufficient Balance",
  "description": "The account does not have enough balance for amount plus fee.",
  "translation": "Saldo insuficiente para o valor mais a tarifa.",
  "code": "PPP003003"
}

Campos da resposta

CampoDescrição
transaction_keyIdentificador da movimentação (UUID).
typeTipo da movimentação: DEPOSIT, WITHDRAWAL, TRANSFER, INVESTMENT_APPLY ou INVESTMENT_REDEEM.
amountValor em centavos, inteiro e maior que zero (R$ 10,50 = 1050).
fee_amountTarifa cobrada, em centavos. R$ 2,00 (200) na transferência; zero nas outras.
descriptionTexto livre que aparece no extrato e no comprovante, até 140 caracteres.
balanceSaldo da conta (só o dinheiro parado nela, sem os investimentos), em centavos. Na resposta de uma movimentação, é o saldo logo depois dela.
created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
400PPP000003Missing Idempotency KeyThis route moves money and requires the Idempotency-Key header with a UUID.Esta operação exige o cabeçalho Idempotency-Key com um UUID.
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
404PPP002001Account Not FoundThe account_key does not exist for this customer.Conta não encontrada.
404PPP003002Destination Account Not FoundThe destination_account_key does not exist.Conta de destino não encontrada.
409PPP000004Idempotency Key ReusedThis Idempotency-Key was already used by another request.Esta Idempotency-Key já foi usada em outro pedido.
409PPP002005Account Not ActiveThe account is blocked or closed and cannot move money.A conta está bloqueada ou encerrada e não pode movimentar dinheiro.
409PPP003003Insufficient BalanceThe account does not have enough balance for amount plus fee.Saldo insuficiente para o valor mais a tarifa.
409PPP003007Destination Account Not ActiveThe destination account is blocked or closed and cannot receive money.A conta de destino está bloqueada ou encerrada e não pode receber dinheiro.
422PPP003004Invalid AmountThe amount must be an integer greater than zero, in cents.O valor precisa ser maior que zero, em centavos.
422PPP003005Amount Above LimitThe amount is above the limit for this type of operation.O valor está acima do limite para este tipo de operação.
422PPP003006Same Account TransferThe destination account must be different from the origin account.A conta de destino precisa ser diferente da conta de origem.

Movimentações e extrato

Extrato

Os lançamentos da conta, do mais novo para o mais antigo, em páginas. Cada linha é um efeito no saldo: débito ou crédito, valor, saldo logo depois e a outra conta envolvida, quando há. Uma transferência aparece como dois lançamentos na origem (valor e tarifa) e um no destino. from e to são datas de movimento (o calendário do banco): cada lançamento fica no dia de movimento em que a transação aconteceu (business_date), mesmo depois de o dia fechar. Conta bloqueada ou encerrada continua podendo consultar.

Endpoint/customer/{customer_key}/account/{account_key}/transactions
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....
account_keyIdentificador da conta (UUID).

Parâmetros de query

CampoTipoDescriçãoValores aceitos
pagestringNúmero da página, a partir de 1 (padrão 1).inteiro a partir de 1
limitstringItens por página, de 1 a 100 (padrão 20).de 1 a 100
typestringFiltra o extrato por tipo de movimentação.DEPOSIT · WITHDRAWAL · TRANSFER · INVESTMENT_APPLY · INVESTMENT_REDEEM
fromstringData inicial do período (AAAA-MM-DD), inclusive, no calendário do banco (data de movimento).data AAAA-MM-DD
tostringData final do período (AAAA-MM-DD), inclusive, no calendário do banco (data de movimento).data AAAA-MM-DD

* obrigatório.

Exemplo de requisição

curl -X GET 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/account/b043541b-19bc-4c23-a079-b18cbff9354c/transactions?limit=5' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Uma página do extrato.

STATUS 200Últimos lançamentos
{
  "items": [
    {
      "transaction_key": "42ff2a44-cb70-41d9-9d9e-8c25bc377dd6",
      "type": "TRANSFER",
      "direction": "DEBIT",
      "category": "FEE",
      "amount": 200,
      "balance_after": 179800,
      "description": "Aluguel",
      "counterparty_account_key": "4ec7839a-1587-4d71-99de-7b2576e03be5",
      "business_date": "2026-11-03",
      "created_at": "2026-10-11T19:43:59Z"
    },
    {
      "transaction_key": "42ff2a44-cb70-41d9-9d9e-8c25bc377dd6",
      "type": "TRANSFER",
      "direction": "DEBIT",
      "category": "PRINCIPAL",
      "amount": 20000,
      "balance_after": 180000,
      "description": "Aluguel",
      "counterparty_account_key": "4ec7839a-1587-4d71-99de-7b2576e03be5",
      "business_date": "2026-11-03",
      "created_at": "2026-10-11T19:43:59Z"
    },
    {
      "transaction_key": "97d971c5-10dc-4350-bdb1-ff8dad5de744",
      "type": "DEPOSIT",
      "direction": "CREDIT",
      "category": "PRINCIPAL",
      "amount": 200000,
      "balance_after": 200000,
      "description": "Salário",
      "counterparty_account_key": null,
      "business_date": "2026-11-03",
      "created_at": "2026-10-11T19:43:59Z"
    }
  ],
  "page": 1,
  "limit": 5,
  "total": 3
}

Campos da resposta

CampoDescrição
itemsA lista de resultados.
items[].transaction_keyIdentificador da movimentação (UUID).
items[].typeTipo da movimentação: DEPOSIT, WITHDRAWAL, TRANSFER, INVESTMENT_APPLY ou INVESTMENT_REDEEM.
items[].directionDEBIT (saiu da conta) ou CREDIT (entrou).
items[].categoryPRINCIPAL (o valor da operação) ou FEE (a tarifa).
items[].amountValor em centavos, inteiro e maior que zero (R$ 10,50 = 1050).
items[].balance_afterSaldo da conta logo depois deste lançamento, em centavos.
items[].descriptionTexto livre que aparece no extrato e no comprovante, até 140 caracteres.
items[].counterparty_account_keyA outra conta da movimentação (na transferência). Nulo quando o dinheiro veio ou foi para fora da conta.
items[].business_dateData de movimento: o "hoje" do banco, a data real simulada. Num lançamento ou comprovante, o dia de movimento em que a transação aconteceu.
items[].created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).
pageNúmero da página, a partir de 1 (padrão 1).
limitItens por página, de 1 a 100 (padrão 20).
totalTotal de itens que passam pelos filtros, somando todas as páginas.

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
400PPP000005Invalid Parameter(varia: diz o que está errado)Parâmetros inválidos foram fornecidos na requisição.
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
404PPP002001Account Not FoundThe account_key does not exist for this customer.Conta não encontrada.

Movimentações e extrato

Comprovante

O comprovante de uma movimentação, para a conta de origem ou de destino. Não mostra saldo: o destino não pode ver o saldo da origem. Mostra o dia de movimento da transação (business_date). Transação que não envolve a conta responde 404.

Endpoint/customer/{customer_key}/account/{account_key}/transaction/{transaction_key}
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....
account_keyIdentificador da conta (UUID).
transaction_keyIdentificador da movimentação (UUID).

Exemplo de requisição

curl -X GET 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/account/b043541b-19bc-4c23-a079-b18cbff9354c/transaction/42ff2a44-cb70-41d9-9d9e-8c25bc377dd6' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Comprovante.

STATUS 200Comprovante da transferência
{
  "transaction_key": "42ff2a44-cb70-41d9-9d9e-8c25bc377dd6",
  "type": "TRANSFER",
  "amount": 20000,
  "fee_amount": 200,
  "description": "Aluguel",
  "origin_account_key": "b043541b-19bc-4c23-a079-b18cbff9354c",
  "destination_account_key": "4ec7839a-1587-4d71-99de-7b2576e03be5",
  "business_date": "2026-11-03",
  "created_at": "2026-10-11T19:43:59Z"
}

Campos da resposta

CampoDescrição
transaction_keyIdentificador da movimentação (UUID).
typeTipo da movimentação: DEPOSIT, WITHDRAWAL, TRANSFER, INVESTMENT_APPLY ou INVESTMENT_REDEEM.
amountValor em centavos, inteiro e maior que zero (R$ 10,50 = 1050).
fee_amountTarifa cobrada, em centavos. R$ 2,00 (200) na transferência; zero nas outras.
descriptionTexto livre que aparece no extrato e no comprovante, até 140 caracteres.
origin_account_keyConta de onde o dinheiro saiu. Nulo no depósito e no resgate (o dinheiro vem de fora da conta).
destination_account_keyConta que recebe a transferência. Obrigatório só no TRANSFER; pode ser de outro cliente, mas não a própria conta.
business_dateData de movimento: o "hoje" do banco, a data real simulada. Num lançamento ou comprovante, o dia de movimento em que a transação aconteceu.
created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
404PPP002001Account Not FoundThe account_key does not exist for this customer.Conta não encontrada.
404PPP003001Transaction Not FoundThe transaction_key does not exist or does not involve this account.Transação não encontrada.

Perfil do investidor

Perguntas do questionário

As 7 perguntas do questionário de perfil (suitability), na ordem (Q1 a Q7), cada uma com 4 opções. O código da opção (1 a 4) é também a pontuação dela.

Endpoint/suitability_questions
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Exemplo de requisição

curl -X GET 'http://localhost:3000/suitability_questions' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Perguntas e opções.

STATUS 200Questionário
{
  "items": [
    {
      "code": "Q1",
      "text": "Por quanto tempo pretende deixar o dinheiro investido?",
      "dimension": "objetivo",
      "options": [
        {
          "code": 1,
          "text": "Menos de 1 ano"
        },
        {
          "code": 2,
          "text": "1 a 3 anos"
        },
        {
          "code": 3,
          "text": "3 a 5 anos"
        },
        {
          "code": 4,
          "text": "Mais de 5 anos"
        }
      ]
    },
    {
      "code": "Q2",
      "text": "Qual o principal objetivo?",
      "dimension": "objetivo",
      "options": [
        {
          "code": 1,
          "text": "Preservar o dinheiro"
        },
        {
          "code": 2,
          "text": "Reserva com algum ganho"
        },
        {
          "code": 3,
          "text": "Crescer o patrimônio"
        },
        {
          "code": 4,
          "text": "Maximizar o retorno aceitando oscilações"
        }
      ]
    },
    {
      "code": "Q3",
      "text": "Se seus investimentos caíssem 10% em um mês, você...",
      "dimension": "tolerância",
      "options": [
        {
          "code": 1,
          "text": "Resgataria tudo"
        },
        {
          "code": 2,
          "text": "Resgataria uma parte"
        },
        {
          "code": 3,
          "text": "Manteria"
        },
        {
          "code": 4,
          "text": "Aplicaria mais"
        }
      ]
    },
    {
      "code": "Q4",
      "text": "Quanto da sua renda mensal sobra para investir?",
      "dimension": "situação financeira",
      "options": [
        {
          "code": 1,
          "text": "Quase nada"
        },
        {
          "code": 2,
          "text": "Até 10%"
        },
        {
          "code": 3,
          "text": "10% a 30%"
        },
        {
          "code": 4,
          "text": "Mais de 30%"
        }
      ]
    },
    {
      "code": "Q5",
      "text": "Você pode precisar deste dinheiro de repente?",
      "dimension": "liquidez",
      "options": [
        {
          "code": 1,
          "text": "Sim, a qualquer momento"
        },
        {
          "code": 2,
          "text": "Provavelmente em até 1 ano"
        },
        {
          "code": 3,
          "text": "Dificilmente"
        },
        {
          "code": 4,
          "text": "Não"
        }
      ]
    },
    {
      "code": "Q6",
      "text": "Em quais produtos você já investiu?",
      "dimension": "experiência",
      "options": [
        {
          "code": 1,
          "text": "Nenhum ou só poupança"
        },
        {
          "code": 2,
          "text": "Tesouro/CDB"
        },
        {
          "code": 3,
          "text": "Fundos multimercado ou de ações"
        },
        {
          "code": 4,
          "text": "Ações, derivativos ou cripto"
        }
      ]
    },
    {
      "code": "Q7",
      "text": "Como você descreve seu conhecimento financeiro?",
      "dimension": "conhecimento",
      "options": [
        {
          "code": 1,
          "text": "Nenhum"
        },
        {
          "code": 2,
          "text": "Básico"
        },
        {
          "code": 3,
          "text": "Intermediário (entendo risco e retorno)"
        },
        {
          "code": 4,
          "text": "Avançado (formação ou atuação na área)"
        }
      ]
    }
  ]
}

Campos da resposta

CampoDescrição
itemsA lista de resultados.
items[].codeCódigo. Na pergunta: Q1 a Q7. Na opção: 1 a 4, que é também a pontuação. Na taxa: CDI ou SELIC.
items[].textTexto da pergunta ou da opção.
items[].dimensionO que a pergunta avalia (objetivo, tolerância, situação financeira, liquidez, experiência, conhecimento).
items[].optionsAs 4 opções da pergunta.
items[].options[].codeCódigo. Na pergunta: Q1 a Q7. Na opção: 1 a 4, que é também a pontuação. Na taxa: CDI ou SELIC.
items[].options[].textTexto da pergunta ou da opção.

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna

Perfil do investidor

Responder o questionário

Recebe uma resposta para cada uma das 7 perguntas e devolve o perfil: Conservador (7 a 13 pontos), Moderado (14 a 20) ou Arrojado (21 a 28). Quem responde a opção 1 na Q3 ou na Q5 é Conservador qualquer que seja a soma (forced_by_rule = true quando isso muda o resultado). O perfil vale 24 meses; responder de novo cria uma avaliação nova e guarda a anterior.

Endpoint/customer/{customer_key}/suitability
MétodoPOST

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).
Content-Typesimapplication/json

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....

Corpo da requisição

CampoTipoDescriçãoValores aceitos
answers*array de objetosUma resposta para cada uma das 7 perguntas.
answers[].question_code*stringCódigo da pergunta (Q1 a Q7).
answers[].option_code*integerCódigo da opção escolhida (1 a 4).

* obrigatório.

Exemplo de requisição

curl -X POST 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/suitability' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"answers": [{"question_code": "Q1", "option_code": 2}, {"question_code": "Q2", "option_code": 2}, {"question_code": "Q3", "option_code": 2}, {"question_code": "Q4", "option_code": 2}, {"question_code": "Q5", "option_code": 2}, {"question_code": "Q6", "option_code": 2}, {"question_code": "Q7", "option_code": 2}]}'

Respostas

201 Avaliação criada.

STATUS 201Perfil Moderado (14 pontos)
{
  "suitability_assessment_key": "845f79aa-afd2-4dcf-8336-2f3bc900876a",
  "profile": "MODERATE",
  "maximum_risk_level": 3,
  "score": 14,
  "forced_by_rule": false,
  "valid_until": "2028-10-11T19:43:59Z",
  "created_at": "2026-10-11T19:43:59Z",
  "answers": [
    {
      "question_code": "Q1",
      "option_code": 2
    },
    {
      "question_code": "Q2",
      "option_code": 2
    },
    {
      "question_code": "Q3",
      "option_code": 2
    },
    {
      "question_code": "Q4",
      "option_code": 2
    },
    {
      "question_code": "Q5",
      "option_code": 2
    },
    {
      "question_code": "Q6",
      "option_code": 2
    },
    {
      "question_code": "Q7",
      "option_code": 2
    }
  ]
}
STATUS 422Resposta faltando
{
  "title": "Incomplete Answers",
  "description": "Every question must be answered exactly once, and only existing questions can be answered.",
  "translation": "Cada pergunta do questionário precisa de exatamente uma resposta.",
  "code": "PPP004002"
}

Campos da resposta

CampoDescrição
suitability_assessment_keyIdentificador da avaliação de perfil (UUID).
profilePerfil do investidor: CONSERVATIVE, MODERATE ou AGGRESSIVE.
maximum_risk_levelRisco máximo de produto. No perfil: até que risco ele aceita (Conservador 2, Moderado 3, Arrojado 5). No filtro do catálogo: só produtos com risco até este (1 a 5).
scorePontuação do questionário (soma dos códigos das opções, de 7 a 28).
forced_by_ruletrue quando a trava da Q3/Q5 mudou o perfil para Conservador; false quando os pontos já davam esse perfil.
valid_untilAté quando o perfil vale (24 meses depois da resposta), em UTC.
created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).
answersUma resposta para cada uma das 7 perguntas.
answers[].question_codeCódigo da pergunta (Q1 a Q7).
answers[].option_codeCódigo da opção escolhida (1 a 4).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
422PPP004002Incomplete AnswersEvery question must be answered exactly once, and only existing questions can be answered.Cada pergunta do questionário precisa de exatamente uma resposta.
422PPP004003Invalid OptionThe option_code does not belong to the question.A opção escolhida não pertence a esta pergunta.

Perfil do investidor

Perfil atual

A avaliação mais recente do cliente, com as respostas que levaram ao perfil.

Endpoint/customer/{customer_key}/suitability
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....

Exemplo de requisição

curl -X GET 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/suitability' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Avaliação vigente.

STATUS 200Perfil vigente
{
  "suitability_assessment_key": "845f79aa-afd2-4dcf-8336-2f3bc900876a",
  "profile": "MODERATE",
  "maximum_risk_level": 3,
  "score": 14,
  "forced_by_rule": false,
  "valid_until": "2028-10-11T19:43:59Z",
  "created_at": "2026-10-11T19:43:59Z",
  "answers": [
    {
      "question_code": "Q1",
      "option_code": 2
    },
    {
      "question_code": "Q2",
      "option_code": 2
    },
    {
      "question_code": "Q3",
      "option_code": 2
    },
    {
      "question_code": "Q4",
      "option_code": 2
    },
    {
      "question_code": "Q5",
      "option_code": 2
    },
    {
      "question_code": "Q6",
      "option_code": 2
    },
    {
      "question_code": "Q7",
      "option_code": 2
    }
  ]
}

Campos da resposta

CampoDescrição
suitability_assessment_keyIdentificador da avaliação de perfil (UUID).
profilePerfil do investidor: CONSERVATIVE, MODERATE ou AGGRESSIVE.
maximum_risk_levelRisco máximo de produto. No perfil: até que risco ele aceita (Conservador 2, Moderado 3, Arrojado 5). No filtro do catálogo: só produtos com risco até este (1 a 5).
scorePontuação do questionário (soma dos códigos das opções, de 7 a 28).
forced_by_ruletrue quando a trava da Q3/Q5 mudou o perfil para Conservador; false quando os pontos já davam esse perfil.
valid_untilAté quando o perfil vale (24 meses depois da resposta), em UTC.
created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).
answersUma resposta para cada uma das 7 perguntas.
answers[].question_codeCódigo da pergunta (Q1 a Q7).
answers[].option_codeCódigo da opção escolhida (1 a 4).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
404PPP004001Suitability Not FoundThe customer has never answered the suitability questionnaire.O cliente ainda não respondeu o questionário de perfil de investidor.

Perfil do investidor

Histórico de perfis

Todas as avaliações do cliente, da mais nova para a mais antiga.

Endpoint/customer/{customer_key}/suitabilities
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....

Exemplo de requisição

curl -X GET 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/suitabilities' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Avaliações.

STATUS 200Avaliações
{
  "items": [
    {
      "suitability_assessment_key": "845f79aa-afd2-4dcf-8336-2f3bc900876a",
      "profile": "MODERATE",
      "maximum_risk_level": 3,
      "score": 14,
      "forced_by_rule": false,
      "valid_until": "2028-10-11T19:43:59Z",
      "created_at": "2026-10-11T19:43:59Z",
      "answers": [
        {
          "question_code": "Q1",
          "option_code": 2
        },
        {
          "question_code": "Q2",
          "option_code": 2
        },
        {
          "question_code": "Q3",
          "option_code": 2
        },
        {
          "question_code": "Q4",
          "option_code": 2
        },
        {
          "question_code": "Q5",
          "option_code": 2
        },
        {
          "question_code": "Q6",
          "option_code": 2
        },
        {
          "question_code": "Q7",
          "option_code": 2
        }
      ]
    }
  ]
}

Campos da resposta

CampoDescrição
itemsA lista de resultados.
items[].suitability_assessment_keyIdentificador da avaliação de perfil (UUID).
items[].profilePerfil do investidor: CONSERVATIVE, MODERATE ou AGGRESSIVE.
items[].maximum_risk_levelRisco máximo de produto. No perfil: até que risco ele aceita (Conservador 2, Moderado 3, Arrojado 5). No filtro do catálogo: só produtos com risco até este (1 a 5).
items[].scorePontuação do questionário (soma dos códigos das opções, de 7 a 28).
items[].forced_by_ruletrue quando a trava da Q3/Q5 mudou o perfil para Conservador; false quando os pontos já davam esse perfil.
items[].valid_untilAté quando o perfil vale (24 meses depois da resposta), em UTC.
items[].created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).
items[].answersUma resposta para cada uma das 7 perguntas.
items[].answers[].question_codeCódigo da pergunta (Q1 a Q7).
items[].answers[].option_codeCódigo da opção escolhida (1 a 4).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.

Produtos e preços

Catálogo de produtos

Os produtos de investimento: títulos do Tesouro, CDB e LCI do banco e o fundo FGV Quant Capital. Dá para filtrar por tipo e por risco máximo, por exemplo para mostrar só o que cabe no perfil do cliente.

Endpoint/products
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de query

CampoTipoDescriçãoValores aceitos
typestringFiltra o catálogo por tipo de produto.PUBLIC_BOND · PRIVATE_BOND · FUND
maximum_risk_levelstringRisco máximo de produto. No perfil: até que risco ele aceita (Conservador 2, Moderado 3, Arrojado 5). No filtro do catálogo: só produtos com risco até este (1 a 5).de 1 a 5

* obrigatório.

Exemplo de requisição

curl -X GET 'http://localhost:3000/products' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Produtos.

STATUS 200Catálogo completo
{
  "items": [
    {
      "product_key": "681c7fed-81ea-483f-8a5f-caef127c411c",
      "name": "Tesouro Selic 2031",
      "type": "PUBLIC_BOND",
      "issuer": "Tesouro Nacional",
      "indexer": "SELIC",
      "rate_description": "100% da Selic",
      "risk_level": 1,
      "term_months": null,
      "minimum_application_amount": 5000
    },
    {
      "product_key": "6bcbbf83-3789-4d74-8f99-f944b569d36e",
      "name": "CDB Triple P Bank Liquidez",
      "type": "PRIVATE_BOND",
      "issuer": "Triple P Bank",
      "indexer": "CDI",
      "rate_description": "95% do CDI",
      "risk_level": 2,
      "term_months": null,
      "minimum_application_amount": 5000
    },
    {
      "product_key": "cf85fbe3-a0b0-4e7b-b534-a8a602641c41",
      "name": "LCI Triple P Bank 12 meses",
      "type": "PRIVATE_BOND",
      "issuer": "Triple P Bank",
      "indexer": "CDI",
      "rate_description": "93% do CDI, isento de IR",
      "risk_level": 2,
      "term_months": 12,
      "minimum_application_amount": 10000
    },
    {
      "product_key": "3e60dd05-c365-467b-90fa-dfebf7db22ef",
      "name": "Tesouro Prefixado 2029",
      "type": "PUBLIC_BOND",
      "issuer": "Tesouro Nacional",
      "indexer": "PREFIXED",
      "rate_description": "13% a.a.",
      "risk_level": 2,
      "term_months": null,
      "minimum_application_amount": 5000
    },
    {
      "product_key": "eff42ff3-f013-440d-9ea2-760ecc9e3fa6",
      "name": "Tesouro IPCA+ 2035",
      "type": "PUBLIC_BOND",
      "issuer": "Tesouro Nacional",
      "indexer": "IPCA",
      "rate_description": "IPCA + 6% a.a.",
      "risk_level": 3,
      "term_months": null,
      "minimum_application_amount": 5000
    },
    {
      "product_key": "cdf625c2-ce65-4211-890b-712c49cb8c8d",
      "name": "FGV Quant Capital FIM",
      "type": "FUND",
      "issuer": "FGV Quant Capital",
      "indexer": "QUOTA",
      "rate_description": "Varia com a cota",
      "risk_level": 3,
      "term_months": null,
      "minimum_application_amount": 10000
    }
  ]
}
STATUS 200Só risco até 2
{
  "items": [
    {
      "product_key": "681c7fed-81ea-483f-8a5f-caef127c411c",
      "name": "Tesouro Selic 2031",
      "type": "PUBLIC_BOND",
      "issuer": "Tesouro Nacional",
      "indexer": "SELIC",
      "rate_description": "100% da Selic",
      "risk_level": 1,
      "term_months": null,
      "minimum_application_amount": 5000
    },
    {
      "product_key": "6bcbbf83-3789-4d74-8f99-f944b569d36e",
      "name": "CDB Triple P Bank Liquidez",
      "type": "PRIVATE_BOND",
      "issuer": "Triple P Bank",
      "indexer": "CDI",
      "rate_description": "95% do CDI",
      "risk_level": 2,
      "term_months": null,
      "minimum_application_amount": 5000
    },
    {
      "product_key": "cf85fbe3-a0b0-4e7b-b534-a8a602641c41",
      "name": "LCI Triple P Bank 12 meses",
      "type": "PRIVATE_BOND",
      "issuer": "Triple P Bank",
      "indexer": "CDI",
      "rate_description": "93% do CDI, isento de IR",
      "risk_level": 2,
      "term_months": 12,
      "minimum_application_amount": 10000
    },
    {
      "product_key": "3e60dd05-c365-467b-90fa-dfebf7db22ef",
      "name": "Tesouro Prefixado 2029",
      "type": "PUBLIC_BOND",
      "issuer": "Tesouro Nacional",
      "indexer": "PREFIXED",
      "rate_description": "13% a.a.",
      "risk_level": 2,
      "term_months": null,
      "minimum_application_amount": 5000
    }
  ]
}

Campos da resposta

CampoDescrição
itemsA lista de resultados.
items[].product_keyIdentificador do produto (UUID).
items[].nameNome. No cadastro: nome completo do cliente, de 3 a 120 caracteres, sem espaço nas pontas.
items[].typeTipo da movimentação: DEPOSIT, WITHDRAWAL, TRANSFER, INVESTMENT_APPLY ou INVESTMENT_REDEEM.
items[].issuerQuem emitiu o produto.
items[].indexerIndexador: SELIC, CDI, PREFIXED, IPCA ou QUOTA (fundo).
items[].rate_descriptionRentabilidade em texto, para mostrar ao cliente ("95% do CDI").
items[].risk_levelRisco do produto, de 1 (menor) a 5.
items[].term_monthsPrazo de cada aplicação, em meses (a LCI: 12). Nulo: resgate a qualquer dia. Com prazo, só resgata no vencimento.
items[].minimum_application_amountAplicação mínima, em centavos.

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna

Produtos e preços

Consultar produto

O produto e o preço do dia (o último publicado até a data de movimento). O preço sai como texto com 8 casas decimais.

Endpoint/product/{product_key}
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
product_keyIdentificador do produto (UUID).

Exemplo de requisição

curl -X GET 'http://localhost:3000/product/cdf625c2-ce65-4211-890b-712c49cb8c8d' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Produto com o preço do dia.

STATUS 200Fundo com o preço do dia
{
  "product_key": "cdf625c2-ce65-4211-890b-712c49cb8c8d",
  "name": "FGV Quant Capital FIM",
  "type": "FUND",
  "issuer": "FGV Quant Capital",
  "indexer": "QUOTA",
  "rate_description": "Varia com a cota",
  "risk_level": 3,
  "term_months": null,
  "minimum_application_amount": 10000,
  "price": {
    "price_date": "2026-11-03",
    "unit_price": "102.00000000"
  }
}
STATUS 404Produto inexistente
{
  "title": "Product Not Found",
  "description": "The product_key does not exist.",
  "translation": "Produto não encontrado.",
  "code": "PPP005001"
}

Campos da resposta

CampoDescrição
product_keyIdentificador do produto (UUID).
nameNome. No cadastro: nome completo do cliente, de 3 a 120 caracteres, sem espaço nas pontas.
typeTipo da movimentação: DEPOSIT, WITHDRAWAL, TRANSFER, INVESTMENT_APPLY ou INVESTMENT_REDEEM.
issuerQuem emitiu o produto.
indexerIndexador: SELIC, CDI, PREFIXED, IPCA ou QUOTA (fundo).
rate_descriptionRentabilidade em texto, para mostrar ao cliente ("95% do CDI").
risk_levelRisco do produto, de 1 (menor) a 5.
term_monthsPrazo de cada aplicação, em meses (a LCI: 12). Nulo: resgate a qualquer dia. Com prazo, só resgata no vencimento.
minimum_application_amountAplicação mínima, em centavos.
priceO preço do dia do produto (o último publicado até a data de movimento).
price.price_dateData de movimento a que o preço se refere.
price.unit_pricePreço de uma cota, como texto com 8 casas decimais ("1.25000000"), para não ser lido como float.

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP005001Product Not FoundThe product_key does not exist.Produto não encontrado.

Produtos e preços

Histórico de preços

Os preços publicados do produto, um por data de movimento, do mais antigo para o mais novo.

Endpoint/product/{product_key}/prices
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
product_keyIdentificador do produto (UUID).

Parâmetros de query

CampoTipoDescriçãoValores aceitos
fromstringData inicial do período (AAAA-MM-DD), inclusive, no calendário do banco (data de movimento).data AAAA-MM-DD
tostringData final do período (AAAA-MM-DD), inclusive, no calendário do banco (data de movimento).data AAAA-MM-DD

* obrigatório.

Exemplo de requisição

curl -X GET 'http://localhost:3000/product/cdf625c2-ce65-4211-890b-712c49cb8c8d/prices' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Preços.

STATUS 200Preços do fundo
{
  "items": [
    {
      "price_date": "2026-10-11",
      "unit_price": "105.33466498"
    },
    {
      "price_date": "2026-10-12",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-13",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-14",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-15",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-16",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-19",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-20",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-21",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-22",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-23",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-26",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-27",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-28",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-29",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-10-30",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-11-02",
      "unit_price": "102.00000000"
    },
    {
      "price_date": "2026-11-03",
      "unit_price": "102.00000000"
    }
  ]
}

Campos da resposta

CampoDescrição
itemsA lista de resultados.
items[].price_dateData de movimento a que o preço se refere.
items[].unit_pricePreço de uma cota, como texto com 8 casas decimais ("1.25000000"), para não ser lido como float.

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP005001Product Not FoundThe product_key does not exist.Produto não encontrado.

Carteira sugerida

Sugestão de carteira

Divide um valor entre os produtos da carteira-modelo do perfil do cliente, com o peso de cada um. É só simulação: não olha saldo e não grava nada. Os valores somam exatamente o pedido; os centavos que sobram do arredondamento vão para o produto de maior peso.

Endpoint/customer/{customer_key}/portfolio_suggestion
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....

Parâmetros de query

CampoTipoDescriçãoValores aceitos
amount*stringValor a dividir pela carteira, em centavos. Mínimo R$ 1.000,00 (100000).inteiro, só dígitos

* obrigatório.

Exemplo de requisição

curl -X GET 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/portfolio_suggestion?amount=100000' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Sugestão.

STATUS 200R$ 1.000,00 na carteira Moderada
{
  "profile": "MODERATE",
  "amount": 100000,
  "items": [
    {
      "product_key": "681c7fed-81ea-483f-8a5f-caef127c411c",
      "name": "Tesouro Selic 2031",
      "weight_bps": 2500,
      "amount": 25000
    },
    {
      "product_key": "6bcbbf83-3789-4d74-8f99-f944b569d36e",
      "name": "CDB Triple P Bank Liquidez",
      "weight_bps": 2000,
      "amount": 20000
    },
    {
      "product_key": "3e60dd05-c365-467b-90fa-dfebf7db22ef",
      "name": "Tesouro Prefixado 2029",
      "weight_bps": 2000,
      "amount": 20000
    },
    {
      "product_key": "eff42ff3-f013-440d-9ea2-760ecc9e3fa6",
      "name": "Tesouro IPCA+ 2035",
      "weight_bps": 2000,
      "amount": 20000
    },
    {
      "product_key": "cdf625c2-ce65-4211-890b-712c49cb8c8d",
      "name": "FGV Quant Capital FIM",
      "weight_bps": 1500,
      "amount": 15000
    }
  ]
}
STATUS 422Valor abaixo do mínimo
{
  "title": "Suggestion Amount Too Low",
  "description": "The amount for a portfolio suggestion must be at least 100000 cents (R$ 1,000.00).",
  "translation": "O valor mínimo para pedir uma sugestão de carteira é R$ 1.000,00.",
  "code": "PPP006001"
}

Campos da resposta

CampoDescrição
profilePerfil do investidor: CONSERVATIVE, MODERATE ou AGGRESSIVE.
amountValor em centavos, inteiro e maior que zero (R$ 10,50 = 1050).
itemsA lista de resultados.
items[].product_keyIdentificador do produto (UUID).
items[].nameNome. No cadastro: nome completo do cliente, de 3 a 120 caracteres, sem espaço nas pontas.
items[].weight_bpsPeso do produto na carteira, em pontos-base (10000 = 100%).
items[].amountValor em centavos, inteiro e maior que zero (R$ 10,50 = 1050).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
409PPP004004Suitability ExpiredThe investor profile is more than 24 months old and must be answered again.O perfil de investidor tem mais de 24 meses e precisa ser refeito.
409PPP004005Suitability RequiredThe customer needs an investor profile before this operation.O cliente precisa responder o questionário de perfil de investidor antes desta operação.
422PPP006001Suggestion Amount Too LowThe amount for a portfolio suggestion must be at least 100000 cents (R$ 1,000.00).O valor mínimo para pedir uma sugestão de carteira é R$ 1.000,00.

Ordens e posição

Aplicar ou resgatar

A ordem é feita na conta do caminho, como as movimentações; conta de outro cliente responde 404. Aplicação (APPLY): debita a conta e compra cotas ao preço do dia (cotas = valor ÷ preço, truncadas em 8 casas). Exige perfil válido e produto dentro do risco do perfil. Resgate (REDEEM): vende cotas ao preço do dia e credita a conta; pode ser por valor ou total (redeem_all), e não depende do perfil. Produto com prazo (a LCI) só é resgatado no vencimento da aplicação. A resposta do resgate traz o custo das cotas vendidas, o resultado e a rentabilidade.

Endpoint/customer/{customer_key}/account/{account_key}/investment_order
MétodoPOST

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).
Idempotency-KeysimUUID novo por pedido; reenviar com a mesma chave não repete o efeito. Ver Idempotência.
Content-Typesimapplication/json

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....
account_keyIdentificador da conta (UUID).

Corpo da requisição

CampoTipoDescriçãoValores aceitos
product_key*stringIdentificador do produto (UUID).até 36 caracteres
side*stringAPPLY (aplicação) ou REDEEM (resgate).APPLY · REDEEM
amountintegerValor em centavos. Obrigatório na aplicação; no resgate, use amount (resgate por valor) ou redeem_all (resgate total).
redeem_allbooleanResgate total: vende todas as cotas. Use true no lugar de amount.só true

* obrigatório.

Exemplo de requisição

curl -X POST 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/account/b043541b-19bc-4c23-a079-b18cbff9354c/investment_order' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>' \
  -H 'Idempotency-Key: ecad6b89-2c4a-4ce2-9762-d8728aeaacae' \
  -H 'Content-Type: application/json' \
  -d '{"product_key": "cdf625c2-ce65-4211-890b-712c49cb8c8d", "side": "APPLY", "amount": 100000}'

Respostas

201 Ordem executada.

200 Repetição do mesmo pedido: devolve a ordem original sem aplicar ou resgatar de novo.

STATUS 201Aplicação de R$ 1.000,00 no fundo
{
  "investment_order_key": "a2b5b62b-fd40-421a-bf37-7c3b3808895a",
  "product_key": "cdf625c2-ce65-4211-890b-712c49cb8c8d",
  "side": "APPLY",
  "source": "CUSTOMER",
  "amount": 100000,
  "unit_price": "102.00000000",
  "quantity": "9.80392156",
  "maturity_date": null,
  "status": "EXECUTED",
  "transaction_key": "5beee41e-48dd-400a-afb7-21a32b5d870f"
}
STATUS 201Aplicação de R$ 100,00 na LCI (com vencimento)
{
  "investment_order_key": "be2b11b2-d793-4f30-84a4-caa70db8271c",
  "product_key": "cf85fbe3-a0b0-4e7b-b534-a8a602641c41",
  "side": "APPLY",
  "source": "CUSTOMER",
  "amount": 10000,
  "unit_price": "111.82266423",
  "quantity": "0.89427309",
  "maturity_date": "2027-11-03",
  "status": "EXECUTED",
  "transaction_key": "0346f1fb-72e2-43fb-8b52-334658341ac8"
}
STATUS 201Resgate de R$ 300,00 do fundo
{
  "investment_order_key": "880df567-a99c-4f5f-9b55-244c0aed464c",
  "product_key": "cdf625c2-ce65-4211-890b-712c49cb8c8d",
  "side": "REDEEM",
  "source": "CUSTOMER",
  "amount": 30000,
  "unit_price": "102.00000000",
  "quantity": "2.94117647",
  "maturity_date": null,
  "status": "EXECUTED",
  "transaction_key": "4bd7a432-2b2c-49d0-a497-f0cd5b6755ec",
  "acquisition_cost_amount": 30000,
  "result_amount": 0,
  "return_bps": 0
}
STATUS 422Abaixo da aplicação mínima
{
  "title": "Below Minimum Application",
  "description": "The amount is below the product's minimum application.",
  "translation": "O valor está abaixo da aplicação mínima do produto.",
  "code": "PPP007002"
}
STATUS 422Resgate da LCI antes do vencimento
{
  "title": "Redemption Not Allowed",
  "description": "This product can only be redeemed at maturity.",
  "translation": "Este produto só pode ser resgatado no vencimento.",
  "code": "PPP007005"
}

Campos da resposta

CampoDescrição
investment_order_keyIdentificador da ordem de investimento (UUID).
product_keyIdentificador do produto (UUID).
sideAPPLY (aplicação) ou REDEEM (resgate).
sourceQuem pediu a ordem: CUSTOMER ou MATURITY (o fechamento do dia, pagando uma aplicação vencida).
amountValor em centavos, inteiro e maior que zero (R$ 10,50 = 1050).
unit_pricePreço de uma cota, como texto com 8 casas decimais ("1.25000000"), para não ser lido como float.
quantityQuantidade de cotas, como texto com 8 casas decimais.
maturity_dateVencimento desta aplicação (só em produto com prazo). Nulo nos outros casos.
statusStatus da conta: ACTIVE (movimenta), BLOCKED (não movimenta, só consulta) ou CLOSED (encerrada).
transaction_keyIdentificador da movimentação (UUID).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
400PPP000003Missing Idempotency KeyThis route moves money and requires the Idempotency-Key header with a UUID.Esta operação exige o cabeçalho Idempotency-Key com um UUID.
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
404PPP002001Account Not FoundThe account_key does not exist for this customer.Conta não encontrada.
404PPP005001Product Not FoundThe product_key does not exist.Produto não encontrado.
409PPP000004Idempotency Key ReusedThis Idempotency-Key was already used by another request.Esta Idempotency-Key já foi usada em outro pedido.
409PPP002005Account Not ActiveThe account is blocked or closed and cannot move money.A conta está bloqueada ou encerrada e não pode movimentar dinheiro.
409PPP003003Insufficient BalanceThe account does not have enough balance for amount plus fee.Saldo insuficiente para o valor mais a tarifa.
409PPP004004Suitability ExpiredThe investor profile is more than 24 months old and must be answered again.O perfil de investidor tem mais de 24 meses e precisa ser refeito.
409PPP004005Suitability RequiredThe customer needs an investor profile before this operation.O cliente precisa responder o questionário de perfil de investidor antes desta operação.
409PPP007004Insufficient PositionThe redemption is larger than the customer's position in this product.O resgate é maior do que o que você tem aplicado neste produto.
422PPP003004Invalid AmountThe amount must be an integer greater than zero, in cents.O valor precisa ser maior que zero, em centavos.
422PPP007002Below Minimum ApplicationThe amount is below the product's minimum application.O valor está abaixo da aplicação mínima do produto.
422PPP007003Product Not SuitableThe product's risk level is above what the investor profile allows.O risco do produto está acima do permitido para o seu perfil de investidor.
422PPP007005Redemption Not AllowedThis product can only be redeemed at maturity.Este produto só pode ser resgatado no vencimento.

Ordens e posição

Consultar ordem

A ordem, com o mesmo corpo da resposta de criação. Ordem de outra conta ou de outro cliente responde 404.

Endpoint/customer/{customer_key}/account/{account_key}/investment_order/{investment_order_key}
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....
account_keyIdentificador da conta (UUID).
investment_order_keyIdentificador da ordem de investimento (UUID).

Exemplo de requisição

curl -X GET 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/account/b043541b-19bc-4c23-a079-b18cbff9354c/investment_order/a2b5b62b-fd40-421a-bf37-7c3b3808895a' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Ordem.

STATUS 200Ordem de aplicação
{
  "investment_order_key": "a2b5b62b-fd40-421a-bf37-7c3b3808895a",
  "product_key": "cdf625c2-ce65-4211-890b-712c49cb8c8d",
  "side": "APPLY",
  "source": "CUSTOMER",
  "amount": 100000,
  "unit_price": "102.00000000",
  "quantity": "9.80392156",
  "maturity_date": null,
  "status": "EXECUTED",
  "transaction_key": "5beee41e-48dd-400a-afb7-21a32b5d870f"
}

Campos da resposta

CampoDescrição
investment_order_keyIdentificador da ordem de investimento (UUID).
product_keyIdentificador do produto (UUID).
sideAPPLY (aplicação) ou REDEEM (resgate).
sourceQuem pediu a ordem: CUSTOMER ou MATURITY (o fechamento do dia, pagando uma aplicação vencida).
amountValor em centavos, inteiro e maior que zero (R$ 10,50 = 1050).
unit_pricePreço de uma cota, como texto com 8 casas decimais ("1.25000000"), para não ser lido como float.
quantityQuantidade de cotas, como texto com 8 casas decimais.
maturity_dateVencimento desta aplicação (só em produto com prazo). Nulo nos outros casos.
statusStatus da conta: ACTIVE (movimenta), BLOCKED (não movimenta, só consulta) ou CLOSED (encerrada).
transaction_keyIdentificador da movimentação (UUID).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
404PPP002001Account Not FoundThe account_key does not exist for this customer.Conta não encontrada.
404PPP007001Order Not FoundThe investment_order_key does not exist for this customer.Ordem não encontrada.

Ordens e posição

Histórico de ordens

As ordens da conta, da mais nova para a mais antiga, em páginas, com filtro opcional por produto (produto inexistente devolve lista vazia).

Endpoint/customer/{customer_key}/account/{account_key}/investment_orders
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....
account_keyIdentificador da conta (UUID).

Parâmetros de query

CampoTipoDescriçãoValores aceitos
pagestringNúmero da página, a partir de 1 (padrão 1).inteiro a partir de 1
limitstringItens por página, de 1 a 100 (padrão 20).de 1 a 100
product_keystringIdentificador do produto (UUID).até 36 caracteres

* obrigatório.

Exemplo de requisição

curl -X GET 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/account/b043541b-19bc-4c23-a079-b18cbff9354c/investment_orders' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Uma página de ordens.

STATUS 200Ordens da conta
{
  "items": [
    {
      "investment_order_key": "880df567-a99c-4f5f-9b55-244c0aed464c",
      "product_key": "cdf625c2-ce65-4211-890b-712c49cb8c8d",
      "side": "REDEEM",
      "source": "CUSTOMER",
      "amount": 30000,
      "unit_price": "102.00000000",
      "quantity": "2.94117647",
      "maturity_date": null,
      "status": "EXECUTED",
      "transaction_key": "4bd7a432-2b2c-49d0-a497-f0cd5b6755ec",
      "acquisition_cost_amount": 30000,
      "result_amount": 0,
      "return_bps": 0
    },
    {
      "investment_order_key": "be2b11b2-d793-4f30-84a4-caa70db8271c",
      "product_key": "cf85fbe3-a0b0-4e7b-b534-a8a602641c41",
      "side": "APPLY",
      "source": "CUSTOMER",
      "amount": 10000,
      "unit_price": "111.82266423",
      "quantity": "0.89427309",
      "maturity_date": "2027-11-03",
      "status": "EXECUTED",
      "transaction_key": "0346f1fb-72e2-43fb-8b52-334658341ac8"
    },
    {
      "investment_order_key": "a2b5b62b-fd40-421a-bf37-7c3b3808895a",
      "product_key": "cdf625c2-ce65-4211-890b-712c49cb8c8d",
      "side": "APPLY",
      "source": "CUSTOMER",
      "amount": 100000,
      "unit_price": "102.00000000",
      "quantity": "9.80392156",
      "maturity_date": null,
      "status": "EXECUTED",
      "transaction_key": "5beee41e-48dd-400a-afb7-21a32b5d870f"
    }
  ],
  "page": 1,
  "limit": 20,
  "total": 3
}

Campos da resposta

CampoDescrição
itemsA lista de resultados.
items[].investment_order_keyIdentificador da ordem de investimento (UUID).
items[].product_keyIdentificador do produto (UUID).
items[].sideAPPLY (aplicação) ou REDEEM (resgate).
items[].sourceQuem pediu a ordem: CUSTOMER ou MATURITY (o fechamento do dia, pagando uma aplicação vencida).
items[].amountValor em centavos, inteiro e maior que zero (R$ 10,50 = 1050).
items[].unit_pricePreço de uma cota, como texto com 8 casas decimais ("1.25000000"), para não ser lido como float.
items[].quantityQuantidade de cotas, como texto com 8 casas decimais.
items[].maturity_dateVencimento desta aplicação (só em produto com prazo). Nulo nos outros casos.
items[].statusStatus da conta: ACTIVE (movimenta), BLOCKED (não movimenta, só consulta) ou CLOSED (encerrada).
items[].transaction_keyIdentificador da movimentação (UUID).
items[].acquisition_cost_amountCusto de aquisição das cotas, em centavos. Na posição: quanto custaram as cotas que a conta tem. No resgate: o custo das cotas vendidas (custo médio).
items[].result_amountResultado em centavos: valor menos custo de aquisição. Pode ser negativo.
items[].return_bpsRentabilidade em pontos-base: resultado ÷ custo (400 = 4%).
pageNúmero da página, a partir de 1 (padrão 1).
limitItens por página, de 1 a 100 (padrão 20).
totalTotal de itens que passam pelos filtros, somando todas as páginas.

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
404PPP002001Account Not FoundThe account_key does not exist for this customer.Conta não encontrada.

Ordens e posição

Posição e capital total

Cada investimento ao preço do dia (valor atual, custo, resultado e rentabilidade) e o total: quanto o cliente tem nesta conta (caixa + investimentos) e a divisão entre os dois, em pontos-base. É calculado na hora da consulta. Posições zeradas só aparecem com include_closed=true.

Endpoint/customer/{customer_key}/account/{account_key}/positions
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).

Parâmetros de caminho

CampoDescrição
customer_keyIdentificador do cliente (UUID). É ele que vai no caminho das rotas /customer/....
account_keyIdentificador da conta (UUID).

Parâmetros de query

CampoTipoDescriçãoValores aceitos
include_closedstringtrue para incluir posições zeradas.true · false

* obrigatório.

Exemplo de requisição

curl -X GET 'http://localhost:3000/customer/4f9a35d6-e269-4e84-a9f6-b50552c5ce56/account/b043541b-19bc-4c23-a079-b18cbff9354c/positions' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>'

Respostas

200 Posições e total.

STATUS 200Posições e capital total
{
  "items": [
    {
      "product_key": "cf85fbe3-a0b0-4e7b-b534-a8a602641c41",
      "quantity": "0.89427309",
      "unit_price": "111.82266423",
      "current_amount": 9999,
      "acquisition_cost_amount": 10000,
      "result_amount": -1,
      "return_bps": -1
    },
    {
      "product_key": "cdf625c2-ce65-4211-890b-712c49cb8c8d",
      "quantity": "6.86274509",
      "unit_price": "102.00000000",
      "current_amount": 69999,
      "acquisition_cost_amount": 70000,
      "result_amount": -1,
      "return_bps": 0
    }
  ],
  "total": {
    "investments_amount": 79998,
    "acquisition_cost_amount": 80000,
    "result_amount": -2,
    "return_bps": 0,
    "cash_amount": 99800,
    "net_worth_amount": 179798,
    "cash_bps": 5550,
    "investments_bps": 4450
  }
}

Campos da resposta

CampoDescrição
itemsA lista de resultados.
items[].product_keyIdentificador do produto (UUID).
items[].quantityQuantidade de cotas, como texto com 8 casas decimais.
items[].unit_pricePreço de uma cota, como texto com 8 casas decimais ("1.25000000"), para não ser lido como float.
items[].current_amountValor das cotas deste produto ao preço do dia, em centavos.
items[].acquisition_cost_amountCusto de aquisição das cotas, em centavos. Na posição: quanto custaram as cotas que a conta tem. No resgate: o custo das cotas vendidas (custo médio).
items[].result_amountResultado em centavos: valor menos custo de aquisição. Pode ser negativo.
items[].return_bpsRentabilidade em pontos-base: resultado ÷ custo (400 = 4%).
totalTotais da posição: soma dos investimentos e o capital do cliente no banco.
total.investments_amountValor de todas as posições ao preço do dia, em centavos. Fica fora do balance: o que foi aplicado saiu do saldo.
total.acquisition_cost_amountCusto de aquisição das cotas, em centavos. Na posição: quanto custaram as cotas que a conta tem. No resgate: o custo das cotas vendidas (custo médio).
total.result_amountResultado em centavos: valor menos custo de aquisição. Pode ser negativo.
total.return_bpsRentabilidade em pontos-base: resultado ÷ custo (400 = 4%).
total.cash_amountDinheiro na conta (o saldo), em centavos.
total.net_worth_amountCapital total do cliente no banco: caixa + investimentos ao preço do dia, em centavos.
total.cash_bpsParte do capital que está em caixa, em pontos-base.
total.investments_bpsParte do capital que está investida, em pontos-base (10000 − cash_bps).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
404PPP001001Customer Not FoundThe customer_key does not exist.Cliente não encontrado.
404PPP002001Account Not FoundThe account_key does not exist for this customer.Conta não encontrada.

Operação interna

Bloquear, desbloquear ou encerrar conta (banco)

A operação do banco muda o status de qualquer conta de cliente. Bloquear e encerrar exigem motivo; encerrar exige saldo zero e nenhum investimento. Um evento de status é gravado com actor = BANK.

Endpoint/internal/account/{account_key}/status
MétodoPATCH

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).
OPERATOR-TOKENsimToken da operação do banco (variável OPERATOR_TOKEN).
Content-Typesimapplication/json

Parâmetros de caminho

CampoDescrição
account_keyIdentificador da conta (UUID).

Corpo da requisição

CampoTipoDescriçãoValores aceitos
status*stringStatus da conta: ACTIVE (movimenta), BLOCKED (não movimenta, só consulta) ou CLOSED (encerrada).ACTIVE · BLOCKED · CLOSED
reasonstringMotivo da mudança de status, até 255 caracteres. Obrigatório para bloquear e encerrar.até 255 caracteres

* obrigatório.

Exemplo de requisição

curl -X PATCH 'http://localhost:3000/internal/account/4ec7839a-1587-4d71-99de-7b2576e03be5/status' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>' \
  -H 'OPERATOR-TOKEN: <OPERATOR_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"status": "BLOCKED", "reason": "Suspeita de fraude em análise"}'

Respostas

200 Status alterado.

STATUS 200Banco bloqueia a conta
{
  "account_key": "4ec7839a-1587-4d71-99de-7b2576e03be5",
  "status": "BLOCKED",
  "balance": 20000,
  "investments_amount": 0,
  "net_worth_amount": 20000,
  "created_at": "2026-10-11T19:43:59Z",
  "updated_at": "2026-10-11T19:44:00Z"
}
STATUS 200Banco desbloqueia a conta
{
  "account_key": "4ec7839a-1587-4d71-99de-7b2576e03be5",
  "status": "ACTIVE",
  "balance": 20000,
  "investments_amount": 0,
  "net_worth_amount": 20000,
  "created_at": "2026-10-11T19:43:59Z",
  "updated_at": "2026-10-11T19:44:00Z"
}

Campos da resposta

CampoDescrição
account_keyIdentificador da conta (UUID).
statusStatus da conta: ACTIVE (movimenta), BLOCKED (não movimenta, só consulta) ou CLOSED (encerrada).
balanceSaldo da conta (só o dinheiro parado nela, sem os investimentos), em centavos. Na resposta de uma movimentação, é o saldo logo depois dela.
investments_amountValor de todas as posições ao preço do dia, em centavos. Fica fora do balance: o que foi aplicado saiu do saldo.
net_worth_amountCapital total do cliente no banco: caixa + investimentos ao preço do dia, em centavos.
created_atQuando o registro foi criado, em UTC (AAAA-MM-DDTHH:MM:SSZ).
updated_atQuando o registro mudou pela última vez, em UTC.

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
403PPP000007Operator OnlyThis route belongs to the bank operation and requires the OPERATOR-TOKEN header.Esta rota é da operação do banco e exige o token de operador.
404PPP002001Account Not FoundThe account_key does not exist for this customer.Conta não encontrada.
409PPP002003Invalid Status TransitionThe account cannot move from its current status to the requested one.A conta não pode passar do status atual para o status pedido.
409PPP002004Account Not EmptyThe account must have zero balance and no investment position to be closed.Só é possível encerrar uma conta sem saldo e sem investimentos.
422PPP002006Status Reason RequiredBlocking or closing an account requires a reason.Bloquear ou encerrar a conta exige um motivo.

Operação interna

Data de movimento

O "hoje" do banco. Só avança pelo fechamento do dia.

Endpoint/internal/business_date
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).
OPERATOR-TOKENsimToken da operação do banco (variável OPERATOR_TOKEN).

Exemplo de requisição

curl -X GET 'http://localhost:3000/internal/business_date' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>' \
  -H 'OPERATOR-TOKEN: <OPERATOR_TOKEN>'

Respostas

200 Data de movimento atual.

STATUS 200Data atual
{
  "business_date": "2026-11-03"
}

Campos da resposta

CampoDescrição
business_dateData de movimento: o "hoje" do banco, a data real simulada. Num lançamento ou comprovante, o dia de movimento em que a transação aconteceu.

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
403PPP000007Operator OnlyThis route belongs to the bank operation and requires the OPERATOR-TOKEN header.Esta rota é da operação do banco e exige o token de operador.

Operação interna

Fechar o dia

Busca os preços do dia (Tesouro e administradora do fundo) e as taxas CDI e Selic nas fontes externas, calcula o preço do CDB e da LCI, grava a provisão de receita do dia que fecha (a foto do fim do dia e o que o banco ganharia com ela até o próximo dia; a de sexta vale 3 dias), paga as aplicações da LCI que vencem (transações do dia novo) e abre o próximo dia útil. É tudo ou nada: se uma fonte falhar, nada é gravado e a data não anda. close_date precisa ser a data de movimento atual, então um clique duplo não fecha dois dias.

Endpoint/internal/day_close
MétodoPOST

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).
OPERATOR-TOKENsimToken da operação do banco (variável OPERATOR_TOKEN).
Content-Typesimapplication/json

Corpo da requisição

CampoTipoDescriçãoValores aceitos
close_date*stringData de movimento que está sendo fechada. Precisa ser a data atual (GET /internal/business_date).data AAAA-MM-DD

* obrigatório.

Exemplo de requisição

curl -X POST 'http://localhost:3000/internal/day_close' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>' \
  -H 'OPERATOR-TOKEN: <OPERATOR_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"close_date": "2026-11-03"}'

Respostas

201 Dia fechado; a resposta traz a nova data, os preços e as taxas.

STATUS 409Data que não é a atual
{
  "title": "Business Date Mismatch",
  "description": "The close_date is not the current business date.",
  "translation": "A data a fechar não é a data de movimento atual.",
  "code": "PPP000006"
}
STATUS 201Dia fechado
{
  "business_date": "2026-11-04",
  "prices": [
    {
      "product_key": "681c7fed-81ea-483f-8a5f-caef127c411c",
      "name": "Tesouro Selic 2031",
      "unit_price": "112.91439545"
    },
    {
      "product_key": "6bcbbf83-3789-4d74-8f99-f944b569d36e",
      "name": "CDB Triple P Bank Liquidez",
      "unit_price": "112.15034516"
    },
    {
      "product_key": "cf85fbe3-a0b0-4e7b-b534-a8a602641c41",
      "name": "LCI Triple P Bank 12 meses",
      "unit_price": "111.87999782"
    },
    {
      "product_key": "3e60dd05-c365-467b-90fa-dfebf7db22ef",
      "name": "Tesouro Prefixado 2029",
      "unit_price": "111.20586561"
    },
    {
      "product_key": "eff42ff3-f013-440d-9ea2-760ecc9e3fa6",
      "name": "Tesouro IPCA+ 2035",
      "unit_price": "109.29617633"
    },
    {
      "product_key": "cdf625c2-ce65-4211-890b-712c49cb8c8d",
      "name": "FGV Quant Capital FIM",
      "unit_price": "110.61135282"
    }
  ],
  "rates": [
    {
      "code": "CDI",
      "annual_rate_bps": 1490
    },
    {
      "code": "SELIC",
      "annual_rate_bps": 1500
    }
  ]
}

Campos da resposta

CampoDescrição
business_dateData de movimento: o "hoje" do banco, a data real simulada. Num lançamento ou comprovante, o dia de movimento em que a transação aconteceu.
pricesOs preços gravados no fechamento, um por produto.
prices[].product_keyIdentificador do produto (UUID).
prices[].nameNome. No cadastro: nome completo do cliente, de 3 a 120 caracteres, sem espaço nas pontas.
prices[].unit_pricePreço de uma cota, como texto com 8 casas decimais ("1.25000000"), para não ser lido como float.
ratesAs taxas de referência do novo dia.
rates[].codeCódigo. Na pergunta: Q1 a Q7. Na opção: 1 a 4, que é também a pontuação. Na taxa: CDI ou SELIC.
rates[].annual_rate_bpsTaxa ao ano, em pontos-base (1490 = 14,9%).

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
403PPP000007Operator OnlyThis route belongs to the bank operation and requires the OPERATOR-TOKEN header.Esta rota é da operação do banco e exige o token de operador.
409PPP000006Business Date MismatchThe close_date is not the current business date.A data a fechar não é a data de movimento atual.
502PPP005006Price Source UnavailableAn external price or rate source is down, timed out or answered an error.Uma fonte externa de preço ou taxa está fora do ar ou respondeu erro. Nada foi gravado.
502PPP005007Invalid Price Source ResponseAn external price or rate source answered something that makes no sense.Uma fonte externa de preço ou taxa respondeu algo sem sentido. Nada foi gravado.

Operação interna

Receita do banco

Quanto o banco recebeu de tarifas no período (a parte realizada, inclusive no dia ainda aberto) e quanto estima ter ganho com a taxa do fundo, o spread do CDB e da LCI e o float. O período é em datas de movimento. As estimativas são as provisões que o fechamento gravou para cada dia fechado: base do fim do dia × taxa do dia × dias corridos até o próximo dia de movimento / 365, truncado em centavos (a de sexta vale 3 dias). O dia aberto ainda não tem provisão. Cada dia aparece em daily, e o valor é a soma deles. Nada disso movimenta dinheiro. Sem datas, usa o mês da data de movimento atual.

Endpoint/internal/revenue
MétodoGET

Cabeçalhos

CabeçalhoObrigatórioDescrição
INTERNAL-TOKENsimToken interno da API (variável INTERNAL_TOKEN).
OPERATOR-TOKENsimToken da operação do banco (variável OPERATOR_TOKEN).

Parâmetros de query

CampoTipoDescriçãoValores aceitos
fromstringData inicial do período (AAAA-MM-DD), inclusive, no calendário do banco (data de movimento).data AAAA-MM-DD
tostringData final do período (AAAA-MM-DD), inclusive, no calendário do banco (data de movimento).data AAAA-MM-DD

* obrigatório.

Exemplo de requisição

curl -X GET 'http://localhost:3000/internal/revenue' \
  -H 'INTERNAL-TOKEN: <INTERNAL_TOKEN>' \
  -H 'OPERATOR-TOKEN: <OPERATOR_TOKEN>'

Respostas

200 Receita no período.

STATUS 200Tarifas do mês
{
  "from": "2026-11-01",
  "to": "2026-11-30",
  "days": 1,
  "realized": {
    "fee_amount": 2600
  },
  "estimated": {
    "fund_administration_fee": {
      "amount": 1,
      "daily": [
        {
          "date": "2026-11-02",
          "calendar_days": 1,
          "base_amount": 49999,
          "annual_rate_bps": 100,
          "amount": 1
        }
      ]
    },
    "own_issue_spread": {
      "amount": 1,
      "daily": [
        {
          "date": "2026-11-02",
          "calendar_days": 1,
          "base_amount": 50000,
          "cdi_annual_rate_bps": 1490,
          "amount": 1
        }
      ]
    },
    "float": {
      "amount": 56821,
      "daily": [
        {
          "date": "2026-11-02",
          "calendar_days": 1,
          "base_amount": 138266700,
          "annual_rate_bps": 1500,
          "amount": 56821
        }
      ]
    }
  },
  "total_amount": 59423
}

Campos da resposta

CampoDescrição
fromData inicial do período (AAAA-MM-DD), inclusive, no calendário do banco (data de movimento).
toData final do período (AAAA-MM-DD), inclusive, no calendário do banco (data de movimento).
daysQuantos dias fechados do período têm provisão (o dia aberto ainda não tem).
realizedA receita que de fato entrou na conta do banco no período.
realized.fee_amountSoma das tarifas que entraram na conta do banco no período, em centavos.
estimatedO que o banco estima ter ganho no período com o dinheiro sob sua guarda: as provisões gravadas no fechamento de cada dia. Não movimenta dinheiro.
estimated.fund_administration_feeTaxa de administração do fundo: por dia, patrimônio do fundo no fim do dia × taxa × dias corridos / 365.
estimated.fund_administration_fee.amountValor estimado, em centavos (truncado para baixo em cada provisão). Na estimativa, é a soma dos dias. Pode ser zero.
estimated.fund_administration_fee.dailyO detalhe da estimativa: a provisão de cada dia fechado do período.
estimated.fund_administration_fee.daily[].dateO dia de movimento que fechou.
estimated.fund_administration_fee.daily[].calendar_daysDias corridos que a provisão cobre, até o próximo dia de movimento: 1 de segunda a quinta, 3 na sexta (o fim de semana rende).
estimated.fund_administration_fee.daily[].base_amountBase do dia, em centavos: o patrimônio do fundo ao preço do dia, o custo em carteira de CDB e LCI ou a soma dos saldos de cliente, no fim do dia.
estimated.fund_administration_fee.daily[].annual_rate_bpsTaxa anual do dia, em pontos-base: a taxa de administração do fundo ou a Selic vigente no dia.
estimated.fund_administration_fee.daily[].amountValor estimado, em centavos (truncado para baixo em cada provisão). Na estimativa, é a soma dos dias. Pode ser zero.
estimated.own_issue_spreadSpread do CDB e da LCI: por dia, custo em carteira × CDI do dia × (1 − percentual do CDI pago) × dias corridos / 365, somado entre os produtos.
estimated.own_issue_spread.amountValor estimado, em centavos (truncado para baixo em cada provisão). Na estimativa, é a soma dos dias. Pode ser zero.
estimated.own_issue_spread.dailyO detalhe da estimativa: a provisão de cada dia fechado do período.
estimated.own_issue_spread.daily[].dateO dia de movimento que fechou.
estimated.own_issue_spread.daily[].calendar_daysDias corridos que a provisão cobre, até o próximo dia de movimento: 1 de segunda a quinta, 3 na sexta (o fim de semana rende).
estimated.own_issue_spread.daily[].base_amountBase do dia, em centavos: o patrimônio do fundo ao preço do dia, o custo em carteira de CDB e LCI ou a soma dos saldos de cliente, no fim do dia.
estimated.own_issue_spread.daily[].cdi_annual_rate_bpsCDI anual vigente no dia, em pontos-base.
estimated.own_issue_spread.daily[].amountValor estimado, em centavos (truncado para baixo em cada provisão). Na estimativa, é a soma dos dias. Pode ser zero.
estimated.floatFloat: por dia, saldo das contas de cliente no fim do dia (sem a conta do banco) × Selic do dia × dias corridos / 365.
estimated.float.amountValor estimado, em centavos (truncado para baixo em cada provisão). Na estimativa, é a soma dos dias. Pode ser zero.
estimated.float.dailyO detalhe da estimativa: a provisão de cada dia fechado do período.
estimated.float.daily[].dateO dia de movimento que fechou.
estimated.float.daily[].calendar_daysDias corridos que a provisão cobre, até o próximo dia de movimento: 1 de segunda a quinta, 3 na sexta (o fim de semana rende).
estimated.float.daily[].base_amountBase do dia, em centavos: o patrimônio do fundo ao preço do dia, o custo em carteira de CDB e LCI ou a soma dos saldos de cliente, no fim do dia.
estimated.float.daily[].annual_rate_bpsTaxa anual do dia, em pontos-base: a taxa de administração do fundo ou a Selic vigente no dia.
estimated.float.daily[].amountValor estimado, em centavos (truncado para baixo em cada provisão). Na estimativa, é a soma dos dias. Pode ser zero.
total_amountReceita total do período: realizada + estimadas, em centavos.

Erros

HTTPCódigoTítuloDescrição (en)Descrição (pt)
400PPP000001Bad Request(varia: diz o que está errado)Payload Inválido
400PPP000005Invalid Parameter(varia: diz o que está errado)Parâmetros inválidos foram fornecidos na requisição.
403PPP000002ForbiddenRequest must be internalRequisição precisa ser interna
403PPP000007Operator OnlyThis route belongs to the bank operation and requires the OPERATOR-TOKEN header.Esta rota é da operação do banco e exige o token de operador.

Saúde

Identificação do serviço

Nome do serviço e o processo que respondeu. Não exige token.

Endpoint/
MétodoGET

Exemplo de requisição

curl -X GET 'http://localhost:3000/'

Respostas

200 Serviço no ar.

STATUS 200Serviço no ar
{
  "service": "bootcamp-library-api",
  "id": "8"
}

Campos da resposta

CampoDescrição
serviceNome do serviço.
idIdentificador do processo da API que respondeu.

Saúde

Health check

Confere que a API e o banco de dados estão respondendo. Não exige token. Responde sem corpo.

Endpoint/health_check
MétodoGET

Exemplo de requisição

curl -X GET 'http://localhost:3000/health_check'

Respostas

204 Tudo no ar.

STATUS 204Tudo no ar

Sem corpo.