---
title: "Emitir uma DC-e pela API"
description: "Como emitir uma DC-e — síncrono ou assíncrono, as 3 modalidades de emissão, idempotência, e como agrupar emissões em lote sem perder a repetição segura."
source_url: https://nfe.io/docs/documentacao/declaracao-de-conteudo-eletronica/integracao-api/emitir-uma-declaracao-de-conteudo/
product: documentacao
last_updated: 2026-10-07
tags: ["dce", "integracao", "emissao"]
integration: ["api"]
---

# Emitir uma DC-e pela API

```
POST /v1/subscriptions/{subscriptionId}/taxpayers/{taxpayerId}/contentdeclarations
```

O serviço faz tudo em uma chamada: numeração, assinatura com o certificado da empresa, validação contra o schema e transmissão à SEFAZ.

`{subscriptionId}` é a assinatura sobre a qual você opera e `{taxpayerId}` é o contribuinte emitente — [veja Autenticação](../autenticacao.md) para o que cada um tem de ser. Em produção o host é `https://api.nfe.io`.

## Síncrono ou assíncrono

| Você envia | Você recebe |
|---|---|
| Sem o cabeçalho `Prefer` | `200` com o documento em estado terminal (autorizado, rejeitado ou recusado) |
| `Prefer: respond-async` | `202` com o `id` e o cabeçalho `Location`, para consultar depois |

:::caution O modo síncrono também pode responder `202`
Se a autorização não chegar a um estado terminal dentro do tempo de espera do servidor, a resposta degrada para `202` com `Location` — mesmo sem você ter pedido o modo assíncrono. Trate `202` como resultado possível **sempre**, não só quando você pediu.
:::

`serie` e `number` são atribuídos pelo servidor — não os envie. Eles aparecem na resposta assim que a numeração é definida.

## O ambiente vai no corpo, e tem de bater com o cadastro da empresa

`environment` é **obrigatório**: `1` produção, `2` homologação. Ausente, `0` ou fora desses dois valores, a resposta é `400`. O valor tem de coincidir com o **ambiente cadastrado da empresa** na plataforma — divergente, a resposta é `400` com a regra `B10-10` nomeando os dois valores. Empresa sem ambiente cadastrado é homologação, e em homologação o documento não tem validade fiscal.

## As 3 modalidades de emissão

O campo `emitterType` define quem está emitindo e como o emitente se identifica:

| Modalidade | Identificação em `issuer` | `emitterParty` |
|---|---|---|
| `SelfIssuer` (emissão própria) | Somente `cnpj`, com dígito verificador válido. Enviar `cpf` é recusado | Não exigido |
| `Marketplace` | Exatamente um entre `cnpj`, `cpf` ou `idOthers` — identificação do cliente do marketplace | `site` **obrigatório** |
| `Carrier` (transportadora) | Exatamente um entre `cnpj`, `cpf` ou `idOthers` | Não exigido |

O destinatário, em qualquer modalidade, sempre informa exatamente um entre `cnpj`, `cpf` ou `idOthers`.

```json title="Emissão própria (SelfIssuer)"
{
  "emitterType": "SelfIssuer",
  "emissionType": "Normal",
  "environment": 2,
  "issuer": {
    "cnpj": "11222333000181",
    "name": "EMPRESA EXEMPLO LTDA - MATRIZ",
    "address": {
      "street": "Rua Exemplo",
      "number": "1000",
      "neighborhood": "Centro",
      "cityCode": 3550308,
      "cityName": "São Paulo",
      "state": "SP",
      "postalCode": "01001000"
    }
  },
  "recipient": {
    "cnpj": "99887766000105",
    "name": "EMPRESA EXEMPLO LTDA - FILIAL",
    "address": {
      "street": "Avenida Exemplo",
      "number": "250",
      "neighborhood": "Jardim Exemplo",
      "cityCode": 3304557,
      "cityName": "Rio de Janeiro",
      "state": "RJ",
      "postalCode": "20010000"
    },
    "email": "contato@exemplo.com.br"
  },
  "items": [
    {
      "itemNumber": 1,
      "description": "EQUIPAMENTO DE EXEMPLO",
      "ncm": "84713012",
      "quantity": 2,
      "unitValue": 1500.00,
      "totalValue": 3000.00
    }
  ],
  "transport": {
    "mode": "Carrier",
    "carrierCnpj": "12345678000195"
  }
}
```

```json title="Marketplace (identifica o cliente, informa o site)"
{
  "emitterType": "Marketplace",
  "emissionType": "Normal",
  "environment": 2,
  "issuer": {
    "cpf": "11144477735",
    "name": "VENDEDOR EXEMPLO",
    "address": {
      "street": "Rua do Vendedor",
      "number": "45",
      "neighborhood": "Centro",
      "cityCode": 3550308,
      "cityName": "São Paulo",
      "state": "SP",
      "postalCode": "01001000"
    }
  },
  "recipient": {
    "cnpj": "99887766000105",
    "name": "COMPRADOR EXEMPLO LTDA",
    "address": {
      "street": "Avenida Exemplo",
      "number": "250",
      "neighborhood": "Jardim Exemplo",
      "cityCode": 3304557,
      "cityName": "Rio de Janeiro",
      "state": "RJ",
      "postalCode": "20010000"
    }
  },
  "items": [
    {
      "itemNumber": 1,
      "description": "ACESSORIO DE EXEMPLO",
      "quantity": 1,
      "unitValue": 89.90,
      "totalValue": 89.90
    }
  ],
  "transport": { "mode": "Mail" },
  "emitterParty": { "site": "https://marketplace.exemplo.com.br" },
  "additionalInfo": { "marketplaceInfo": "Pedido 123456" }
}
```

## Repetição segura (`Idempotency-Key`)

Envie o cabeçalho `Idempotency-Key` com uma chave escolhida por você (por exemplo, o identificador do pedido no seu sistema) para que um reenvio — timeout de rede, retry automático — não emita um segundo documento nem consuma numeração fiscal de novo.

| Situação | Resposta |
|---|---|
| Primeira chamada com a chave | Segue normalmente (`200`/`202`) |
| Repetição depois de concluída | O **mesmo código de status** da primeira resposta, com `Location` apontando para o documento já criado, e **sem corpo** — consulte o `Location` para ler o documento |
| Repetição enquanto a primeira ainda está em andamento | `409` — nunca se enfileira uma segunda emissão |
| A chamada anterior falhou na validação (`400`/`422`) | A chave é **liberada** — pode reenviar a mesma chave com o corpo corrigido |

A chave é isolada por assinatura ([veja Autenticação](../autenticacao.md)). **A emissão em lote (`$batch`) recusa este cabeçalho com `400`** — para ter a garantia em vários documentos, veja [Agrupar emissões em lote](#agrupar-em-lote).

## Agrupar emissões em lote com repetição segura {#agrupar-em-lote}

Para emitir vários documentos juntos **e** poder reenviá-los depois de um timeout, chame a emissão unitária uma vez por documento: cada chamada com a sua `Idempotency-Key`, e o mesmo cabeçalho `X-Batch-Id` em todas. É o mesmo caminho que o painel da NFE.io usa para emitir em lote.

```http title="Uma das chamadas do lote — entre elas, mudam só a chave e o corpo"
POST /v1/subscriptions/{subscriptionId}/taxpayers/{taxpayerId}/contentdeclarations
Authorization: Bearer {token}
Content-Type: application/json
Prefer: respond-async
Idempotency-Key: pedido-0042-item-1
X-Batch-Id: pedido-2026-10-06-0042
```

| Você quer | Como |
|---|---|
| Reenviar o lote inteiro sem duplicar | Repita cada chamada com a **mesma** `Idempotency-Key` de antes. O que já foi criado volta como repetição; só o que faltou é emitido |
| Saber quais documentos são do lote | O campo `batchId` vem na resposta `202`, no detalhe e na listagem — filtre com `$filter=batchId eq 'pedido-2026-10-06-0042'` ([veja Como consultar](./como-consultar-uma-declaracao-de-conteudo.md)) |
| Emitir sem lote | Não envie o cabeçalho. Ele é opcional, e o documento fica sem `batchId` |

O valor do `X-Batch-Id` é **seu**: a API não o interpreta, só guarda e devolve.

- Aceita de 1 a 64 caracteres entre letras, dígitos, ponto, sublinhado, dois-pontos e hífen — cabe um GUID, uma data ou o número do seu pedido.
- Valor fora do formato é recusado com `400`, sem criar documento. Ele **não** é truncado nem corrigido.
- O lote não interfere na chave: dois documentos do mesmo lote com chaves diferentes são os dois criados.

## Emissão em lote

```
POST /v1/subscriptions/{subscriptionId}/taxpayers/{taxpayerId}/contentdeclarations/$batch
```

Recebe um array de declarações e aceita cada uma independentemente. A resposta é sempre `200`, com um resultado por item — a posição no array de entrada volta em `index`.

:::caution O lote é sempre assíncrono, e não tem repetição segura
Cada item aceito volta com `status: 202` — nenhum item traz o documento pronto na resposta; acompanhe cada `id` por `GET {id}`.

**Esta rota recusa `Idempotency-Key` com `400`**, e nada é criado. A API recusa em vez de ignorar de propósito: uma chave só não protege vários documentos, e quem reenviasse achando estar protegido emitiria o lote inteiro de novo. Sem a chave, é isso o que acontece: reenviar o mesmo lote **emite os documentos de novo**. Se o seu processo reenvia, use o [agrupamento com repetição segura](#agrupar-em-lote).
:::

Todos os itens compartilham o mesmo `batchId`: o valor do cabeçalho `X-Batch-Id`, se você o enviar, ou um identificador gerado pelo servidor.

```json title="Resposta do lote — um item aceito, um recusado"
[
  {
    "index": 0,
    "status": 202,
    "id": "0f6b1f5c-9a5e-4a0e-9c1b-2f4e6d8a1b23",
    "batchId": "7c9a2f10-4b3d-4e91-8a5f-1d2c3b4a5e6f"
  },
  {
    "index": 1,
    "status": 422,
    "batchId": "7c9a2f10-4b3d-4e91-8a5f-1d2c3b4a5e6f"
  }
]
```

:::note Lote por planilha é outro caminho
Quem não integra pela API emite em lote subindo uma **planilha** no painel — o formato do arquivo está em [Template de planilha para emissão de DC-e em lote](../template-de-planilha-para-emissao-de-dce.md). Esse caminho não usa a rota `$batch`: cada linha vira uma emissão própria, com idempotência por linha.
:::

## Erros de validação

| Código | O que significa |
|---|---|
| `400` | Requisição recusada na validação de forma ou identidade — campo obrigatório ausente, identificação incompatível com a modalidade, `environment` ausente ou divergente do cadastro (`B10-10`), `X-Batch-Id` fora do formato, ou `Idempotency-Key` enviada ao `$batch`. `errors[]` traz `name` (o campo ou o cabeçalho) e `reason` (a explicação) |
| `422` | Requisição bem formada, mas em violação de regra de negócio. `errors[]` lista **todas** as violações, cada uma com `name`, `reason` e `rule` (o identificador da regra) separados |
| `401` | Token ausente, expirado, com audiência errada, ou chave de API no lugar de JWT |
| `403` | Token válido mas sem o escopo/papel da operação, ou a assinatura da URL não é do token nem acessível ao usuário (`type` termina em `subscription-scope-undetermined`) |
| `409` | Já existe uma emissão em andamento com a mesma `Idempotency-Key` |

Todo erro vem em `application/problem+json` (RFC 9457): `type` é o identificador estável para o seu código, no espaço `https://docs.nfe.io/errors/dce/…`; `title` e `detail` são texto para pessoas, em inglês.

```json title="400 — campo obrigatório ausente"
{
  "type": "https://docs.nfe.io/errors/dce/validation-failed",
  "status": 400,
  "errors": [
    { "name": "emitterType", "reason": "emitterType is required." }
  ]
}
```

```json title="422 — violações de regra, uma por campo"
{
  "status": 422,
  "errors": [
    { "name": "recipient.address.cityCode", "reason": "The recipient's city code does not exist in the IBGE table.", "rule": "E10-10" },
    { "name": "items", "reason": "The number of items must be between 1 and 999.", "rule": "L-ITEMS" }
  ]
}
```

## Veja também

- [Conceitos da DC-e](../conceitos.md)
- [Autenticação](../autenticacao.md)
- [Como consultar uma DC-e](./como-consultar-uma-declaracao-de-conteudo.md)
