Documentação para desenvolvedores

Do primeiro sandbox ao documento em produção. Exemplos com dados fictícios.

Esta documentação descreve a versão 1 em rascunho. O produto está em pré-lançamento; endpoints e campos podem mudar até a publicação oficial.

Introdução

A API do EmiteHub emite documentos fiscais eletrônicos. Você envia um JSON no modelo canônico do documento; a plataforma valida, monta o XML no layout vigente, assina com o certificado do emitente, transmite à SEFAZ e devolve o resultado.

A versão 1 cobre MDF-e, CT-e, NF-e, NFC-e e NFS-e (padrão nacional), todos com o mesmo ciclo de vida, a mesma autenticação e os mesmos webhooks. Os exemplos desta página usam o MDF-e; os demais documentos seguem o mesmo padrão:

DocumentoEmitirArquivo PDFEventos
MDF-e (58)POST /v1/mdfe/damdfecancel, close, drivers, documents · GET /v1/mdfe/open
CT-e (57)POST /v1/cte/dactecancel, correction
NF-e (55)POST /v1/nfe/danfecancel, correction
NFC-e (65)POST /v1/nfce/danfecancel (CSC em PUT /v1/issuers/{id}/nfce-csc)
NFS-ePOST /v1/nfse/danfsecancel

Para todos: GET /v1/{doc}/{id}, POST /v1/{doc}/{id}/refresh e GET /v1/{doc}/{id}/xml. Status dos serviços da SEFAZ em GET /v1/sefaz/status. Detalhes na referência completa.

Ambientes

AmbienteURL baseChaveValidade fiscal
Sandboxhttps://sandbox.api.emitehub.com/v1emitehub_test_…Não (homologação SEFAZ)
Produçãohttps://api.emitehub.com/v1emitehub_live_…Sim

Uma chave de teste nunca emite em produção.

Autenticação

Envie a chave no cabeçalho X-API-Key em toda requisição. A chave é exibida uma única vez na criação; guarde-a em cofre de segredos. Use-a apenas de servidor para servidor, nunca em navegador ou aplicativo.

curl "https://sandbox.api.emitehub.com/v1/issuers" \
  -H "X-API-Key: $EMITEHUB_API_KEY"
Dica: você pode restringir a chave a IPs de saída fixos e definir validade. Troque chaves com sobreposição para migrar sem interrupção.

Primeiro MDF-e

1. Cadastre o emitente e o certificado

POST /v1/issuers
{ "cnpj": "00000000000191", "legalName": "Transportes Exemplo Ltda",
  "stateRegistration": "123456789", "taxRegime": "3",
  "rntrc": "00000000", "address": { … } }

PUT /v1/issuers/{issuerId}/certificate      # multipart: file (.pfx) + password

2. Emita

POST /v1/mdfe
X-API-Key: emitehub_test_…
Idempotency-Key: 6f1c2e0a-0000-4000-8000-000000000001

{
  "issuerId": "iss_exemplo",
  "emitterType": "transport_provider", "transporterType": "etc",
  "ufStart": "SP", "ufEnd": "MG",
  "loadingCities": [{ "ibgeCode": "3550308", "name": "São Paulo" }],
  "vehicle": { "plate": "ABC1D23", "plateUf": "SP", "tareKg": 8000 },
  "drivers": [{ "name": "Condutor Exemplo", "cpf": "00000000000" }],
  "unloadings": [{
    "city": { "ibgeCode": "3170206", "name": "Uberlândia" },
    "documents": [{ "type": "nfe", "accessKey": "3500…(44 dígitos)" }]
  }],
  "totals": { "cargoValue": 15000.00, "cargoWeight": 12000, "weightUnit": "kg" }
}

# resposta
202 { "id": "mdfe_exemplo", "status": "processing" }

Todos os campos, regras e tabelas estão no schema MdfeInput da referência. Antes de enviar, o EmiteHub valida o payload (chave de acesso com dígito verificador, CPF/CNPJ, placas, municípios IBGE, entre outros) e responde 422 com os campos com problema, sem consumir numeração.

3. Acompanhe

GET /v1/mdfe/{id}      # status, chave de acesso, protocolo, eventos

Ciclo de vida

StatusSignificado
processingRecebido, em validação, assinatura ou transmissão
authorizedAutorizado pela SEFAZ; XML e PDF disponíveis
rejectedRejeitado; veja rejection.sefazCode, message e hint
deniedDenegado pela SEFAZ
contingencyEmitido em contingência, aguardando transmissão
closed · cancelledMDF-e encerrado ou cancelado por evento

Eventos do MDF-e: POST /mdfe/{id}/close, /cancel, /drivers, /documents. Novos status podem ser adicionados; trate valores desconhecidos de forma genérica.

Idempotência

Toda operação que emite ou altera um documento exige o cabeçalho Idempotency-Key (um UUID por operação). Repetir a requisição com a mesma chave e o mesmo corpo devolve o mesmo resultado, sem gerar outro número. Mesma chave com corpo diferente retorna 409.

Em caso de timeout, repita a requisição com a mesma chave. Nunca gere uma chave nova para tentar de novo o mesmo documento.

Webhooks

Cadastre uma URL em POST /webhooks com os eventos desejados. O secret aparece só na criação.

EventoQuando
mdfe.authorizedDocumento autorizado
mdfe.rejectedDocumento rejeitado
mdfe.closed · mdfe.cancelledEvento autorizado
certificate.expiringCertificado perto de vencer

Cada entrega traz o cabeçalho X-EmiteHub-Signature: sha256=<HMAC-SHA256 do corpo>. Valide antes de processar e responda 2xx em até 10 s; caso contrário, reenviamos com espera progressiva. Use o id do evento para deduplicar.

// Node.js
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers['x-emitehub-signature']));

Arquivos (XML e PDF)

GET /mdfe/{id}/xml e /damdfe devolvem uma URL assinada e temporária (cerca de 1 hora) em url e expiresAt. Baixe o arquivo e guarde sua cópia; peça um novo link quando precisar.

Erros

{ "error": { "code": "VALIDATION_ERROR", "message": "Chave de acesso inválida",
  "details": [{ "field": "unloadings[0].documents[0].accessKey", "issue": "dígito verificador incorreto" }] } }
HTTPQuandoO que fazer
400Requisição malformadaCorrija; repetir não resolve
401 · 403Chave inválida, expirada ou IP não autorizadoVerifique a chave e as permissões
404Recurso inexistenteConfira o id
409Conflito (ex.: mesma Idempotency-Key com corpo diferente)Reutilize o corpo original ou use outra operação
422Regra fiscal ou de estadoLeia details e sefazCode
429Limite excedidoAguarde Retry-After
5xxErro nossoTente de novo com espera progressiva e a mesma Idempotency-Key

Decida pelo código HTTP e pelo error.code; não interprete o texto de message.

Limites

O limite de requisições por minuto depende do plano. As respostas trazem RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset; ao exceder, a API responde 429 com Retry-After.

Paginação e sincronização

Listagens aceitam page, limit (até 100) e updatedSince (ISO 8601 UTC, inclusivo). Para manter uma cópia local, guarde o maior updatedAt e grave por id com upsert.

Próximo passo: veja a referência completa de todos os endpoints e schemas, ou entre na lista de espera para receber acesso ao sandbox.