Autenticação da DC-e
A DC-e se integra por token Bearer: toda requisição leva Authorization: Bearer <token>. O token é um JWT emitido pelo provedor de identidade da NFE.io, a partir de uma credencial da sua assinatura.
As bibliotecas oficiais (Node.js, PHP, Ruby) autenticam de outra forma e não servem para a DC-e. A integração é feita direto na API, com o token descrito nesta página — a coleção Postman ajuda a começar.
O token
| Emissor | https://idx.nfe.io/realms/nfeio |
| Audiência e escopos | já vêm configurados na credencial — você não precisa pedir escopo |
| Validade | 20 minutos (expires_in) |
| Assinatura do token | claim client_sub — é o valor que vai em {subscriptionId} na URL |
O painel tem um catálogo de tokens de API, mas a DC-e ainda não os aceita. Use o token obtido como descrito abaixo.
Como obter o token
São dois passos: pedir a credencial uma vez, e trocá-la por token sempre que precisar.
1. Peça a credencial ao suporte
A credencial é um par client_id e client_secret vinculado à sua assinatura. Hoje ela é provisionada pela equipe da NFE.io: fale com o suporte informando a assinatura que vai integrar com a DC-e.
Guarde o client_secret como segredo — em cofre ou variável de ambiente, nunca no código-fonte nem no navegador. Quem tem o par emite documento fiscal em nome da assinatura.
2. Troque a credencial por um token
Faça um POST no endpoint de token, com corpo application/x-www-form-urlencoded e o fluxo client_credentials:
curl -X POST "https://idx.nfe.io/realms/nfeio/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=SEU_CLIENT_ID" \
-d "client_secret=SEU_CLIENT_SECRET"
A resposta segue o padrão OAuth 2.0:
{
"access_token": "eyJhbGciOi...",
"expires_in": 1200,
"token_type": "Bearer"
}
Use o access_token no cabeçalho de toda requisição à DC-e:
curl "https://api.nfe.io/v1/subscriptions/{subscriptionId}/taxpayers/{taxpayerId}/contentdeclarations" \
-H "Authorization: Bearer <access_token>"
Validade e renovação
O token vale 20 minutos — o expires_in da resposta diz quanto, em segundos. Não há renovação: quando ele expira, peça um token novo com a mesma credencial. Uma requisição com token vencido responde 401; trate o 401 pedindo um token novo e repetindo a chamada uma vez.
Para não pedir um token a cada requisição, guarde o token em memória e reaproveite-o até perto do fim da validade.
Erros comuns na obtenção
| Resposta do endpoint de token | O que houve |
|---|---|
401 com "error": "invalid_client" | client_id ou client_secret errado. Confira o par recebido do suporte |
400 com "error": "unsupported_grant_type" | grant_type ausente ou diferente de client_credentials, ou corpo enviado como JSON em vez de form-urlencoded |
Acesso ao produto
Um token válido não basta para emitir. O acesso à DC-e depende de duas condições comerciais, e cada uma tem a sua recusa, em application/problem+json:
| Recusa | type | O que fazer |
|---|---|---|
403 | https://docs.nfe.io/errors/dce/product-not-contracted | A DC-e não está contratada na assinatura. Fale com o time comercial |
403 | https://docs.nfe.io/errors/dce/issuance-not-released | A emissão de DC-e é liberada por empresa, e esta empresa ainda não está liberada neste ambiente. Fale com o suporte |
Leia o type, não o texto da mensagem, para distinguir um caso do outro.
O endereço
A DC-e responde em https://api.nfe.io, o host compartilhado da plataforma — ela se distingue pelo caminho, não pelo host. Há um host só, para os dois ambientes da SEFAZ.
O ambiente vem do cadastro da empresa, não do host nem da assinatura. O environment que você envia na emissão (1 produção, 2 homologação) tem de coincidir com o ambiente da DC-e cadastrado na empresa; divergente, a resposta é 422 com a regra B10-10. Para homologar sua integração, use uma empresa em homologação e envie environment: 2 — no mesmo https://api.nfe.io.
Assinatura e contribuinte: os dois vão na URL
Toda rota da DC-e carrega a assinatura e o contribuinte emitente, nesta ordem e em minúsculas:
/v1/subscriptions/{subscriptionId}/taxpayers/{taxpayerId}/contentdeclarations…
O isolamento dos documentos é pela assinatura do caminho. São duas dimensões distintas: a assinatura diz de quem são os documentos; o taxpayerId diz qual contribuinte emite (a empresa cujo certificado assina o documento). Nenhum cabeçalho de assinatura é lido.
taxpayers na URL, companyId no corpoDentro da plataforma o contribuinte se chama empresa. Por isso o campo companyId que a consulta em lista devolve carrega exatamente o valor que você pôs em {taxpayerId}.
| Parte do caminho | Regra | Fora da regra |
|---|---|---|
{subscriptionId} | A assinatura do próprio token (claim client_sub). Aceita com ou sem o prefixo sub_ | 403, type https://docs.nfe.io/errors/dce/subscription-scope-undetermined |
{taxpayerId} | Uma empresa cadastrada nessa assinatura | 404, type https://docs.nfe.io/errors/dce/company-not-in-subscription — em todas as rotas, leitura ou escrita |