obligations.v1). O upload por planilha tem um guia próprio. A
conciliação com os débitos bancários está em Conciliação assistida, e o
diagnóstico e os alertas em Liquidez e alertas.
Chaves do módulo
Todas as chaves nascem desligadas. Com qualquer uma desligada, as rotas correspondentes não existem e respondem404.
Sem armazenamento de arquivo configurado, a prévia de CSV responde
503 FILE_STORAGE_UNAVAILABLE.
Regras comuns
- Autenticação: header
X-API-KEYda aplicação. Sem token,403. Perfil ou origem de outra aplicação responde404 NOT_FOUND. - Valores: string decimal
^\d{1,15}\.\d{2,4}$, nunca número JSON. Totais são somas decimais exatas:"1000.0400"+"0.2000"="1000.2400". - Instantes: RFC 3339 com fuso na entrada. As respostas devolvem UTC com
Z. - Corpo estrito: campo desconhecido no corpo responde
400 VALIDATION_ERROR. - Erros: o envelope é o mesmo das demais rotas (
code,message,codeDescription,requestId). Erros da carga trazem tambémerrors, uma lista comcode,field,line,external_obligation_idesettlement_idquando houver. - Identificadores:
{importId}é oidda carga na Malvo. Oimport_iddo contrato é a sua chave idempotente.
Origens
A origem (source_system) identifica o ERP dentro da aplicação. Ela é registrada uma vez, com o
modo de monitoramento, e vale para todos os perfis que recebem cargas dela.
POST /obligation-sources
Registra a origem. Responde 201 na criação e 200 se ela já existia; nesse caso, o modo é
atualizado.
Registrar origem
Origem registrada 201
GET /obligation-sources
Lista as origens da aplicação em results. Com OBLIGATION_SOURCE_ACTIVITY_ENABLED, cada origem
traz também o último sinal de atividade, a cadência e os perfis cobertos (os que têm carga ativa da
origem).
Origens com cadência
profiles, import_id é o id da carga ativa na Malvo. effective_source_as_of é o
source_as_of da carga avançado pelas confirmações de atividade (veja
Cadência).
Cargas
Uma carga é a fotografia completa dos títulos de um perfil no recorte declarado, no formatoobligations.v1. A validação é inteira e a troca da carga ativa é atômica: uma recusa não mexe na
carga ativa nem nos títulos. Não há aceite parcial de linhas válidas.
channel diz por onde a carga chegou: api ou csv. API e CSV compartilham a identidade da
origem e a ordem das versões.
POST /financial-profiles/{id}/obligation-imports
Recebe a carga em JSON, no formato do contrato v1. O
financial_profile_id do corpo precisa ser o da rota e o source_system precisa estar registrado.
Uma carga válida e finalizada é ativada na hora.
O conteúdo é comparado depois de normalizar a ordem das coberturas, dos títulos e das liquidações,
com o texto exato dos valores. API e CSV com o mesmo conjunto geram o mesmo conteúdo.
Carga ativada 201
Carga recusada 422
409 IMPORT_ID_CONFLICT
422 SOURCE_NOT_REGISTERED
Regras de troca atômica
A carga é conferida contra o estado atual antes de qualquer escrita:
A ativação também absorve as correspondências confirmadas cujo título mudou (veja
Absorção pela origem).
POST /financial-profiles/{id}/obligation-imports/preview
Recebe um CSV em multipart/form-data com duas partes, metadata e file, valida e guarda a
prévia sem ativar. O formato do arquivo, o csv_format e os erros específicos estão no
guia CSV.
Prévia de CSV 200
outstanding_amount maior que zero e vencimento anterior à data civil de source_as_of em
America/Sao_Paulo.
GET /financial-profiles/{id}/obligation-imports/{importId}
Devolve a carga com o estado, o motivo, as contagens, o arquivo (sem o conteúdo) e os erros. Uma
prévia guardada traz também preview. Carga de outro perfil ou de outra aplicação responde
404 NOT_FOUND.
Carga não ativada 200
POST /financial-profiles/{id}/obligation-imports/{importId}/activate
Ativa uma prévia de CSV sem erros, com a mesma troca atômica da API. Exige a permissão
obligation_imports.activate_csv da aplicação, desligada por padrão. O dono ou um admin do time
concede a permissão no console. O corpo exige actor (até 128 caracteres, quem ativou do lado do
parceiro) e reason (até 500). Os dois ficam na carga como activated_by e activation_reason.
Ativar prévia
Prévia ativada 200
403 PERMISSION_REQUIRED
Títulos
GET /financial-profiles/{id}/obligations?scope=&direction=&status=&page=&pageSize=
Devolve a agenda do perfil e os títulos, paginados (pageSize padrão 20, máximo 500).
Valor fora da lista responde
400 VALIDATION_ERROR.
agenda.state é missing com reason: "NO_ACTIVE_IMPORT" enquanto nenhuma carga finalizada e
completa estiver ativa. O diagnóstico usa esse estado para mostrar “Diagnóstico indisponível —
agenda financeira não informada”. Com carga ativa, o estado é available e cada origem traz a carga
ativa e a mais recente que não virou ativa. Uma carga válida com zero títulos e
empty_snapshot_confirmed: true fica ativa com data e escopo: significa “nenhum título em aberto no
recorte declarado”, não ausência de dívida.
Com OBLIGATION_SOURCE_ACTIVITY_ENABLED, a resposta traz quality, a qualidade da agenda para o
diagnóstico (veja Cadência).
Agenda e títulos 200
obligation_id (id na Malvo), active, active_import_id, removed_at, version_id e
version_created_at.
GET /financial-profiles/{id}/obligations/{obligationId}
Um título, as liquidações da versão atual e todas as versões guardadas em versions, da mais
recente para a mais antiga. Cada versão aponta a carga que a trouxe (obligation_import_id).
Título de outro perfil responde 404 NOT_FOUND.
Título com versões 200
Cadência e atividade por origem (FR-035)
ComOBLIGATION_SOURCE_ACTIVITY_ENABLED, cada origem tem uma cadência calculada na leitura, pelo
relógio do servidor. Os prazos são requisitos de produto do PRD v1.3 (seção 10, “Frequência e
elegibilidade das cargas”), não norma externa. A elegibilidade é da origem: um problema em um
perfil deixa a origem inelegível em todos os perfis cobertos.
Cada motivo traz o perfil afetado (
financial_profile_id, quando houver) e o prazo vencido
(due_at). Todo prazo vence depois do instante: às 06h00 em ponto a carga do dia anterior ainda
vale. O último sinal (heartbeat_at) é a confirmação de atividade ou a ativação de carga mais
recente da origem. activity_due_at é o próximo sinal esperado (uma hora depois de heartbeat_at);
a origem só perde a atividade depois de duas horas sem sinal.
Perder a atividade é registrado em activity_lost_at e só volta com uma carga válida: novas
confirmações não bastam. Os outros motivos somem quando a carga que falta chega. A perda de
elegibilidade é estado de qualidade dos dados, não deterioração financeira.
Depois de cada ciclo de sincronização bancária do perfil, a Malvo publica
financial_profile.bank_position_updated.
Responder com uma fotografia daquele perfil é opcional e não muda os prazos da tabela.
POST /obligation-sources/{sourceId}/activity
Confirma a atividade da origem. Uma confirmação cobre todos os perfis da origem.
confirmed_until: instante até o qual a origem confirma não ter alteração pendente de envio. Ele avança osource_as_ofefetivo de todos os perfis cobertos. Não cria carga, não mexe em títulos e não dispara sincronização bancária. Uma confirmação mais antiga que a guardada não faz o valor recuar.pending_changes(opcional, até 100): alteração relevante já feita no ERP (baixa, cancelamento, reabertura, valor ou vencimento) que ainda não chegou numa carga. Ela limita osource_as_ofefetivo do perfil aochanged_ate abre o prazo de carga do modo. Uma carga ativada no perfil comsource_as_ofigual ou posterior aochanged_atresolve a alteração.- Numa origem
csv, a confirmação fica registrada e as alterações pendentes valem, mas oconfirmed_untilnão avança osource_as_ofefetivo: o CSV é análise pontual da fotografia.
Confirmar atividade
200 é o estado da origem, no mesmo formato de um item de GET /obligation-sources.
Com a alteração pendente acima, o source_as_of efetivo do perfil fica limitado ao changed_at e o
prazo de carga do modo api vence 2 horas depois.
Atividade confirmada 200
Cadência com carga vencida
422 ACTIVITY_INSTANT_IN_FUTURE
Qualidade para o diagnóstico
GET /financial-profiles/{id}/obligations traz quality quando a cadência está ligada:
favorable_conclusion_allowed:falsese não houver carga ativa, se alguma origem do perfil estiver inelegível ou se a coerência com o banco falhar.reasons:NO_ACTIVE_IMPORT,SOURCE_INELIGIBLE,SOURCE_BEHIND_BANK_POSITIONouBANK_POSITION_UNKNOWN.oldest_bank_position_at: posição mais antiga das contas bancárias vinculadas, peloupdateDateTimedo saldo da API Contas.sources[].coherence.state:coherent,source_behind_bank(osource_as_ofefetivo é anterior à posição bancária mais antiga em mais de 1 hora,limit_seconds: 3600) oubank_position_unknown.lag_secondsé a posição mais antiga menos osource_as_ofefetivo: negativo quando o ERP está à frente do banco.
source_as_of efetivo às 06h30 e posição mais antiga às 08h00 dão 1h30 de
diferença, acima de 1 hora. O diagnóstico não conclui cobertura favorável, mas pode mostrar uma
insuficiência já demonstrável.
Retenção
Nenhum valor de título vai para log.
Base normativa
- Contrato
obligations.v1(Títulos do ERP — contrato v1): campos, padrões, estados, idempotência e versões. - API Contas do Open Finance Brasil 2.5.1
(
api_accounts_-_open_finance_brasil/2.5.1.yaml, lido em 12/10/2026): padrão de valor^\d{1,15}\.\d{2,4}$eAccountBalancesData.updateDateTimecomo posição bancária da coerência. Títulos de ERP não fazem parte dessa API. - Lei nº 13.709/2018 (LGPD), art. 6º, 7º, 15, 16 e 18, e a política de retenção da Malvo aprovada em 10/10/2026.
- PRD v1.3, seções 10, 13 e 14 (FR-027, FR-034, FR-035; AC-029 a AC-031, AC-035, AC-048 a AC-051, AC-054, AC-062 e AC-063). Os prazos da FR-035 (06h00, 2 horas, 15 minutos, atividade por hora, 24 horas no CSV e coerência de 1 hora) são regras de produto, sem norma externa.