Documentação da API
A API do Emitti abstrai a complexidade de emitir notas fiscais de serviço no Brasil. Você envia JSON; nós cuidamos da fila, retentativas, assinatura digital (XMLDSig), envelopamento SOAP e do webhook de confirmação. Toda emissão é assíncrona.
Introdução
Base URL: https://api.emitti.com.br/v1. Respostas em JSON e códigos HTTP padrão. O fluxo é sempre: você faz o POST, recebe 202 Accepted na hora, e o resultado final chega por webhook (ou consulta).
codigo_municipio (IBGE) define a prefeitura.Autenticação
Autentique com sua API key via Bearer token. Chaves sk_test_ usam o sandbox; sk_live_ emitem de verdade. Gere e revogue chaves no painel.
Authorization: Bearer sk_live_a1b2c3d4...Quickstart
Três passos para a primeira nota:
- No painel, gere uma API key e cadastre um emitente com o certificado A1 (.pfx).
- Faça o
POST /v1/nfsecom os dados do serviço. - Receba o resultado no seu webhook (ou consulte por
GET).
Sandbox
Chaves sk_test_ processam em sandbox: respostas determinísticas, sem tocar a prefeitura nem exigir certificado. Use para testar sua integração de ponta a ponta sem custo.
- Por padrão a nota é autorizada (número
SANDBOX-*). - Gatilho de rejeição: tomador com documento todo zeros (ex.
00000000000000) → respostaREJECTED, para você exercitar o tratamento de erro.
{
"status": "REJECTED",
"sandbox": true,
"resultado": {
"codigo_emitti": "tomador_documento_invalido",
"mensagem": "[sandbox] CPF/CNPJ do tomador inválido (gatilho de teste)."
}
}Emitir uma NFS-e
POST/v1/nfse
Headers
| Header | Obrigatório | Descrição |
|---|---|---|
Authorization | Sim | Bearer da sua API key. |
Idempotency-Key | Recomendado | UUID único — evita nota duplicada em retry. |
Exemplo — cURL
curl -X POST https://api.emitti.com.br/v1/nfse \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7f3a-...-91b" \
-d '{
"referencia_externa": "pedido-2026-00871",
"prestador": { "cnpj": "12345678000190", "inscricao_municipal": "1122334" },
"tomador": {
"razao_social": "Cliente Exemplo LTDA",
"cnpj": "98765432000110",
"email": "financeiro@cliente.com.br"
},
"servico": {
"codigo_municipio": "3550308",
"codigo_servico": "01.05",
"discriminacao": "Assinatura mensal do plano SaaS - Junho/2026.",
"valor_servicos": 499.90,
"aliquota_iss": 2.0,
"iss_retido": false
}
}'Exemplo — Node.js
const res = await fetch("https://api.emitti.com.br/v1/nfse", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.EMITTI_API_KEY,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
prestador: { cnpj: "12345678000190", inscricao_municipal: "1122334" },
tomador: { razao_social: "Cliente Exemplo LTDA", cnpj: "98765432000110" },
servico: {
codigo_municipio: "3550308",
codigo_servico: "01.05",
discriminacao: "Assinatura mensal do plano SaaS",
valor_servicos: 499.9,
aliquota_iss: 2.0,
},
}),
});
const emissao = await res.json(); // { emissao_id, status: "QUEUED" }Resposta — 202 Accepted
{
"emissao_id": "emi_8f3a9c2e1b7d4f60",
"status": "QUEUED",
"referencia_externa": "pedido-2026-00871",
"created_at": "2026-06-24T14:30:00Z"
}Estados possíveis: QUEUED → PROCESSING → AUTHORIZED | REJECTED | FAILED_INTERNAL.
Consultar uma emissão
GET/v1/nfse/{emissao_id}
Útil para reconciliação. Recomendamos confiar nos webhooks como fonte primária.
{
"emissao_id": "emi_8f3a9c2e1b7d4f60",
"status": "AUTHORIZED",
"resultado": {
"numero_nfse": "00012845",
"codigo_verificacao": "ABCD-1234"
}
}Cancelar uma NFS-e
DELETE/v1/nfse/{emissao_id}
Assíncrono: gera o cancelamento na prefeitura. Só é possível cancelar uma nota AUTHORIZED. O resultado chega por webhook (nfse.canceled).
Substituir uma NFS-e
POST/v1/nfse/{emissao_id}/substituicao
Emite uma nova nota (corpo igual ao de POST /v1/nfse) referenciando a antiga; ao autorizar a nova, a antiga é cancelada automaticamente (exigência de muitas prefeituras).
Baixar PDF e XML
GET/v1/nfse/{emissao_id}/pdf— DANFE/RPS renderizado
GET/v1/nfse/{emissao_id}/xml— XML autorizado
Webhooks
Quando a emissão chega a um estado final, enviamos um POST para a URL configurada no painel. Todo webhook traz o header X-Emitti-Signature (HMAC-SHA256 do corpo). Sempre valide a assinatura antes de confiar no payload.
Payload — autorizada
{
"event_type": "nfse.authorized",
"data": {
"emissao_id": "emi_8f3a9c2e1b7d4f60",
"status": "AUTHORIZED",
"nfse": { "numero": "00012845", "codigo_verificacao": "ABCD-1234",
"url_pdf": "https://files.emitti.com.br/...pdf" }
}
}Payload — rejeitada (erro traduzido)
{
"event_type": "nfse.rejected",
"data": {
"emissao_id": "emi_2d1c0b9a8f7e",
"status": "REJECTED",
"erro": {
"codigo_emitti": "tomador_documento_invalido",
"mensagem": "O CNPJ do tomador é inválido ou não está na Receita Federal.",
"sugestao": "Verifique o CNPJ '98765432000110' e reenvie.",
"retentavel": false
}
}
}codigo_emitti (estável), mensagem humana, sugestao e o flag retentavel.Verificando a assinatura — Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
function verificar(req) {
const assinatura = req.headers["x-emitti-signature"];
const esperado = createHmac("sha256", process.env.EMITTI_WEBHOOK_SECRET)
.update(req.rawBody)
.digest("hex");
return timingSafeEqual(Buffer.from(assinatura), Buffer.from(esperado));
}Erros
| Código | Significado | Causa típica |
|---|---|---|
400 | Bad Request | JSON malformado ou campo inválido. |
401 | Unauthorized | API key ausente, inválida ou revogada. |
402 | Payment Required | Limite do plano atingido. |
409 | Conflict | Idempotency-Key reutilizada com payload diferente. |
422 | Unprocessable | Dado semanticamente inválido (ex.: CNPJ com DV errado, emitente inexistente). |
429 | Too Many Requests | Rate limit excedido. |
5xx | Server Error | Falha interna do Emitti (não sua). |
emitti · infraestrutura fiscal para o Brasil