NFS-e Importada

Endpoints da WebApiAlcance para gerenciar NFS-e importadas - notas fiscais de serviço trazidas de planilhas (XLSX) ou lançadas manualmente, com seus valores de serviços e retenções fiscais (PIS, COFINS, ISS, IRRF, CSLL, INSS). Cobrem criação individual, importação em lote idempotente (upsert), listagem por cliente e por usuário (com filtro de competência), busca por ID, atualização parcial, exclusão individual e exclusão em lote por competência (limpar e reimportar).

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.

Base path

/api/v1/nfse-importada

Escopos necessários

  • nfse_importada:read - listagem por cliente, listagem por usuário e busca por ID
  • nfse_importada:write - criação individual, importação em lote, atualização, exclusão individual e exclusão em lote

O escopo é derivado da rota: o prefixo /nfse-importada vira o recurso nfse_importada (hífen → underscore), e o método HTTP define a ação (read para GET, write para POST/PUT/PATCH/DELETE).

Endpoints

POST /nfse-importada/

Cria uma NFS-e individual, usada para registro manual. O cliente dono da NFS-e é definido por id_customer, e o id_usuario_created vem do usuário autenticado (não é aceito no corpo).

Parâmetros

Este endpoint não recebe parâmetros de rota ou query - os dados vão no corpo da requisição (NfseImportadaCreate). Obrigatórios: id_customer, cnpj_prestador, numero_nfse, data_emissao, mes_apuracao.

Request

{
  "id_customer": 1,
  "cnpj_prestador": "12345678000199",
  "cnpj_tomador": "98765432000188",
  "numero_nfse": "2026-000123",
  "data_emissao": "2026-05-15",
  "mes_apuracao": "2026-05",
  "valor_servicos": "1500.00",
  "valor_pis": "9.75",
  "valor_cofins": "45.00",
  "valor_iss": "75.00",
  "valor_irrf": "22.50",
  "valor_csll": "15.00",
  "origem": "manual"
}

Response 201 Created

{
  "success": true,
  "message": "NFS-e criada com sucesso!",
  "data": {
    "id": 1,
    "id_customer": 1,
    "cnpj_prestador": "12345678000199",
    "cnpj_tomador": "98765432000188",
    "numero_nfse": "2026-000123",
    "data_emissao": "2026-05-15",
    "mes_apuracao": "2026-05",
    "valor_servicos": "1500.00",
    "valor_pis": "9.75",
    "valor_cofins": "45.00",
    "valor_iss": "75.00",
    "valor_irrf": "22.50",
    "valor_csll": "15.00",
    "valor_inss": "0.00",
    "origem": "manual",
    "dados_brutos": null,
    "id_usuario_created": 42,
    "created_at": "2026-05-16T10:00:00",
    "updated_at": "2026-05-16T10:00:00"
  }
}

Escopo: nfse_importada:write

POST /nfse-importada/bulk

Importação em lote idempotente (upsert). Recebe uma lista de NFS-e em JSON e faz upsert pela constraint UNIQUE (id_customer, cnpj_prestador, numero_nfse, data_emissao): inexistente cria (criadas++), existente idêntica ignora (ignoradas++) e existente com valores diferentes atualiza (atualizadas++). Erros individuais não abortam o lote - são reportados no array errors com o índice do item no payload.

Parâmetros

Este endpoint não recebe parâmetros de rota ou query - o corpo é um NfseImportadaBulkCreate, objeto com items (lista de NfseImportadaCreate, mínimo 1 e máximo 500 itens por chamada; limite defensivo contra DoS).

Request

{
  "items": [
    {
      "id_customer": 1,
      "cnpj_prestador": "12345678000199",
      "numero_nfse": "2026-000123",
      "data_emissao": "2026-05-15",
      "mes_apuracao": "2026-05",
      "valor_servicos": "1500.00"
    }
  ]
}

Response 200 OK

{
  "success": true,
  "message": "Importação processada.",
  "data": {
    "criadas": 12,
    "atualizadas": 3,
    "ignoradas": 5,
    "errors": [
      {
        "index": 7,
        "numero_nfse": "2026-000130",
        "motivo": "cnpj_prestador inválido (deve ter 14 dígitos)."
      }
    ]
  }
}

Escopo: nfse_importada:write

GET /nfse-importada/by-customer/{customer_id}

Lista as NFS-e de um cliente, com filtro opcional de competência e paginação. Lista vazia é um estado válido - retorna 200 OK com data: [] e a mensagem "Nenhuma NFS-e encontrada.".

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
customer_idintpathsimID do cliente (customer.id).
mes_apuracaostr (YYYY-MM)querynãoCompetência fiscal. Ex.: 2026-05.
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": "NFS-e recuperadas com sucesso!",
  "data": [
    {
      "id": 1,
      "id_customer": 1,
      "cnpj_prestador": "12345678000199",
      "cnpj_tomador": "98765432000188",
      "numero_nfse": "2026-000123",
      "data_emissao": "2026-05-15",
      "mes_apuracao": "2026-05",
      "valor_servicos": "1500.00",
      "valor_pis": "9.75",
      "valor_cofins": "45.00",
      "valor_iss": "75.00",
      "valor_irrf": "22.50",
      "valor_csll": "15.00",
      "valor_inss": "0.00",
      "origem": "import",
      "dados_brutos": null,
      "id_usuario_created": 42,
      "created_at": "2026-05-16T10:00:00",
      "updated_at": "2026-05-16T10:00:00"
    }
  ]
}

Escopo: nfse_importada:read

GET /nfse-importada/by-user

Lista todas as NFS-e do usuário autenticado (consolidação geral, multi-tenant), com filtro opcional de competência. Lista vazia retorna 200 OK com data: [] e a mensagem "Nenhuma NFS-e encontrada.".

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
mes_apuracaostr (YYYY-MM)querynãoCompetência fiscal. Ex.: 2026-05.

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "NFS-e recuperadas com sucesso!",
  "data": [
    {
      "id": 1,
      "id_customer": 1,
      "cnpj_prestador": "12345678000199",
      "cnpj_tomador": "98765432000188",
      "numero_nfse": "2026-000123",
      "data_emissao": "2026-05-15",
      "mes_apuracao": "2026-05",
      "valor_servicos": "1500.00",
      "valor_pis": "9.75",
      "valor_cofins": "45.00",
      "valor_iss": "75.00",
      "valor_irrf": "22.50",
      "valor_csll": "15.00",
      "valor_inss": "0.00",
      "origem": "import",
      "dados_brutos": null,
      "id_usuario_created": 42,
      "created_at": "2026-05-16T10:00:00",
      "updated_at": "2026-05-16T10:00:00"
    }
  ]
}

Escopo: nfse_importada:read

DELETE /nfse-importada/by-customer/{customer_id}/competencia/{mes_apuracao}

Exclusão em lote - remove todas as NFS-e de um cliente em uma competência. Usado quando o front quer "limpar e reimportar". O mes_apuracao no path deve seguir o formato YYYY-MM (validado na rota; formato inválido retorna 422).

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
customer_idintpathsimID do cliente (customer.id).
mes_apuracaostr (YYYY-MM)pathsimCompetência fiscal a limpar.

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "NFS-e removidas em lote.",
  "data": { "removidas": 17 }
}

Escopo: nfse_importada:write

GET /nfse-importada/{nfse_id}

Busca uma NFS-e importada pelo ID inteiro. Quando não encontrada, retorna 404 Not Found.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
nfse_idintpathsimID interno da NFS-e.

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "NFS-e encontrada!",
  "data": {
    "id": 1,
    "id_customer": 1,
    "cnpj_prestador": "12345678000199",
    "cnpj_tomador": "98765432000188",
    "numero_nfse": "2026-000123",
    "data_emissao": "2026-05-15",
    "mes_apuracao": "2026-05",
    "valor_servicos": "1500.00",
    "valor_pis": "9.75",
    "valor_cofins": "45.00",
    "valor_iss": "75.00",
    "valor_irrf": "22.50",
    "valor_csll": "15.00",
    "valor_inss": "0.00",
    "origem": "manual",
    "dados_brutos": null,
    "id_usuario_created": 42,
    "created_at": "2026-05-16T10:00:00",
    "updated_at": "2026-05-16T10:00:00"
  }
}

Escopo: nfse_importada:read

PUT /nfse-importada/{nfse_id}

Atualiza uma NFS-e existente. O corpo é um NfseImportadaUpdate com campos parciais - somente o que vier preenchido é alterado. Campos imutáveis (id_customer, id_usuario_created, id, created_at, updated_at) não são aceitos aqui, para bloquear mass-assignment.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
nfse_idintpathsimID interno da NFS-e.

Request (NfseImportadaUpdate - todos os campos opcionais)

{
  "valor_servicos": "1800.00",
  "valor_iss": "90.00"
}

Response 200 OK

{
  "success": true,
  "message": "NFS-e atualizada!",
  "data": {
    "id": 1,
    "id_customer": 1,
    "cnpj_prestador": "12345678000199",
    "cnpj_tomador": "98765432000188",
    "numero_nfse": "2026-000123",
    "data_emissao": "2026-05-15",
    "mes_apuracao": "2026-05",
    "valor_servicos": "1800.00",
    "valor_pis": "9.75",
    "valor_cofins": "45.00",
    "valor_iss": "90.00",
    "valor_irrf": "22.50",
    "valor_csll": "15.00",
    "valor_inss": "0.00",
    "origem": "manual",
    "dados_brutos": null,
    "id_usuario_created": 42,
    "created_at": "2026-05-16T10:00:00",
    "updated_at": "2026-05-16T11:42:00"
  }
}

Alterar cnpj_prestador, numero_nfse ou data_emissao pode colidir com a constraint UNIQUE (id_customer, cnpj_prestador, numero_nfse, data_emissao) - nesse caso a API retorna 409 Conflict.

Escopo: nfse_importada:write

DELETE /nfse-importada/{nfse_id}

Remove uma NFS-e importada pelo ID.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
nfse_idintpathsimID interno da NFS-e.

Request

Sem corpo de requisição.

Response 200 OK

{
  "success": true,
  "message": "NFS-e removida!",
  "data": null
}

Escopo: nfse_importada:write

Schemas

NfseImportadaBase / NfseImportadaCreate

Campos compartilhados entre criação e leitura. NfseImportadaCreate é idêntico ao NfseImportadaBase (também usado como item do bulk import). Valores monetários são Decimal (2 casas), nunca negativos.

CampoTipoObrigatórioObservações
id_customerintsimID do cliente (customer.id) dono da NFS-e. Imutável após criação.
cnpj_prestadorstr (máx. 20)simCNPJ do prestador. Normalizado para 14 dígitos (aceita com/sem máscara).
cnpj_tomadorstr (máx. 20)nãoCNPJ do tomador. Opcional (NF-e para PF não tem); vazio vira null.
numero_nfsestr (máx. 50)simNúmero da NFS-e (string, acomoda prefixos/letras de prefeituras).
data_emissaodatesimData de emissão (YYYY-MM-DD) - origem da competência fiscal.
mes_apuracaostr (YYYY-MM)simCompetência fiscal, computada a partir de data_emissao.
valor_servicosDecimalnãoValor bruto dos serviços (R$). Default 0. Não pode ser negativo.
valor_pisDecimalnãoPIS retido/devido (R$). Default 0.
valor_cofinsDecimalnãoCOFINS retido/devido (R$). Default 0.
valor_issDecimalnãoISS retido/devido (R$). Default 0.
valor_irrfDecimalnãoIRRF retido (R$). Default 0.
valor_csllDecimalnãoCSLL retido (R$). Default 0.
valor_inssDecimalnãoINSS retido (R$). Default 0.
origemstr (máx. 20)nãoimport (planilha XLSX) ou manual (formulário). Default import.
dados_brutosdictnãoPayload bruto do XLSX para auditoria/debug. JSON serializável, máx. 16 KiB.

NfseImportadaUpdate

Todos os campos são opcionais; apenas os enviados (exclude_unset) são atualizados. Não inclui id_customer nem campos gerenciados (bloqueio de mass-assignment). Mesmas regras de validação do Base (CNPJ com 14 dígitos, valores ≥ 0, origemmanual, dados_brutos ≤ 16 KiB).

CampoTipoObservações
cnpj_prestadorstr (máx. 20)Vazio vira null. Faz parte da UNIQUE.
cnpj_tomadorstr (máx. 20)Vazio vira null ("limpar" tomador é válido).
numero_nfsestr (máx. 50)Faz parte da UNIQUE.
data_emissaodateFaz parte da UNIQUE.
mes_apuracaostr (YYYY-MM)Competência fiscal.
valor_servicosDecimalNão pode ser negativo.
valor_pisDecimalNão pode ser negativo.
valor_cofinsDecimalNão pode ser negativo.
valor_issDecimalNão pode ser negativo.
valor_irrfDecimalNão pode ser negativo.
valor_csllDecimalNão pode ser negativo.
valor_inssDecimalNão pode ser negativo.
origemstr (máx. 20)import ou manual.
dados_brutosdictJSON serializável, máx. 16 KiB.

NfseImportadaRead

Estende o NfseImportadaBase com os campos gerenciados (server-side).

CampoTipoNotas
idintID interno da NFS-e importada.
id_usuario_createdintID do usuário que criou (multi-tenant).
created_atdatetimeTimestamp de criação (server-side).
updated_atdatetimeTimestamp da última atualização.

(mais todos os campos de NfseImportadaBase.)

NfseImportadaBulkCreate

Payload do endpoint POST /nfse-importada/bulk.

CampoTipoObrigatórioObservações
itemsList[NfseImportadaCreate]simMínimo 1, máximo 500 itens por chamada (limite anti-DoS).

NfseImportadaBulkResult

Resultado consolidado do bulk import (retornado em data).

CampoTipoObservações
criadasintQuantidade de NFS-e criadas (novas).
atualizadasintQuantidade atualizadas (já existiam com valores diff).
ignoradasintQuantidade ignoradas (já existiam idênticas).
errorsList[NfseImportadaBulkError]Itens que falharam - não interrompem o lote.

NfseImportadaBulkError

Detalhe de um item que falhou no bulk import.

CampoTipoObservações
indexintÍndice do item no payload original (0-based).
numero_nfsestrNúmero da NFS-e, se conhecido (pode ser null).
motivostrMensagem de erro.

Notas

  • Toda resposta segue o envelope { success, message, data } (ou { success: false, message, details } em erro).
  • Não há upload multipart nem task Celery neste domínio. O parsing do XLSX ocorre no cliente/front-end; a WebApi recebe as linhas já normalizadas em JSON via POST /nfse-importada/bulk, que é síncrono e idempotente.

Armadilha de roteamento FastAPI. As rotas de path estático (POST /bulk, GET /by-customer/{customer_id}, GET /by-user, DELETE /by-customer/{customer_id}/competencia/{mes_apuracao}) são declaradas antes da rota dinâmica /{nfse_id}. A ordem é intencional - invertê-la faria o router casar by-user/bulk como um nfse_id.

  • Chave de idempotência (UNIQUE): (id_customer, cnpj_prestador, numero_nfse, data_emissao). Por isso CNPJs são normalizados a 14 dígitos antes de qualquer validação - evita que dois formatos do mesmo CNPJ virem dois registros.
  • Mapeamento de erros do service para HTTP: "não encontrada" → 404, "já existe" → 409, "acesso negado" → 403, demais → 422.
  • Relaciona-se com Customers - id_customer referencia o cliente dono da NFS-e.