Open Finance
ProduçãoExtrato, saldo e identidade bancária do seu cliente, com o consentimento dele
Seu cliente autoriza no banco dele e você passa a ver o extrato, o saldo e a identidade bancária dentro da sua esteira — sem pedir PDF, sem print de tela e sem o cliente digitar nada que você já poderia consultar.
- Financeiras e fintechs que usam extrato bancário para decidir crédito e hoje pedem PDF ao cliente
- Plataformas de gestão financeira que precisam ler contas de vários bancos por cliente
- Operações de cobrança e conciliação que precisam enxergar a movimentação real antes de negociar
- A integração ponto a ponto com a API do agregador, refeita em cada serviço
- Tabela própria de contas, transações e identidade copiada na mão do agregador
- Cofre caseiro para guardar as credenciais do agregador
- Um agregador de Open Finance — quem é participante regulado é o provedor contratado, hoje a Pluggy
- Uma autorização do Banco Central nem uma licença de instituição de pagamento
- Um iniciador de pagamento — este building block só lê dados, não movimenta dinheiro
- Um motor de decisão de crédito (isso é o Decision Platform)
24 endpoints em 6 recursos.
/open-finance/api/v1/provider-configs/open-finance/api/v1/open-finance/api/v1/items/open-finance/api/v1/open-finance/api/v1/webhooks/open-finance/healthResumo executivo
O Open Finance traz para dentro da sua aplicação o que o seu cliente autorizou o banco dele a compartilhar: quais contas ele tem, quanto tem nelas, o que entrou e saiu, e quem é o titular segundo o próprio banco. O cliente autoriza uma vez, na tela do banco dele, e a partir daí os dados chegam pela sua API em vez de chegarem por PDF anexado num e-mail.
Na prática isso resolve a etapa mais frágil de qualquer análise de crédito no Brasil: pedir comprovante de renda e extrato ao próprio candidato. Documento enviado pelo cliente pode ser editado, chega em formatos diferentes de cada banco e leva dias. O extrato via Open Finance vem do banco, categorizado, e leva minutos.
Um esclarecimento que muda tudo no material comercial: a Catalisa não é participante do Open Finance Brasil. Quem é instituição participante regulada é o provedor agregador contratado — hoje a Pluggy, cuja entidade Pluggy Brasil Instituição de Pagamento Ltda. está ativa no diretório oficial com os papéis de Dados e Pagamentos. Este building block é a camada de software que fala com esse provedor, guarda o resultado com escopo de tenant e entrega para o resto da plataforma. O enquadramento regulatório completo está na §14 e é a primeira coisa a ler antes de qualquer proposta.
Está em produção desde janeiro de 2026, no stack de produção em open-finance.bb.catalisa.app.
| Atributo | Valor |
|---|---|
| Identificador | open-finance |
| Categoria | Financeiro |
| Escopo | Tenant (exige organizationId no token em todas as rotas autenticadas) |
| Porta (standalone) | 3016 |
| Path alias | @open-finance |
| Prefixo HTTP | /open-finance |
| Schema no PostgreSQL | openfinance |
| Status | Produção desde 2026-01 |
| Depende de | PostgreSQL, Redis (eventos), IAM, e uma conta ativa no provedor agregador |
O problema
negócioO cenário. Uma financeira precisa saber se o candidato ao crédito tem renda, se ela é recorrente e se o dinheiro sai antes do fim do mês. A informação existe — está no banco do candidato. O que não existe é um caminho para chegar até ela sem passar pelo próprio candidato.
O que trava hoje.
- O comprovante vem do interessado. Pedir extrato em PDF a quem quer o crédito é pedir a prova a quem tem interesse no resultado. Detectar adulteração é trabalho manual, caro e falho.
- Cada banco entrega um formato. PDF, OFX, CSV, imagem escaneada. Cada um exige um parser, e o parser quebra quando o banco muda o cabeçalho do relatório.
- A resposta demora dias, e o cliente desiste. Entre pedir o documento e recebê-lo passam-se dias. Nesse intervalo, o concorrente aprovou.
- Integrar o agregador direto vira dívida técnica. A API do agregador tem o vocabulário dele —
item,connector,execution status. Cada serviço que integra copia esse vocabulário para dentro, e trocar de fornecedor deixa de ser uma opção. - As credenciais do agregador ficam por aí.
clientIdeclientSecretacabam em variável de ambiente, iguais para todos os clientes da plataforma, sem separação e sem rotação. - O consentimento tem prazo e ninguém controla. A autorização vale no máximo 12 meses pela regra do Open Finance Brasil. Sem controle, o dado envelhece em silêncio e a base vira histórico de qualidade desconhecida.
flowchart LR D(["A informação existe — está no banco do candidato"]) D --> C["Único caminho hoje:<br/>pedir ao próprio candidato"] C --> P1["Prova pedida a quem<br/>tem interesse no resultado"] C --> P2["PDF, OFX, CSV, imagem —<br/>um parser por banco"] C --> P3["Dias de espera —<br/>o concorrente aprova antes"] C --> P4["Integrar o agregador direto<br/>vira dívida técnica"] C --> P5["clientId e clientSecret<br/>em variável de ambiente"] C --> P6["Consentimento vence em 12 meses<br/>e ninguém controla"]
O custo de não resolver. O custo direto é o tempo entre o pedido e a decisão. O indireto é o crédito concedido com base em documento que ninguém validou. E há o custo de oportunidade de ignorar um ecossistema que já é enorme: na semana de referência de 31/07/2026, o painel oficial do Open Finance Brasil registrava 239,8 milhões de consentimentos ativos na ótica dos receptores e mais de 8,1 bilhões de chamadas de dados cadastrais e transacionais em uma única semana (Dashboard do Cidadão, dados atualizados em 13/08/2026). O comportamento de compartilhar dado bancário deixou de ser exceção.
Proposta de valor
negócio| Antes | Depois |
|---|---|
| O candidato envia extrato em PDF, que alguém confere na mão | O candidato autoriza no banco dele e o extrato chega pela API, categorizado |
| Cada banco tem um formato e um parser | Um formato canônico único, igual para todas as instituições |
| A credencial do agregador é uma só, em variável de ambiente | Uma credencial por organização, cifrada com AES-256-GCM no banco |
| Trocar de agregador é reescrever quem consome | Trocar de agregador é implementar uma interface; quem consome não muda |
| A conexão bancária não se liga a nada | O item se liga a uma pessoa do Customers por personId |
O dado vem da fonte. O extrato é entregue pelo banco ao provedor sob consentimento do titular, não digitado nem anexado pelo interessado.
Um vocabulário só, para todas as instituições. CanonicalAccount, CanonicalTransaction e CanonicalIdentity são tipos nossos. Quem consome nunca vê o formato do provedor.
Credencial por organização, cifrada. Cada cliente da plataforma pode usar a própria conta no provedor. As credenciais são cifradas com AES-256-GCM antes de tocar o banco e nunca voltam em resposta de API.
A conexão tem dono e tem prazo. Todo item pertence a uma organização, pode ser ligado a uma pessoa e carrega consentExpiresAt — a data em que a autorização do titular deixa de valer.
flowchart LR ORG["Organização<br/>do token do IAM"] --> CFG["Provider config<br/>credencial cifrada"] CFG --> IT["Item<br/>a conexão autorizada"] IT -->|personId| PES["Pessoa no Customers"] IT --> EXP["consentExpiresAt<br/>no máximo 12 meses"] IT --> DADOS["Contas · transações · identidade<br/>em formato canônico"]
Casos de uso reais
negócioCaso 1 — Análise de crédito sem pedir extrato ao candidato Cenário ilustrativo
Financeira de crédito pessoal, cerca de 4 mil propostas por mês, ticket médio baixo e margem que não suporta análise manual.
A esteira parava na etapa "enviar extrato dos últimos três meses". Metade dos candidatos não enviava; da outra metade, boa parte enviava foto de tela. O tempo médio entre proposta e decisão era de três dias, e a maior parte disso era espera por documento.
A esteira chama POST /connect/token, abre o widget do provedor no navegador do candidato e ele autoriza no próprio banco. O retorno traz o identificador do item, que a esteira registra com POST /items/sync informando o personId do Customers. Em seguida, POST /items/:id/sync-data traz contas, transações e identidade de uma vez. O motor lê o extrato por GET /accounts/:accountId/transactions com filtro de período.
A etapa de documento sai do caminho crítico. A decisão passa a depender de dado vindo do banco, e o candidato não precisa achar, baixar e anexar nada.
flowchart LR
subgraph A["Antes — dias de espera"]
A1["Proposta"] --> A2["Envie o extrato<br/>dos últimos 3 meses"]
A2 --> A3["Metade não envia<br/>parte envia foto de tela"]
A3 --> A4["Análise manual"]
A4 --> A5["Decisão em ~3 dias"]
end
subgraph D["Depois — minutos"]
D1["Proposta"] --> D2["POST /connect/token<br/>widget do provedor"]
D2 --> D3["Candidato autoriza no banco"]
D3 --> D4["POST /items/sync com personId"]
D4 --> D5["POST /items/:id/sync-data"]
D5 --> D6["GET /accounts/:accountId/transactions"]
D6 --> D7["Decisão com dado da fonte"]
endCaso 2 — Renegociação de dívida com base no que a pessoa de fato tem Cenário ilustrativo
Operação de cobrança que negocia parcelamento por telefone e por mensagem.
A proposta de parcela era chutada a partir do valor da dívida, sem qualquer noção da capacidade real de pagamento. Acordo alto demais quebra no segundo mês; acordo baixo demais deixa dinheiro na mesa.
Com o consentimento do devedor, GET /items/:itemId/accounts traz saldo e limite de crédito, e GET /accounts/:accountId/transactions traz a movimentação. A operação passa a propor parcela olhando o padrão de entrada e saída, não o tamanho da dívida.
A proposta deixa de ser um chute. E, como cada OpenFinanceTransaction guarda categoria, nome e CNPJ do estabelecimento, dá para separar despesa recorrente de gasto eventual.
flowchart LR C["Consentimento do devedor"] --> S["GET /items/:itemId/accounts<br/>saldo e limite de crédito"] C --> T["GET /accounts/:accountId/transactions<br/>movimentação real"] S --> P["Capacidade de pagamento observada"] T --> P T --> R["Categoria, merchantName e merchantCnpj<br/>separam recorrente de eventual"] R --> P P --> O["Parcela proposta pelo que entra e sai,<br/>não pelo tamanho da dívida"]
Caso 3 — Verificar a titularidade da conta antes de pagar Cenário ilustrativo
Plataforma que faz repasse a prestadores e precisa confirmar que a conta informada pertence mesmo ao prestador.
A conferência era manual: alguém comparava o CPF do cadastro com o nome digitado no campo de conta. Erro de digitação virava repasse devolvido, e má-fé virava repasse para o titular errado.
Depois de o prestador conectar a conta, GET /items/:itemId/identity devolve nome completo, tipo e número do documento, data de nascimento e endereço como o banco tem registrado. O cadastro é conferido contra a fonte, não contra o que foi digitado.
A conferência vira automação. Divergência entre o CPF do cadastro e o do banco vira exceção tratada, em vez de repasse errado descoberto depois.
flowchart LR
P["Prestador conecta a conta"] --> I["GET /items/:itemId/identity"]
I --> D["Nome completo, tipo e número do documento,<br/>data de nascimento e endereço<br/>como o BANCO tem registrado"]
D --> Q{"Confere com o cadastro?"}
Q -->|sim| OK["Repasse liberado"]
Q -->|não| EX["Exceção tratada<br/>antes de o dinheiro sair"]Caso 4 — O ecossistema brasileiro chegou a uma escala que muda a conversa Referência de mercado
O painel oficial do Open Finance Brasil registrava, na semana de 31/07/2026, 239,8 milhões de consentimentos ativos (ótica dos receptores) e 8,1 bilhões de chamadas de dados cadastrais e transacionais em uma semana; a iniciação de pagamento saltou de 79 para 134 milhões de chamadas semanais em cerca de cinco semanas (Dashboard do Cidadão).
Essa escala não é acessível a qualquer empresa. Participar do ecossistema exige ser instituição autorizada a funcionar pelo Banco Central — é o que diz o art. 1º da Resolução Conjunta nº 1, de 4 de maio de 2020. Uma empresa não autorizada não vira participante; ela entra como parceira contratada de uma participante, pelo art. 36 da mesma resolução.
Este building block assume que você é a parte não autorizada e desenha para esse cenário: você contrata um agregador que é participante regulado, guarda as credenciais dele cifradas por organização e opera sobre um formato canônico que não te amarra a esse fornecedor. Não vendemos autorização do BACEN, e não fingimos ter uma.
O acesso ao ecossistema fica claro no contrato e no material comercial: quem responde ao regulador é o agregador, e a §14 explica exatamente onde termina a responsabilidade de cada um.
flowchart LR V["Você — entidade NÃO autorizada pelo BACEN"] -->|"art. 36 — contratação de parceria"| AG["Agregador — instituição de pagamento autorizada"] AG -->|"art. 1º — participante do Open Finance Brasil"| ECO["Ecossistema Open Finance Brasil"] CAT["Catalisa Open Finance"] -->|"camada de software sobre o participante"| V ECO --> RISK["Risco regulatório fica com o agregador"] V --> RISK2["Risco contratual e de continuidade fica com quem contrata"]
Mercado e diferenciais
negócioPanorama. O mercado brasileiro de acesso a dados bancários é disputado por participantes locais, e isso não é acaso: participar do Open Finance Brasil exige autorização do Banco Central. Pluggy, Belvo e Klavi estão os três ativos no diretório oficial, com papéis de Dados e de Pagamentos. Os grandes nomes internacionais — Plaid, Tink (da Visa), TrueLayer e Yapily — não operam no Brasil: consulta às páginas de cobertura dos quatro em 2026-08-16 não encontrou o país em nenhuma delas, e nenhum deles publica preço em número.
Dentro do Brasil, os três agregadores vendem essencialmente a mesma coisa em formatos diferentes: acesso a dados por API, com preço a partir de um mínimo mensal. A Pluggy é a única que publica o piso — R$ 2.500/mês em Dados e R$ 500/mês em Pagamentos (pluggy.ai/precos) — e a Belvo publica o plano Launch a R$ 6.000/mês no Brasil (belvo.com/pt-br/planos-precos).
Este building block não compete com nenhum deles. Ele fica em cima de um deles. A comparação relevante para quem compra não é "Catalisa contra Pluggy", é "usar este building block sobre um agregador" contra "integrar o agregador direto" contra "virar participante você mesmo".
| Critério | Catalisa Open Finance (sobre um agregador) | Integrar o agregador direto | Participação direta no Open Finance |
|---|---|---|---|
| Quem é o participante regulado | O agregador contratado | O agregador contratado | Você, se autorizado pelo BACEN |
| Custo de acesso | O do agregador + a plataforma | O do agregador | Autorização, certificação, diretor responsável, operação |
| Quem pode | Qualquer empresa | Qualquer empresa | Só instituição autorizada pelo BACEN |
| Vocabulário de dados | Canônico próprio | O do fornecedor, dentro do seu código | O do padrão Open Finance Brasil |
| Trocar de fornecedor | Implementar uma interface | Reescrever quem consome | Não se aplica |
| Credencial por cliente da plataforma | Sim, cifrada por organização | Uma só, no ambiente | Não se aplica |
| Isolamento multi-tenant | Nativo, pelo token do IAM | Você constrói | Você constrói |
| Persistência do extrato | Inclusa, com escopo de tenant | Você constrói | Você constrói |
| Ligação com o cadastro de pessoas | personId do Customers | Você constrói | Você constrói |
| Iniciação de pagamento | Não (§15) | Depende do agregador | Sim, com o papel adequado |
Nossos diferenciais
- A credencial é do cliente, não da plataforma.
OpenFinanceProviderConfigguardaclientIdeclientSecretcifrados com AES-256-GCM por organização. Cada cliente da plataforma pode ter a própria conta no agregador — o que significa que o contrato de parceria do art. 36 é dele com o agregador, e não uma cadeia opaca de subcontratação. É difícil de copiar porque exige que a multi-tenancy chegue até a credencial externa, não só até a linha do banco. - O formato canônico é a barreira contra o aprisionamento.
CanonicalAccount,CanonicalTransaction,CanonicalIdentityeCanonicalConnectorsão tipos nossos, e a interfaceOpenFinanceProviderdefine exatamente o que um agregador precisa saber fazer. Trocar de fornecedor é uma classe nova; quem consome não muda uma linha. - A conexão entra na esteira que já existe.
POST /items/:id/link-personamarra o item ao cadastro do Customers, e cada sincronização publica evento no barramento. O extrato vira entrada de decisão sem integração nova.
Quando escolher o concorrente. Se a sua empresa é uma instituição autorizada pelo Banco Central e o volume justifica, participe diretamente: você elimina o custo por requisição e o intermediário, e ganha acesso ao padrão completo. Se você tem um único produto e uma única equipe consumindo os dados, integre o agregador direto — a camada canônica só se paga quando há mais de um consumidor ou quando trocar de fornecedor é uma possibilidade real. Se o que você precisa é iniciação de pagamento — Pix por Open Finance, Pix Automático, cobrança —, este building block não faz isso e a Pluggy e a Belvo fazem: vá direto a elas. E se a sua necessidade é dado de vínculo empregatício, dado fiscal ou estimativa de renda, o catálogo da Belvo é mais largo que o que expomos aqui. Este building block ganha quando há vários clientes na mesma plataforma, cada um com o próprio acesso, e o extrato precisa conversar com o resto da esteira.
Modelo de cobrança e ROI
negócioUnidade de cobrança. Precificação em definição. Não há medição de uso implementada neste building block (§15). O que existe hoje é o custo repassado do agregador, que é contratado por quem vai usar.
O que dispara custo. O custo real está no agregador, não aqui:
| Driver | Onde pesa |
|---|---|
| Conexões ativas por organização | O agregador cobra mínimo mensal e excedente por requisição |
| Sincronizações de dados | POST /items/:id/sync-data percorre contas, transações e identidade — é a chamada mais cara do conjunto |
| Chamadas ao provedor | Listagem de conectores, atualização de item, teste de conexão |
Comparação de custo — cenário: plataforma com 20 organizações clientes, cada uma com cerca de 500 conexões ativas e sincronização semanal. Preços consultados em 2026-08-16.
| Com este building block | Integrando o agregador direto | Participação direta | |
|---|---|---|---|
| Piso do fornecedor | Pluggy a partir de R$ 2.500/mês em Dados, ou Belvo a partir de R$ 6.000/mês no Launch | O mesmo | Zero de fornecedor |
| Excedente por requisição | Do agregador, valor não publicado | O mesmo | Não se aplica |
| Camada de tenant, credencial cifrada e persistência | Inclusa | Você constrói | Você constrói |
| Custo regulatório | Nenhum direto — o participante é o agregador | Nenhum direto | Autorização BACEN, certificação, diretor responsável, conformidade contínua |
| Quem contrata o agregador | Cada organização pode contratar a própria | Você contrata uma vez | Não se aplica |
Estimativa para orientar conversa, não proposta comercial. O preço por requisição acima do mínimo não é publicado por nenhum dos agregadores — pergunte na negociação, porque é ele que define o custo em escala. Confira as tabelas públicas na data da sua análise.
ROI. A conta não fecha comparando com o agregador, porque o agregador continua sendo contratado nos dois cenários. Ela fecha em dois lugares. O primeiro é o processo substituído: uma etapa de "envie o extrato" que atrasa a decisão em dias e derruba parte do funil. O segundo é a camada que não precisa ser escrita — credencial cifrada por organização, formato canônico, persistência com escopo de tenant, ligação com o cadastro de pessoas e tratamento de webhook. É o tipo de código que parece pequeno na estimativa e grande no calendário.
Arquitetura
As camadas e o caminho da requisição
flowchart TD
NAV["Navegador do cliente final"]
APPCLI["Sua aplicação"]
BANCO["Banco do cliente<br/>tela de consentimento"]
APPCLI -->|"1 · POST /connect/token"| HONO
APPCLI -->|"3 · POST /items/sync"| HONO
NAV -->|"2 · abre o widget do provedor com o token"| BANCO
BANCO -->|"autoriza"| NAV
subgraph HONO["Hono app · basePath('/open-finance')"]
R1["/api/v1/provider-configs"]
R2["/api/v1/connect · /api/v1/connectors"]
R3["/api/v1/items · /api/v1/accounts"]
R4["/api/v1/transactions"]
R5["/api/v1/webhooks/:providerType — público"]
end
HONO -->|"authMiddleware → requirePermission → requireOrganization"| SVC
subgraph SVC["services/"]
S1["ProviderConfigService · credencial"]
S2["ItemService · conexão e sync"]
S3["AccountService · TransactionService"]
S4["IdentityService · WebhookHandler"]
end
SVC -->|"decryptCredentials (AES-256-GCM)"| FAB
SVC -->|Prisma| PG["PostgreSQL<br/>schema 'openfinance' · 6 tabelas"]
subgraph FAB["createOpenFinanceProvider()"]
F1["PLUGGY → PluggyProvider"]
F2["BELVO → não implementado"]
end
FAB -->|"mapeamento para o formato canônico"| PLU["api.pluggy.ai"]
PLU -->|"webhook do provedor"| R5O fluxo de consentimento, que é o coração do módulo
O consentimento não é uma caixa que o seu sistema marca: é um ato do titular, praticado na tela do banco dele. A sua aplicação nunca vê senha bancária — ela vê um token de widget na ida e um identificador de item na volta.
sequenceDiagram autonumber participant App as Sua aplicação participant OF as Open Finance (Catalisa) participant Wid as Widget do provedor participant Cli as Cliente (titular) participant Banco as Banco do cliente App->>OF: POST /api/v1/connect/token OF-->>App: accessToken App->>Wid: entrega o token ao front e abre o widget Wid->>Cli: escolhe o banco Cli->>Banco: AUTORIZA no ambiente do próprio banco Note over Cli,Banco: É aqui que o consentimento é dado — com prazo e finalidade Banco-->>Wid: autorização concedida Wid-->>App: itemId App->>OF: POST /api/v1/items/sync (externalItemId, personId) OF-->>App: item criado, com consentExpiresAt gravado App->>OF: POST /api/v1/items/:id/sync-data OF-->>App: contas, transações e identidade Banco-->>OF: o provedor avisa mudanças por webhook Note over OF: POST /api/v1/webhooks/pluggy
Atenção. O consentimento tem prazo. Pela Resolução Conjunta nº 1/2020, art. 10, § 1º, III, o prazo é "limitado a doze meses". A data vem do provedor e fica em OpenFinanceItem.consentExpiresAt. Passado o prazo, é preciso novo consentimento — a norma não prevê renovação automática (art. 10, § 2º). Ver §14 e §15.
stateDiagram-v2 direction LR [*] --> SemConsentimento state "Sem consentimento" as SemConsentimento state "Consentimento concedido — consentExpiresAt gravado" as Concedido state "Consentimento vencido — máximo de 12 meses" as Vencido state "Consentimento revogado" as Revogado SemConsentimento --> Concedido: titular autoriza no banco Concedido --> Concedido: sync-data e refresh dentro do prazo Concedido --> Vencido: passa a data de consentExpiresAt Concedido --> Revogado: DELETE do item, revogação imediata pelo art. 15, § 3º Vencido --> Concedido: NOVO consentimento, do zero pelo connect/token Revogado --> Concedido: NOVO consentimento, do zero pelo connect/token
Atenção. O building block grava e devolve consentExpiresAt, mas não bloqueia a leitura de um item vencido nem emite alerta. A transição para "vencido" acontece no calendário, não no código — o controle é do seu processo (§15).
Decisões não óbvias. São seis, e cada uma tem consequência direta em segurança ou em custo.
A credencial é validada antes de ser guardada
POST /provider-configs constrói o provedor e chama testConnection() antes de cifrar e persistir. Credencial errada falha na criação, e não seis horas depois na primeira sincronização de um cliente.
flowchart LR A["POST /provider-configs"] --> B["Constrói o provedor"] B --> C["testConnection() no agregador"] C -->|falha| D["400 — nada é gravado"] C -->|sucesso| E["Cifra com AES-256-GCM"] E --> F["Grava no banco"]
Cifragem AES-256-GCM com formato iv:authTag:ciphertext
GCM é autenticado: adulterar o registro no banco quebra a decifragem em vez de produzir credencial silenciosamente errada. A chave mestra vem de OPENFINANCE_CREDENTIAL_MASTER_KEY, lida direto de process.env para funcionar mesmo em provedores instanciados fora do container.
O token do provedor é cacheado com folga
O PluggyAuth guarda a chave de API por 2 horas menos 5 minutos e serializa refreshes concorrentes numa única promessa. Sem isso, uma rajada de requisições dispararia N autenticações simultâneas contra o provedor.
O tenant do webhook vem do item, nunca da configuração usada para interpretar
O provedor não envia organização no evento. O handler procura o item pelo identificador externo em todas as configurações daquele provedor e usa a organização e a configuração donas do item. Evento sem referência de item é recusado, em vez de ser atribuído a uma organização arbitrária: perder um evento órfão raro é melhor que misturar dado financeiro entre clientes.
flowchart TD
W["Webhook do provedor<br/>sem organizationId"] --> Q{"Tem referência de item?"}
Q -->|não| REJ["400 — evento recusado"]
Q -->|sim| B["Procura o item pelo externalId<br/>em TODAS as configs daquele provedor"]
B --> R{"Item encontrado?"}
R -->|não| REJ
R -->|sim| T["Usa a organização e a config DONAS do item"]
T --> P["Processa e grava em openfinance_webhook_events"]O formato canônico é o contrato interno
Nenhum serviço, rota ou repositório conhece o vocabulário da Pluggy. A tradução acontece em pluggy.mapper.ts, e é o único lugar a mudar quando o provedor muda.
Exclusão do item é lógica no nosso lado e definitiva no provedor
DELETE /items/:id chama deleteItem no provedor — o que encerra a conexão lá — e depois marca deletedAt aqui. As contas, transações e identidade já sincronizadas permanecem no banco. Isso é deliberado, para não perder histórico usado em decisão já tomada, e tem consequência de LGPD que a §14 detalha.
flowchart LR D["DELETE /items/:id"] --> P["deleteItem no provedor<br/>conexão encerrada lá"] P --> L["deletedAt no item aqui<br/>exclusão lógica"] L --> K["Contas, transações e identidade<br/>PERMANECEM no banco"] K --> G["Pedido de eliminação sob LGPD<br/>exige expurgo à parte — §14"]
Monolito vs. standalone. Em monolito, os serviços vêm do container TypeDI e a chamada é direta. Em standalone — o modo de produção — o Open Finance sobe na porta registrada 3016 e é alcançado por HTTP. Em nenhum dos dois modos o building block chama outros building blocks: ele só precisa do IAM para validar o token.
Conceitos e modelo de dados
Glossário
| Termo | Significa |
|---|---|
| Provider config | A credencial de uma organização no agregador, cifrada. Uma organização pode ter várias; uma é a padrão. |
| Connector | Uma instituição financeira disponível no agregador. Tem identificador numérico, nome, país, logotipo e a lista de produtos que oferece. |
| Item | Uma conexão entre um titular e uma instituição, criada quando o cliente autoriza. É a unidade do consentimento e carrega consentExpiresAt. |
| External ID | O identificador do recurso no provedor. Guardado junto de cada registro para permitir ressincronização. |
| Connect token | Token de curta duração que o front usa para abrir o widget de autorização do provedor. |
| Consentimento | A autorização do titular, dada no ambiente do banco dele. Tem finalidade determinada e prazo limitado a 12 meses. |
| Sincronização | A operação que busca no provedor e grava aqui. refresh atualiza o item; sync-data percorre contas, transações e identidade. |
Modelo de dados — schema openfinance no PostgreSQL.
| Modelo Prisma | Tabela | Propósito | Campos-chave |
|---|---|---|---|
OpenFinanceProviderConfig | openfinance_provider_configs | Credencial da organização no agregador | organizationId, providerType, credentials (cifrado), isDefault, isActive, deletedAt; único (organizationId, name) |
OpenFinanceItem | openfinance_items | A conexão autorizada | organizationId, configId, externalId, personId, status, connectorId, connectorName, lastSyncAt, consentExpiresAt, deletedAt; único (configId, externalId) |
OpenFinanceAccount | openfinance_accounts | Conta bancária do item | accountType, name, number, balance, creditLimit, availableCredit, bankData; único (itemId, externalId); cascata a partir do item |
OpenFinanceTransaction | openfinance_transactions | Lançamento da conta | transactionType, description, amount, date, category, merchantName, merchantCnpj; único (accountId, externalId); cascata a partir da conta |
OpenFinanceIdentity | openfinance_identities | Titular segundo o banco | fullName, documentNumber (CPF/CNPJ), birthDate, email, phoneNumber, address; um por item, cascata |
OpenFinanceWebhookEvent | openfinance_webhook_events | Eventos recebidos do provedor | eventType, externalEventId, payload, processed, processedAt, error |
Enumerações
| Enum | Valores |
|---|---|
OpenFinanceProviderType | PLUGGY · BELVO (declarado, não implementado — ver §15) |
OpenFinanceItemStatus | PENDING · LOGIN_ERROR · OUTDATED · UPDATING · UPDATED · WAITING_USER_INPUT · WAITING_USER_ACTION |
OpenFinanceAccountType | CHECKING · SAVINGS · CREDIT_CARD · INVESTMENT · LOAN |
OpenFinanceTransactionType | CREDIT · DEBIT |
Como as seis tabelas se ligam
erDiagram
OpenFinanceProviderConfig ||--o{ OpenFinanceItem : "credencial da organização"
OpenFinanceItem ||--o{ OpenFinanceAccount : "cascata"
OpenFinanceItem ||--o| OpenFinanceIdentity : "um por item, cascata"
OpenFinanceAccount ||--o{ OpenFinanceTransaction : "cascata"
OpenFinanceProviderConfig {
string organizationId
string providerType
string credentials "cifrado AES-256-GCM"
boolean isDefault
boolean isActive
}
OpenFinanceItem {
string organizationId
string configId
string externalId
string personId "pessoa no Customers"
string status
int connectorId
datetime lastSyncAt
datetime consentExpiresAt
}
OpenFinanceAccount {
string accountType
string number
decimal balance
decimal creditLimit
json bankData
}
OpenFinanceTransaction {
string transactionType
decimal amount
datetime date
string category
string merchantCnpj
}
OpenFinanceIdentity {
string fullName
string documentNumber
datetime birthDate
string address
}
OpenFinanceWebhookEvent {
string eventType
string externalEventId
json payload
boolean processed
}
OpenFinanceWebhookEventaparece solto de propósito: ele guarda a carga bruta do evento e não tem chave estrangeira para o item — é por isso que ele não cai por cascata num expurgo (§14).
Estados de um item
stateDiagram-v2 [*] --> PENDING: POST /items/sync state "PENDING — criado, ainda sem dado" as PENDING state "UPDATING — buscando" as UPDATING state "WAITING_USER_INPUT — precisa de MFA" as WAITING_USER_INPUT state "WAITING_USER_ACTION — o banco exige ação do titular" as WAITING_USER_ACTION state "LOGIN_ERROR — credencial recusada" as LOGIN_ERROR state "UPDATED — dado ok" as UPDATED state "OUTDATED — dado velho" as OUTDATED PENDING --> UPDATING PENDING --> WAITING_USER_INPUT PENDING --> WAITING_USER_ACTION PENDING --> LOGIN_ERROR UPDATING --> UPDATED UPDATED --> OUTDATED: passa do prazo do provedor
Atenção. Nenhuma transição é decidida aqui: o status vem do provedor e é espelhado. POST /items/:id/refresh é o que puxa o estado novo. WAITING_USER_ACTION aparece quando o banco exige ação do titular.
| Status | O que significa | O que fazer |
|---|---|---|
PENDING | Item criado, ainda sem dado | Aguarde e chame refresh |
UPDATING | O provedor está buscando | Aguarde |
UPDATED | Dado ok | Pode chamar sync-data |
OUTDATED | O provedor considera o dado velho | Chame refresh e, se preciso, sync-data |
WAITING_USER_INPUT | Precisa de MFA | Leve o titular de volta ao widget |
WAITING_USER_ACTION | O banco exige ação do titular | Leve o titular de volta ao widget |
LOGIN_ERROR | Credencial recusada | Refaça a conexão pelo connect/token |
Referência da API
Prefixo: /open-finance. Em staging, a base é https://open-finance.bb.stg.catalisa.app; em produção, https://open-finance.bb.catalisa.app.
Todas as rotas abaixo, exceto o webhook, aplicam nesta ordem: authMiddleware → requirePermission(...) → requireOrganization.
Configuração de provedor — /open-finance/api/v1/provider-configs
| Método | Rota | Descrição | Permissão |
|---|---|---|---|
POST | /open-finance/api/v1/provider-configs | Cria a configuração; testa a credencial antes de cifrar e gravar | OPENFINANCE_ADMIN |
GET | /open-finance/api/v1/provider-configs | Lista, paginado | OPENFINANCE_READ |
GET | /open-finance/api/v1/provider-configs/:id | Busca uma configuração | OPENFINANCE_READ |
PATCH | /open-finance/api/v1/provider-configs/:id | Atualiza nome, credenciais, ajustes ou status | OPENFINANCE_ADMIN |
DELETE | /open-finance/api/v1/provider-configs/:id | Exclusão lógica. Responde 204 | OPENFINANCE_ADMIN |
POST | /open-finance/api/v1/provider-configs/:id/set-default | Marca como padrão da organização | OPENFINANCE_ADMIN |
POST | /open-finance/api/v1/provider-configs/:id/test | Testa a conexão com o provedor | OPENFINANCE_ADMIN |
Conexão — /open-finance/api/v1/connect e /connectors
| Método | Rota | Descrição | Permissão |
|---|---|---|---|
POST | /open-finance/api/v1/connect/token | Gera o token do widget de autorização. Responde 201 | OPENFINANCE_WRITE |
GET | /open-finance/api/v1/connectors | Lista as instituições disponíveis. Filtros: filter[name], filter[types], filter[countries], configId | OPENFINANCE_READ |
GET | /open-finance/api/v1/connectors/:id | Detalhe de uma instituição. O :id é numérico | OPENFINANCE_READ |
Itens — /open-finance/api/v1/items
| Método | Rota | Descrição | Permissão |
|---|---|---|---|
POST | /open-finance/api/v1/items/sync | Registra o item criado no widget. Responde 201 | OPENFINANCE_WRITE |
GET | /open-finance/api/v1/items | Lista itens. Filtros: filter[status], filter[personId], filter[businessId] | OPENFINANCE_READ |
GET | /open-finance/api/v1/items/:id | Busca um item | OPENFINANCE_READ |
POST | /open-finance/api/v1/items/:id/refresh | Atualiza estado e consentExpiresAt a partir do provedor | OPENFINANCE_WRITE |
POST | /open-finance/api/v1/items/:id/sync-data | Sincroniza contas, transações e identidade | OPENFINANCE_WRITE |
POST | /open-finance/api/v1/items/:id/link-person | Liga o item a uma pessoa do Customers | OPENFINANCE_WRITE |
DELETE | /open-finance/api/v1/items/:id | Encerra a conexão no provedor e faz exclusão lógica aqui. Responde 204 | OPENFINANCE_ADMIN |
Contas, transações e identidade
| Método | Rota | Descrição | Permissão |
|---|---|---|---|
GET | /open-finance/api/v1/items/:itemId/accounts | Contas do item. Filtro: filter[accountType] | OPENFINANCE_READ |
GET | /open-finance/api/v1/accounts/:id | Busca uma conta | OPENFINANCE_READ |
GET | /open-finance/api/v1/accounts/:accountId/transactions | Lançamentos da conta. Filtros: filter[startDate], filter[endDate], filter[category] | OPENFINANCE_READ |
GET | /open-finance/api/v1/transactions/:id | Busca um lançamento | OPENFINANCE_READ |
GET | /open-finance/api/v1/items/:itemId/identity | Identidade do titular no item | OPENFINANCE_READ |
Webhook do provedor
| Método | Rota | Descrição | Permissão |
|---|---|---|---|
POST | /open-finance/api/v1/webhooks/:providerType | Recebe eventos do agregador. :providerType é pluggy (aceito sem diferenciar maiúsculas) | Nenhuma — rota pública, sem authMiddleware e sem requireOrganization |
Saúde
| Método | Rota | Descrição |
|---|---|---|
GET | /open-finance/health | Nome do serviço e versão |
Paginação. As listagens usam
page[number]epage[size]. O padrão é 20, exceto em transações, onde é 50, e em conectores, onde é 50.
POST /open-finance/api/v1/provider-configs
Cria a credencial da organização no agregador. Aceita o corpo direto ou embrulhado em data.attributes.
Request
{
"name": "Pluggy Produção",
"providerType": "PLUGGY",
"credentials": {
"clientId": "...",
"clientSecret": "..."
},
"isDefault": true,
"isActive": true
}{
"name": "Pluggy Produção",
"providerType": "PLUGGY",
"credentials": {
"clientId": "...",
"clientSecret": "..."
},
"isDefault": true,
"isActive": true
}| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
name | string (1 a 100) | Sim | Único dentro da organização |
providerType | PLUGGY | BELVO | Sim | O schema aceita os dois; só PLUGGY funciona (§15) |
credentials | object de strings | Sim | Para a Pluggy: clientId e clientSecret. Cifrado antes de gravar |
isDefault | boolean | Não | Usado quando a requisição não informa configId |
isActive | boolean | Não | Padrão true |
settings | object | Não | Ajustes específicos do provedor |
Resposta 201 — o registro criado. As credenciais nunca voltam, em nenhuma resposta.
Erros
| Status | Quando |
|---|---|
400 | Corpo reprovado no Zod; credenciais recusadas pelo provedor no teste de conexão; providerType sem implementação |
403 | Falta OPENFINANCE_ADMIN ou falta organizationId no token |
409 | Já existe configuração com esse nome na organização |
POST /open-finance/api/v1/connect/token
Gera o token que o seu front usa para abrir o widget de autorização do provedor. É o passo 1 do fluxo de consentimento.
Request
{
"configId": "e0d1c2b3-...",
"webhookUrl": "https://open-finance.bb.catalisa.app/open-finance/api/v1/webhooks/pluggy",
"clientUserId": "b1000000-0000-0000-0000-000000000001"
}{
"configId": "e0d1c2b3-...",
"webhookUrl": "https://open-finance.bb.catalisa.app/open-finance/api/v1/webhooks/pluggy",
"clientUserId": "b1000000-0000-0000-0000-000000000001"
}| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
configId | string (UUID) | Não | Omitido, usa a configuração padrão da organização |
webhookUrl | string (URL) | Não | Para onde o provedor avisa as mudanças deste item |
clientUserId | string | Não | Rastreia quem iniciou a conexão. Omitido, usa o sub do token |
Resposta 201
{
"data": {
"type": "openfinance-connect-token",
"attributes": { "accessToken": "...", "expiresAt": null }
}
}{
"data": {
"type": "openfinance-connect-token",
"attributes": { "accessToken": "...", "expiresAt": null }
}
}
expiresAtvolta nulo porque o provedor não devolve a data. Os tokens de conexão da Pluggy são de curta duração — trate como válido por poucos minutos e gere um novo a cada abertura do widget, em vez de reaproveitar.
POST /open-finance/api/v1/items/sync
Passo 6 do fluxo: registra aqui o item que o widget criou no provedor. Idempotente por (configId, externalItemId) — chamar de novo atualiza em vez de duplicar.
Request
{
"configId": "e0d1c2b3-...",
"externalItemId": "a1b2c3d4-...",
"personId": "f5e4d3c2-...",
"businessId": "proposta-88213",
"metadata": { "origem": "esteira-credito" }
}{
"configId": "e0d1c2b3-...",
"externalItemId": "a1b2c3d4-...",
"personId": "f5e4d3c2-...",
"businessId": "proposta-88213",
"metadata": { "origem": "esteira-credito" }
}| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
externalItemId | string | Sim | O identificador do item devolvido pelo widget |
configId | string (UUID) | Não | Omitido, usa a configuração padrão |
personId | string (UUID) | Não | Pessoa no Customers |
businessId | string | Não | Seu identificador de negócio |
metadata | object | Não | Campo livre |
Resposta 201 — o item, já com status, connectorName, lastSyncAt e consentExpiresAt.
Depois disso, chame POST /items/:id/sync-data para trazer contas, transações e identidade. O sync sozinho não traz dado bancário.
GET /open-finance/api/v1/accounts/:accountId/transactions
Query string
| Parâmetro | Descrição |
|---|---|
page[number] | Página, padrão 1 |
page[size] | Tamanho, padrão 50 |
filter[startDate] | Data inicial |
filter[endDate] | Data final |
filter[category] | Categoria atribuída pelo provedor |
Resposta 200
{
"data": [
{
"type": "openfinance-transaction",
"id": "9f8e7d6c-...",
"attributes": {
"transactionType": "DEBIT",
"description": "PIX ENVIADO",
"amount": -150.0,
"currencyCode": "BRL",
"date": "2026-08-14T00:00:00.000Z",
"category": "Transfers",
"merchantName": "Mercado Exemplo",
"merchantCnpj": "00000000000191"
}
}
],
"meta": { "totalItems": 45, "totalPages": 1, "currentPage": 1, "pageSize": 50 }
}{
"data": [
{
"type": "openfinance-transaction",
"id": "9f8e7d6c-...",
"attributes": {
"transactionType": "DEBIT",
"description": "PIX ENVIADO",
"amount": -150.0,
"currencyCode": "BRL",
"date": "2026-08-14T00:00:00.000Z",
"category": "Transfers",
"merchantName": "Mercado Exemplo",
"merchantCnpj": "00000000000191"
}
}
],
"meta": { "totalItems": 45, "totalPages": 1, "currentPage": 1, "pageSize": 50 }
}Atenção ao campo links.self das respostas. Ele é montado como /api/v1/open-finance/..., com os segmentos invertidos em relação ao caminho real, que é /open-finance/api/v1/.... Não navegue pelos links — monte as URLs a partir desta seção. É um defeito conhecido, registrado na §15.
Início rápido
Do zero ao primeiro extrato, em staging. Este caminho exige uma conta ativa no agregador — sem credencial válida, o passo 2 falha por design.
flowchart LR P1["1 · IAM<br/>token"] --> P2["2 · provider-configs<br/>credencial do agregador"] P2 --> P3["3 · connectors<br/>quais bancos existem"] P3 --> P4["4 · connect/token<br/>o consentimento começa"] P4 --> P5["5 · items/sync + sync-data<br/>registra e traz os dados"] P5 --> P6["6 · accounts/:id/transactions<br/>o extrato"] P6 --> P7["7 · consentExpiresAt<br/>anote o vencimento"]
1. Autenticar no IAM
TOKEN=$(curl -s -X POST https://iam.bb.stg.catalisa.app/iam/api/v1/users/login \
-H "Content-Type: application/json" \
-d '{
"email": "admin@catalisa.app",
"password": "root123456",
"organizationId": "b0000000-0000-0000-0000-000000000001"
}' | jq -r .accessToken)
BASE=https://open-finance.bb.stg.catalisa.appTOKEN=$(curl -s -X POST https://iam.bb.stg.catalisa.app/iam/api/v1/users/login \
-H "Content-Type: application/json" \
-d '{
"email": "admin@catalisa.app",
"password": "root123456",
"organizationId": "b0000000-0000-0000-0000-000000000001"
}' | jq -r .accessToken)
BASE=https://open-finance.bb.stg.catalisa.app2. Cadastrar a credencial do agregador
CONFIG=$(curl -s -X POST "$BASE/open-finance/api/v1/provider-configs" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "Pluggy Sandbox",
"providerType": "PLUGGY",
"credentials": {"clientId":"<seu-client-id>","clientSecret":"<seu-client-secret>"},
"isDefault": true
}')
CONFIG_ID=$(echo "$CONFIG" | jq -r '.data.id')CONFIG=$(curl -s -X POST "$BASE/open-finance/api/v1/provider-configs" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "Pluggy Sandbox",
"providerType": "PLUGGY",
"credentials": {"clientId":"<seu-client-id>","clientSecret":"<seu-client-secret>"},
"isDefault": true
}')
CONFIG_ID=$(echo "$CONFIG" | jq -r '.data.id')Se a credencial estiver errada, esta chamada responde 400 agora — a conexão é testada antes de a configuração ser gravada.
3. Ver quais instituições estão disponíveis
curl -s "$BASE/open-finance/api/v1/connectors?page[size]=5" \
-H "Authorization: Bearer $TOKEN" \
| jq '.data[] | {id, nome: .attributes.name, mfa: .attributes.hasMFA}'curl -s "$BASE/open-finance/api/v1/connectors?page[size]=5" \
-H "Authorization: Bearer $TOKEN" \
| jq '.data[] | {id, nome: .attributes.name, mfa: .attributes.hasMFA}'4. Gerar o token do widget — o consentimento começa aqui
curl -s -X POST "$BASE/open-finance/api/v1/connect/token" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d "{\"configId\":\"$CONFIG_ID\"}" | jq -r '.data.attributes.accessToken'curl -s -X POST "$BASE/open-finance/api/v1/connect/token" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d "{\"configId\":\"$CONFIG_ID\"}" | jq -r '.data.attributes.accessToken'Esse token vai para o seu front, que abre o widget do provedor. O cliente escolhe o banco e autoriza no ambiente do próprio banco. Nenhuma credencial bancária passa pela sua aplicação nem pela nossa. Ao final, o widget devolve o itemId.
5. Registrar o item e trazer os dados
ITEM=$(curl -s -X POST "$BASE/open-finance/api/v1/items/sync" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d "{\"configId\":\"$CONFIG_ID\",\"externalItemId\":\"<itemId-do-widget>\"}")
ITEM_ID=$(echo "$ITEM" | jq -r '.data.id')
echo "$ITEM" | jq '.data.attributes | {status, connectorName, consentExpiresAt}'
curl -s -X POST "$BASE/open-finance/api/v1/items/$ITEM_ID/sync-data" \
-H "Authorization: Bearer $TOKEN" | jq '.data.attributes.lastSyncAt'ITEM=$(curl -s -X POST "$BASE/open-finance/api/v1/items/sync" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d "{\"configId\":\"$CONFIG_ID\",\"externalItemId\":\"<itemId-do-widget>\"}")
ITEM_ID=$(echo "$ITEM" | jq -r '.data.id')
echo "$ITEM" | jq '.data.attributes | {status, connectorName, consentExpiresAt}'
curl -s -X POST "$BASE/open-finance/api/v1/items/$ITEM_ID/sync-data" \
-H "Authorization: Bearer $TOKEN" | jq '.data.attributes.lastSyncAt'6. Ler o extrato
ACCOUNT_ID=$(curl -s "$BASE/open-finance/api/v1/items/$ITEM_ID/accounts" \
-H "Authorization: Bearer $TOKEN" | jq -r '.data[0].id')
curl -s "$BASE/open-finance/api/v1/accounts/$ACCOUNT_ID/transactions?page[size]=10" \
-H "Authorization: Bearer $TOKEN" | jq '.data[] | .attributes | {date, description, amount}'ACCOUNT_ID=$(curl -s "$BASE/open-finance/api/v1/items/$ITEM_ID/accounts" \
-H "Authorization: Bearer $TOKEN" | jq -r '.data[0].id')
curl -s "$BASE/open-finance/api/v1/accounts/$ACCOUNT_ID/transactions?page[size]=10" \
-H "Authorization: Bearer $TOKEN" | jq '.data[] | .attributes | {date, description, amount}'7. Anotar quando o consentimento vence
curl -s "$BASE/open-finance/api/v1/items/$ITEM_ID" \
-H "Authorization: Bearer $TOKEN" | jq '.data.attributes.consentExpiresAt'curl -s "$BASE/open-finance/api/v1/items/$ITEM_ID" \
-H "Authorization: Bearer $TOKEN" | jq '.data.attributes.consentExpiresAt'Guarde essa data no seu lado e trate o vencimento. O building block não bloqueia leitura de item com consentimento vencido (§15).
Credenciais de staging, conforme AMBIENTES.md. Nunca cole
clientSecretde produção em documentação ou script — guarde com SOPS (SECRETS-SOPS-REFERENCE.md).Os comandos acima não foram executados durante a redação deste documento; valide no seu ambiente antes de copiar para um runbook.
Receitas
Manter o extrato atualizado sem estourar o custo do agregador
Objetivo: dado fresco o suficiente para decidir, sem pagar por sincronização desnecessária.
flowchart TD
Q(["Preciso de dado atual"])
Q --> W{"O provedor já avisou<br/>por webhook?"}
W -->|sim| SD["POST /items/:id/sync-data<br/>caro, mas justificado"]
W -->|não| N{"Preciso de transação nova<br/>ou só do estado do item?"}
N -->|"só o estado"| RF["POST /items/:id/refresh<br/>barato"]
N -->|"transação nova"| SD
RF --> ST["status · lastSyncAt · consentExpiresAt"]
SD --> DT["contas · transações · identidade"]1. O barato — só atualiza estado e consentExpiresAt
curl -s -X POST "$BASE/open-finance/api/v1/items/$ITEM_ID/refresh" \
-H "Authorization: Bearer $TOKEN" | jq '.data.attributes | {status, lastSyncAt, consentExpiresAt}'curl -s -X POST "$BASE/open-finance/api/v1/items/$ITEM_ID/refresh" \
-H "Authorization: Bearer $TOKEN" | jq '.data.attributes | {status, lastSyncAt, consentExpiresAt}'Resposta esperada: um objeto com o status vindo do provedor, o lastSyncAt recém-atualizado e a data do consentimento. O formato é este — os valores abaixo são ilustrativos, porque dependem do seu item:
{
"status": "UPDATED",
"lastSyncAt": "2026-08-16T12:04:11.000Z",
"consentExpiresAt": "2027-02-10T00:00:00.000Z"
}{
"status": "UPDATED",
"lastSyncAt": "2026-08-16T12:04:11.000Z",
"consentExpiresAt": "2027-02-10T00:00:00.000Z"
}2. O caro — percorre contas, transações e identidade
curl -s -X POST "$BASE/open-finance/api/v1/items/$ITEM_ID/sync-data" \
-H "Authorization: Bearer $TOKEN"curl -s -X POST "$BASE/open-finance/api/v1/items/$ITEM_ID/sync-data" \
-H "Authorization: Bearer $TOKEN"Resposta esperada: o item com lastSyncAt novo — e, a partir daí, as contas e transações já disponíveis nas rotas de leitura.
Armadilhas.
refreshnão traz transação nova. Ele atualiza o item. Quem traz dado é osync-data.sync-datapercorre todas as contas e, para cada uma, uma página de até 100 transações. Em item com muitas contas, é a chamada mais cara do conjunto — e o agregador cobra por requisição acima do mínimo mensal.- Prefira reagir ao webhook
transactions.updateda sincronizar por relógio. Sincronizar de hora em hora em milhares de itens é a forma mais rápida de descobrir o custo marginal do seu contrato. - A gravação é do tipo insere-ou-atualiza por
(accountId, externalId), então sincronizar de novo não duplica lançamento.
Cadastrar o webhook do provedor e conferir que ele chega
Objetivo: parar de perguntar ao provedor e passar a ser avisado.
Aponte o webhook do agregador para:
https://open-finance.bb.catalisa.app/open-finance/api/v1/webhooks/pluggyhttps://open-finance.bb.catalisa.app/open-finance/api/v1/webhooks/pluggyVocê também pode informar webhookUrl na criação do token de conexão, para que o provedor avise sobre aquele item específico.
Depois de um evento chegar, confirme que ele foi processado:
SELECT event_type, processed, processed_at, error
FROM openfinance.openfinance_webhook_events
ORDER BY created_at DESC LIMIT 10;SELECT event_type, processed, processed_at, error
FROM openfinance.openfinance_webhook_events
ORDER BY created_at DESC LIMIT 10;Armadilhas.
- Evento sem referência de item é recusado com
400. É proposital: sem o item não há como saber a qual organização o evento pertence, e atribuir a uma configuração arbitrária misturaria dado financeiro entre clientes. O provedor reenvia, e o que sobra é reconciliado na próxima sincronização. - Item desconhecido também é recusado. Se o webhook chegar antes de você ter chamado
POST /items/sync, ele não tem onde pousar. Registre o item logo após o widget fechar. - A rota é pública e o caminho contém o tipo do provedor. Trate a URL como endereço operacional e monitore o volume.
Ligar a conexão bancária ao cadastro de pessoas
curl -s -X POST "$BASE/open-finance/api/v1/items/$ITEM_ID/link-person" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"personId":"f5e4d3c2-..."}'
# depois, listar tudo que aquela pessoa conectou
curl -s "$BASE/open-finance/api/v1/items?filter[personId]=f5e4d3c2-..." \
-H "Authorization: Bearer $TOKEN" | jq '.data[] | .attributes.connectorName'curl -s -X POST "$BASE/open-finance/api/v1/items/$ITEM_ID/link-person" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"personId":"f5e4d3c2-..."}'
# depois, listar tudo que aquela pessoa conectou
curl -s "$BASE/open-finance/api/v1/items?filter[personId]=f5e4d3c2-..." \
-H "Authorization: Bearer $TOKEN" | jq '.data[] | .attributes.connectorName'Armadilhas. O personId também pode ser informado já no POST /items/sync, o que evita uma chamada. E vale conferir a identidade: GET /items/:itemId/identity traz o documento como o banco tem registrado, e divergência com o cadastro é exceção a tratar, não detalhe a ignorar.
Rotacionar a credencial do agregador sem derrubar a operação
curl -s -X PATCH "$BASE/open-finance/api/v1/provider-configs/$CONFIG_ID" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"credentials":{"clientId":"<novo>","clientSecret":"<novo>"}}'
curl -s -X POST "$BASE/open-finance/api/v1/provider-configs/$CONFIG_ID/test" \
-H "Authorization: Bearer $TOKEN" | jqcurl -s -X PATCH "$BASE/open-finance/api/v1/provider-configs/$CONFIG_ID" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"credentials":{"clientId":"<novo>","clientSecret":"<novo>"}}'
curl -s -X POST "$BASE/open-finance/api/v1/provider-configs/$CONFIG_ID/test" \
-H "Authorization: Bearer $TOKEN" | jqArmadilhas.
- A credencial antiga é substituída na hora. Combine a janela com quem administra a conta no agregador.
- Rotacionar a chave mestra
OPENFINANCE_CREDENTIAL_MASTER_KEYé outra coisa e não está automatizado: todas as configurações existentes ficam indecifráveis. Não troque essa variável sem um plano de recadastro. - Uma alternativa a rotacionar é criar uma configuração nova, testá-la, marcá-la como padrão com
set-defaulte só então desativar a antiga. Itens existentes continuam apontando para a configuração antiga peloconfigId.
Encerrar uma conexão a pedido do titular
curl -s -o /dev/null -w "%{http_code}\n" -X DELETE \
"$BASE/open-finance/api/v1/items/$ITEM_ID" \
-H "Authorization: Bearer $TOKEN"
# 204curl -s -o /dev/null -w "%{http_code}\n" -X DELETE \
"$BASE/open-finance/api/v1/items/$ITEM_ID" \
-H "Authorization: Bearer $TOKEN"
# 204Armadilhas — leia com atenção. Essa chamada encerra a conexão no provedor e marca deletedAt no item aqui. Ela não apaga as contas, transações e identidade já sincronizadas: esses registros continuam no banco. Se o pedido do titular for de eliminação de dados, e não apenas de revogação de acesso, o expurgo precisa ser feito à parte. Veja a §14.
Integração com outros building blocks
| Building block | Como se relaciona | Obrigatório |
|---|---|---|
| IAM | Emite o token; exige OPENFINANCE_READ/WRITE/ADMIN e organizationId | Sim |
| Customers | O item se liga a uma pessoa por personId, e a identidade bancária confere o cadastro | Não, mas é o uso natural |
| Decision Platform | Consome extrato e saldo como entrada de regra de decisão | Não |
| Pricing Engine | Usa o comportamento financeiro como insumo de precificação por risco | Não |
| Webhooks Engine | Repassa os eventos openfinance.* ao sistema do cliente | Não |
| Audit Trail | Registra quem consultou dado bancário — recomendado, dada a sensibilidade | Não |
| Data Store | Guarda o estado da esteira que consome o extrato | Não |
Eventos publicados
| Evento | Quando |
|---|---|
openfinance.provider_config.created / .updated / .deleted | Ciclo de vida da credencial |
openfinance.item.synced | Item registrado a partir do widget |
openfinance.item.refreshed | Estado do item atualizado |
openfinance.item.person_linked | Item ligado a uma pessoa |
openfinance.item.deleted | Conexão encerrada |
openfinance.data.synced | Contas, transações e identidade sincronizadas |
openfinance.webhook.<tipo> | Evento recebido do provedor, repassado ao barramento |
flowchart TD CLI["Seu cliente"] -->|"autoriza no banco"| AGG["Agregador (Pluggy)"] AGG -->|"dados sob consentimento"| OF["Open Finance<br/>contas · transações · identidade"] OF -->|personId| CUS["Customers"] OF -->|"openfinance.data.synced"| WHE["Webhooks Engine"] WHE --> SIS["Sistema do cliente"] OF -->|"extrato e saldo"| DP["Decision Platform<br/>aprova ou recusa"] DP --> PE["Pricing Engine<br/>preço por risco"]
O argumento comercial está na segunda metade do diagrama: o extrato não para numa tela de consulta. Ele entra na decisão e no preço, com a mesma identidade e a mesma organização do começo ao fim — e é isso que um agregador, sozinho, não entrega.
Configuração e operação
Variáveis de ambiente
| Variável | Descrição | Obrigatória | Padrão |
|---|---|---|---|
OPENFINANCE_CREDENTIAL_MASTER_KEY | Chave mestra da cifragem das credenciais. 64 caracteres hexadecimais (32 bytes). Gere com openssl rand -hex 32 | Sim, para usar este módulo | — |
DATABASE_URL | PostgreSQL, schema openfinance | Sim | — |
REDIS_URL | Publicação dos eventos openfinance.* | Sim | — |
MODULE_IAM_URL | Endereço do IAM em standalone | Sim | — |
PORT | Porta no modo standalone | Não | 3000 no main.ts; 3016 é a porta registrada em DEFAULT_MODULE_PORTS |
PROVIDER_WEBHOOK_SIGNATURE_ENFORCEMENT | off ou on. Controla o comportamento quando um provedor não oferece verificação de assinatura | Não | off |
A chave mestra não é rotacionável automaticamente. Trocá-la torna todas as configurações existentes indecifráveis. Guarde com SOPS e trate como segredo de longo prazo.
As credenciais do agregador não são variável de ambiente: elas vivem cifradas no banco, por organização, e entram pela API.
Dependências de infraestrutura
| Dependência | Para quê |
|---|---|
PostgreSQL, schema openfinance | As seis tabelas |
| Redis | Publicação de eventos |
| Saída para a internet | Chamadas ao agregador (https://api.pluggy.ai por padrão) |
| URL pública alcançável | Recepção do webhook do agregador |
Limites
| Limite | Valor |
|---|---|
| Nome da configuração | 1 a 100 caracteres, único por organização |
| Página padrão em listagens | 20 itens |
| Página padrão em transações | 50 itens |
| Página padrão em conectores | 50 itens |
Transações por conta em sync-data | 100 por conta e por execução |
| Cache do token do agregador | 2 horas menos 5 minutos |
| Prazo máximo do consentimento | 12 meses, por norma — o valor real vem do provedor |
Catálogo de erros
| Status | Código | Significa | O que fazer |
|---|---|---|---|
400 | VALIDATION | Corpo reprovado no Zod; credenciais recusadas no teste; providerType sem implementação; webhook sem referência de item | Leia a mensagem — ela distingue os casos |
400 | — | connectorId não numérico na rota de conector | Use o identificador numérico do conector |
401 | UNAUTHORIZED | Token ausente ou inválido | Renove no IAM |
403 | FORBIDDEN | Falta a permissão de Open Finance, ou falta organizationId no token | Autentique informando a organização |
404 | NOT_FOUND | Recurso inexistente, excluído logicamente ou de outra organização | Confira o identificador e o tenant |
409 | CONFLICT | Já existe configuração com esse nome | Escolha outro nome |
500 | INTERNAL | Falha na chamada ao agregador | Veja o log; use POST /provider-configs/:id/test para isolar |
Observabilidade.
GET /open-finance/healthdevolve nome e versão. Não testa o agregador — para isso,POST /provider-configs/:id/test.- Todo evento recebido do provedor é gravado em
openfinance_webhook_eventscomprocessed,processedAteerror. É a trilha para responder "esse aviso chegou?". OpenFinanceItem.lastSyncAtestatusrespondem "esse dado está fresco?".status = OUTDATEDsignifica que o provedor considera o dado velho.- Nenhuma credencial aparece em log. Os erros de autenticação com o agregador registram o código de status, não o segredo.
Segurança e compliance
Quem é o participante regulado — leia antes de vender
A Catalisa não é instituição participante do Open Finance Brasil, e este building block não a torna uma. O art. 1º da Resolução Conjunta nº 1, de 4 de maio de 2020 é explícito: a implementação do Sistema Financeiro Aberto cabe a "instituições financeiras, instituições de pagamento e demais instituições autorizadas a funcionar pelo Banco Central do Brasil".
Quem é participante, no arranjo deste módulo, é o agregador contratado. A entidade Pluggy Brasil Instituição de Pagamento Ltda. (CNPJ 37.943.755/0001-30) consta como ativa no diretório oficial de participantes, com os papéis de Dados e de Pagamentos.
Uma empresa não autorizada que consome dados via API de um agregador se enquadra no art. 36 — Da Contratação de Parceria, que admite "a contratação de parceria por parte das instituições de que trata o art. 1º com entidades não autorizadas a funcionar pelo Banco Central do Brasil". Disso decorrem consequências concretas:
| O que a norma diz | Consequência prática |
|---|---|
| Art. 36, § 1º: o compartilhamento "pressupõe prévio e expresso consentimento do cliente" | O consentimento é condição, não formalidade |
| Art. 36, § 5º, I: é vedada a parceria "entre instituições autorizadas a funcionar pelo Banco Central" | Se a sua empresa é autorizada pelo BACEN, essa via não serve — você precisa participar diretamente |
| Art. 36, § 5º, II: é vedado ao parceiro "atuar em nome da instituição contratante" | O parceiro não é preposto nem correspondente do agregador |
| Art. 36, § 6º e art. 37 | A contratação exige parecer do diretor responsável do participante e diligência documentada sobre o parceiro |
| Art. 52 | O Banco Central "poderá vetar ou impor restrições ao compartilhamento de que trata o art. 36" |
Em resumo: o risco regulatório fica com o agregador; o risco contratual e de continuidade fica com quem contrata. Se o Banco Central vetar um compartilhamento, quem perde o acesso é você. O material comercial pode dizer "opere sobre o Open Finance Brasil por meio de um participante regulado". Não pode dizer "somos participantes do Open Finance".
Um segundo ponto de honestidade técnica: os agregadores brasileiros oferecem conexão via Open Finance e também acesso direto a instituições fora do escopo regulado. A Pluggy anuncia "Open Finance + acesso direto" (pluggy.ai). Nem toda conexão feita por este building block é, portanto, uma conexão de Open Finance regulado. O enquadramento de cada conector precisa ser confirmado com o agregador antes de ser apresentado como tal.
Consentimento: prazo, renovação e revogação
O consentimento é dado pelo titular no ambiente do banco dele. Nenhuma credencial bancária passa pela sua aplicação nem pela nossa: o widget do provedor abre com um token de curta duração e a autenticação acontece do lado da instituição.
| Regra | O que diz a norma | Como aparece aqui |
|---|---|---|
| Prazo | Art. 10, § 1º, III: prazo "compatível com as finalidades", limitado a doze meses | O valor vem do provedor e é gravado em OpenFinanceItem.consentExpiresAt |
| Finalidade | Art. 10, § 1º, II: o consentimento se refere a finalidades determinadas | Definida no seu contrato com o titular, fora deste módulo |
| Renovação | Art. 10, § 2º: alterar finalidade, prazo, instituição ou escopo exige novo consentimento | Não há renovação automática. Ao vencer, o fluxo recomeça no POST /connect/token |
| Como não obter | Art. 10, § 3º: vedado por contrato de adesão, com opção pré-marcada, ou de forma presumida | A coleta é do widget do provedor; a sua interface não deve pré-marcar nada |
| Revogação | Art. 15: a qualquer tempo, pelo mesmo canal em que foi concedido | DELETE /items/:id encerra a conexão no provedor |
| Prazo da revogação | Art. 15, § 3º: imediata para compartilhamento de dados; até 1 dia para iniciação de pagamento | O encerramento no provedor é síncrono na chamada |
| Vedação | Art. 15, § 2º: é vedado ao transmissor propor a revogação, salvo suspeita de fraude | Regra do participante, não deste módulo |
Duas limitações que precisam estar claras. Primeiro: o vencimento do consentimento não é obrigado por este building block. consentExpiresAt é gravado e devolvido, mas nenhuma rotina bloqueia a leitura de um item vencido, e nenhum alerta é emitido. Guarde essa data do seu lado e trate o vencimento no seu processo. Segundo: revogar não apaga. O DELETE encerra a conexão no provedor e faz exclusão lógica do item, mas contas, transações e identidade já sincronizadas permanecem no banco. Isso é deliberado, para não perder o insumo de uma decisão já tomada — mas significa que um pedido de eliminação sob a LGPD exige expurgo à parte.
O que é armazenado, e por quanto tempo
Este é o building block que guarda o dado mais sensível do catálogo. O inventário é curto e vale conhecê-lo inteiro:
| Tabela | O que guarda | Sensibilidade |
|---|---|---|
openfinance_identities | Nome completo, CPF ou CNPJ, data de nascimento, e-mail, telefone e endereço, como o banco registra | Dado pessoal direto |
openfinance_accounts | Tipo de conta, número, saldo, limite e crédito disponível, código do banco e agência | Dado financeiro |
openfinance_transactions | Descrição, valor, data, categoria, nome e CNPJ do estabelecimento | Dado financeiro comportamental — permite inferir hábitos, saúde, filiação e localização |
openfinance_items | A conexão, o titular ligado e o prazo do consentimento | Metadado de consentimento |
openfinance_provider_configs | Credenciais do agregador, cifradas com AES-256-GCM | Segredo de acesso |
openfinance_webhook_events | Carga bruta do evento do provedor | Pode conter fragmentos dos dados acima |
Retenção: não há política automática. Nada expira, nada é apagado por rotina. Os registros permanecem enquanto a linha existir no banco. Para atender a um pedido de eliminação hoje, o caminho é manual: apagar as linhas de openfinance_items do titular — openfinance_accounts, openfinance_transactions e openfinance_identities caem por cascata — e limpar as cargas correspondentes em openfinance_webhook_events, que não cai por cascata. Defina essa política antes de entrar em produção com dado real.
Isolamento entre tenants
O organizationId vem do token, nunca do corpo. Todas as 22 rotas autenticadas aplicam requireOrganization. Os acessos são verificados assim:
- Configurações e itens comparam
organizationIdda linha com o do token e devolvem404quando difere — não403, para não confirmar que o recurso existe. - Contas, transações e identidade não guardam
organizationIdpróprio: a verificação sobe pela cadeia até o item, e o item pertence à organização. O métodogetByIdWithOrgValidationexiste exatamente para isso. - O webhook é a exceção que exigiu cuidado especial: o provedor não envia organização no evento. O handler resolve o tenant a partir do item referenciado, procurando em todas as configurações daquele tipo de provedor, e recusa o evento quando não há item. Atribuir a organização a partir da configuração usada para interpretar o payload misturaria dado financeiro entre clientes.
Credenciais e criptografia
As credenciais do agregador são cifradas com AES-256-GCM antes de tocar o banco, no formato iv:authTag:ciphertext com vetor de inicialização de 12 bytes gerado por chamada. GCM é modo autenticado: adulterar o registro quebra a decifragem em vez de produzir credencial silenciosamente errada. A chave mestra vem de OPENFINANCE_CREDENTIAL_MASTER_KEY, validada como 64 caracteres hexadecimais. As credenciais nunca voltam em resposta de API e nunca aparecem em log.
Webhook do provedor
A rota POST /api/v1/webhooks/:providerType é pública por necessidade — quem chama é o agregador. Duas coisas importam aqui. A primeira é que a Pluggy não oferece esquema de assinatura de webhook; a recomendação do próprio provedor é restringir por endereço de origem e usar URL única e difícil de adivinhar. A segunda é que o comportamento da plataforma diante de um provedor sem assinatura é controlado por PROVIDER_WEBHOOK_SIGNATURE_ENFORCEMENT (§13). Combine URL específica por ambiente com restrição de origem na borda.
Autorização
| Permissão | Dá acesso a |
|---|---|
OPENFINANCE_READ | Leitura de configurações, conectores, itens, contas, transações e identidade |
OPENFINANCE_WRITE | Token de conexão, registro e atualização de item, sincronização, ligação com pessoa |
OPENFINANCE_ADMIN | Ciclo de vida da credencial do agregador e exclusão de item |
Dada a sensibilidade, conceda OPENFINANCE_READ com o mesmo critério com que se concede acesso a extrato bancário — porque é exatamente isso —, e registre os acessos com o Audit Trail.
Limitações conhecidas
| Limitação | Impacto | Situação |
|---|---|---|
| Só a Pluggy está implementada | OpenFinanceProviderType declara PLUGGY e BELVO, e o schema de criação aceita os dois — mas a fábrica só constrói o provedor da Pluggy. Criar configuração BELVO falha com 400 e a mensagem "Unsupported Open Finance provider type". | Declarado, não implementado — não anuncie multi-provedor |
| Sem iniciação de pagamento | Este building block só lê dados. Não inicia Pix, não faz cobrança e não movimenta dinheiro, embora os agregadores contratados ofereçam isso. | Fora do escopo atual |
| O vencimento do consentimento não é obrigado | consentExpiresAt é gravado e devolvido, mas nenhuma rotina bloqueia leitura de item vencido nem emite alerta. Controle no seu lado. | Roadmap — é a lacuna de conformidade mais relevante (§14) |
| Revogar não apaga o dado já sincronizado | DELETE /items/:id encerra a conexão no provedor e faz exclusão lógica do item; contas, transações e identidade permanecem no banco. | Por design, com consequência de LGPD — expurgo é manual |
| Sem política de retenção | Nenhum dado expira. Extrato de 2026 continua lá em 2030. | Roadmap |
Campo links.self com os segmentos invertidos | As respostas montam /api/v1/open-finance/..., enquanto o caminho real é /open-finance/api/v1/.... Um cliente que navegue pelos links recebe 404. | Defeito conhecido — monte as URLs a partir da §9 |
| Sem endpoint de listagem global de contas ou transações | Só há listagem aninhada: contas por item e transações por conta. Não existe "todas as transações da organização". | Não implementado |
sync-data traz 100 transações por conta | Uma execução não pagina até o fim do histórico. Contas com muito volume exigem execuções repetidas. | Conhecido |
sync-data é síncrono | A operação percorre contas, transações e identidade dentro da requisição HTTP. Item grande pode se aproximar do tempo limite do gateway. | Conhecido — não há fila |
| Sem medição de uso | Não há contagem de conexões ativas nem de chamadas ao agregador por organização. É o que bloqueia a precificação (§6). | Roadmap |
| Chave mestra sem rotação automatizada | Trocar OPENFINANCE_CREDENTIAL_MASTER_KEY torna todas as configurações indecifráveis. Não há recadastro assistido. | Não implementado |
expiresAt do token de conexão sempre nulo | O provedor não devolve a data, e o mapeamento grava undefined. Trate o token como válido por poucos minutos. | Limitação do provedor |
| Sem verificação de assinatura no webhook da Pluggy | O provedor não oferece esquema de assinatura; a recomendação dele é restrição por origem e URL única. | Limitação do provedor (§14) |
| Nem toda conexão é Open Finance regulado | Os agregadores oferecem também acesso direto a instituições fora do escopo regulado. O building block não distingue os dois na resposta. | Confirme o enquadramento de cada conector com o agregador |
| A documentação anterior deste módulo estava incorreta | A versão anterior deste README informava porta 3018, schema open_finance, a variável OPEN_FINANCE_ENCRYPTION_KEY e endpoints inexistentes (GET /accounts, GET /accounts/:id/balance, GET /transactions, GET /identity/:itemId). Nada disso existe. | Corrigido nesta versão — confira integrações feitas com base na versão antiga |
Perguntas frequentes
A Catalisa é participante do Open Finance Brasil?
Não. Participar exige ser instituição autorizada a funcionar pelo Banco Central (art. 1º da Resolução Conjunta nº 1/2020). Quem é participante é o agregador contratado — hoje a Pluggy, ativa no diretório oficial com os papéis de Dados e Pagamentos. A Catalisa entrega a camada de software sobre esse participante. O enquadramento completo, com os artigos, está na §14, e ele deve ser lido antes de qualquer proposta comercial.
Então eu preciso de autorização do BACEN para usar isso?
Não, e é justamente esse o desenho. Uma empresa não autorizada consome dados como parceira contratada de uma participante, pelo art. 36 da mesma resolução. O que você precisa é de um contrato com o agregador. A ressalva importante é o inverso: se a sua empresa é autorizada pelo Banco Central, o art. 36, § 5º, I veda essa via — você precisa participar diretamente.
Quanto tempo vale a autorização do meu cliente?
No máximo 12 meses, pela norma. A data efetiva vem do provedor e fica em consentExpiresAt no item. Não existe renovação automática: alterar prazo, finalidade, instituição ou escopo exige novo consentimento (art. 10, § 2º). E atenção — o building block guarda a data mas não bloqueia leitura de item vencido, então o controle é seu (§15).
O meu cliente digita a senha do banco na minha tela?
Não, em nenhum momento. O fluxo abre o widget do provedor, e a autenticação acontece no ambiente da instituição financeira. Nem a sua aplicação nem a nossa vê credencial bancária. O que volta para você é um identificador de item.
Quais bancos estão disponíveis?
Depende do agregador contratado. GET /connectors responde com a lista real da sua conta, incluindo país, tipo, se exige segundo fator e quais produtos oferece. A Pluggy anuncia mais de 130 instituições (pluggy.ai); confirme na sua conta, porque a cobertura muda e pode variar por plano.
Posso usar a minha própria conta na Pluggy?
Pode, e essa é a forma recomendada. As credenciais são cadastradas por organização em POST /provider-configs, cifradas com AES-256-GCM antes de tocar o banco. Cada cliente da plataforma pode ter a própria conta no agregador, o que deixa a relação contratual com o participante clara — e permite que cada um negocie o próprio contrato.
Como eu apago o dado bancário de um cliente que pediu?
Hoje, manualmente. DELETE /items/:id encerra a conexão no provedor e faz exclusão lógica do item, mas contas, transações e identidade continuam no banco. Para eliminação, apague as linhas de openfinance_items do titular — o restante cai por cascata — e limpe as cargas correspondentes em openfinance_webhook_events, que não cai. Não há automação para isso (§15); defina o procedimento antes de subir dado real.
Dá para trocar de agregador depois?
A arquitetura foi feita para isso: a interface OpenFinanceProvider define o contrato e os tipos canônicos isolam quem consome. Trocar é implementar uma classe. Hoje, porém, só a Pluggy está implementada — BELVO está declarado no enum e não funciona (§15). Trate como capacidade arquitetural, não como funcionalidade disponível.
O que acontece se o cliente trocar a senha do banco?
O item vai para LOGIN_ERROR e as sincronizações passam a falhar. O caminho é refazer a conexão: novo POST /connect/token, o cliente autoriza de novo e você chama POST /items/sync com o novo identificador. Monitore o status dos itens — OUTDATED e LOGIN_ERROR são os dois que exigem ação.
Isso serve para iniciar um Pix?
Não. Este building block só lê dados. Iniciação de pagamento existe no ecossistema e os agregadores oferecem, mas não está implementada aqui (§15). Para movimentar dinheiro, veja o Payments e o BaaS.
Qual o volume real do Open Finance no Brasil hoje?
Na semana de referência de 31/07/2026, o painel oficial registrava 239,8 milhões de consentimentos ativos na ótica dos receptores e mais de 8,1 bilhões de chamadas de dados cadastrais e transacionais em uma única semana (Dashboard do Cidadão, atualizado em 13/08/2026). O número de consentimentos ativos vinha crescendo cerca de 3,1 milhões por semana. Consulte o painel na data da sua apresentação — ele é público e atualizado semanalmente.
Padrão: PADRAO-DOCUMENTACAO.md · Índice: INDEX.md