engineAPIengineAPI
// guias

Guia: NFSe

Emita Notas Fiscais de Serviços Eletrônicas (NFSe) para municípios via engineAPI.

NFSe: Nota Fiscal de Serviços Eletrônica

A NFSe é o documento fiscal para prestação de serviços. Diferente da NFe, é autorizada pela prefeitura municipal (ABRASF) ou pela SEFIN/ADN (Padrão Nacional). Cada padrão tem seu próprio contrato.

Endpoint base: https://api.engineapi.com.br/v1/nfse

Quais campos enviar? A lista completa de campos (todos os parâmetros, tipos e obrigatoriedade), navegável por grupo (Identificação, Tomador, Serviço, Retenções, DPS Nacional...) e gerada direto do contrato real, está no Catálogo de campos: NFSe. Para navegar por endpoint em vez de por documento, veja a Referência por Endpoint.

Multi-prefeitura

Suporte a padrões municipais (ABRASF) e ao Padrão Nacional (SEFIN/ADN)

ISS incluso

Alíquota e retenções conforme legislação local, passthrough, a Engine não calcula

Emissão assistida

Com resolverTributacao: true, o Cérebro Fiscal completa campos ausentes da DPS

NFSe é o único módulo em produção que exige issuerId explícito no corpo da requisição. Em NFe e NFCe o campo é opcional (obrigatório só a partir do segundo emissor); ver Autenticação.


Diferenças NFSe vs NFe

AspectoNFe (mercadorias)NFSe (serviços)
Autorizada porSEFAZ EstadualPrefeitura (ABRASF) ou SEFIN/ADN (Padrão Nacional)
Imposto principalICMSISS
Padrão únicoSim (SEFAZ)Não, depende do provider configurado no emissor
CancelamentoAté 24hPOST /v1/nfse/{id}/cancelar, prazo definido no provider/legislação municipal

Emitir NFSe

servico.itemListaServico é obrigatório no fluxo manual, só pode ficar de fora com resolverTributacao: true.

O exemplo abaixo inclui dpsNacional. O emissor no Padrão Nacional (o provider da engineAPI hoje) rejeita com 400 ("Campos fiscais do Padrão Nacional ausentes: opSimpNac, cTribNac, tribISSQN") sem esse bloco. dpsNacional só pode ficar de fora com resolverTributacao: true (emissão assistida, ver abaixo): o Cérebro Fiscal preenche a partir do cadastro do emissor.

bash
curl -X POST https://api.engineapi.com.br/v1/nfse \
  -H "x-api-key: SUA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "issuerId": "ISSUER_ID",
    "tomador": {
      "cnpjCpf": "99888777000100",
      "razaoSocial": "Cliente Exemplo SA",
      "email": "financeiro@cliente.com.br",
      "telefone": "11999998888",
      "endereco": {
        "logradouro": "Av Paulista",
        "numero": "1000",
        "bairro": "Bela Vista",
        "codigoMunicipio": "3550308",
        "uf": "SP",
        "cep": "01310100"
      }
    },
    "servico": {
      "codigoMunicipio": "3550308",
      "itemListaServico": "01.01",
      "discriminacao": "Desenvolvimento de software sob encomenda",
      "valorServicos": 5000.00,
      "aliquotaIss": 2.00
    },
    "dpsNacional": {
      "opSimpNac": 1,
      "cTribNac": "010701",
      "tribISSQN": 1
    },
    "competencia": "2026-04-01",
    "informacoesComplementares": "Referente ao projeto engineAPI, Fase 1"
  }'

Campos de Referência

CampoTipoObrigatórioDescrição
issuerIdstring (UUID)SimID da empresa prestadora, único módulo que exige este campo hoje
tomador.cnpjCpfstringSimNão é cnpj/cpf
tomador.razaoSocialstringSimNão é nome
tomador.enderecoobjetoSimlogradouro, numero, bairro, codigoMunicipio, uf, cep obrigatórios
servico.codigoMunicipiostringSimCódigo IBGE do município de prestação
servico.itemListaServicostringSim no fluxo manualCódigo LC 116 (ex.: 01.01), não é codigoServico. Só pode faltar com resolverTributacao: true
servico.discriminacaostringSimNão é descricao
servico.valorServicosnúmeroSimNão é valor
servico.aliquotaIssnúmeroNãoN/A. Não existem valorISS/issRetido no payload; retenção fica em retencoes.*
retencoes.{irrf,csll,cofins,pis,inss,outrasRetencoes}númeroNãoInterino: qualquer valor ≠ 0 recusa com 422 RETENCOES_NAO_SUPORTADAS. O motor ainda não escreve retenções na DPS do Padrão Nacional; aceitar e descartar seria pior que recusar. Omita o bloco (ou mande tudo 0/ausente)
competenciastring (yyyy-MM-dd)NãoFormato de data completo, não YYYY-MM; default é a data de emissão
dpsNacional.*objetoSim no Padrão Nacional (o provider da engineAPI hoje)Bloco do Padrão Nacional (opSimpNac, cTribNac, tribISSQN mínimos), passthrough, ignorado por providers ABRASF. Sem ele (e sem resolverTributacao: true), a SEFIN rejeita com 400
ibsCbs.*objetoNão (obrigatório por lei a partir de 01/10/2026 na regra geral)Grupo IBS/CBS da Reforma Tributária na DPS: cIndOp, cst, cClassTrib são os mínimos. Passthrough puro: o Cérebro Fiscal não resolve nenhum deles, e a DPS não leva alíquota nem valor (quem calcula é o sistema nacional). indDest: "1" recusa com 422 IBSCBS_DPS_DESTINATARIO_NAO_SUPORTADO. Ver Reforma Tributária: datas que importam e Campos da NFSe
informacoesComplementaresstringNãoN/A
resolverTributacaobooleanNãoEmissão assistida: completa dpsNacional/servico.itemListaServico/servico.codigoNBS ausentes a partir do cadastro do emissor. Não toca em ibsCbs

Não existem os campos valorISS, issRetido nem codigoServico no payload de NFSe. Use aliquotaIss (a engineAPI não calcula o valor do ISS) e itemListaServico.


Emissão assistida (Cérebro Fiscal)

Com "resolverTributacao": true, campos ausentes da DPS (dpsNacional.cTribNac, opSimpNac, tribISSQN, tpRetISSQN, servico.itemListaServico, servico.codigoNBS) são preenchidos a partir do cadastro do emissor (cTribNacPadrao/servicoPadraoLc116 em Issuer). Requer feature de plano + Issuer.fiscalBrainEnabled (403 sem isso). Campo obrigatório sem fonte cadastrada → 422 com camposNaoResolvidos e nada é emitido. aliquotaIss nunca é opinada pelo Cérebro: se o payload mandar um valor mesmo assim, ele é ignorado (ecoar um valor divergente do cálculo da SEFIN causa rejeição E1235), e esse descarte aparece na resposta, em avisos[] (string), além do log do servidor. Sem a flag, o comportamento é o de sempre (passthrough manual, sem avisos).


Códigos de Serviço LC116

Os códigos (servico.itemListaServico) são definidos pela Lei Complementar 116/2003:

CódigoServiço
01.01Análise e desenvolvimento de sistemas
01.07Suporte técnico em informática
17.01Assessoria, consultoria, pesquisa
17.06Propaganda e publicidade
26.01Serviços de coleta, busca e entrega

Cada município pode ter lista complementar de códigos. Consulte a legislação local ou o site da prefeitura emissora.


Response de Sucesso

Envelopado em { data, meta }:

json
{
  "data": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "numero": 1234,
    "codigoVerificacao": "ABC12345",
    "chaveAcesso": "35260211222333...",
    "protocol": "135260000001234",
    "status": "AUTHORIZED",
    "dataEmissao": "2026-04-27T02:00:00.000Z",
    "valorServicos": "5000",
    "downloads": {
      "xml": "/v1/nfse/xml/3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "pdf": "/v1/nfse/pdf/3fa85f64-5717-4562-b3fc-2c963f66afa6"
    }
  },
  "meta": {
    "requestId": "req_abc123",
    "timestamp": "2026-07-06T12:00:00.000Z"
  }
}

valorServicos é string decimal ("5000", sem zeros à direita), não number cru, mesmo tratamento do amount de NFe/NFCe (evita imprecisão de ponto flutuante e o Decimal.js interno vazando cru). Não há issuer/customer embutidos, nem linkNfse/pdfUrl/xmlUrl/xmlPath na resposta de emissão. downloads.xml/downloads.pdf apontam pras rotas reais de download (GET /v1/nfse/xml/{id}, GET /v1/nfse/pdf/{id}).

data.avisos (array de string) é opcional, só aparece quando há algo a avisar sobre a emissão que acabou de acontecer (hoje: emissão assistida com servico.aliquotaIss informado no payload, o valor foi ignorado, ver Emissão assistida acima). Ausência do campo = nada a avisar.


Cancelar NFSe

bash
curl -X POST https://api.engineapi.com.br/v1/nfse/{id}/cancelar \
  -H "x-api-key: SUA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "motivo": "Serviço não prestado, contrato cancelado", "codigoMotivo": "2" }'

{id} é o id (UUID) retornado na emissão. Diferente de NFe/NFCe (campo justificativa), o corpo do cancelamento de NFSe usa motivo.

CampoObrigatórioDescrição
motivoSimTexto livre do motivo (vai verbatim no evento como xMotivo).
codigoMotivoNãoCódigo oficial do motivo (cMotivo, Padrão Nacional): "1" Erro na emissão · "2" Serviço não prestado · "9" Outros. Aceita string ou número. Default: "9" (Outros). Informe o código real sempre que souber; ele fica gravado no evento fiscal permanente. Valor fora do enum → 400 antes de qualquer chamada à SEFIN.

Se a SEFIN rejeitar o cancelamento, a resposta 400 traz erros[] estruturado (codigo/descricao/complemento verbatim do Padrão Nacional), nunca vazio, pra sua aplicação ramificar por código.

As regras de cancelamento variam por município/provider. A API retornará erro se o cancelamento não for permitido.


Consultar NFSe na SEFIN

bash
curl https://api.engineapi.com.br/v1/nfse/{id}/consultar \
  -H "x-api-key: SUA_API_KEY"

{id} é o id (UUID) retornado na emissão. Consulta a NFS-e diretamente na SEFIN/ADN pela chave de acesso e atualiza o xmlContent local quando a SEFIN devolve um XML.

A consulta na SEFIN (NfseConsulta) enxerga apenas se o documento existe: ela não informa eventos de cancelamento. Por isso, se a NFS-e já tiver cancelamento homologado no seu cadastro (status: CANCELED), a engineAPI cruza os dois dados antes de responder: o status retornado nunca volta AUTORIZADA para um documento cancelado, vem CANCELADA, refletindo o estado real. Para qualquer outro desfecho, o status é repassado verbatim da SEFIN.


Próximos passos