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-importadaEscopos necessários
nfse_importada:read- listagem por cliente, listagem por usuário e busca por IDnfse_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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
customer_id | int | path | sim | ID do cliente (customer.id). |
mes_apuracao | str (YYYY-MM) | query | não | Competência fiscal. Ex.: 2026-05. |
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": "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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
mes_apuracao | str (YYYY-MM) | query | não | Competê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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
customer_id | int | path | sim | ID do cliente (customer.id). |
mes_apuracao | str (YYYY-MM) | path | sim | Competê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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nfse_id | int | path | sim | ID 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nfse_id | int | path | sim | ID 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
nfse_id | int | path | sim | ID 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.
| Campo | Tipo | Obrigatório | Observações |
|---|---|---|---|
id_customer | int | sim | ID do cliente (customer.id) dono da NFS-e. Imutável após criação. |
cnpj_prestador | str (máx. 20) | sim | CNPJ do prestador. Normalizado para 14 dígitos (aceita com/sem máscara). |
cnpj_tomador | str (máx. 20) | não | CNPJ do tomador. Opcional (NF-e para PF não tem); vazio vira null. |
numero_nfse | str (máx. 50) | sim | Número da NFS-e (string, acomoda prefixos/letras de prefeituras). |
data_emissao | date | sim | Data de emissão (YYYY-MM-DD) - origem da competência fiscal. |
mes_apuracao | str (YYYY-MM) | sim | Competência fiscal, computada a partir de data_emissao. |
valor_servicos | Decimal | não | Valor bruto dos serviços (R$). Default 0. Não pode ser negativo. |
valor_pis | Decimal | não | PIS retido/devido (R$). Default 0. |
valor_cofins | Decimal | não | COFINS retido/devido (R$). Default 0. |
valor_iss | Decimal | não | ISS retido/devido (R$). Default 0. |
valor_irrf | Decimal | não | IRRF retido (R$). Default 0. |
valor_csll | Decimal | não | CSLL retido (R$). Default 0. |
valor_inss | Decimal | não | INSS retido (R$). Default 0. |
origem | str (máx. 20) | não | import (planilha XLSX) ou manual (formulário). Default import. |
dados_brutos | dict | não | Payload 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, origem ∈ manual, dados_brutos ≤ 16 KiB).
| Campo | Tipo | Observações |
|---|---|---|
cnpj_prestador | str (máx. 20) | Vazio vira null. Faz parte da UNIQUE. |
cnpj_tomador | str (máx. 20) | Vazio vira null ("limpar" tomador é válido). |
numero_nfse | str (máx. 50) | Faz parte da UNIQUE. |
data_emissao | date | Faz parte da UNIQUE. |
mes_apuracao | str (YYYY-MM) | Competência fiscal. |
valor_servicos | Decimal | Não pode ser negativo. |
valor_pis | Decimal | Não pode ser negativo. |
valor_cofins | Decimal | Não pode ser negativo. |
valor_iss | Decimal | Não pode ser negativo. |
valor_irrf | Decimal | Não pode ser negativo. |
valor_csll | Decimal | Não pode ser negativo. |
valor_inss | Decimal | Não pode ser negativo. |
origem | str (máx. 20) | import ou manual. |
dados_brutos | dict | JSON serializável, máx. 16 KiB. |
NfseImportadaRead
Estende o NfseImportadaBase com os campos gerenciados (server-side).
| Campo | Tipo | Notas |
|---|---|---|
id | int | ID interno da NFS-e importada. |
id_usuario_created | int | ID do usuário que criou (multi-tenant). |
created_at | datetime | Timestamp de criação (server-side). |
updated_at | datetime | Timestamp da última atualização. |
(mais todos os campos de NfseImportadaBase.)
NfseImportadaBulkCreate
Payload do endpoint POST /nfse-importada/bulk.
| Campo | Tipo | Obrigatório | Observações |
|---|---|---|---|
items | List[NfseImportadaCreate] | sim | Mínimo 1, máximo 500 itens por chamada (limite anti-DoS). |
NfseImportadaBulkResult
Resultado consolidado do bulk import (retornado em data).
| Campo | Tipo | Observações |
|---|---|---|
criadas | int | Quantidade de NFS-e criadas (novas). |
atualizadas | int | Quantidade atualizadas (já existiam com valores diff). |
ignoradas | int | Quantidade ignoradas (já existiam idênticas). |
errors | List[NfseImportadaBulkError] | Itens que falharam - não interrompem o lote. |
NfseImportadaBulkError
Detalhe de um item que falhou no bulk import.
| Campo | Tipo | Observações |
|---|---|---|
index | int | Índice do item no payload original (0-based). |
numero_nfse | str | Número da NFS-e, se conhecido (pode ser null). |
motivo | str | Mensagem 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_customerreferencia o cliente dono da NFS-e.