Captura¶
Fluxo da IA Captura: captura enviar sobe um arquivo e devolve o id do
documento; captura status consulta esse id e, quando o processamento
termina, devolve a id_captura; captura get traz a prévia extraída para
essa id_captura; captura aceitar/captura recusar decidem o que fazer
com a prévia.
Verificado contra a produção em 2026-08-19 com dois recibos em PDF gerados
para o teste. É o único grupo que escreve em outro cadastro sem avisar e
o único cujo id de recurso não é uuid v4 — veja as duas notas abaixo.
captura enviarcria um fornecedor no cadastro de pessoas. Não é oaceitarque faz isso: assim que a IA termina de extrair, oprevia_evento_financeiro.fornecedorjá vem com umidquepessoa getresolve, comcriado_emdo dia e perfilFornecedor. Um segundo documento do mesmo CNPJ reaproveita o registro em vez de duplicar. Ou seja: subir um documento é uma escrita no cadastro de pessoas, não uma leitura. Quem for exercitar isso em produção deve contar com esse registro a mais.Os ids da Captura são uuid v7, não v4 (
01a01a96-61f5-7007-…, com7na posição da versão). Qualquer validação local que exija v4 recusaria um id legítimo. A API, por sua vez, valida o formato: um id que não é uuid devolve400("O ID da captura informado é inválido") e um uuid válido porém inexistente devolve404("Captura não encontrada com o ID informado").Este grupo devolve erro em outro envelope. Onde o resto da API usa
{"timestamp", "status", "error", "message", "path"}, a Captura responde{"error": "mensagem"}. Serve para distinguir rota inexistente de id inexistente:/v1/captura/documentos/{id}(rota que não existe) devolve o404 page not foundem texto puro do gateway, enquanto as rotas reais devolvem JSON.
captura enviar ✅¶
POST /v1/captura/documentos — multipart/form-data. Responde 201, não 200.
| Parâmetro | Obrig. | Descrição |
|---|---|---|
<arquivo> |
sim | Argumento posicional. Caminho de um arquivo local (PDF, JPEG, PNG ou BMP; máximo de 10 MB) |
--descricao |
não | Descrição do documento (máximo de 255 caracteres) |
200.
O CLI valida que o arquivo existe e é legível antes de enviar. Retorna
{id, nome}, onde id identifica o documento para captura status.
O tipo do arquivo é validado pela API, não pelo CLI: um .txt volta
415 com "Formato não suportado. Aceitos: PDF, JPEG, PNG, BMP." — mensagem
clara o bastante para não valer duplicar a regra localmente.
--descricaoé escrita sem leitura. Nenhuma das três respostas de leitura (status,get,aceitar) devolve a descrição enviada, e a descrição do evento financeiro criado vem do texto que a IA extraiu do documento, não dela. Serve para o histórico na interface web; pelo CLI não há como conferir o que foi gravado. O limite de 255 caracteres ficou sem exercitar: comprová-lo exigiria mais um upload, e documento enviado não tem como ser apagado (vejacaptura recusar).
captura status ✅¶
GET /v1/captura/documentos/status
| Parâmetro | Obrig. | Padrão | Descrição |
|---|---|---|---|
--ids |
sim | — | IDs de documentos separados por vírgula (até 20) |
--pagina |
não | 1 |
Número da página |
--tamanho-pagina |
não | 10 |
Itens por página (qualquer inteiro de 1 a 20) |
Retorna {itens[], paginacao}; cada item traz status_documento e a lista
de capturas geradas (pode estar vazia). O processamento é assíncrono —
repita a consulta até o status chegar a um estado final. Aqui o
total_itens confere com len(itens).
--idscom mais de um id nunca funcionou até 2026-08-19. O OpenAPI publicado declarastyle: form, explode: false— os ids juntos num parâmetro só, separados por vírgula — e a API responde400("O valor informado para o campo 'ids' é inválido") exatamente a essa forma. Ela quer o parâmetro repetido:ids=a&ids=b. Com um id as duas formas coincidem, e foi por isso que o defeito atravessou incólume todo teste que passava um id só. Varridas e descartadas: vírgula, espaço,|,;, JSON eids[]=(todas400). Funciona tambémids[0]=a&ids[1]=b, que é o que a opçãoquerydo Symfony geraria — mas o CLI manda a forma repetida, que é a canônica.
Limites medidos, todos com mensagem própria: mais de 20 ids devolve 400
("O campo 'ids' não pode conter mais de 20 itens"), e o CLI passou a barrar
isso antes da ida à API.
Zero é buraco na validação da API, e o CLI é mais rígido de propósito.
tamanho_pagina=0epagina=0respondem200e caem no default (a resposta volta comtamanho_pagina: 10, pagina_atual: 1), enquanto-1devolve400("deve ser maior ou igual a 1") — ou seja, a API valida o negativo e deixa o zero passar como se fosse ausente. O CLI recusa--tamanho-pagina 0localmente: zero não é um tamanho de página, e aceitar silenciosamente um valor que não faz o que foi pedido é o mesmo defeito do descarte silencioso.--pagina 0segue passando, e a API normaliza para 1.--idsvazio ou ausente é recusado pela própria API (400), sem descarte silencioso — ao contrário do que acontece com parâmetro desconhecido, que aqui também some sem avisar (zzz_bogus=abcdevolve a resposta inalterada).
Estados possíveis, do OpenAPI e confirmados no fluxo real —
status_documento: PENDENTE, PROCESSANDO, EXTRAINDO_DADOS,
APLICANDO_REGRAS, AGUARDANDO_VINCULO_LANCAMENTO,
CRIANDO_LANCAMENTOS_FINANCEIROS, PRONTO, IGNORADO, RESOLVIDO,
ERRO, EXCLUIDO; status_captura: PROCESSANDO, PENDENTE, ACEITA,
REJEITADA, FALHA. Na verificação o documento percorreu
EXTRAINDO_DADOS → AGUARDANDO_VINCULO_LANCAMENTO → PRONTO em poucos
segundos, e foi para RESOLVIDO depois do aceite.
captura get ✅¶
GET /v1/captura/{id}
| Parâmetro | Obrig. | Descrição |
|---|---|---|
<id> |
sim | Argumento posicional. id_captura, obtido em captura status |
Retorna {id, id_documento, status, previa_evento_financeiro}. A prévia só
vem enquanto status é PENDENTE: depois do aceite a resposta encolhe para
{id, id_documento, status}, e depois da recusa o get passa a responder
404.
sugestao_evento_financeironão existe na resposta real. O OpenAPI a declara e este arquivo a documentava; a API nunca a devolveu, nem com a captura emPENDENTE. Documentar campo de resposta a partir da spec é o mesmo erro que documentar filtro a partir dela.
A previa_evento_financeiro traz tipo, valor, data_competencia,
descricao, observacao, referencia_externa, fornecedor{id, nome,
documento}, categoria{id, nome} e parcelas[] com data_vencimento,
metodo_pagamento e composicao_valor. Tanto o fornecedor.id quanto o
categoria.id apontam para registros que existem de verdade no cadastro.
captura aceitar ✅¶
POST /v1/captura/{id} — sem corpo. Responde 200.
| Parâmetro | Obrig. | Descrição |
|---|---|---|
<id> |
sim | Argumento posicional. id_captura cuja prévia será aceita |
Cria o evento financeiro a partir da prévia e retorna
{id, status, evento_financeiro{id, valor, tipo, data_competencia,
descricao}}.
Não há volta. O evento financeiro criado é uma conta a pagar/receber comum, e a API não publica
DELETEde evento financeiro — só sai pela interface web. Some-se a isso que o documento também não temDELETEe a captura aceita não pode mais ser recusada:aceitaré a transição mais cara do CLI inteiro. Decida antes de chamar.É idempotente, e isso é a parte boa. Chamar
aceitarde novo na mesma captura devolve200com{id, status: "ACEITA"}e semevento_financeiro— não cria um segundo lançamento. Conferido contandoconta-a-pagar listantes e depois: 37 → 38, com duas chamadas de aceite.
O evento criado guarda o caminho de volta: em parcela get, o
evento.referencia vem {id: <id_captura>, origem: "LANCAMENTO_FINANCEIRO"}.
É o único vínculo legível entre o financeiro e a captura que o originou.
captura recusar ✅¶
DELETE /v1/captura/{id} — sem corpo. Resposta 204 No Content, renderizada como [].
| Parâmetro | Obrig. | Descrição |
|---|---|---|
<id> |
sim | Argumento posicional. id_captura a ser recusada |
renderizada como [].
É o único caminho de volta do grupo, e funciona. Recusar a captura tira o documento da listagem de status — comprovado: consultar os dois ids enviados passou a devolver só o outro. Como não existe
DELETE /v1/captura/documentos/{id}(a rota devolve o404 page not founddo gateway), recusar é a única forma de fazer um documento enviado desaparecer. Se você for exercitar este grupo, planeje recusar tudo que não precisar aceitar.
Depois da recusa, captura get no mesmo id responde 404 — a captura sai do
caminho de leitura de vez. Mas recusar de novo continua respondendo 204
e saindo com código 0: a recusa é idempotente e nunca acusa "já
recusada", ao contrário do get.
Uma captura já aceita não pode ser recusada: 409 ("A captura não pode
ser recusada no status atual (já foi aceita ou está em processamento)"),
que o CLI classifica corretamente como client_error e sai com 1.