Pular para o conteúdo

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. 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 CNPJ
    curl -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. 2

    Envie o certificado A1

    O arquivo .pfx vai em base64. O SendNF confere CNPJ e validade e guarda cifrado.

    Envie o certificado A1
    curl -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. 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é-emita
    curl -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. 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.

    Emita
    curl -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étodoCaminhoPara quê
GET/v1/lookup/cnpj/:cnpjDados públicos da empresa, sistema da prefeitura e códigos de serviço sugeridos
POST/v1/companiesCadastra a empresa emitente (basta o CNPJ)
GET/v1/companies/:idEmpresa com dados públicos e checklist do que falta
PATCH/v1/companies/:idAtualiza dados e segredos (CSC, senha da prefeitura)
PUT/v1/companies/:id/certificateEnvia o certificado A1 (.pfx em base64 + senha)
GET/v1/companies/:id/readinessChecklist de emissão de NFS-e e NFC-e com guias
POST/v1/nfse/previewPré-emite a NFS-e sem transmitir
POST/v1/nfseEmite a NFS-e (aceita ?wait=true e Idempotency-Key)
POST/v1/nfce/previewPré-emite a NFC-e sem transmitir
POST/v1/nfceEmite a NFC-e
GET/v1/documentsLista notas com filtros (status, empresa, tipo, referência, datas)
GET/v1/documents/:idStatus e detalhes da nota
GET/v1/documents/:id/pdfPDF (DANFSe ou DANFC-e)
GET/v1/documents/:id/xmlXML autorizado
POST/v1/documents/:id/cancelCancela a nota autorizada
POST/v1/documents/:id/retryReenvia uma nota parada
GET/v1/service-codes?q=Busca na lista nacional de serviços (pública)
POST/v1/webhooksCria um endpoint de webhook (devolve o segredo uma vez)
GET/v1/usagePlano e uso do mês
POST/mcpServidor MCP (Streamable HTTP, mesma chave de API)

Teste e produção

Chaves nt_test_ emitem em homologação; nt_live_ emitem notas reais. Notas, webhooks e listagens nunca se misturam entre os modos.

Idempotência

Idempotency-Key em POST /v1/nfse e /v1/nfce: repetir a mesma chave devolve a mesma nota.

Limites

Até 600 requisições por minuto por chave, com rajada de 120. Acima disso, 429 com Retry-After.

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.

EventoQuando
document.createdA nota foi criada e entrou na fila
document.authorizedA Receita, a prefeitura ou a SEFAZ autorizou a nota
document.rejectedA nota foi rejeitada; o motivo vem explicado
document.blockedFalta um dado da empresa (certificado, inscrição…); a nota volta sozinha quando ele chega
document.contingencyNFC-e emitida em contingência porque a SEFAZ não respondeu
document.cancelledCancelamento registrado
document.cancel_deniedO órgão recusou o cancelamento
certificate.expiringO certificado A1 vence em 30, 15, 7 ou 1 dia
certificate.expiredO 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.

Terminal
claude mcp add --transport http sendnf https://api.sendnf.com/mcp \
  --header "Authorization: Bearer nt_test_..."

Todas as ferramentas e a configuração do Claude Desktop

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_.