API
API de nota fiscal feita para quem integra
JSON simples em https://api.sendnf.com, chaves separadas para teste e produção, pré-emissão para mostrar o erro na sua tela e webhooks assinados para saber o resultado.
Resposta curta: Para emitir uma NFS-e pelo SendNF, cadastre a empresa com POST /v1/companies (só o CNPJ), envie o certificado A1, pré-emita com POST /v1/nfse/preview e emita com POST /v1/nfse. A nota volta autorizada com PDF e XML.
Primeiros passos
Da chave de teste à primeira nota
- 1
Cadastre a empresa pelo CNPJ
Com uma chave de teste (nt_test_…), as notas saem em homologação, sem valor fiscal. O resto dos dados vem da Receita.
Cadastre a empresa pelo CNPJcurl -X POST https://api.sendnf.com/v1/companies \ -H "Authorization: Bearer $SENDNF_KEY" \ -H "Content-Type: application/json" \ -d '{ "cnpj": "11.222.333/0001-81", "default_service_code": "010101", "default_iss_rate": 200 }' - 2
Envie o certificado A1
O arquivo .pfx vai em base64. O SendNF confere CNPJ e validade e guarda cifrado.
Envie o certificado A1curl -X PUT https://api.sendnf.com/v1/companies/$EMPRESA/certificate \ -H "Authorization: Bearer $SENDNF_KEY" \ -H "Content-Type: application/json" \ -d "{ \"pfx_base64\": \"$(base64 -w0 certificado.pfx)\", \"password\": \"$SENHA_PFX\" }" - 3
Pré-emita
Mesmo corpo da emissão. Nada é transmitido; você recebe os problemas com guia e, com certificado, a validação no layout oficial.
Pré-emitacurl -X POST https://api.sendnf.com/v1/nfse/preview \ -H "Authorization: Bearer $SENDNF_KEY" \ -H "Content-Type: application/json" \ -d '{ "company_id": "'$EMPRESA'", "taker": { "document": "529.982.247-25", "name": "Maria Silva" }, "items": [{ "description": "Consultoria", "unit_amount": 50000 }] }' - 4
Emita
Com ?wait=true a resposta espera até cerca de 25 segundos pelo resultado. A Idempotency-Key garante que a mesma nota não sai duas vezes.
Emitacurl -X POST "https://api.sendnf.com/v1/nfse?wait=true" \ -H "Authorization: Bearer $SENDNF_KEY" \ -H "Idempotency-Key: pedido-1042" \ -H "Content-Type: application/json" \ -d '{ "company_id": "'$EMPRESA'", "reference": "pedido-1042", "taker": { "document": "529.982.247-25", "name": "Maria Silva", "email": "maria@exemplo.com" }, "items": [{ "description": "Consultoria", "unit_amount": 50000 }] }'
SDK e resposta
Do seu código para a nota autorizada
O SDK TypeScript do SendNF tipa entrada e saída, transforma erros em exceções com o campo que falhou e verifica a assinatura dos webhooks.
import { SendNF } from '@sendnf/sdk';
const sendnf = new SendNF({ apiKey: process.env.SENDNF_KEY! });
const nota = await sendnf.nfse.create(
{
company_id: empresaId,
reference: pedido.id,
taker: { document: cliente.cnpj, name: cliente.nome, email: cliente.email },
items: [{ description: 'Mensalidade do plano Pro', unit_amount: 7900 }],
},
{ idempotencyKey: pedido.id, waitSeconds: 20 },
);
console.log(nota.status, nota.nfse_number, nota.urls.pdf);{
"id": "0192f1c5-0a11-7e42-8b6d-1c9e4f2a7b30",
"object": "nfse",
"mode": "live",
"status": "authorized",
"reference": "pedido-1042",
"nfse_number": "1042",
"route": "nacional",
"total": 150000,
"urls": {
"pdf": "https://api.sendnf.com/v1/documents/0192f1c5-…/pdf",
"xml": "https://api.sendnf.com/v1/documents/0192f1c5-…/xml",
"public_query": "https://www.nfse.gov.br/ConsultaPublica/?tpc=1&chave=…"
}
}HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": {
"code": "fiscal_profile_invalid",
"message": "CPF ou CNPJ do tomador inválido.",
"field": "taker.document"
}
}Referência
Endpoints
| Método | Caminho | Para quê |
|---|---|---|
| GET | /v1/lookup/cnpj/:cnpj | Dados públicos da empresa, sistema da prefeitura e códigos de serviço sugeridos |
| POST | /v1/companies | Cadastra a empresa emitente (basta o CNPJ) |
| GET | /v1/companies/:id | Empresa com dados públicos e checklist do que falta |
| PATCH | /v1/companies/:id | Atualiza dados e segredos (CSC, senha da prefeitura) |
| PUT | /v1/companies/:id/certificate | Envia o certificado A1 (.pfx em base64 + senha) |
| GET | /v1/companies/:id/readiness | Checklist de emissão de NFS-e e NFC-e com guias |
| POST | /v1/nfse/preview | Pré-emite a NFS-e sem transmitir |
| POST | /v1/nfse | Emite a NFS-e (aceita ?wait=true e Idempotency-Key) |
| POST | /v1/nfce/preview | Pré-emite a NFC-e sem transmitir |
| POST | /v1/nfce | Emite a NFC-e |
| GET | /v1/documents | Lista notas com filtros (status, empresa, tipo, referência, datas) |
| GET | /v1/documents/:id | Status e detalhes da nota |
| GET | /v1/documents/:id/pdf | PDF (DANFSe ou DANFC-e) |
| GET | /v1/documents/:id/xml | XML autorizado |
| POST | /v1/documents/:id/cancel | Cancela a nota autorizada |
| POST | /v1/documents/:id/retry | Reenvia uma nota parada |
| GET | /v1/service-codes?q= | Busca na lista nacional de serviços (pública) |
| POST | /v1/webhooks | Cria um endpoint de webhook (devolve o segredo uma vez) |
| GET | /v1/usage | Plano e uso do mês |
| POST | /mcp | Servidor MCP (Streamable HTTP, mesma chave de API) |
Teste e produção
Idempotência
Limites
Webhooks
Saiba na hora quando a nota sai
Os eventos são assinados no padrão Standard Webhooks (headers webhook-id, webhook-timestamp e webhook-signature) e reenviados com espera crescente se o seu endpoint falhar.
| Evento | Quando |
|---|---|
| document.created | A nota foi criada e entrou na fila |
| document.authorized | A Receita, a prefeitura ou a SEFAZ autorizou a nota |
| document.rejected | A nota foi rejeitada; o motivo vem explicado |
| document.blocked | Falta um dado da empresa (certificado, inscrição…); a nota volta sozinha quando ele chega |
| document.contingency | NFC-e emitida em contingência porque a SEFAZ não respondeu |
| document.cancelled | Cancelamento registrado |
| document.cancel_denied | O órgão recusou o cancelamento |
| certificate.expiring | O certificado A1 vence em 30, 15, 7 ou 1 dia |
| certificate.expired | O certificado A1 venceu |
POST /webhooks/sendnf HTTP/1.1
webhook-id: 7c1d9a52-3f0e-4c1b-9d6e-2a8f4b7e1c90
webhook-timestamp: 1792512000
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
{
"id": "7c1d9a52-3f0e-4c1b-9d6e-2a8f4b7e1c90",
"type": "document.authorized",
"mode": "live",
"created_at": "2026-10-20T14:32:05.120Z",
"data": { "id": "0192f1c5-…", "object": "nfse", "status": "authorized", "reference": "pedido-1042", "nfse_number": "1042" }
}import { verifyWebhook } from '@sendnf/sdk';
app.post('/webhooks/sendnf', express.text({ type: '*/*' }), async (req, res) => {
const ok = await verifyWebhook({
secret: process.env.SENDNF_WEBHOOK_SECRET!,
id: req.header('webhook-id')!,
timestamp: req.header('webhook-timestamp')!,
signature: req.header('webhook-signature')!,
body: req.body,
});
if (!ok) return res.sendStatus(400);
const event = JSON.parse(req.body);
if (event.type === 'document.authorized') await marcarNotaEmitida(event.data.reference, event.data.urls.pdf);
res.sendStatus(204);
});MCP
A mesma API, para agentes de IA
O servidor MCP do SendNF expõe 21 ferramentas com a mesma chave de API: consultar CNPJ, buscar código de serviço, pré-emitir, emitir, cancelar. No Claude Code basta um comando.
claude mcp add --transport http sendnf https://api.sendnf.com/mcp \
--header "Authorization: Bearer nt_test_..."Perguntas de quem integra
Como testar a API sem emitir nota de verdade?
Use uma chave nt_test_. Tudo o que ela emite vai para o ambiente de homologação (sem valor fiscal), e a homologação é ilimitada em todos os planos, inclusive no grátis.
Como evitar nota duplicada quando a requisição falha?
Envie o header Idempotency-Key com um identificador seu (o id do pedido, por exemplo). Repetir a mesma chave devolve a mesma nota, com o header Idempotent-Replayed: true, em vez de criar outra.
A API é síncrona?
A emissão entra numa fila e é transmitida em seguida. Com ?wait=true a resposta espera até cerca de 25 segundos pelo resultado; sem ele, a resposta volta na hora com status pending e o resultado chega por webhook.
Em que formato vão os valores?
Sempre em centavos, como inteiros: R$ 1.500,00 é 150000. A alíquota de ISS vai em centésimos de ponto percentual: 200 é 2%.
Como sei por que uma nota foi rejeitada?
O campo rejections traz o código e a mensagem do órgão, já com uma explicação em português para os erros mais comuns. Antes de emitir, a pré-emissão (POST /v1/nfse/preview) aponta a maioria desses problemas sem transmitir nada.
Dá para emitir pelos meus clientes (multiempresa)?
Sim. Uma conta tem várias empresas emitentes, cada uma com o próprio certificado, e cada nota informa o company_id. Os planos Pro e Scale incluem 5 e 30 CNPJs.
Pegue uma chave de teste agora
Homologação ilimitada e grátis. Quando a integração estiver pronta, troque nt_test_ por nt_live_.