> ## Documentation Index
> Fetch the complete documentation index at: https://docs.engineapi.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Emissão de NFS-e

> Como emitir o documento fiscal de serviços no Padrão Nacional (SEFIN/ADN) ou em prefeituras ABRASF.

A NFS-e é o documento fiscal para prestação de **serviços**. Diferente da NF-e, é 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`

A emissão **assina a DPS com o certificado A1 do emissor**. O `prontoPara.nfse`
do cadastro recusa antes de gastar nDPS, com nome próprio em `faltando.nfse`:
`certificado` (`422 CERTIFICADO_AUSENTE`), `certificadoVencido`
(`422 CERTIFICADO_VENCIDO`) ou `certificadoOutroCnpj`
(`422 CERTIFICADO_OUTRO_CNPJ`, raiz diferente; filial com e-CNPJ da matriz
não recusa). Emissor em **sandbox** não passa por essas
checagens. NF-e e NFC-e seguem cobrando só a presença do A1.

<Info>
  **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: NFS-e**](/api-reference/campos-nfse). Para navegar por endpoint
  em vez de por documento, veja a [Referência da API](/api-reference/overview).
</Info>

<CardGroup cols={3}>
  <Card title="Multi-prefeitura" icon="building-columns">
    Suporte a padrões municipais (ABRASF) e ao Padrão Nacional (SEFIN/ADN)
  </Card>

  <Card title="ISS incluso" icon="percent">
    Alíquota e retenções conforme legislação local, passthrough, a Engine não calcula
  </Card>

  <Card title="Emissão assistida" icon="wand-magic-sparkles">
    Com `resolverTributacao: true`, o Cérebro Fiscal completa campos ausentes da DPS
  </Card>
</CardGroup>

<Info>
  **NFS-e é o único módulo em produção que exige `issuerId` explícito no corpo da
  requisição.** Em NF-e e NFC-e o campo é opcional (obrigatório só a partir do segundo
  emissor); ver [Autenticação](/authentication#multi-tenancy).
</Info>

***

## Diferenças NFS-e vs NF-e

| Aspecto           | NF-e (mercadorias) | NFS-e (serviços)                                                               |
| ----------------- | ------------------ | ------------------------------------------------------------------------------ |
| Autorizada por    | SEFAZ Estadual     | Prefeitura (ABRASF) ou SEFIN/ADN (Padrão Nacional)                             |
| Imposto principal | ICMS               | ISS                                                                            |
| Padrão único      | Sim (SEFAZ)        | Não, depende do provider configurado no emissor                                |
| Cancelamento      | Até 24h            | `POST /v1/nfse/{id}/cancelar`, prazo definido no provider/legislação municipal |

***

## Emitir NFS-e

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

<Info>
  **`dpsNacional` pode ficar de fora.** Sem o bloco, a engineAPI deriva `opSimpNac`,
  `cTribNac` e `tribISSQN` do cadastro do emissor (`crt`/`mei`/`cTribNacPadrao`) — não é
  preciso adivinhar código do ADN. O `regApTribSN` só é derivado para `Issuer.crt = 1`
  (Simples): para `Issuer.crt = 2` (Simples com excesso de sublimite) **ele é obrigatório
  na requisição**, porque o `crt` sozinho não diz o regime de apuração e não existe padrão
  desse regime no cadastro — sem ele a emissão recusa com **422**
  (`CRT2_SEM_REGIME_APURACAO`), inclusive com `resolverTributacao: true`. Se o emissor não
  tiver `cTribNacPadrao` cadastrado, a emissão rejeita com **400** (`"Campos fiscais do
      Padrão Nacional ausentes: cTribNac"`) pedindo o cadastro ou o campo manual. Num emissor
  `crt = 2` sem `regApTribSN`, porém, esse **400** não chega a acontecer: a recusa **422**
  acima vem primeiro, porque o **400** só nasce no provider, depois dessa verificação.
  `dpsNacional` informado explicitamente (como no exemplo abaixo) sempre vence a
  derivação, campo a campo. Ver [O que significa cada código de `opSimpNac`](#opsimpnac)
  antes de copiar o exemplo para um emissor de regime diferente.
</Info>

<CodeGroup>
  ```bash cURL theme={null}
  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": 3,
        "cTribNac": "010701",
        "tribISSQN": 1
      },
      "competencia": "2026-04-01",
      "informacoesComplementares": "Referente ao projeto engineAPI, Fase 1"
    }'
  ```

  ```typescript TypeScript theme={null}
  const nfse = await fetch('https://api.engineapi.com.br/v1/nfse', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.ENGINE_API_KEY!,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      issuerId: 'ISSUER_ID',
      tomador: {
        cnpjCpf: '99888777000100',
        razaoSocial: 'Cliente Exemplo SA',
        email: 'financeiro@cliente.com.br',
        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,
      },
      // Emissor do Simples Nacional (ME/EPP) — se a engineAPI não recebesse este
      // bloco, ela derivaria os mesmos valores do cadastro do emissor.
      dpsNacional: {
        opSimpNac: 3,     // 1=Não Optante, 2=Optante MEI, 3=Optante ME/EPP
        cTribNac: '010701',
        tribISSQN: 1,     // 1=operação tributável
      },
      competencia: '2026-04-01',
    }),
  }).then(r => r.json());

  console.log('NFSe autorizada:', nfse.data.numero);
  ```
</CodeGroup>

<h3 id="opsimpnac">
  Códigos de `opSimpNac`
</h3>

`dpsNacional.opSimpNac` declara a situação do **emissor** perante o Simples Nacional —
não é uma escolha livre, é o enum oficial do leiaute da DPS. Copiar o valor errado do
regime do seu emissor é rejeitado pela SEFIN em produção (`422 E0160`, "a opção de
situação perante o Simples Nacional do prestador informada na DPS não está de acordo com
o cadastro").

| Código | Significado    | Quando usar                                                                                                               |
| ------ | -------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `1`    | Não Optante    | Emissor no regime Normal (`Issuer.crt = 3`)                                                                               |
| `2`    | Optante MEI    | Emissor MEI (`Issuer.mei = true` **ou** `Issuer.crt = 4`)                                                                 |
| `3`    | Optante ME/EPP | Emissor do Simples Nacional, com ou sem excesso de sublimite (`Issuer.crt = 1` **ou** `Issuer.crt = 2`), que não seja MEI |

`Issuer.crt` aceita 4 valores (`create-company.dto.ts`): `1` (Simples), `2` (Simples com
excesso de sublimite de receita bruta), `3` (Regime Normal) e `4` (MEI). Um emissor
`crt = 2` **continua optante do Simples** mesmo com o sublimite estourado — não é Não
Optante; é essa diferença que a tabela acima cobre.

`dpsNacional.regApTribSN` (regime de apuração do Simples) só acompanha `opSimpNac = 3` —
MEI (`opSimpNac = 2`) não leva o campo. Quando você omite `dpsNacional` inteiro, a
engineAPI deriva `opSimpNac` a partir do cadastro do emissor com esta mesma tabela: é o
que mata a rejeição de quem copia o exemplo desta página sem ajustar ao próprio regime.

Para `Issuer.crt = 1` a derivação também completa `regApTribSN = '1'` (não existe outro
valor possível nesse enum). Para `Issuer.crt = 2` o `crt` sozinho **não diz o regime de
apuração**: não há campo de cadastro pra isso, e chutar `'1'` arriscaria trocar a rejeição
`E0160` (divergência cadastral) por uma rejeição de regime incoerente na SEFIN. Sem
`dpsNacional.regApTribSN` explícito, a emissão de um emissor `crt = 2` recusa com **422**
(`CRT2_SEM_REGIME_APURACAO`) antes de transmitir — informe o campo pra seguir.

Essa exigência vale em **qualquer forma de emissão**: `dpsNacional` omitido, parcial,
preenchido com `opSimpNac`, `cTribNac` e `tribISSQN`, ou com `resolverTributacao: true`. Mandar
o trio completo não dispensa o `regApTribSN`; ligar a tributação assistida também não, porque o
Cérebro Fiscal não tem fonte pra esse campo neste regime. Declarar `opSimpNac = 1` num emissor
`crt = 2` não é saída — é exatamente a divergência cadastral que gera a `E0160`. Emissor MEI
(`Issuer.mei = true` ou `Issuer.crt = 4`) não é afetado: deriva `opSimpNac = 2`, que não leva o
campo. A verificação roda no último ponto antes da transmissão, sobre a DPS final: nada é
transmitido e nenhum número de DPS é consumido.

***

## Campos de Referência

| Campo                                    | Tipo                  | Obrigatório                                                         | Descrição                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ---------------------------------------- | --------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `issuerId`                               | string (UUID)         | **Sim**                                                             | ID da empresa prestadora, único módulo que exige este campo hoje                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `tomador.cnpjCpf`                        | string                | **Sim**                                                             | **Não é `cnpj`/`cpf`**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `tomador.razaoSocial`                    | string                | **Sim**                                                             | **Não é `nome`**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `tomador.endereco`                       | objeto                | **Sim**                                                             | `logradouro`, `numero`, `bairro`, `codigoMunicipio`, `uf`, `cep` obrigatórios                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `servico.codigoMunicipio`                | string                | **Sim**                                                             | Código IBGE do município de prestação                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `servico.itemListaServico`               | string                | **Sim** no fluxo manual                                             | Código LC 116 (ex.: `01.01`), **não é `codigoServico`**. Só pode faltar com `resolverTributacao: true`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `servico.discriminacao`                  | string                | **Sim**                                                             | **Não é `descricao`**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `servico.valorServicos`                  | número                | **Sim**                                                             | **Não é `valor`**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `servico.aliquotaIss`                    | número                | Não                                                                 | Alíquota do ISS em %. Não existem `valorISS`/`issRetido` no payload: a retenção fica em `retencoes.*`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `servico.descontoIncondicionado`         | número                | Não                                                                 | Desconto concedido sem condição, em R\$ (até 2 casas). Vai para `vDescCondIncond/vDescIncond` na DPS e **reduz a base de cálculo do ISSQN** apurada pelo sistema nacional                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `servico.descontoCondicionado`           | número                | Não                                                                 | Desconto sujeito a condição, em R\$ (até 2 casas). Vai para `vDescCondIncond/vDescCond` na DPS. É informativo: **não** reduz a base de cálculo                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `servico.valorDeducoes`                  | número                | Não                                                                 | Dedução da base de cálculo, em R\$ (até 2 casas). Vai para `vDedRed/vDR` na DPS. Dedução por percentual ou por lista de documentos ainda não é suportada                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `servico.codigoNBS`                      | string                | Não                                                                 | Código NBS 2.0, 9 dígitos (a pontuação usual é aceita: `1.1401.20.00`). Vai para `cServ/cNBS` na DPS. Com `resolverTributacao: true` o Cérebro Fiscal preenche quando ausente                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `servico.codigoTributacaoMunicipio`      | string                | Não                                                                 | Código de tributação municipal do ISSQN, **3 dígitos**. Vai para `cServ/cTribMun` na DPS. É o mesmo campo de `dpsNacional.cTribMun`: informar os dois com valores diferentes recusa com `422 CTRIBMUN_CONFLITO`                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `tomador.inscricaoMunicipal`             | string                | Não                                                                 | Inscrição municipal do tomador (até 15 caracteres). Vai para `toma/IM` na DPS                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `rps`                                    | objeto                | Não                                                                 | **Recusado no Padrão Nacional** (`422 RPS_SEM_CAMPO_NO_PADRAO_NACIONAL`): o leiaute da DPS não tem campo de RPS, a própria DPS ocupa esse lugar. Só tem efeito na emissão pelo provedor municipal                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `retencoes.issRetidoPor`                 | string                | Não                                                                 | `"tomador"` ou `"intermediario"`. Vira `tpRetISSQN` (2 ou 3) na DPS; ausente = ISS não retido. Ver [Serviço B2B com retenção](#servio-b2b-com-reteno)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `retencoes.{irrf,csll,inss}`             | número                | Não                                                                 | Valor **retido** em R\$. Vai para `vRetIRRF`, `vRetCSLL` e `vRetCP` na DPS                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `retencoes.{pis,cofins,outrasRetencoes}` | número                | Não                                                                 | **Sem campo de valor na DPS.** `vPis`/`vCofins` do leiaute são débito de apuração própria, não retenção: valor ≠ 0 recusa com `422 RETENCAO_SEM_CAMPO_NO_LEIAUTE`. Declare a retenção em `dpsNacional.tpRetPisCofins`                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `dpsNacional.tpRetPisCofins`             | string                | Não                                                                 | Indicador de retenção de PIS/COFINS (códigos 0 a 9 da tabela oficial). Exige `dpsNacional.cstPisCofins`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `competencia`                            | string (`yyyy-MM-dd`) | Não                                                                 | **Formato de data completo, não `YYYY-MM`**; default é a data de emissão                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `dpsNacional.*`                          | objeto                | Não                                                                 | Bloco do Padrão Nacional (`opSimpNac`, `cTribNac`, `tribISSQN` mínimos), passthrough, ignorado por providers ABRASF. Informado explicitamente, sempre vence. Ausente, a engineAPI deriva do cadastro do emissor (ver [O que significa cada código de `opSimpNac`](#opsimpnac)); sem `Issuer.cTribNacPadrao` cadastrado, rejeita com **400** pedindo o campo. Num emissor `crt = 2` sem `dpsNacional.regApTribSN` esse **400** não chega a acontecer: a verificação do `crt = 2` recusa antes, com **422** (`CRT2_SEM_REGIME_APURACAO`), mesmo quando o `cTribNacPadrao` também falta (o **400** nasce no provider, depois dessa verificação) |
| `ibsCbs.*`                               | objeto                | Nã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](/guides/reforma-datas) e [Campos da NFS-e](/api-reference/campos-nfse)                                                                                                                                                                                                               |
| `informacoesComplementares`              | string                | Não                                                                 | Observações da nota, até 2000 caracteres. Vai para `serv/infoCompl/xInfComp` na DPS. O leiaute limita este campo aos caracteres latinos básicos: travessão, aspas curvas, reticências e emoji recusam com **400**. Quebra de linha e tabulação são aceitas e viram espaço na emissão                                                                                                                                                                                                                                                                                                                                                         |
| `resolverTributacao`                     | boolean               | Não                                                                 | Emissão assistida: completa `dpsNacional`/`servico.itemListaServico`/`servico.codigoNBS` ausentes a partir do cadastro do emissor. **Não toca em `ibsCbs`**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |

### Validação de endereço e alíquota

`tomador.endereco.complemento` é opcional e aceita até **156 caracteres** (o teto do
leiaute, publicado no contrato OpenAPI). Você pode omiti-lo ou enviar uma string
vazia ou composta só por espaços dentro desse teto, que a API trata como ausência e
não escreve na DPS; acima de 156 caracteres a recusa é **400** nomeando o campo,
mesmo que o conteúdo seja só espaço. Valor de tipo errado (por exemplo, um número)
também devolve **400** dizendo o tipo esperado.
Já `logradouro`, `numero` e `bairro` exigem conteúdo depois de remover os espaços das
bordas: branco somente devolve **400** com o nome do campo, antes de reservar número da
DPS. Os quatro campos recusam caracteres fora da faixa do leiaute, como travessão,
aspas curvas, reticências e emoji.

`servico.aliquotaIss` aceita de `0` a `9.99`, com no máximo duas casas decimais. Valor
com três ou mais casas, ou dois dígitos na parte inteira, devolve **400** antes da
emissão; a API não arredonda uma alíquota declarada pelo integrador.

<Warning>
  Não existem os campos `valorISS`, `issRetido` nem `codigoServico` no payload de NFS-e.
  Use `aliquotaIss` (a engineAPI não calcula o valor do ISS), `retencoes.issRetidoPor` e
  `itemListaServico`.
</Warning>

***

## Serviço B2B com retenção

Na prestação para empresa, o tomador costuma reter tributos na fonte e recolher no lugar do
prestador. A DPS declara isso em dois lugares do leiaute nacional, e a engineAPI escreve os
dois a partir do bloco `retencoes`:

| O que você informa                  | O que sai na DPS                   | Quem calcula                                                                           |
| ----------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------- |
| `retencoes.issRetidoPor: "tomador"` | `tribMun/tpRetISSQN = 2`           | O valor do ISS retido é calculado pelo sistema nacional a partir da base e da alíquota |
| `retencoes.inss`                    | `tribFed/vRetCP`                   | Você (valor retido em R\$)                                                             |
| `retencoes.irrf`                    | `tribFed/vRetIRRF`                 | Você                                                                                   |
| `retencoes.csll`                    | `tribFed/vRetCSLL`                 | Você                                                                                   |
| `dpsNacional.tpRetPisCofins`        | `tribFed/piscofins/tpRetPisCofins` | Você (é um indicador, não um valor)                                                    |

<Info>
  **A alíquota da retenção federal é sua.** A engineAPI transcreve o valor retido que você
  informou e confere coerência: ela não aplica percentual de IRRF/CSLL/PIS/COFINS/INSS,
  porque isso depende da natureza do serviço, da dispensa por valor mínimo e do regime do
  tomador. Quem calcula é o seu sistema (ou o seu contador).
</Info>

### PIS/COFINS: indicador, não valor

O leiaute do Padrão Nacional **não tem campo para o valor retido de PIS e COFINS**. Os
campos `vPis` e `vCofins` da DPS existem, mas são o **débito de apuração própria** do
prestador, e o total de retenções da NFS-e é calculado sem eles:

```
vTotalRet = vRetCP + vRetIRRF + vRetCSLL + ISSQN retido
```

Por isso a retenção de PIS/COFINS (e da CSLL, quando você quer declará-la junto) se informa
pelo **indicador** `dpsNacional.tpRetPisCofins`, que aceita os códigos 0 a 9 da tabela
oficial. Os mais usados:

| Código | Significado                         |
| ------ | ----------------------------------- |
| `0`    | PIS/COFINS/CSLL não retidos         |
| `1`    | PIS/COFINS retidos                  |
| `3`    | PIS/COFINS/CSLL retidos             |
| `4`    | PIS/COFINS retidos, CSLL não retido |

Mandar valor em `retencoes.pis` ou `retencoes.cofins` recusa com
`422 RETENCAO_SEM_CAMPO_NO_LEIAUTE`: escrever esse valor em `vPis`/`vCofins` declararia um
débito próprio que não é o seu.

<Warning>
  **Conciliação pelo valor líquido:** como PIS e COFINS retidos não entram no `vTotalRet` da
  NFS-e, eles também não são descontados do valor líquido do documento
  (`vLiq = serviço menos descontos menos retenções`). Quem concilia recebimento pelo líquido
  da nota precisa subtrair PIS/COFINS por fora, a partir do indicador declarado.
</Warning>

```bash cURL theme={null}
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",
      "endereco": {
        "logradouro": "Av Paulista",
        "numero": "1000",
        "bairro": "Bela Vista",
        "codigoMunicipio": "3550308",
        "uf": "SP",
        "cep": "01310100"
      }
    },
    "servico": {
      "codigoMunicipio": "3550308",
      "itemListaServico": "01.07",
      "discriminacao": "Suporte tecnico mensal",
      "valorServicos": 10000.00,
      "aliquotaIss": 5.00
    },
    "retencoes": {
      "issRetidoPor": "tomador",
      "irrf": 150.00,
      "csll": 100.00,
      "inss": 1100.00
    },
    "dpsNacional": {
      "opSimpNac": 3,
      "cTribNac": "010701",
      "tribISSQN": 1,
      "cstPisCofins": "01",
      "tpRetPisCofins": "3"
    }
  }'
```

O documento autorizado sai com `tpRetISSQN = 2`, os três valores federais nos campos
próprios e `tpRetPisCofins = 3` (PIS/COFINS/CSLL retidos). O que foi declarado volta também
na consulta: `GET /v1/nfse/{id}` e a listagem trazem o campo `retencoes` com os nomes do
leiaute (`tpRetISSQN`, `vRetCP`, `vRetIRRF`, `vRetCSLL`, `tpRetPisCofins`), ou nulo quando
não houve retenção.

<Warning>
  **Recusas antes de consumir numeração.** Retenção maior que `servico.valorServicos`
  (`422 RETENCAO_MAIOR_QUE_SERVICO`), valor em `retencoes.pis`/`retencoes.cofins` ou
  `retencoes.outrasRetencoes` (`422 RETENCAO_SEM_CAMPO_NO_LEIAUTE`), indicador sem
  `dpsNacional.cstPisCofins` (`422 RETENCAO_PIS_COFINS_SEM_CST`), indicador que contradiz
  `retencoes.csll` em qualquer dos dois sentidos (`422 RETENCAO_PIS_COFINS_INCOERENTE`) e
  `issRetidoPor` conflitando com `dpsNacional.tpRetISSQN` (`422 RETENCAO_ISS_CONFLITO`)
  param a emissão **antes** de transmitir: nenhum número de DPS é gasto.
</Warning>

<Note>
  **No sandbox, a emissão com retenção funciona, mas nada é escrito.** A emissão simulada não
  gera uma DPS real, então a retenção declarada não entra em documento nenhum e não fica
  registrada na nota. A resposta traz isso em `avisos[]`, para você exercitar o fluxo sem
  concluir que a retenção foi transmitida. Em ambiente configurado para o provedor municipal
  (que também não escreve retenção), a emissão recusa com
  `422 RETENCAO_PROVIDER_NAO_ESCREVE`, porque ali a nota sairia de verdade sem o que você
  declarou.

  O grupo `intermediario` (identificação de quem intermediou a prestação) ainda não é aceito
  no payload: `issRetidoPor: "intermediario"` declara a retenção pelo intermediário, mas sem
  identificá-lo.
</Note>

***

<h2 id="emisso-assistida-crebro-fiscal">
  Emissão assistida (Cérebro Fiscal)
</h2>

Isto é diferente da derivação básica de `opSimpNac`/`cTribNac`/`tribISSQN` (e de
`regApTribSN` só para `Issuer.crt = 1`; sempre ativa, sem custo, ver
[Códigos de `opSimpNac`](#opsimpnac)): a assistida completa
mais campos (`itemListaServico`, `codigoNBS`), audita cada resolução e exige plano/feature
habilitados.

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.
O Cérebro Fiscal **não** tem fonte para `regApTribSN` num emissor `crt = 2`: nesse caso a
recusa sai nomeada, com `CRT2_SEM_REGIME_APURACAO`, dizendo qual campo informar — ligar
`resolverTributacao` não dispensa o campo.
`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ódigo  | Serviço                               |
| ------- | ------------------------------------- |
| `01.01` | Análise e desenvolvimento de sistemas |
| `01.07` | Suporte técnico em informática        |
| `17.01` | Assessoria, consultoria, pesquisa     |
| `17.06` | Propaganda e publicidade              |
| `26.01` | Serviços de coleta, busca e entrega   |

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

***

## Response de Sucesso

Envelopado em `{ data, meta }`:

```json theme={null}
{
  "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"
  }
}
```

<Warning>
  `valorServicos` é **string decimal** (`"5000"`, sem zeros à direita), não `number` cru, mesmo tratamento
  do `amount` de NF-e/NFC-e (evita imprecisão de ponto flutuante). 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}`).
</Warning>

<Info>
  `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](#emisso-assistida-crebro-fiscal)
  acima). Ausência do campo = nada a avisar.
</Info>

<Info>
  Para empresa do Simples, o percentual total de tributos do cadastro tem um mês de referência:
  `pTotTribSNCompetencia` no formato `AAAA-MM`. Esse percentual muda todo mês. Se a emissão
  usar um mês anterior ou se o mês não estiver cadastrado, a nota segue — o valor é declaratório
  — e a resposta traz um aviso para confirmar com o contador e atualizar o cadastro. Um valor
  enviado em `dpsNacional.pTotTribSN` tem prioridade e segue exatamente como foi informado.
</Info>

***

## Cancelar NFS-e

```bash theme={null}
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 NF-e/NFC-e (campo
`justificativa`), o corpo do cancelamento de NFS-e usa `motivo`.

| Campo          | Obrigatório | Descrição                                                                                                                                                                                                                                                                                                                            |
| -------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `motivo`       | Sim         | Texto livre do motivo (vai verbatim no evento como `xMotivo`).                                                                                                                                                                                                                                                                       |
| `codigoMotivo` | Não         | Có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,
para sua aplicação ramificar por código.

<Warning>
  As regras de cancelamento variam por município/provider. A API retorna erro se o cancelamento não for permitido.
</Warning>

***

## Consultar NFS-e na SEFIN

```bash theme={null}
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.

<Warning>
  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.
</Warning>

***

## Cobertura da NFS-e por município

Nem todo município já aderiu ao Sistema Nacional da NFS-e (Padrão Nacional/ADN)
— quem não aderiu emite pelo padrão municipal antigo (ABRASF), quando o
município tiver um provider configurado. Antes de cadastrar um emissor ou
diagnosticar por que uma DPS foi rejeitada, consulte a cobertura do IBGE:

```bash theme={null}
curl "https://api.engineapi.com.br/v1/nfse/cobertura/5211701?ambiente=1" \
  -H "x-api-key: SUA_API_KEY"
```

```json theme={null}
{
  "ibge": "5211701",
  "ambiente": 1,
  "situacao": "aderente",
  "aderente": true,
  "desde": "2026-08-13T12:00:00.000Z",
  "fonte": "https://adn.nfse.gov.br/parametrizacao/5211701/convenio",
  "detalhes": {
    "aderenteEmissorNacional": true,
    "situacaoEmissaoPadraoContribuintesRFB": 1,
    "mensagem": null
  }
}
```

| Campo      | Descrição                                                                                                                                                                                                                                                                                  |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ambiente` | `1` Produção · `2` Produção Restrita (mesmo domínio de `ambienteFiscal` do emissor). **Cobertura é POR AMBIENTE** — um município pode ser aderente num e não aderente no outro. Omitido, usa o ambiente fiscal do primeiro emissor do parceiro autenticado, ou `1` sem emissor cadastrado. |
| `situacao` | `aderente` · `nao_aderente` · `desconhecido`.                                                                                                                                                                                                                                              |
| `aderente` | Espelho booleano de `situacao` (`true`/`false`/`null`).                                                                                                                                                                                                                                    |
| `desde`    | Instante da última consulta que confirmou o veredito, ou `null` se nunca consultado.                                                                                                                                                                                                       |

<Warning>
  `situacao` igual a `"desconhecido"` **não é "não aderente"**. Acontece quando não há
  certificado A1 disponível para consultar o ADN (leitura exige mTLS com um
  certificado ICP-Brasil — qualquer emissor do parceiro serve), quando a
  última consulta falhou por rede/timeout, ou quando o espelho está velho
  demais sem reconsulta bem-sucedida. `desconhecido` nunca bloqueia emissão —
  só `nao_aderente` **confirmado** bloqueia.
</Warning>

A engineAPI mantém um espelho local (TTL padrão 24h, renovado automaticamente
quando você cadastra/atualiza certificado, muda `ibgeCode` ou troca de
ambiente) e revalida diariamente as linhas vencidas — nunca varre os \~5.570
municípios de uma vez (o ADN não tem endpoint de lista em massa). Use
`?atualizar=true` para forçar uma reconsulta imediata, ignorando o TTL:

```bash theme={null}
curl "https://api.engineapi.com.br/v1/nfse/cobertura/5211701?ambiente=2&atualizar=true" \
  -H "x-api-key: SUA_API_KEY"
```

O cadastro de empresa consome o **mesmo espelho**: `prontoPara.nfse` em
`POST/GET/PATCH /v1/companies` só marca o município como bloqueante quando a
cobertura é `nao_aderente` confirmada — nesse caso `faltando.nfse` inclui
`municipioAderente`, e a resposta traz `prontoPara.cobertura: { situacao,
consultadoEm }` (campo aditivo, presente sempre que já houve alguma consulta).

O `POST /v1/companies` consulta a cobertura na hora do cadastro, usando o
certificado de **qualquer** emissor do mesmo parceiro (o recém-criado ainda
não tem o próprio). Sem certificado no parceiro, a cobertura fica
`desconhecido` e o cadastro **não falha**.

A mesma resposta traz `avisos[]` (array sempre presente, vazio quando não há
aviso). Só emite um item quando a cobertura é `nao_aderente` confirmada:

```json theme={null}
{
  "code": "MUNICIPIO_NAO_ADERENTE_PADRAO_NACIONAL",
  "message": "O município Jandaia/GO (IBGE 5211701) não adere ao Padrão Nacional da NFS-e no ambiente 2; NF-e e NFC-e seguem disponíveis.",
  "ambiente": 2,
  "consultadoEm": "2026-08-13T00:00:00.000Z"
}
```

`desconhecido` **não** gera aviso. Na emissão, o mesmo código vira `422`
antes de numerar se o espelho já diz `nao_aderente`; se a SEFIN ainda assim
devolver `E0037`, a recusa também sai `422` com `details.mensagemSefin`
preservando a mensagem original. Ver [Erros e respostas](/guides/errors).

***

## Veja também

* **[Emissão de NF-e](/guides/emitir-nfe):** para venda de mercadorias (ICMS).
* **[Webhooks](/guides/webhooks):** receba eventos de NFS-e em tempo real.
* **[Paginação](/guides/paginacao):** contrato de `page`/`limit`/`sortBy` usado em `GET /v1/nfse`.
