Conciliação bancária com IA no Brasil — o que instalar e a receita do agente

Como um agente de IA reconcilia conta-corrente e cartão contra fonte externa (planilha contábil, recebimentos esperados, NFS-e emitidas). Instala Cumbuca Open Finance Data MCP, usuário autoriza via OF (CPF + biometria), agente roda receita determinística: janelas mensais, casamento por valor + data ±N + contraparte (CPF/CNPJ), divergências para revisão. Bacen-spec normalizado, data-source-agnóstico.

Top pick
cumbuca-of-data-mcp
Last verified
Eval method
auxiliar-reconciliacao-bancaria-documented-characteristics-v1
Eval score
6.5/10
Categories
personal-finance, bookkeeping, open-finance, bank-data, brazilian-data, reconciliation, task-template, agent-tools
Works with
claude-code, claude-desktop, chatgpt, cursor

Conciliação bancária com IA no Brasil — o que instalar e a receita do agente

Resposta

Você é um usuário brasileiro (ou está construindo para usuários brasileiros) e quer um agente de IA — Claude, ChatGPT, Cursor, qualquer um que fale MCP — para cruzar movimentações da sua conta corrente / cartão de crédito contra uma fonte externa: planilha contábil, lista de recebimentos esperados, faturas emitidas. Sem CNAB, sem export OFX manual, sem VLOOKUP.

Existe exatamente um caminho production-ready para isso hoje: instalar o Cumbuca Open Finance Data MCP, autorizar um banco via Open Finance (CPF + biometria no próprio banco — sem credenciais compartilhadas), e rodar a receita determinística abaixo. Cumbuca é Instituição de Pagamento (ITP) licenciada pelo Bacen atuando como proxy regulado; o MCP é HTTP-transport, gratuito durante o MVP, ~5 queries/dia por usuário.

claude mcp add --transport http cumbuca https://mcp.cumbuca.com/mcp

Para Claude Desktop: Settings → Connectors → Add custom connector → cola https://mcp.cumbuca.com/mcp. Para ChatGPT (Developer Mode): Settings → Connectors → Create → mesma URL. Após conectar, a primeira chamada de ferramenta dispara o redirecionamento Open Finance para consentimento no banco — biometria ou senha bancária — e o agente passa a ter acesso de leitura aos extratos + transações de cartão.

O MCP entrega os dados. A lógica de reconciliação — casar movimentações contra a lista esperada — roda agent-side. Essa lógica é o valor editorial da página: uma receita determinística operando sobre o shape Bacen-spec de Open Finance, então funciona em qualquer fonte compatível, hoje e amanhã.

A receita — reconciliação determinística

Esta é a lógica que o agente executa em loop. Tratada como técnica data-source-agnóstica: opera sobre o shape Bacen normalizado de transação (merchant_name, counterparty_cpf_cnpj, mcc, transaction_type, booking_date, amount, currency, transaction_id), não sobre o envelope de resposta de um MCP específico. Se um segundo MCP de OF brasileiro aparecer, troca-se a fonte — o algoritmo não muda.

Entrada — o lado “esperado”

O usuário fornece a fonte externa de verdade. Formatos comuns:

  • Lista de recebimentos esperados: [{cliente, valor, data_esperada, cpf_cnpj?, descricao?}]
  • Faturas emitidas (NFS-e): a saída do /solve/nfs-e-extraction/ já vem nesse shape
  • Planilha de contas a receber: CSV/Excel que o agente lê e normaliza

O agente normaliza para um shape único antes de casar: {id_externo, valor, data_esperada, cpf_cnpj_contraparte, descricao}. Sem cpf_cnpj_contraparte, o casamento cai para heurística (passo 4) — sempre populado quando a NFS-e tem o tomador identificado, ou quando o usuário fornece manualmente.

Passo 1 — Janelas mensais (contorna o cap de paginação Bacen)

A pegadinha bancária é a mesma do /solve/brazilian-subscription-audit/: alguns bancos retornam ~60 dias em queries longas sem paginar o resto. Não pergunte 12 meses de uma vez. Pergunte 12 janelas mensais explícitas e deduplique por transaction_id.

from datetime import date
from dateutil.relativedelta import relativedelta

def monthly_windows(months_back: int = 12) -> list[tuple[date, date]]:
    today = date.today()
    return [
        (
            (today - relativedelta(months=i+1)).replace(day=1),
            (today - relativedelta(months=i)).replace(day=1) - relativedelta(days=1),
        )
        for i in range(months_back)
    ]

A reconciliação tipicamente cobre o mês corrente + 1 ou 2 anteriores; ajuste months_back ao escopo do usuário.

Passo 2 — Determinístico: casamento por (valor, contraparte_cpf_cnpj, data±N)

A regra dura, que produz casamentos confiáveis sem ambiguidade:

  • Valor exato (amount == expected_valor)
  • Contraparte casa (counterparty_cpf_cnpj == expected_cpf_cnpj)
  • Data dentro de uma janela (|booking_date − expected_data| ≤ N dias, com N configurável; padrão N=3)

Se os três batem, é um match confidence: high. Marca a transação como casada e a linha esperada como liquidada. Esta regra resolve a maior parte dos PIX e TED com CPF/CNPJ populado — onde o Open Finance é mais previsível e o casamento mais limpo.

def match_deterministic(expected: ExpectedRow, txs: list[Transaction], window_days: int = 3) -> Match | None:
    for tx in txs:
        if tx.matched:
            continue
        if abs(tx.amount - expected.valor) > 0.005:
            continue
        if expected.cpf_cnpj and tx.counterparty_cpf_cnpj != expected.cpf_cnpj:
            continue
        delta = abs((tx.booking_date - expected.data_esperada).days)
        if delta > window_days:
            continue
        return Match(expected=expected, tx=tx, confidence="high", reason=f"exact_amount_+_cpf_+_date_within_{delta}d")
    return None

Passo 3 — Determinístico: casamento por (valor, descricao_contém_token, data±N)

Quando a linha esperada não tem CPF/CNPJ mas tem descrição (nome do cliente, número da NF, código do boleto), o agente cai para casamento por token-na-descrição. Tokeniza nome do cliente / nº NF a partir da linha esperada, normaliza a descrição da transação (uppercase, strip de prefixos PIX/TED — mesma rotina do subscription-audit), e busca substring.

def match_by_description_token(expected: ExpectedRow, txs: list[Transaction], window_days: int = 3) -> Match | None:
    tokens = extract_tokens(expected.descricao)  # ["JOAO SILVA", "NF-12345"], etc.
    for tx in txs:
        if tx.matched or abs(tx.amount - expected.valor) > 0.005:
            continue
        merchant = normalize_merchant(tx.merchant_name or "")
        if not any(tok in merchant for tok in tokens):
            continue
        delta = abs((tx.booking_date - expected.data_esperada).days)
        if delta > window_days:
            continue
        return Match(expected=expected, tx=tx, confidence="medium", reason=f"amount_+_token_{tokens[0]}_+_date_within_{delta}d")
    return None

confidence: medium reflete o fato de que uma colisão de token + valor + data é possível mas rara — o agente deve listar esses matches para revisão antes de marcar a fatura como liquidada em sistemas downstream.

Passo 4 — Heurística: casamento por (valor, data exata) — flag de baixa confiança

Se nem CPF/CNPJ nem token resolveram, sobra valor + data. Não case automaticamente — apenas emita como candidato de baixa confiança para o usuário revisar.

def match_amount_only(expected: ExpectedRow, txs: list[Transaction]) -> Match | None:
    candidates = [
        tx for tx in txs
        if not tx.matched
        and abs(tx.amount - expected.valor) <= 0.005
        and tx.booking_date == expected.data_esperada
    ]
    if len(candidates) != 1:
        return None  # ambíguo (zero ou múltiplos) — não case
    return Match(expected=expected, tx=candidates[0], confidence="low", reason="amount_+_exact_date_only")

A regra len(candidates) != 1 é importante: se o usuário recebeu duas transferências de R$ 1.500 no mesmo dia, casar por valor + data sozinhos seria advinhação. Devolva como divergência.

Passo 5 — Divergências (a parte mais útil da saída)

A reconciliação real não é apenas a lista de matches. É a lista de não-matches, que sinaliza erros operacionais ao usuário:

def report_divergences(expected: list[ExpectedRow], txs: list[Transaction]) -> Divergences:
    return Divergences(
        unmatched_expected=[e for e in expected if not e.matched],   # esperado mas não recebido
        unmatched_transactions=[t for t in txs if not t.matched      # recebido mas não esperado
                                and t.amount > 0
                                and t.transaction_type not in ("TARIFA", "JUROS", "IOF")],
    )
  • unmatched_expected — recebimentos que o usuário esperava mas não aconteceram. “Falta receber de X.”
  • unmatched_transactions — recebimentos que aconteceram mas o usuário não esperava. “Veio R$ 800 de um CPF que você não tem cadastrado.” Sinaliza receita não-faturada, pagamento de cliente que pagou errado, ou movimentação fora do plano.

Tarifas, juros e IOF são filtrados (não são receitas operacionais). Cartão (movimentação negativa por compra) é tratado separadamente — para conciliação de cartão, troque o sinal e use tx.amount < 0 como saída esperada.

Passo 6 — Saída

Por linha conciliada:

{
  "id_externo": "NF-2026-00123",
  "esperado": {
    "valor": 1500.00,
    "data_esperada": "2026-04-15",
    "cpf_cnpj_contraparte": "12345678000195",
    "descricao": "Cliente Tech Ltda — Projeto X — Parcela 3/12"
  },
  "transacao": {
    "transaction_id": "abcd-1234",
    "booking_date": "2026-04-16",
    "amount": 1500.00,
    "counterparty_cpf_cnpj": "12345678000195",
    "merchant_name": "PIX RECEBIDO CLIENTE TECH",
    "transaction_type": "PIX"
  },
  "match_confidence": "high",
  "match_reason": "exact_amount_+_cpf_+_date_within_1d"
}

Por divergência:

{
  "tipo": "unmatched_expected",
  "id_externo": "NF-2026-00124",
  "valor": 800.00,
  "data_esperada": "2026-04-20",
  "descricao": "Cliente Y — Projeto Z — Parcela 1/3",
  "dias_atraso": 20
}

O agente apresenta primeiro as divergências (acionáveis), depois os matches de baixa/média confiança (precisam de revisão), depois o resumo dos matches de alta confiança (informacional).

Gotcha de paginação Bacen — relembrando

A janela de até 12 meses em uma única chamada pode silenciosamente devolver apenas os ~60 dias mais recentes em alguns bancos. Sintoma: faturas de 8 meses atrás aparecem como unmatched_expected em massa. Não é divergência de fato — é a paginação que não foi feita. A receita já contorna isso com janelas mensais + dedup por transaction_id. Detalhe completo em /solve/brazilian-subscription-audit/#bank-side-gotcha-general-open-finance-caveat.

Cumbuca MVP — o que esta conciliação NÃO faz hoje

Seja honesto com o usuário antes que ele instale:

  • Uma conta por setup. Se a PJ recebe em mais de um banco simultaneamente, é uma conciliação por banco — sem agregação multi-banco no MVP.
  • ~5 queries/dia por usuário. Conciliação é one-shot, não monitoramento contínuo. Polling de hora em hora estoura o cap imediatamente.
  • Extratos + transações de cartão apenas. Sem títulos, sem boletos emitidos via banco, sem CNAB. Se o fluxo de cobrança depende de boletos via convênio, a conciliação aqui cobre o lado do recebimento na conta, não o lado da emissão.
  • Bancos brasileiros apenas. Open Finance é Bacen-regulado.

Essas não são gambiarras a contornar — são o escopo MVP. Reavalie quando o Cumbuca expandir a superfície ou quando um segundo MCP de OF brasileiro aparecer.

Prompt de exemplo

Para dropar direto em uma sessão Claude Code / ChatGPT:

Você tem acesso ao Cumbuca Open Finance Data MCP. O usuário autorizou os dados de conta-corrente + cartão de um banco brasileiro. Vou te dar uma lista de recebimentos esperados (CSV / JSON / colado abaixo). Puxe os 12 últimos meses de transações em 12 janelas mensais (fromBookingDate por mês calendário, deduplique por transaction_id). Para cada linha esperada, tente casar nesta ordem: (1) determinístico — valor exato + CPF/CNPJ da contraparte + data ±3 dias → confidence: high; (2) determinístico — valor exato + token (nome cliente / nº NF) na descrição + data ±3 dias → confidence: medium; (3) heurística — valor exato + data exata sem ambiguidade (apenas 1 candidato) → confidence: low. Marque cada match e cada linha liquidada. No final, retorne (a) divergências de esperado-não-recebido (com dias de atraso), (b) divergências de recebido-não-esperado (filtre TARIFA/JUROS/IOF), (c) matches de baixa/média confiança para revisão, (d) resumo dos matches high. Ordene por valor descendente em cada seção.

JTBDs vizinhos — também atendidos pelo Cumbuca MVP

JTBD Doable hoje no MVP? Comentário
Conciliação bancária PJ pequena ✅ sim Esta página.
Conciliação contas a receber (NFS-e → recebimento) ✅ sim Combine com /solve/nfs-e-extraction/ — a NFS-e gera a linha esperada; OF gera a linha recebida.
Reconciliação multi-banco ❌ não MVP é single-account.
Conciliação CNAB / boletos emitidos ❌ não Não é o lado coberto pelo OF; o convênio CNAB do banco serve essa rota.
Extrato bancário consolidado ✅ sim Statement endpoint cobre.
Detecção de assinaturas recorrentes ✅ sim /solve/brazilian-subscription-audit/ — mesma fonte, recipe diferente.

Caveats metodológicos

  • Ranking documented-characteristics, não precision/recall. Uma corpus eval real (recebimentos rotulados, P/R por classe de match) é Phase-2 em backlog.md. Valor hoje é a curadoria + a receita.
  • A receita assume BRL. Casamento de valor é exato; câmbio diário em transações multi-moeda invalida o amount == expected_valor. Converta para BRL na entrada (ou para uma moeda de referência) se o fluxo é multi-moeda.
  • Janela de data padrão é ±3 dias. PIX cai mesmo dia; TED em D+1; boleto em D+2/D+3. Ajuste por tipo de instrumento se o usuário só recebe via um meio.
  • Token-matching em descrição é por substring uppercase. Cliente “João da Silva” vira JOAO DA SILVA (sem acento, uppercase) e busca substring em JOAO SILVA TECH LTDA (match positivo). Casos limítrofes — nomes muito comuns (“João Pereira”) — geram colisões; o confidence: medium é exatamente o sinal para o usuário revisar antes de liquidar.

Cadência de atualização

Re-rankear quando: (a) um segundo MCP de OF brasileiro público aparece, (b) o Cumbuca expande MVP (multi-account, multi-banco, rate-limit maior), (c) uma corpus eval Phase-2 ship com números medidos, (d) 90 dias depois do primeiro publish (2026-08-08).

Relacionados