Fluxos de uso
Guias ponta a ponta dos cenários mais comuns da API. Os exemplos usam curl e a
base https://api.conttrole.io; troque pela sua URL de integração se tiver um
domínio próprio. Toda chamada autenticada leva o header
Authorization: Bearer ck_live_sua_chave (ver Autenticação).
O detalhe de cada campo (tipos, enums, obrigatoriedade) está na Referência da API. Aqui o foco é a ordem das chamadas.
1. Emitir uma nota fiscal
A emissão tem dois momentos: criar o documento (rascunho, síncrono) e emitir (processamento assíncrono junto ao fisco). Você acompanha pelo status.
Passo 0 — pré-requisitos
- Cliente cadastrado: o
clientIdé de um cliente que já existe na sua empresa. O cadastro de clientes é feito no painel (ainda não há endpoint de clientes na API). Anote oiddo cliente. - Empresa configurada: certificado digital e configurações fiscais válidas no painel — sem isso a emissão é rejeitada.
Passo 1 — criar o documento
curl -X POST https://api.conttrole.io/v1/fiscal-documents \
-H "Authorization: Bearer ck_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{
"type": "NFE",
"clientId": "cli_xxx",
"operationNature": "Venda de mercadoria",
"items": [
{ "code": "P1", "description": "Produto 1", "cfop": "5102",
"ncm": "61091000", "unit": "UN", "quantity": 2, "unitValue": 50.0 }
],
"payments": [
{ "method": "PIX", "value": 100.0 }
]
}'Resposta 201 — o documento nasce em DRAFT (ainda sem número definitivo):
{ "id": "doc_abc", "type": "NFE", "status": "DRAFT", "number": 0, "runId": null }Passo 2 — emitir
curl -X POST https://api.conttrole.io/v1/fiscal-documents/doc_abc/emit \
-H "Authorization: Bearer ck_live_sua_chave"Resposta 202 com runId — o documento entra em PROCESSING e o trabalho roda
em segundo plano:
{ "documentId": "doc_abc", "status": "PROCESSING", "runId": "run_..." }Atalho: envie
"emit": trueno corpo do passo 1 para criar e emitir numa só chamada. A resposta 201 já volta comstatus: "PROCESSING"e orunId.
Passo 3 — acompanhar o status
Há duas formas de saber o desfecho da emissão. Prefira webhooks: cadastre um
endpoint uma vez e a API te avisa com um POST assinado (document.authorized /
document.rejected) assim que a nota muda de estado — sem ficar consultando.
Veja Receber eventos via webhook.
Quando webhooks não forem viáveis, use polling como alternativa: consulte o
documento até sair de PROCESSING.
curl https://api.conttrole.io/v1/fiscal-documents/doc_abc \
-H "Authorization: Bearer ck_live_sua_chave"status | Significado |
|---|---|
DRAFT | Rascunho, ainda não emitido |
PROCESSING | Em emissão (aguarde) |
AUTHORIZED | Autorizada pelo fisco — tem accessKey |
REJECTED | Rejeitada — veja rejectionReason; corrija e emita de novo |
CANCELLED | Cancelada |
Passo 4 — baixar XML e DANFE
Depois de AUTHORIZED:
# XML autorizado (application/xml)
curl https://api.conttrole.io/v1/fiscal-documents/doc_abc/xml \
-H "Authorization: Bearer ck_live_sua_chave" -o nota.xml
# DANFE/DANFSe em PDF (application/pdf)
curl https://api.conttrole.io/v1/fiscal-documents/doc_abc/danfe \
-H "Authorization: Bearer ck_live_sua_chave" -o nota.pdf2. Definir os impostos da nota
Há três formas de preencher os impostos dos itens, nesta prioridade:
-
Explícito no item — você manda os campos tributários direto:
{ "code": "P1", "description": "Produto 1", "cfop": "5102", "unit": "UN", "quantity": 1, "unitValue": 100, "icmsSituation": "00", "icmsRate": 18, "pisSituation": "01", "pisRate": 1.65 } -
Regra tributária — referencie uma regra por
taxRuleId, no documento (vale pra todos os itens) ou no item (sobrepõe a do documento):{ "type": "NFE", "clientId": "cli_xxx", "taxRuleId": "rule_abc", "items": [ { "code": "P1", "description": "...", "cfop": "5102", "unit": "UN", "quantity": 1, "unitValue": 100 } ] }A regra é casada por UF do destinatário + tipo de cliente. Crie/liste regras em
/v1/tax-rules(ver fluxo 3). -
Configuração da empresa — sem imposto no item e sem
taxRuleId, vale a configuração tributária padrão da empresa. NCM ausente também herda o da empresa.
O
taxRuleIdprecisa ser de uma regra da sua empresa — caso contrário a criação responde 422invalid_tax_rule.
3. Criar e usar regras tributárias
Regras tributárias evitam repetir impostos em cada nota. Requer escopo
tax-rules:write para criar e tax-rules:read para listar.
Criar uma regra (NF-e)
curl -X POST https://api.conttrole.io/v1/tax-rules \
-H "Authorization: Bearer ck_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{
"name": "Venda dentro de SP",
"model": "NFE",
"nfeTaxRules": [
{ "states": ["SP"], "clientTypes": ["CONTRIBUINTE"],
"icmsCst": "00", "icmsPIcms": 18,
"pisCst": "01", "pisPPis": 1.65,
"cofinsCst": "01", "cofinsPCofins": 7.6 }
]
}'Resposta 201 com o id da regra — use esse id como taxRuleId na criação
da nota (fluxo 2).
Listar regras
curl "https://api.conttrole.io/v1/tax-rules?model=NFE&isActive=true" \
-H "Authorization: Bearer ck_live_sua_chave"
PATCH /v1/tax-rules/{id}atualiza a regra — enviarnfeTaxRules/nfseTaxRulessubstitui integralmente as sub-regras daquele modelo.DELETE /v1/tax-rules/{id}faz soft delete.
4. Cancelar uma nota autorizada
Síncrono — a API chama o fisco e devolve o resultado. A justificativa tem de ter 15 a 255 caracteres (regra SEFAZ) e o cancelamento respeita o prazo legal.
curl -X POST https://api.conttrole.io/v1/fiscal-documents/doc_abc/cancel \
-H "Authorization: Bearer ck_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{ "justification": "Cancelamento a pedido do cliente." }'5. Corrigir uma nota (Carta de Correção)
Para NF-e/NFC-e autorizadas: evento 110110, síncrono. Texto de 15 a 1000 caracteres, prazo de 30 dias, máximo de 20 correções. Não corrige valores fiscais nem dados de emitente/destinatário, e não se aplica a NFS-e.
curl -X POST https://api.conttrole.io/v1/fiscal-documents/doc_abc/correction-letter \
-H "Authorization: Bearer ck_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{ "correction": "Correção do endereço de entrega do produto." }'6. Inutilizar uma faixa de numeração
Quando um intervalo de números de NF-e/NFC-e foi pulado e precisa ser declarado como inutilizado:
curl -X POST https://api.conttrole.io/v1/fiscal-inutilizations \
-H "Authorization: Bearer ck_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{ "series": 1, "startNumber": 10, "endNumber": 20,
"justification": "Numeração pulada por falha de sistema." }'7. Receber eventos via webhook (sem polling)
Em vez de consultar o status repetidamente, cadastre um endpoint e receba um
POST assinado quando a nota muda de estado. Requer escopo webhooks:write para
criar e webhooks:read para listar/consultar entregas.
Passo 1 — criar o endpoint
curl -X POST https://api.conttrole.io/v1/webhooks \
-H "Authorization: Bearer ck_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{
"url": "https://seu-app.com/webhooks/conttrole",
"description": "Eventos fiscais de produção",
"events": ["DOCUMENT_AUTHORIZED", "DOCUMENT_REJECTED"]
}'urldeve ser HTTPS (em produção) e pública — IPs privados/internos são bloqueados (proteção anti-SSRF).eventsfiltra o que você recebe; vazio = todos. Eventos disponíveis:DOCUMENT_AUTHORIZED,DOCUMENT_REJECTED,DOCUMENT_CANCELLED,DOCUMENT_INUTILIZED.
Resposta 201 — o secret (whsec_…) vem uma única vez; guarde-o para
validar as assinaturas:
{ "id": "ep_abc", "url": "https://seu-app.com/webhooks/conttrole",
"events": ["DOCUMENT_AUTHORIZED", "DOCUMENT_REJECTED"],
"isActive": true, "secret": "whsec_xxxxxxxx" }Perdeu o
secret? Gere outro comPOST /v1/webhooks/{id}/rotate-secret(o antigo deixa de valer).
Passo 2 — validar a assinatura no seu endpoint
Cada entrega traz o header X-Conttrole-Signature: t=<timestamp>,v1=<hmac>, onde
hmac é o HMAC-SHA256 de "<timestamp>.<corpo-cru>" usando o seu secret.
Recalcule e compare (comparação em tempo constante):
import crypto from "node:crypto";
function isValid(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(
signatureHeader.split(",").map((kv) => kv.split("=")),
);
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(parts.v1),
);
}Responda 2xx rapidamente. Em falha (timeout ou status ≠ 2xx) a entrega é reagendada automaticamente com backoff (até 20 tentativas / 24h).
Passo 3 — testar e inspecionar entregas
# Dispara um evento de teste para o endpoint
curl -X POST https://api.conttrole.io/v1/webhooks/ep_abc/test \
-H "Authorization: Bearer ck_live_sua_chave"
# Histórico de entregas (status, tentativas)
curl https://api.conttrole.io/v1/webhooks/ep_abc/deliveries \
-H "Authorization: Bearer ck_live_sua_chave"
# Reenviar manualmente uma entrega que falhou
curl -X POST https://api.conttrole.io/v1/webhooks/ep_abc/deliveries/del_xyz/redeliver \
-H "Authorization: Bearer ck_live_sua_chave"Para editar (PATCH /v1/webhooks/{id}), remover (DELETE) e os demais detalhes
de segurança e payload dos eventos, veja Webhooks.