NFS-e/Repasse: NF-e Salvador Médicos

Endpoints da WebApiAlcance para gerenciar as NFS-e do município de Salvador voltadas a médicos dentro do módulo NFS-e/Repasse. Cobrem o registro manual de NF-e, listagem, busca por ID e pelo número da nota, download do arquivo binário (imagem) da NFS-e, atualização, exclusão e a emissão assíncrona no portal Nota Salvador - esta última persiste os dados, cria um Worker no estado AGENDADO e enfileira uma task Celery executada por um scraper.

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 do módulo NFS-e/Repasse

O primeiro segmento da rota é nfse-repasse (compartilhado por todo o módulo). O escopo é derivado dele: nfse_repasse:read para leitura e nfse_repasse:write para escrita. Ou seja, os recursos internos do módulo (nfe-salvador-medicos, médicos, prestadores etc.) compartilham o mesmo par de escopos nfse_repasse:read / nfse_repasse:write, e não escopos por sub-recurso.

Base path

/api/v1/nfse-repasse/nfe-salvador-medicos

Escopos necessários

  • nfse_repasse:read - listagem, busca por ID, busca por número da nota e download do arquivo (GET)
  • nfse_repasse:write - registro, atualização, exclusão e emissão (POST/PUT/DELETE)

O escopo é derivado do primeiro segmento da rota: /nfse-repasse vira o recurso nfse_repasse (hífen → underscore), compartilhado por todo o módulo. O método HTTP define a ação (read para GET, write para POST/PUT/DELETE).

Endpoints

POST /nfse-repasse/nfe-salvador-medicos/

Registra manualmente uma NF-e Salvador Médicos (persistência direta, sem disparar automação). Quando não é possível persistir, retorna o envelope de erro { success: false, message: "Nao foi possivel registrar a NF-e.", details: [] }.

Parâmetros

Este endpoint não recebe parâmetros de rota ou query - os dados vão no corpo da requisição (NfeSalvadorMedicosCreate): cliente_id e usuario_id são obrigatórios; os demais campos fiscais são opcionais (str | None).

Request

{
  "cliente_id": 42,
  "usuario_id": 7,
  "status": "REGISTRADA",
  "numero_nota": "2026000123",
  "inscricao_municipal": "123456",
  "cnpj_tomador": "12345678000199",
  "valor_total_nota": "1500.00"
}

Response 200 OK

{
  "success": true,
  "message": "NF-e registrada com sucesso!",
  "data": {
    "id": 10,
    "cliente_id": 42,
    "usuario_id": 7,
    "status": "REGISTRADA",
    "numero_nota": "2026000123",
    "inscricao_municipal": "123456",
    "cnpj_tomador": "12345678000199",
    "valor_total_nota": "1500.00",
    "data_solicitacao": "2026-01-01T12:00:00Z"
  }
}

Escopo: nfse_repasse:write

GET /nfse-repasse/nfe-salvador-medicos/{nfe_id}

Detalha uma NF-e pelo ID inteiro. Quando não encontrada, retorna 404 Not Found com detail: "NF-e não encontrada.".

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
nfe_idintpathsimID interno da NF-e

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "NF-e encontrada!",
  "data": {
    "id": 10,
    "cliente_id": 42,
    "usuario_id": 7,
    "status": "REGISTRADA",
    "numero_nota": "2026000123",
    "inscricao_municipal": "123456",
    "cnpj_tomador": "12345678000199",
    "valor_total_nota": "1500.00",
    "data_solicitacao": "2026-01-01T12:00:00Z"
  }
}

Escopo: nfse_repasse:read

GET /nfse-repasse/nfe-salvador-medicos/numero/{numero_nota}

Busca uma NF-e pelo número da nota emitida. Quando não encontrada, retorna 404 Not Found com detail: "NF-e não encontrada.".

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
numero_notastrpathsimNúmero da nota fiscal emitida

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "NF-e encontrada!",
  "data": {
    "id": 10,
    "cliente_id": 42,
    "usuario_id": 7,
    "status": "REGISTRADA",
    "numero_nota": "2026000123",
    "inscricao_municipal": "123456",
    "cnpj_tomador": "12345678000199",
    "valor_total_nota": "1500.00",
    "data_solicitacao": "2026-01-01T12:00:00Z"
  }
}

Escopo: nfse_repasse:read

GET /nfse-repasse/nfe-salvador-medicos/{nfe_id}/arquivo

Baixa o arquivo binário (imagem) da NFS-e como download. O backend lê o campo arquivo (uma data URL data:<mime>;base64,<payload>), decodifica para bytes e devolve com Content-Disposition: attachment. O nome do arquivo é nfse_<numero_nota|id>.<extensao> e o Content-Type reflete o MIME original (image/png, image/jpeg).

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
nfe_idintpathsimID interno da NF-e

Request

Sem corpo de requisição.

Response 200 OK

Corpo binário da imagem (não é envelope JSON); o Content-Type reflete o MIME original (image/png, image/jpeg). Erros: 404 Not Found quando a NF-e não existe (detail: "NF-e id=<id> nao encontrada.") ou ainda não há arquivo persistido (detail: "NF-e ainda nao possui arquivo persistido."); 500 Internal Server Error quando a data URL armazenada não pode ser decodificada.

Baixar vs. exibir embedado

Use este endpoint quando o front quiser um botão "Baixar" (o navegador salva o arquivo direto). Para exibir a NFS-e embedada no DOM, use GET /nfse-repasse/nfe-salvador-medicos/{nfe_id} e renderize o campo arquivo (data URL) num <img>.

Escopo: nfse_repasse:read

GET /nfse-repasse/nfe-salvador-medicos/

Lista todas as NF-e Salvador Médicos cadastradas. Lista vazia é um estado válido - retorna 200 OK com data: [].

Parâmetros

Este endpoint não recebe parâmetros de rota ou query.

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "NF-e listadas com sucesso!",
  "data": [
    {
      "id": 10,
      "cliente_id": 42,
      "usuario_id": 7,
      "status": "REGISTRADA",
      "numero_nota": "2026000123",
      "inscricao_municipal": "123456",
      "cnpj_tomador": "12345678000199",
      "valor_total_nota": "1500.00",
      "data_solicitacao": "2026-01-01T12:00:00Z"
    }
  ]
}

Escopo: nfse_repasse:read

PUT /nfse-repasse/nfe-salvador-medicos/{nfe_id}

Atualiza uma NF-e existente. O body é um NfeSalvadorMedicosUpdate com campos parciais - somente o que vier preenchido é alterado. Não inclui cliente_id nem usuario_id.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
nfe_idintpathsimID da NF-e

Request (NfeSalvadorMedicosUpdate - todos os campos opcionais)

{
  "status": "REGISTRADA",
  "numero_nota": "2026000123",
  "valor_total_nota": "1500.00"
}

Response 200 OK

{
  "success": true,
  "message": "NF-e atualizada com sucesso!",
  "data": {
    "id": 10,
    "cliente_id": 42,
    "usuario_id": 7,
    "status": "REGISTRADA",
    "numero_nota": "2026000123",
    "inscricao_municipal": "123456",
    "cnpj_tomador": "12345678000199",
    "valor_total_nota": "1500.00",
    "data_solicitacao": "2026-01-01T12:00:00Z"
  }
}

Escopo: nfse_repasse:write

DELETE /nfse-repasse/nfe-salvador-medicos/{nfe_id}

Remove uma NF-e pelo ID.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
nfe_idintpathsimID da NF-e

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "NF-e removida com sucesso!",
  "data": null
}

Escopo: nfse_repasse:write

POST /nfse-repasse/nfe-salvador-medicos/emitir

Enfileira a emissão assíncrona de uma NFS-e no portal Nota Salvador (Médicos). O fluxo localiza o cliente pela inscrição municipal exata do prestador (sem correspondência retorna 404; mais de uma retorna 409), persiste um registro NfeSalvadorMedicos no estado AGENDADO, cria um Worker (automation_type = "nfse_salvador_emissao", queue = "scraping", status = "AGENDADO") e enfileira a task Celery nfse_salvador_emissao via apply_async. Se o broker estiver indisponível, marca o worker como FALHA e retorna 503.

Parâmetros

Este endpoint não recebe parâmetros de rota ou query - os dados vão no corpo da requisição (NfseSalvadorEmitirRequest). O tomador deve ser identificado por apelido_tomador OU cnpj_tomador (exatamente um dos dois).

Request

{
  "inscricao_municipal_prestador": "123456",
  "cnpj_tomador": "12345678000199",
  "valor_total_nota": "1500.00",
  "discriminacao": "Serviços médicos prestados em janeiro/2026.",
  "cnae": "8630501",
  "ctiss": "0801",
  "nbs": "115011000",
  "indicador_operacao": "1",
  "classificacao_tributaria": "01",
  "iss_retido": "N",
  "emitir_definitivo": false
}

Response 200 OK (NfseSalvadorEmitirResponse)

{
  "worker_id": 501,
  "nfe_id": 10,
  "task_id": "a3f8c1e2-9b4d-4e7a-8c1f-2d5b6a8e9f0c",
  "message": "Emissao NFS-e Salvador enfileirada com sucesso. Modo seguro (nao emite)."
}

Acompanhe a execução da task pelo task_id em Worker Automation (GET /worker-automation/status/{task_id}). Erros: 404 Not Found (inscrição municipal não encontrada), 409 Conflict (inscrição corresponde a mais de um cliente) e 503 Service Unavailable (broker Celery indisponível ao enfileirar).

Gate operacional de emissão definitiva

O campo emitir_definitivo só é respeitado quando a env NFSE_SALVADOR_EMITIR_HABILITADO=true (gate server-side por ambiente). Em qualquer outro caso - mesmo com emitir_definitivo: true no payload - a task roda em modo seguro: apenas preenche o formulário no portal, sem emitir a nota de fato. A identificação do cliente é feita por CNPJ/inscrição exata para evitar IDOR.

Escopo: nfse_repasse:write

Schemas

NfeSalvadorMedicosCreate

Herda de NfeSalvadorMedicosBase. Além de cliente_id e usuario_id (obrigatórios), todos os demais campos são opcionais (str | None).

CampoTipoObrigatórioObservações
cliente_idintsimCliente prestador da NFS-e.
usuario_idintsimUsuário que registrou.
statusstrnãoSituação do registro (ex.: AGENDADO, REGISTRADA).
numero_notastrnãoNúmero da nota emitida.
arquivostrnãoData URL da imagem da NFS-e (data:<mime>;base64,...).
inscricao_municipalstrnãoInscrição municipal do prestador.
cnpj_tomadorstrnãoCNPJ/CPF do tomador.
apelido_tomadorstrnãoApelido/identificação do tomador cadastrado no portal.
valor_total_notastrnãoValor total da nota.
iss_retidostrnãoIndicador de ISS retido.
cnaestrnãoCNAE do serviço.
subcnaestrnãoSubitem do CNAE.
aliquotastrnãoAlíquota aplicada.
ctissstrnãoCódigo CTISS.
nbsstrnãoCódigo NBS.
indicador_operacaostrnãoIndicador da operação.
classificacao_tributariastrnãoClassificação tributária.
discriminacaostrnãoDiscriminação do serviço.
issstrnãoValor de ISS.
irrfstrnãoValor de IRRF retido.
csslstrnãoValor de CSLL retido.
cofinsstrnãoValor de COFINS retido.
pisepasepstrnãoValor de PIS/PASEP retido.
inssstrnãoValor de INSS retido.

NfeSalvadorMedicosUpdate

Todos os campos são opcionais; apenas os enviados são atualizados. Não inclui cliente_id nem usuario_id.

CampoTipoObservações
statusstrNova situação do registro.
numero_notastrNúmero da nota emitida.
arquivostrData URL da imagem da NFS-e.
inscricao_municipalstrInscrição municipal do prestador.
cnpj_tomadorstrCNPJ/CPF do tomador.
apelido_tomadorstrApelido do tomador.
valor_total_notastrValor total da nota.
iss_retidostrIndicador de ISS retido.
cnaestrCNAE do serviço.
subcnaestrSubitem do CNAE.
aliquotastrAlíquota aplicada.
ctissstrCódigo CTISS.
nbsstrCódigo NBS.
indicador_operacaostrIndicador da operação.
classificacao_tributariastrClassificação tributária.
discriminacaostrDiscriminação do serviço.
issstrValor de ISS.
irrfstrValor de IRRF retido.
csslstrValor de CSLL retido.
cofinsstrValor de COFINS retido.
pisepasepstrValor de PIS/PASEP retido.
inssstrValor de INSS retido.

NfeSalvadorMedicosRead

Estende NfeSalvadorMedicosBase com campos gerenciados pelo servidor.

CampoTipoNotas
idintIdentificador interno da NF-e.
cliente_idintCliente prestador.
usuario_idintUsuário que registrou.
statusstr?Situação atual.
numero_notastr?Número da nota emitida.
arquivostr?Data URL da imagem da NFS-e.
data_solicitacaodatetime?Timestamp da solicitação (server-side).
(demais campos)str?Todos os campos fiscais herdados de NfeSalvadorMedicosBase (ver NfeSalvadorMedicosCreate).

NfseSalvadorEmitirRequest

Payload para disparar a automação de emissão de NFS-e Salvador. A identificação do tomador exige exatamente um entre apelido_tomador e cnpj_tomador (validação model_validator).

CampoTipoObrigatórioObservações
inscricao_municipal_prestadorstrsim1-20 chars. Identifica o cliente por correspondência exata (.strip()).
valor_total_notastrsim1-50 chars.
discriminacaostrsim1-2000 chars. Discriminação do serviço.
cnaestrsim1-20 chars.
ctissstrsim1-20 chars.
nbsstrsim1-20 chars.
indicador_operacaostrsim1-10 chars.
classificacao_tributariastrsim1-20 chars.
cnpj_tomadorstrcondicionalMáx. 20. Deve ter 11 (CPF) ou 14 (CNPJ) dígitos. Vazio → null.
apelido_tomadorstrcondicionalMáx. 100. Vazio → null. Informe um entre este e cnpj_tomador.
iss_retidostrnãoMáx. 10. Default "N".
aliquotastrnãoMáx. 20.
subcnaestrnãoMáx. 20.
issstrnãoMáx. 10.
irrfstrnãoMáx. 10.
csslstrnãoMáx. 10.
cofinsstrnãoMáx. 10.
pisepasepstrnãoMáx. 10.
inssstrnãoMáx. 10.
emitir_definitivoboolnãoDefault false. Só efetiva a emissão se NFSE_SALVADOR_EMITIR_HABILITADO=true.

NfseSalvadorEmitirResponse

CampoTipoNotas
worker_idintID do Worker criado para acompanhar a automação.
nfe_idintID do registro NfeSalvadorMedicos persistido.
task_idstrUUID da task Celery - consulte em Worker Automation.
messagestrConfirmação, indicando modo definitivo ou modo seguro.

Notas

  • Toda resposta segue o envelope { success, message, data } (ou { success: false, message, details } em erro) - exceto GET /{nfe_id}/arquivo, que devolve o corpo binário da imagem, e POST /emitir, que devolve diretamente o NfseSalvadorEmitirResponse.
  • O registro via POST / é persistência manual e não dispara automação; a emissão de fato ocorre apenas via POST /emitir, que enfileira a task Celery nfse_salvador_emissao na fila scraping (ver Worker Automation).
  • Gate de segurança na emissão: o cliente é resolvido por inscrição municipal exata (evita IDOR) e emitir_definitivo só vale com a env NFSE_SALVADOR_EMITIR_HABILITADO=true; caso contrário a automação roda em modo seguro (apenas preenche o formulário).
  • O campo arquivo é armazenado como data URL base64. O download decodifica o MIME embutido (image/png, image/jpeg) e define o nome como nfse_<numero_nota|id>.<extensao>.
  • Falha ao enfileirar a task marca o Worker como FALHA (com mensagem sanitizada, sem stacktrace/credenciais) e retorna 503.