> ## 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.

# Primeira emissão

> Checklist de pré-requisitos, campos obrigatórios e os erros mais comuns na primeira chamada de emissão.

<Note>
  Este é o **checklist rápido** de pré-requisitos antes da 1ª emissão. Pra ver todos os
  campos, regimes tributários e tratamento de erros, veja o guia completo
  [Emitir NF-e](/guides/emitir-nfe).
</Note>

Antes de emitir sua primeira NF-e em homologação (sandbox), você precisa ter três coisas prontas. Sem uma delas, a emissão vai falhar. Para o que muda ao ir pra produção, ver [Sandbox](/guides/sandbox).

<CardGroup cols={3}>
  <Card title="Conta criada" icon="user-check">
    Partner registrado e token JWT (ou API Key) em mãos
  </Card>

  <Card title="Empresa cadastrada" icon="building">
    CNPJ emissor cadastrado via `POST /v1/companies`
  </Card>

  <Card title="Certificado enviado" icon="shield-halved">
    Arquivo `.pfx` (A1) com senha válida
  </Card>
</CardGroup>

Não tem os três? Volte para o [Quickstart](/quickstart) e siga os passos.

<Tip>
  Fluxo recomendado: **cadastra → lê `prontoPara` → pede só o que falta.** `POST
      /v1/companies` já termina sozinho o que dá para derivar (ex.: `servicoPadraoLc116`/
  `cTribNacPadrao` a partir do CNAE) e devolve `prontoPara` por documento fiscal.
  O mesmo campo volta no `GET /v1/companies` (por item), no `GET /v1/companies/{id}`,
  no `PATCH /v1/companies/{id}`, no `PATCH /v1/companies/{id}/ambiente` e no **201 do
  `POST /v1/companies/{id}/certificate`** — o flip `nfe`/`nfce` de `false` → `true`
  aparece no upload, sem GET extra. `prontoPara.sandbox` é `true` quando o emissor
  está em sandbox (cert/CSC não são cobrados). `avisos[]` vem no mesmo corpo:
  município não aderente ao Padrão Nacional gera
  `MUNICIPIO_NAO_ADERENTE_PADRAO_NACIONAL` (NF-e e NFC-e seguem disponíveis);
  nos 30 dias anteriores ao vencimento do A1, `avisos[]` traz
  `CERTIFICADO_EXPIRANDO` (aviso, não reprovação). Se a validade do A1 não
  pôde ser lida, `avisos[]` traz `CERTIFICADO_VALIDADE_DESCONHECIDA` (também
  aviso). `desconhecido` não gera aviso.
  Peça ao cliente só os campos que
  aparecerem em `prontoPara.faltando.<documento>`, nunca o cadastro inteiro de novo.
</Tip>

***

## Checklist de pré-requisitos

<Steps>
  <Step title="Credencial disponível">
    Você tem uma API Key (`ek_live_`/`ek_test_`, formato recomendado para integração
    server-to-server) ou um JWT de `POST /v1/auth/login` (`data.access_token`, fluxo de
    dashboard)? Ver [Autenticação](/authentication).
  </Step>

  <Step title="Empresa cadastrada">
    Você já fez `POST /v1/companies`? O `id` retornado é o `issuerId`, usado para escolher
    o emissor em qualquer módulo (ver aviso abaixo sobre obrigatoriedade por módulo). A
    resposta já traz `prontoPara: { nfse, nfe, nfce, sandbox, faltando }` — leia ali o que falta
    para CADA documento fiscal, em vez de descobrir com um `422` na hora de emitir.
    `avisos[]` vem junto: se o município não adere ao Padrão Nacional da NFS-e neste
    ambiente, o item `MUNICIPIO_NAO_ADERENTE_PADRAO_NACIONAL` aparece (NF-e e NFC-e
    seguem disponíveis). `desconhecido` não gera aviso e o cadastro nunca falha por cobertura.
  </Step>

  <Step title="Certificado enviado">
    Você já fez `POST /v1/companies/{id}/certificate` (campo multipart **`file`**, não
    `certificate`)? Se sim, a resposta veio sem erro de senha? Sem certificado,
    `prontoPara.nfe`/`prontoPara.nfce`/`prontoPara.nfse` vêm `false`. Certificado
    vencido entra em `faltando.nfse` como `certificadoVencido`; de outra pessoa
    jurídica (raiz diferente), como `certificadoOutroCnpj`. O 201 desta rota já traz `prontoPara` atualizado: não
    precisa de um GET extra para ver o flip.
  </Step>

  <Step title="Ambiente correto para o teste">
    Todo emissor novo nasce em **homologação** (`ambienteFiscal: 2`). Não tente emitir em
    produção sem antes validar em homologação. Ver [Sandbox](/guides/sandbox) para o
    estado real da promoção pra produção.
  </Step>
</Steps>

<Info>
  **NF-e e NFC-e** aceitam **`issuerId` opcional** no payload. Se você só tem uma empresa
  cadastrada, pode omitir; a API usa o seu emissor. Com dois ou mais emissores, informe
  `issuerId` (UUID) para escolher o CNPJ; sem ele a API responde `400`. **NFS-e** exige
  `issuerId` explícito no corpo da requisição.
</Info>

***

## Estrutura de uma NF-e

Uma NF-e é composta por 4 blocos no payload (o emissor não entra no body, é resolvido pela API Key/JWT):

```
POST /v1/nfe
├── 1. Identificação (naturezaOperacao, série, número, todos opcionais)
├── 2. Destinatário (cnpjCpf, nome, endereço)
├── 3. Itens (produtos, NCM, CFOP, impostos)
└── 4. Pagamentos (array, forma + valor)
```

***

## Campos obrigatórios

Um payload com nomes de campo diferentes destes é **rejeitado com 400** antes de chegar
na SEFAZ.

### Raiz

| Campo                | Tipo    | Obrigatório      | Exemplo                                                                             |
| -------------------- | ------- | ---------------- | ----------------------------------------------------------------------------------- |
| `naturezaOperacao`   | string  | Não              | `"VENDA DE MERCADORIA"`                                                             |
| `destinatario`       | objeto  | **Sim**          | ver abaixo                                                                          |
| `items`              | array   | **Sim** (mín. 1) | ver abaixo, **não é `itens`**                                                       |
| `pagamentos`         | array   | **Sim** (mín. 1) | ver abaixo, **não é `pagamento` singular**                                          |
| `resolverTributacao` | boolean | Não              | `true` ativa a emissão assistida (Cérebro Fiscal, requer plano/feature habilitados) |

### Destinatário

| Campo                      | Tipo                     | Obrigatório                        | Exemplo                                    |
| -------------------------- | ------------------------ | ---------------------------------- | ------------------------------------------ |
| `cnpjCpf`                  | string (11 a 14 dígitos) | **Sim**, **não é `cnpj`/`cpf`**    | `"99888777000100"`                         |
| `nome`                     | string                   | **Sim**                            | `"Cliente Exemplo SA"`                     |
| `ie`                       | string                   | Não, **não é `inscricaoEstadual`** | `"1234567890"`                             |
| `indicadorIE`              | número                   | Não (opcional)                     | `1` = contribuinte, `9` = não contribuinte |
| `endereco.logradouro`      | string                   | **Sim**                            | `"Av Brasil"`                              |
| `endereco.numero`          | string                   | **Sim**                            | `"500"`                                    |
| `endereco.bairro`          | string                   | **Sim**                            | `"Centro"`                                 |
| `endereco.codigoMunicipio` | string                   | **Sim**                            | `"3550308"` (São Paulo)                    |
| `endereco.municipio`       | string                   | **Sim**                            | `"São Paulo"`                              |
| `endereco.uf`              | string (2)               | **Sim**                            | `"SP"`                                     |
| `endereco.cep`             | string                   | **Sim**                            | `"01001000"`                               |

### Item (dentro de `items[]`)

| Campo                                | Tipo                    | Obrigatório                | Exemplo                  |
| ------------------------------------ | ----------------------- | -------------------------- | ------------------------ |
| `codigo`                             | string                  | **Sim**                    | `"PROD001"`              |
| `descricao`                          | string                  | **Sim**                    | `"Produto Teste"`        |
| `ncm`                                | string (2 ou 8 dígitos) | **Sim**                    | `"84713012"`             |
| `cfop`                               | string                  | **Sim**                    | `"5102"` (venda interna) |
| `unidade`                            | string                  | **Sim**                    | `"UN"`                   |
| `quantidade`                         | número (min 0.0001)     | **Sim**                    | `2`                      |
| `valorUnitario`                      | número (min 0.01)       | **Sim**                    | `150.00`                 |
| `valorTotal`                         | número                  | Não (calculado se ausente) | `300.00`                 |
| `icms`/`pis`/`cofins`/`ipi`/`ibsCbs` | objeto                  | Não                        | ver abaixo               |

<Info>
  Não há campo `numero` por item: o índice do array já identifica o item. O item não
  exige `icms` preenchido: sem `resolverTributacao`, os campos fiscais viajam como
  passthrough (o que você mandar é o que vai pro XML); com `resolverTributacao: true`, o
  Cérebro Fiscal completa `icms.csosn`/`icms` (Regime Normal)/`ibsCbs` ausentes.
</Info>

### ICMS simplificado (Simples Nacional)

```json theme={null}
"icms": {
  "origem": 0,
  "csosn": "400"
}
```

### ICMS no Regime Normal (Lucro Real / Lucro Presumido)

O emissor `crt: 3` **não informa `cst` à mão**: `icms.cst` é recusado com
`422 CST_NAO_SUPORTADO_NFE` (o leiaute exige a modalidade da base de cálculo, campo fora
deste contrato). O caminho que autoriza é `resolverTributacao: true`: o motor calcula
CST, base, alíquota e valor a partir de NCM, CFOP, UF e origem:

```json theme={null}
{
  "resolverTributacao": true,
  "items": [{
    "codigo": "P1",
    "descricao": "Farinha de Trigo Tipo 1 - 1kg",
    "ncm": "11029000",
    "cfop": "5102",
    "unidade": "UN",
    "quantidade": 2,
    "valorUnitario": 500,
    "icms": { "origem": 0 }
  }]
}
```

Detalhe completo do cenário (pré-requisitos, resposta, XML, erros e limitações) em
[Regime Normal](/guides/regime-normal).

### Pagamentos (array, mínimo 1)

| Campo   | Tipo   | Valores                                                                                    |
| ------- | ------ | ------------------------------------------------------------------------------------------ |
| `forma` | string | `"01"` Dinheiro, `"03"` Cartão crédito, `"04"` Cartão débito, `"15"` Boleto, `"99"` Outros |
| `valor` | número | Valor da parcela/forma                                                                     |

```json theme={null}
"pagamentos": [
  { "forma": "01", "valor": 100.00 }
]
```

***

## Exemplo completo mínimo

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST https://api.engineapi.com.br/v1/nfe \
      -H "x-api-key: ek_test_SUA_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "naturezaOperacao": "VENDA DE MERCADORIA",
        "destinatario": {
          "cnpjCpf": "99888777000100",
          "nome": "Cliente Exemplo SA",
          "indicadorIE": 1,
          "endereco": {
            "logradouro": "Av Brasil",
            "numero": "500",
            "bairro": "Centro",
            "codigoMunicipio": "3550308",
            "municipio": "São Paulo",
            "uf": "SP",
            "cep": "01001000"
          }
        },
        "items": [{
          "codigo": "PROD001",
          "descricao": "Produto Teste",
          "ncm": "84713012",
          "cfop": "5102",
          "unidade": "UN",
          "quantidade": 1,
          "valorUnitario": 100.00,
          "icms": {
            "origem": 0,
            "csosn": "400"
          }
        }],
        "pagamentos": [
          { "forma": "01", "valor": 100.00 }
        ]
      }'
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    const resp = await fetch('https://api.engineapi.com.br/v1/nfe', {
      method: 'POST',
      headers: {
        'x-api-key': apiKey,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        naturezaOperacao: 'VENDA DE MERCADORIA',
        destinatario: {
          cnpjCpf: '99888777000100',
          nome: 'Cliente Exemplo SA',
          indicadorIE: 1,
          endereco: {
            logradouro: 'Av Brasil', numero: '500',
            bairro: 'Centro', codigoMunicipio: '3550308',
            municipio: 'São Paulo', uf: 'SP', cep: '01001000',
          },
        },
        items: [{
          codigo: 'PROD001', descricao: 'Produto Teste',
          ncm: '84713012', cfop: '5102', unidade: 'UN',
          quantidade: 1, valorUnitario: 100.00,
          icms: { origem: 0, csosn: '400' },
        }],
        pagamentos: [{ forma: '01', valor: 100.00 }],
      }),
    });

    const { data } = await resp.json();
    console.log('Autorizada:', data.accessKey, data.status); // "AUTHORIZED"
    ```
  </Tab>
</Tabs>

***

## Resposta de sucesso

Mesmo contrato de resposta nos dois caminhos de emissão (síncrono e lote), envelopado por
`{ data, meta }`:

```json theme={null}
{
  "data": {
    "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "status": "AUTHORIZED",
    "accessKey": "35260211222333000181550010000000011000000019",
    "protocol": "135260000001234",
    "number": 1,
    "series": 1,
    "model": "55",
    "amount": "100",
    "destCNPJ": "99888777000100",
    "destName": "Cliente Exemplo SA",
    "createdAt": "2026-07-06T12:00:00.000Z",
    "updatedAt": "2026-07-06T12:00:01.000Z",
    "downloads": {
      "xml": "/v1/nfe/xml/35260211222333000181550010000000011000000019",
      "pdf": "/v1/nfe/pdf/35260211222333000181550010000000011000000019"
    }
  },
  "meta": {
    "requestId": "req_abc123",
    "timestamp": "2026-07-06T12:00:00.000Z"
  }
}
```

<Check>
  `data.status: "AUTHORIZED"` (inglês, mesmo enum usado em `GET /v1/nfe/{id}` e no webhook
  `invoice.authorized`) está aprovada pela SEFAZ e tem validade fiscal (se o emissor está em
  produção: `ambienteFiscal: 1`). Não há `issuer`/`customer` embutidos, nem
  `xml`/`xmlPath`/`pdfPath`/`invoiceId`/`message`/`success`/`invoice` aninhado: o PDF/XML têm
  rotas próprias de download (`downloads.xml`/`downloads.pdf` apontam pra elas). `amount` é
  **string decimal** (`"100"`, sem zeros à direita), nunca `number` cru nem o `Decimal.js` interno serializado.
</Check>

***

## Como a emissão chega até você

Um payload com nome de campo desconhecido é recusado antes de qualquer processamento
(ver [Campo desconhecido no payload](/guides/errors#campo-desconhecido-no-payload-400)).
Passada essa validação, o caminho síncrono (`POST /v1/nfe`) e o caminho em lote
(`POST /v1/nfe/batch`, que enfileira e devolve o desfecho em `GET /v1/nfe/queue`) convergem
no mesmo destino: a SEFAZ decide, e o resultado chega por webhook ou por consulta.

```mermaid theme={null}
flowchart LR
    A("POST /v1/nfe<br/>ou POST /v1/nfe/batch") --> B{"Payload<br/>conhecido?"}
    B -->|não| C("400<br/>nada é emitido")
    B -->|sim| D("Fila de processamento<br/>GET /v1/nfe/queue")
    D --> E{"SEFAZ"}
    E -->|aprova| F("AUTHORIZED")
    E -->|rejeita| G("REJECTED<br/>400 com error.erros[]")
    F --> H("webhook invoice.authorized<br/>ou GET /v1/nfe/{id}")
    G --> I("webhook invoice.rejected<br/>ou GET /v1/nfe/{id}")

    classDef nucleo fill:#1E56B1,stroke:#0F2A5E,color:#fff
    classDef destaque fill:#2D7AF6,stroke:#0F2A5E,color:#fff
    classDef autorizada fill:#16a34a,stroke:#15803d,color:#fff
    classDef rejeitada fill:#dc2626,stroke:#991b1b,color:#fff
    class A,D nucleo
    class B,E destaque
    class C,G,I rejeitada
    class F,H autorizada
```

***

## Erros mais comuns nesta etapa

| Erro                                  | Causa                                                                              | Solução                                                                                                                      |
| ------------------------------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `403 Nenhum emissor configurado`      | Nenhuma empresa cadastrada para o partner                                          | Faça `POST /v1/companies` primeiro                                                                                           |
| `500`/erro de certificado na emissão  | Nenhum `.pfx` enviado ou senha inválida                                            | Faça `POST /v1/companies/{id}/certificate` com o campo `file` — ou confira antes: `prontoPara.nfe`/`.nfce` `false` já avisam |
| `400` com `error.erros[]` (cStat 539) | Nota duplicada (mesmo número/série)                                                | Incremente o número da nota, ou deixe a API alocar automaticamente                                                           |
| `400` com `error.erros[]` (cStat 225) | Campo NCM ou CFOP inválido                                                         | Verifique a [tabela de CFOP](/conceitos/cfop-ncm-cst)                                                                        |
| `400 Bad Request` (validação)         | Campo obrigatório ausente ou nome de campo errado (ex.: `itens` em vez de `items`) | Confira contra a tabela de campos acima                                                                                      |
| `401 Token inválido`                  | Token expirado (12h)                                                               | Renove em `POST /v1/auth/refresh` ou faça login novamente                                                                    |

<Warning>
  Rejeição SEFAZ é **sempre 400**, nunca 422. O corpo carrega `error.erros[]`
  (`{codigo, descricao}` verbatim da SEFAZ). Veja o formato completo em
  [Erros e Rejeições](/guides/errors). O `422` existe na API, mas só para a
  **emissão assistida** (`resolverTributacao: true`) quando um campo fiscal não tem
  fonte para ser resolvido, não é o mesmo caso de rejeição SEFAZ.
</Warning>

***

## Próximos passos

<CardGroup cols={3}>
  <Card title="Emitir NF-e (guia completo)" icon="file-invoice" href="/guides/emitir-nfe">
    ICMS, IPI, PIS, COFINS. Todos os impostos detalhados
  </Card>

  <Card title="Webhooks" icon="bell" href="/guides/webhooks">
    Receba a confirmação da SEFAZ em tempo real
  </Card>

  <Card title="Certificados" icon="shield-halved" href="/guides/certificates">
    Gerenciar validade e renovação do certificado A1
  </Card>
</CardGroup>
