Uma carga por CSV é análise pontual da fotografia importada. Ela não habilita monitoramento
contínuo entre uploads e perde atualidade 24 horas depois do
source_as_of (veja
Cadência).Fluxo
1
Baixe o modelo
Use o modelo versionado
obligation-import-v1.csv.
A versão do modelo é a do contrato: schema_version: "obligations.v1".2
Registre a origem
POST /obligation-sources com mode: "csv", uma vez por ERP. Veja
Origens.3
Envie a prévia
POST /financial-profiles/{id}/obligation-imports/preview com o arquivo e os metadados da carga.4
Revise
Confira os totais por natureza, os vencidos, as liquidações e os erros. Com qualquer erro, a
prévia fica
rejected e nada é ativado.5
Ative
POST /financial-profiles/{id}/obligation-imports/{importId}/activate com actor e reason.
Exige a permissão obligation_imports.activate_csv da aplicação.O modelo
O cabeçalho tem 22 colunas, com os nomes do contrato em inglês. A ordem das colunas é livre, mas os nomes são exatos e cada um aparece uma vez.record_type, external_obligation_id, direction, category, original_amount,
outstanding_amount, currency, due_date, status, source_version, source_updated_at,
source_reference, bank_obligation_reference, counterparty_id_type, counterparty_id,
counterparty_name, settlement_id, settled_at, settled_amount, settlement_method,
payment_reference, payment_match_id.
Cada linha tem um record_type:
obligation: um título. As colunas de liquidação ficam vazias.settlement: uma liquidação, ligada ao título peloexternal_obligation_id. As colunas do título ficam vazias. Toda liquidação precisa de uma linhaobligationcom o mesmo identificador no arquivo.
import_id, financial_profile_id, source_system, source_as_of, coverage, contagens
e finalização) vão nos metadados do upload, não no arquivo.
Exemplo com a fotografia de 12/10/2026 de um ERP sintético:
titulos-20261012.csv
POST .../obligation-imports/preview:
dois títulos a pagar (1250.10 de valor original, 1000.00 em aberto, uma liquidação de 250.10)
e um a receber de 1000.0400, com as 4 casas preservadas.
Formato do arquivo
O formato é declarado no upload, emcsv_format. A Malvo não adivinha separador nem decimal.
Os três campos de
csv_format são obrigatórios: sem algum deles, a prévia volta com
MISSING_FIELD e o campo (csv_format.encoding, csv_format.delimiter ou
csv_format.decimal_separator).
Por que o decimal é sempre ponto: com ponto e vírgula como separador, 1.000,00 e 1000,00 são
ambíguos entre exportações. O padrão de valor é o mesmo da API (^\d{1,15}\.\d{2,4}$).
Exportação com ponto e vírgula, comum em planilhas em pt-BR, declarada no upload:
titulos-ponto-e-virgula.csv
Fórmulas são texto
Conteúdo de célula é sempre texto de dados. A Malvo nunca avalia fórmula. Uma célula que começa com=, +, - ou @:
- numa coluna de valor, cai no padrão decimal e é recusada com
INVALID_AMOUNT.=SUM(A1:A2)emoutstanding_amounté um erro, não uma soma; - num campo livre (
category,source_reference,counterparty_name), fica guardada exatamente como veio, por exemplo o texto=1+1.
Linhas e colunas
Depois do arquivo, a carga passa pelas mesmas regras da API: estado coerente com o valor em aberto,
moeda
BRL, contagens, cobertura, versões e identidade (veja os
códigos do contrato). Cada erro traz line (linha do arquivo,
começando no cabeçalho como linha 1), field e, quando houver, external_obligation_id e
settlement_id. A linha bruta não é guardada.
Prévia recusada 422
preview, com os totais das linhas que puderam ser lidas: valor fora do
padrão não entra na soma. No exemplo, T-1001 tem original_amount 1000.00 e T-1003 tem
outstanding_amount 500.00; os outros dois valores estão inválidos.
Não há aceite parcial: com qualquer erro, a prévia inteira fica rejected e a carga ativa não
muda. Corrija a exportação e envie de novo com outro import_id.
O upload
POST /financial-profiles/{id}/obligation-imports/preview, em multipart/form-data:
metadata: JSON com os campos da carga do contrato (semobligations) ecsv_format;file: o CSV.
metadata
Enviar a prévia
obligation_count conta as linhas
obligation e settlement_count as linhas settlement. Um reenvio idêntico (mesmo import_id e
mesmo conteúdo) devolve o resultado da primeira prévia. API e CSV com o mesmo conjunto geram o
mesmo conteúdo.
Conjunto vazio
Uma carga sem títulos é válida quando o recorte declarado está vazio de verdade. Envie só o cabeçalho, contagens zero e a confirmação explícita:titulos-vazio.csv
metadata do conjunto vazio
empty_snapshot_confirmed: true, a carga é recusada com EMPTY_SNAPSHOT_NOT_CONFIRMED. Um
arquivo totalmente vazio, sem cabeçalho, é recusado com EMPTY_FILE. Ativada, a carga vazia
significa “nenhum título em aberto no recorte declarado”, na data e no escopo dela. Não significa que
a empresa não tem dívidas.
Ativação e auditoria
A ativação refaz as conferências de versão: se outra carga da mesma origem chegou depois da prévia, a prévia virarejected com o motivo (por exemplo, STALE_SNAPSHOT). Um CSV nunca
sobrescreve uma fotografia mais recente feita por API.
Ficam auditáveis o autor e o motivo da ativação, o nome, o tamanho e o SHA-256 do arquivo, a versão do
modelo, a posição (source_as_of), as contagens, os totais e a chave de idempotência. O CSV original
é guardado em armazenamento privado por 30 dias e os erros por linha também por 30 dias. A trilha de
auditoria não guarda valores.
Base normativa
- Contrato
obligations.v1(Títulos do ERP — contrato v1). - RFC 4180 (formato CSV, aspas duplas) e RFC 3339 (instantes com fuso).
- API Contas do Open Finance Brasil 2.5.1, lida em 12/10/2026: padrão de valor
^\d{1,15}\.\d{2,4}$, reusado nas colunas monetárias. - PRD v1.3, seção 13, “Importação CSV no piloto” (FR-034; AC-048, AC-049, AC-050 e AC-054), e a política de retenção da Malvo aprovada em 10/10/2026 (CSV original e erros por linha: 30 dias).