NFS-e/Repasse: Nota Fiscal
Endpoints da WebApiAlcance para a gestão de notas fiscais de serviço (NFS-e) no fluxo de repasse médico (app single-company webapp-nfs-repasse-medico, migrado do Supabase). Cobrem o CRUD da nota fiscal e seus sub-recursos aninhados - médicos vinculados, recebimentos, status financeiro (contas a receber), integrações hospitalares e o log (append-only) de tentativas de emissão - além do anexo de arquivo da nota (upload manual em base64 e download binário de PDF/XML/imagem).
A EMISSÃO da NFS-e não acontece aqui
Este domínio não expõe endpoints de emissão/baixa de NFS-e. A emissão é uma fase assíncrona posterior, executada por workers Celery (fora deste router). Aqui a API apenas persiste o estado do ciclo: o campo status da nota (rascunho → pronta_emissao → em_processamento → emitida/erro_emissao/cancelada), o timestamp processamento_iniciado_em e o histórico append-only em POST/GET /{nota_id}/emissao-logs. O binário emitido chega depois via upload/vínculo de automação e é servido por GET /{nota_id}/arquivo.
Todos os endpoints exigem autenticação. Veja Autenticação para o fluxo de API Key. O Swagger oficial está em api.contabilidadealcance.com.br/docs.
Escopo compartilhado: nfse-repasse
O primeiro segmento do escopo - nfse-repasse - é compartilhado por todos os recursos do módulo NFS-e/Repasse (notas fiscais, prestadores médicos, tomadores etc.). Uma API Key com nfse_repasse:read enxerga todos esses recursos em leitura, e nfse_repasse:write habilita escrita em todo o módulo. Emita chaves com o menor escopo possível - prefira nfse_repasse:read para integrações que só consultam.
Base path
/api/v1/nfse-repasse/notas-fiscaisEscopos necessários
nfse_repasse:read- leitura: listagens, busca por ID, sub-recursos (médicos, recebimentos, contas a receber, integrações, logs) e download do arquivo.nfse_repasse:write- escrita: criar, atualizar e excluir a nota e seus sub-recursos, além de anexar/remover o arquivo.
Escrita também exige perfil administrador
Na WebApi (contexto single-company), toda leitura (GET) é liberada a qualquer staff autenticado, mas toda escrita (POST/PUT/DELETE) exige adicionalmente o perfil administrador (require_perfil("administrador")). Um token com escopo nfse_repasse:write mas sem perfil administrador recebe 403 Forbidden.
Envelope de resposta
Salvo o download binário (GET /{nota_id}/arquivo), toda resposta segue o envelope padrão:
{
"success": true,
"message": "Nota fiscal criada com sucesso!",
"data": {}
}Erros de negócio são mapeados do service para HTTP: "não encontrado" → 404, "já existe" → 409, "acesso negado" → 403, demais violações → 422.
Endpoints
Armadilha de ordem de rotas (FastAPI)
As rotas de path estático (POST /, GET /by-customer/{customer_id}, GET /) e os sub-recursos aninhados (/{nota_id}/medicos, /{nota_id}/arquivo etc.) são declarados antes das rotas dinâmicas GET|PUT|DELETE /{nota_id}. O FastAPI casa na ordem de declaração; inverter faria by-customer ser interpretado como um {nota_id} inválido. Os segmentos estáticos extras após /{nota_id} (ex.: /medicos) não conflitam com /{nota_id}.
Nota fiscal (recurso principal)
POST /nfse-repasse/notas-fiscais/
Cria uma nota fiscal. O id_usuario_created é forçado pelo service a partir do usuário autenticado (não entra no body). uuid_legado é aceito apenas para correlação com o registro de origem na migração.
Parâmetros
Este endpoint não recebe parâmetros de rota ou query - os dados vão no corpo da requisição (NotaFiscalCreate): obrigatórios id_prestador e id_tomador; os demais campos (referências fiscais, valores monetários, status, descricao_final, competencia_referencia etc.) são opcionais. Ver Schema NotaFiscal.
Request
{
"id_prestador": 1,
"id_tomador": 1,
"id_customer": 1,
"competencia_referencia": "2026-06",
"valor_bruto": "1500.00",
"valor_liquido": "1350.00",
"status": "rascunho"
}Response 201 Created
{
"success": true,
"message": "Nota fiscal criada com sucesso!",
"data": {
"id": 1,
"id_prestador": 1,
"id_tomador": 1,
"id_customer": 1,
"competencia_referencia": "2026-06",
"valor_bruto": "1500.00",
"valor_liquido": "1350.00",
"status": "rascunho",
"tem_arquivo": false,
"id_nfe_salvador": null,
"id_usuario_created": 42,
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-01T12:00:00Z"
}
}Escopo: nfse_repasse:write
GET /nfse-repasse/notas-fiscais/by-customer/{customer_id}
Lista as notas fiscais associadas a um cliente (customer.id), com filtro opcional por status e paginação.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
customer_id | int | path | sim | ID do cliente associado |
status | str | query | não | Filtro por status da nota (ex.: emitida) |
page | int ≥1 | query | não | Página (1-based) |
per_page | int 1..500 | query | não | Itens por página |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Notas fiscais recuperadas com sucesso!",
"data": [
{
"id": 1,
"id_prestador": 1,
"id_tomador": 1,
"id_customer": 1,
"status": "emitida",
"tem_arquivo": true,
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-02T09:30:00Z"
}
]
}Escopo: nfse_repasse:read
GET /nfse-repasse/notas-fiscais/
Lista notas fiscais com filtros combináveis e paginação.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
status | str | query | não | Filtro por status da nota |
id_customer | int | query | não | Filtro por cliente associado (customer.id) |
id_prestador | int | query | não | Filtro por prestador (prestador_medico.id) |
id_tomador | int | query | não | Filtro por tomador (tomador.id) |
page | int ≥1 | query | não | Página (1-based) |
per_page | int 1..500 | query | não | Itens por página |
Request
Sem corpo de requisição.
GET /api/v1/nfse-repasse/notas-fiscais/?status=emitida&id_prestador=1&page=1&per_page=50Response 200 OK
{
"success": true,
"message": "Notas fiscais recuperadas com sucesso!",
"data": [
{
"id": 1,
"id_prestador": 1,
"id_tomador": 1,
"status": "emitida",
"tem_arquivo": true,
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-02T09:30:00Z"
}
]
}Escopo: nfse_repasse:read
GET /nfse-repasse/notas-fiscais/{nota_id}
Detalha uma nota fiscal pelo ID. A resposta não inclui o base64 do arquivo - apenas metadados do anexo (arquivo_nome, arquivo_mime, tem_arquivo) e o vínculo opcional id_nfe_salvador.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID interno da nota |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Nota fiscal encontrada!",
"data": {
"id": 1,
"id_prestador": 1,
"id_tomador": 1,
"id_customer": 1,
"status": "emitida",
"arquivo_nome": "nfse-1234.pdf",
"arquivo_mime": "application/pdf",
"tem_arquivo": true,
"id_nfe_salvador": null,
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-02T09:30:00Z"
}
}Escopo: nfse_repasse:read
PUT /nfse-repasse/notas-fiscais/{nota_id}
Atualiza uma nota. Body NotaFiscalUpdate com campos parciais - somente o que vier preenchido é alterado. id, uuid_legado, id_usuario_created, created_at e updated_at são imutáveis (bloqueio de mass-assignment). Permite setar id_nfe_salvador (vínculo à automação NFS-e) e evoluir o status.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request (NotaFiscalUpdate - todos os campos opcionais)
{
"status": "pronta_emissao",
"id_nfe_salvador": 55
}Response 200 OK
{
"success": true,
"message": "Nota fiscal atualizada!",
"data": {
"id": 1,
"id_prestador": 1,
"id_tomador": 1,
"status": "pronta_emissao",
"id_nfe_salvador": 55,
"tem_arquivo": false,
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-03T14:10:00Z"
}
}Escopo: nfse_repasse:write
DELETE /nfse-repasse/notas-fiscais/{nota_id}
Remove a nota fiscal. Operação destrutiva - a cascata dos sub-recursos (médicos, recebimentos, contas a receber, integrações, logs) é feita pelo banco via ON DELETE CASCADE.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Nota fiscal removida!",
"data": null
}Escopo: nfse_repasse:write
Médicos da nota (/{nota_id}/medicos)
Vínculo entre a nota e os médicos, com o valor bruto atribuído a cada um.
GET /nfse-repasse/notas-fiscais/{nota_id}/medicos
Lista os médicos vinculados à nota.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Médicos recuperados com sucesso!",
"data": [
{
"id": 1,
"id_nota_fiscal": 1,
"id_medico": 1,
"valor_bruto_medico": "1500.00",
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-01T12:00:00Z"
}
]
}Escopo: nfse_repasse:read
POST /nfse-repasse/notas-fiscais/{nota_id}/medicos
Vincula um médico à nota. Body NotaFiscalMedicoCreate (id_nota_fiscal vem da rota).
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
{
"id_medico": 1,
"valor_bruto_medico": "1500.00"
}Response 201 Created
{
"success": true,
"message": "Médico vinculado com sucesso!",
"data": {
"id": 1,
"id_nota_fiscal": 1,
"id_medico": 1,
"valor_bruto_medico": "1500.00",
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-01T12:00:00Z"
}
}Escopo: nfse_repasse:write
PUT /nfse-repasse/notas-fiscais/{nota_id}/medicos/{medico_link_id}
Atualiza o vínculo (campos id_medico, valor_bruto_medico).
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
medico_link_id | int | path | sim | ID interno do vínculo |
Request (NotaFiscalMedicoUpdate - campos parciais)
{
"valor_bruto_medico": "1600.00"
}Response 200 OK
{
"success": true,
"message": "Médico atualizado!",
"data": {
"id": 1,
"id_nota_fiscal": 1,
"id_medico": 1,
"valor_bruto_medico": "1600.00",
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-03T14:10:00Z"
}
}Escopo: nfse_repasse:write
DELETE /nfse-repasse/notas-fiscais/{nota_id}/medicos/{medico_link_id}
Remove o vínculo do médico.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
medico_link_id | int | path | sim | ID interno do vínculo |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Médico removido!",
"data": null
}Escopo: nfse_repasse:write
Recebimentos (/{nota_id}/recebimentos)
Registro dos recebimentos financeiros da nota.
GET /nfse-repasse/notas-fiscais/{nota_id}/recebimentos
Lista os recebimentos da nota.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Recebimentos recuperados com sucesso!",
"data": [
{
"id": 1,
"id_nota_fiscal": 1,
"valor_recebido": "1500.00",
"data_recebimento": "2026-06-15",
"recebido": "S",
"observacao": "PIX",
"id_usuario_registrou": 42,
"created_at": "2026-06-15T10:00:00Z",
"updated_at": "2026-06-15T10:00:00Z"
}
]
}Escopo: nfse_repasse:read
POST /nfse-repasse/notas-fiscais/{nota_id}/recebimentos
Registra um recebimento. Body RecebimentoNotaCreate. O campo recebido aceita boolean no input e é normalizado para 'S'/'N'.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
{
"valor_recebido": "1500.00",
"data_recebimento": "2026-06-15",
"recebido": "S",
"observacao": "PIX"
}Response 201 Created
{
"success": true,
"message": "Recebimento registrado com sucesso!",
"data": {
"id": 1,
"id_nota_fiscal": 1,
"valor_recebido": "1500.00",
"data_recebimento": "2026-06-15",
"recebido": "S",
"observacao": "PIX",
"id_usuario_registrou": 42,
"created_at": "2026-06-15T10:00:00Z",
"updated_at": "2026-06-15T10:00:00Z"
}
}Escopo: nfse_repasse:write
PUT /nfse-repasse/notas-fiscais/{nota_id}/recebimentos/{recebimento_id}
Atualiza um recebimento (campos parciais).
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
recebimento_id | int | path | sim | ID interno do recebimento |
Request (RecebimentoNotaUpdate - campos parciais)
{
"recebido": "S",
"observacao": "Compensado"
}Response 200 OK
{
"success": true,
"message": "Recebimento atualizado!",
"data": {
"id": 1,
"id_nota_fiscal": 1,
"valor_recebido": "1500.00",
"data_recebimento": "2026-06-15",
"recebido": "S",
"observacao": "Compensado",
"created_at": "2026-06-15T10:00:00Z",
"updated_at": "2026-06-16T08:00:00Z"
}
}Escopo: nfse_repasse:write
DELETE /nfse-repasse/notas-fiscais/{nota_id}/recebimentos/{recebimento_id}
Remove um recebimento.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
recebimento_id | int | path | sim | ID interno do recebimento |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Recebimento removido!",
"data": null
}Escopo: nfse_repasse:write
Contas a receber / status financeiro (/{nota_id}/contas-receber)
Histórico do status financeiro da nota (a_receber, recebida, em_atraso, abatida_no_repasse).
GET /nfse-repasse/notas-fiscais/{nota_id}/contas-receber
Lista o status financeiro da nota.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Status financeiro recuperado com sucesso!",
"data": [
{
"id": 1,
"id_nota_fiscal": 1,
"status_financeiro": "a_receber",
"referencia_data": "2026-06-01",
"observacao": "Vencimento em 30 dias",
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-01T12:00:00Z"
}
]
}Escopo: nfse_repasse:read
POST /nfse-repasse/notas-fiscais/{nota_id}/contas-receber
Cria um registro de status financeiro. Body ContaReceberStatusCreate.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
{
"status_financeiro": "a_receber",
"referencia_data": "2026-06-01",
"observacao": "Vencimento em 30 dias"
}Response 201 Created
{
"success": true,
"message": "Status financeiro criado com sucesso!",
"data": {
"id": 1,
"id_nota_fiscal": 1,
"status_financeiro": "a_receber",
"referencia_data": "2026-06-01",
"observacao": "Vencimento em 30 dias",
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-01T12:00:00Z"
}
}Escopo: nfse_repasse:write
PUT /nfse-repasse/notas-fiscais/{nota_id}/contas-receber/{conta_id}
Atualiza um status financeiro (campos parciais).
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
conta_id | int | path | sim | ID interno do registro |
Request (ContaReceberStatusUpdate - campos parciais)
{
"status_financeiro": "recebida"
}Response 200 OK
{
"success": true,
"message": "Status financeiro atualizado!",
"data": {
"id": 1,
"id_nota_fiscal": 1,
"status_financeiro": "recebida",
"referencia_data": "2026-06-01",
"observacao": "Vencimento em 30 dias",
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-16T08:00:00Z"
}
}Escopo: nfse_repasse:write
DELETE /nfse-repasse/notas-fiscais/{nota_id}/contas-receber/{conta_id}
Remove um status financeiro.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
conta_id | int | path | sim | ID interno do registro |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Status financeiro removido!",
"data": null
}Escopo: nfse_repasse:write
Integrações hospitalares (/{nota_id}/integracoes)
Registro de envios da nota a integrações de hospitais/tomadores (pendente, enviado, erro, confirmado).
GET /nfse-repasse/notas-fiscais/{nota_id}/integracoes
Lista as integrações hospitalares da nota.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Integrações hospitalares recuperadas com sucesso!",
"data": [
{
"id": 1,
"id_nota_fiscal": 1,
"id_tomador": 1,
"payload_json": { "guia": "12345" },
"status_integracao": "pendente",
"resposta_integracao": null,
"enviado_at": null,
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-01T12:00:00Z"
}
]
}Escopo: nfse_repasse:read
POST /nfse-repasse/notas-fiscais/{nota_id}/integracoes
Cria uma integração. Body NotaFiscalIntegracaoHospitalCreate (id_tomador obrigatório).
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
{
"id_tomador": 1,
"payload_json": { "guia": "12345" },
"status_integracao": "pendente"
}Response 201 Created
{
"success": true,
"message": "Integração hospitalar criada com sucesso!",
"data": {
"id": 1,
"id_nota_fiscal": 1,
"id_tomador": 1,
"payload_json": { "guia": "12345" },
"status_integracao": "pendente",
"resposta_integracao": null,
"enviado_at": null,
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-01T12:00:00Z"
}
}Escopo: nfse_repasse:write
PUT /nfse-repasse/notas-fiscais/{nota_id}/integracoes/{integracao_id}
Atualiza uma integração (campos parciais: status_integracao, resposta_integracao, enviado_at etc.).
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
integracao_id | int | path | sim | ID interno da integração |
Request (NotaFiscalIntegracaoHospitalUpdate - campos parciais)
{
"status_integracao": "enviado",
"enviado_at": "2026-06-05T09:00:00Z"
}Response 200 OK
{
"success": true,
"message": "Integração hospitalar atualizada!",
"data": {
"id": 1,
"id_nota_fiscal": 1,
"id_tomador": 1,
"status_integracao": "enviado",
"resposta_integracao": null,
"enviado_at": "2026-06-05T09:00:00Z",
"created_at": "2026-06-01T12:00:00Z",
"updated_at": "2026-06-05T09:00:00Z"
}
}Escopo: nfse_repasse:write
DELETE /nfse-repasse/notas-fiscais/{nota_id}/integracoes/{integracao_id}
Remove uma integração.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
integracao_id | int | path | sim | ID interno da integração |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Integração hospitalar removida!",
"data": null
}Escopo: nfse_repasse:write
Logs de emissão (/{nota_id}/emissao-logs)
Trilha append-only das tentativas de emissão de NFS-e feitas pelos workers Celery. Sem PUT/DELETE - só leitura e criação.
GET /nfse-repasse/notas-fiscais/{nota_id}/emissao-logs
Lista os logs de tentativa de emissão da nota.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Logs de emissão recuperados com sucesso!",
"data": [
{
"id": 1,
"id_nota_fiscal": 1,
"status": "erro",
"http_status": 500,
"worker_id": "celery@nfse-1",
"erro_mensagem": "Timeout no webservice da prefeitura",
"duracao_ms": 30120,
"created_at": "2026-06-05T09:05:00Z"
}
]
}Escopo: nfse_repasse:read
POST /nfse-repasse/notas-fiscais/{nota_id}/emissao-logs
Registra uma tentativa de emissão. Body EmissaoNfseLogCreate (status obrigatório: sucesso, erro ou timeout). Tipicamente gravado pelo worker de emissão ao final de cada tentativa.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
{
"status": "erro",
"http_status": 500,
"worker_id": "celery@nfse-1",
"erro_mensagem": "Timeout no webservice da prefeitura",
"duracao_ms": 30120
}Response 201 Created
{
"success": true,
"message": "Log de emissão registrado com sucesso!",
"data": {
"id": 1,
"id_nota_fiscal": 1,
"status": "erro",
"http_status": 500,
"worker_id": "celery@nfse-1",
"erro_mensagem": "Timeout no webservice da prefeitura",
"duracao_ms": 30120,
"created_at": "2026-06-05T09:05:00Z"
}
}Escopo: nfse_repasse:write
Arquivo da nota (/{nota_id}/arquivo)
Anexo binário (PDF/XML/imagem) da nota, persistido no banco como base64 (data URL). Sem OCR.
POST /nfse-repasse/notas-fiscais/{nota_id}/arquivo
Upload manual do arquivo (multipart file). O service valida o tamanho (máx. 15 MB → 413 se exceder), codifica em base64 no formato data:<mime>;base64,... e persiste em nota.arquivo + metadados. Retorna só os metadados (sem o base64).
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
file | UploadFile | form (multipart) | sim | Arquivo da nota |
Request
Envio multipart/form-data com o campo file (binário do arquivo). Não usa corpo JSON.
Response 200 OK
{
"success": true,
"message": "Arquivo anexado com sucesso!",
"data": {
"id_nota_fiscal": 1,
"arquivo_nome": "nfse-1234.pdf",
"arquivo_mime": "application/pdf",
"tem_arquivo": true,
"id_nfe_salvador": null
}
}Escopo: nfse_repasse:write
GET /nfse-repasse/notas-fiscais/{nota_id}/arquivo
Download binário do arquivo da nota. Não usa o envelope JSON - retorna o conteúdo bruto com Content-Type do arquivo (application/pdf, image/png, image/jpeg etc.) e header Content-Disposition: attachment; filename="...".
Resolve primeiro o arquivo próprio da nota; se não houver e a nota estiver vinculada a um registro da automação NFS-e (id_nfe_salvador), faz fallback para o arquivo desse registro. Sem nenhum dos dois → 404.
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
Sem corpo de requisição.
Response 200 OK (corpo binário do arquivo, fora do envelope JSON)
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="nfse-1234.pdf"
%PDF-1.7 ...(bytes)...Escopo: nfse_repasse:read
DELETE /nfse-repasse/notas-fiscais/{nota_id}/arquivo
Remove apenas o arquivo próprio da nota (não afeta o vínculo id_nfe_salvador com a automação). Retorna os metadados atualizados (ArquivoNotaMetaRead).
Parâmetros
| Parâmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nota_id | int | path | sim | ID da nota |
Request
Sem corpo de requisição.
Response 200 OK
{
"success": true,
"message": "Arquivo removido com sucesso!",
"data": {
"id_nota_fiscal": 1,
"arquivo_nome": null,
"arquivo_mime": null,
"tem_arquivo": false,
"id_nfe_salvador": null
}
}Escopo: nfse_repasse:write
Schemas
Schema NotaFiscal
Campos do modelo (via NotaFiscalCreate / NotaFiscalUpdate / NotaFiscalRead).
Identificação / relacionamentos:
id: int- ID interno (só na leitura).id_prestador: int- prestador médico (prestador_medico.id) - obrigatório no Create.id_tomador: int- tomador (tomador.id) - obrigatório no Create.id_customer: int?- cliente associado (customer.id), associação de negócio (portal do cliente).id_nfe_salvador: int?- vínculo ao registro da automação NFS-e (nfe_salvador_medicos.id); habilita o fallback de download.
Referências fiscais (opcionais): id_cnae, id_sub_cnae, id_ctiss, id_nbs, id_indicador_operacao, id_classificacao_tributaria, id_modelo_descricao.
Negócio / emissão:
descricao_final: str?competencia_referencia: str?(≤20, ex.:"2026-06")data_emissao: date?numero_nf: str?(≤50, preenchido após emissão)codigo_verificacao: str?(≤100, após emissão)status: str- um derascunho,pronta_emissao,em_processamento,emitida,erro_emissao,cancelada(defaultrascunho).processamento_iniciado_em: datetime?- quando o worker iniciou a emissão.pdf_nota_url: str?,xml_nota_url: str?- URLs do PDF/XML emitidos.observacoes: str?
Valores monetários (Decimal, default 0): aliquota_iss, valor_bruto, iss_retido, pis_retido, cofins_retido, csll_retido, irpj_retido, inss_retido, valor_liquido.
Somente na leitura (NotaFiscalRead): arquivo_nome, arquivo_mime, tem_arquivo (bool - true se há arquivo próprio ou vínculo de automação), id_usuario_created, uuid_legado, created_at, updated_at.
Metadados de migração
uuid_legado e uuid_solicitacao_nota_legado correlacionam a nota com o registro de origem no Supabase (app webapp-nfs-repasse-medico). uuid_legado é aceito no Create, mas imutável depois (não aparece no Update).
Schemas dos sub-recursos
NotaFiscalMedicoCreate/Update/Read-id_medico: int(obrigatório no Create),valor_bruto_medico: Decimal. Leitura adicionaid,id_nota_fiscal, auditoria e timestamps.RecebimentoNotaCreate/Update/Read-valor_recebido: Decimal,data_recebimento: date?,recebido: 'S'|'N'(aceita boolean no input),observacao: str?,id_usuario_registrou: int?.ContaReceberStatusCreate/Update/Read-status_financeiro: 'a_receber'|'recebida'|'em_atraso'|'abatida_no_repasse',referencia_data: date?,observacao: str?.NotaFiscalIntegracaoHospitalCreate/Update/Read-id_tomador: int(obrigatório),payload_json: Any?,status_integracao: 'pendente'|'enviado'|'erro'|'confirmado',resposta_integracao: str?,enviado_at: datetime?.EmissaoNfseLogCreate/Read(append-only, semupdated_at) -status: 'sucesso'|'erro'|'timeout'(obrigatório),http_status: int?,payload_enviado: Any?,response_bruto: Any?,worker_id: str?,erro_mensagem: str?,duracao_ms: int?,id_usuario: int?.ArquivoNotaMetaRead-id_nota_fiscal,arquivo_nome?,arquivo_mime?,tem_arquivo: bool,id_nfe_salvador?.
Em todos os Create de sub-recurso, id_nota_fiscal vem da rota aninhada (não entra no body) e id_usuario_created é forçado pelo service. uuid_legado é aceito apenas para rastreabilidade da migração.
Notas
- Emissão é assíncrona (Celery). Este router só persiste estado - a emissão real da NFS-e roda em workers Celery posteriores. Acompanhe o ciclo pelo
statusda nota, porprocessamento_iniciado_eme pelo histórico emGET /{nota_id}/emissao-logs. - Download binário quebra o envelope.
GET /{nota_id}/arquivoé a única rota que não retorna{ success, message, data }- devolve os bytes comContent-Type/Content-Disposition. Todas as demais usam o envelope padrão. - Fallback de arquivo. Sem arquivo próprio, o download cai para o registro da automação (
id_nfe_salvador).tem_arquivoreflete qualquer uma das fontes. - Limite de upload: 15 MB por arquivo; acima disso,
413 Request Entity Too Large. - Cascata no banco. O
DELETE /{nota_id}é o único delete que precisa ser chamado para limpar a nota - os sub-recursos são removidos porON DELETE CASCADE.
Leitura livre, escrita administrativa
Reforçando: qualquer staff autenticado lê (nfse_repasse:read); todas as mutações exigem nfse_repasse:write e perfil administrador. API Keys emitidas para clientes IA / gateway MCP devem, por padrão, receber somente nfse_repasse:read.