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 (rascunhopronta_emissaoem_processamentoemitida/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-fiscais

Escopos 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âmetroTipoLocalObrigatórioDescrição
customer_idintpathsimID do cliente associado
statusstrquerynãoFiltro por status da nota (ex.: emitida)
pageint ≥1querynãoPágina (1-based)
per_pageint 1..500querynãoItens 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âmetroTipoLocalObrigatórioDescrição
statusstrquerynãoFiltro por status da nota
id_customerintquerynãoFiltro por cliente associado (customer.id)
id_prestadorintquerynãoFiltro por prestador (prestador_medico.id)
id_tomadorintquerynãoFiltro por tomador (tomador.id)
pageint ≥1querynãoPágina (1-based)
per_pageint 1..500querynãoItens 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=50

Response 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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

Atualiza o vínculo (campos id_medico, valor_bruto_medico).

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID da nota
medico_link_idintpathsimID 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

Remove o vínculo do médico.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID da nota
medico_link_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID da nota
recebimento_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID da nota
recebimento_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID da nota
conta_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID da nota
conta_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID da nota
integracao_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID da nota
integracao_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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 MB413 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID da nota
fileUploadFileform (multipart)simArquivo 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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âmetroTipoLocalObrigatórioDescrição
nota_idintpathsimID 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 de rascunho, pronta_emissao, em_processamento, emitida, erro_emissao, cancelada (default rascunho).
  • 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 adiciona id, 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, sem updated_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 status da nota, por processamento_iniciado_em e pelo histórico em GET /{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 com Content-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_arquivo reflete 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 por ON 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.