Pular para o conteúdo

Referência · v1

API do KontNotas

REST sobre JSON. Toda a base em /api/v1. Esta página documenta apenas endpoints que existem hoje — o que está no roteiro fica fora dela até entrar no ar.

Autenticação

Chave de API no cabeçalho Authorization, formato Bearer. Cada chave pertence a uma organização e carrega escopos — que são as mesmas permissões do produto. Uma chave sem invoice.issue pode criar rascunho, mas não emite.

Requisição
curl https://app.kontnotas.com.br/api/v1/invoices \
  -H "Authorization: Bearer kn_live_SUA_CHAVE_AQUI"

A chave é exibida uma única vez, no momento da criação. O servidor guarda apenas o hash — não há como recuperá-la depois, só gerar outra. Crie e revogue em Desenvolvedores → API.

Ambientes

O prefixo da chave define o ambiente: kn_test_ opera em sandbox, kn_live_ em produção. Uma chave de produção só funciona depois que a organização estiver efetivamente em produção — caso contrário a requisição é recusada com 422, em vez de emitir algo simulado como se fosse real.

Idempotência

Emissão de nota fiscal não pode ser repetida por acidente. Envie Idempotency-Key em todo POST que cria documento — um UUID por tentativa lógica, reutilizado nas repetições da mesma tentativa.

Comportamento
Mesma chave + mesmo corpo   → devolve a resposta original (não cria de novo)
Mesma chave + corpo diferente → 409 KONTNOTAS_IDEMPOTENCY_CONFLICT
Mesma chave, ainda em curso   → 409 (aguarde e consulte)

Timeout não significa falha. Se a conexão cair antes da resposta, repita a requisição com a mesma chave — nunca com uma nova.

Erros

Todo erro devolve o mesmo envelope, com código estável, motivo e a ação sugerida. O campo requestId também vai no cabeçalho X-Request-Id e é o que permite correlacionar com o log do servidor no suporte.

422 Unprocessable Entity
{
  "error": {
    "code": "KONTNOTAS_FISCAL_CONFIG_INCOMPLETE",
    "message": "A alíquota de ISS não está definida para este serviço.",
    "reason": "O serviço svc_01J... não tem issRateBps e o perfil fiscal não define padrão.",
    "action": "Defina a alíquota em Serviços ou em Configurações › Fiscal.",
    "fields": null,
    "requestId": "req_01J8XQ2K7M4N5P6Q7R8S9T0V"
  }
}

Erros de validação trazem fields preenchido, com a mensagem por campo. Segredos e detalhes internos nunca aparecem no envelope — eles vão para o log correlacionado.

Limites de uso

O limite varia por plano e é aplicado por chave. Toda resposta traz os cabeçalhos RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset. Ao estourar, a resposta é 429 com Retry-After.

Notas fiscais

POST/api/v1/invoicesescopo invoice.create

Cria a nota e, por padrão, já solicita a emissão.

O cliente pode ser informado por customerId ou por customer.document — integrações costumam conhecer o CNPJ, não o id interno. Passe "issue": false para deixar em rascunho.

Corpo
{
  "customer": { "document": "11.222.333/0001-81" },
  "documentType": "NFSE",
  "description": "Consultoria de marketing — agosto/2026",
  "competenceDate": "2026-08-31",
  "items": [
    {
      "description": "Gestão de mídias sociais",
      "quantityMilli": 1000,
      "unitPriceCents": 350000,
      "unit": "UN"
    }
  ],
  "issue": true
}

Valores são inteiros em centavos e quantidades em milésimos (1 unidade = 1000). Não há ponto flutuante em lugar nenhum da API — é o que garante que R$ 0,01 nunca desapareça num arredondamento.

GET/api/v1/invoicesescopo invoice.view

Lista as notas da organização, com filtros por status, período e cliente.

Resposta
{
  "object": "list",
  "data": [ { "object": "invoice", "id": "inv_01J...", "status": "authorized", … } ],
  "total": 128,
  "hasMore": true
}
GET/api/v1/invoices/{id}escopo invoice.view

Retorna uma nota com itens, tributos e dados de autorização.

O status segue a máquina de estados do documento: draft → pending → processing → authorized, com rejected, error e cancelled como desfechos. Enquanto estiver em processing, consulte — não reemita.

POST/api/v1/invoices/{id}/cancelescopo invoice.cancel

Solicita o cancelamento, com motivo obrigatório.

O prazo e as regras de cancelamento são do município, não do KontNotas. Quando a nota está fora do prazo, a resposta explica isso em vez de tentar e falhar.

Clientes

POST/api/v1/customersescopo customer.manage

Cadastra um tomador. CPF e CNPJ são validados por dígito verificador.

Corpo
{
  "name": "Clínica Bella Estética Ltda.",
  "documentType": "CNPJ",
  "document": "11222333000181",
  "email": "financeiro@clinicabella.com.br",
  "address": {
    "street": "Avenida Paulista",
    "number": "1000",
    "district": "Bela Vista",
    "cityCode": "3550308",
    "state": "SP",
    "zipCode": "01310200"
  }
}
GET/api/v1/customersescopo customer.view

Lista e busca clientes por nome ou documento.

Webhooks

Em vez de consultar em laço, receba os eventos. Configure os endpoints em Desenvolvedores → Webhooks, onde também ficam o histórico de entregas e o reenvio manual.

Cada entrega é assinada em X-KontNotas-Signature com HMAC-SHA256. O timestamp faz parte da mensagem assinada — o que impede replay de uma entrega capturada. Sempre valide a assinatura antes de agir sobre o payload, e compare em tempo constante.

Verificação (Node.js)
import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(rawBody, header, secret) {
  const [tsPart, sigPart] = header.split(',');
  const timestamp = tsPart.replace('t=', '');
  const received = Buffer.from(sigPart.replace('v1=', ''), 'hex');

  // O timestamp entra na mensagem: sem isso, uma entrega antiga
  // continuaria com assinatura válida para sempre.
  const expected = createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest();

  const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300;
  return fresh && received.length === expected.length && timingSafeEqual(received, expected);
}

Responda 2xx rapidamente e processe depois: entregas sem resposta são reenviadas com backoff. Trate os eventos como at least once — use o id do evento para descartar repetições.