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 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 |
202Se 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.
{
"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"
}
}
{
"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). 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 emissões em lote com repetição segura
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.
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) |
| 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.
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.
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.
[
{
"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"
}
]
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. 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.
{
"type": "https://docs.nfe.io/errors/dce/validation-failed",
"status": 400,
"errors": [
{ "name": "emitterType", "reason": "emitterType is required." }
]
}
{
"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" }
]
}