Webhooks
Como a emissão é assíncrona por padrão, o status final chega no seu ERP via webhook HTTP POST com payload JSON e assinatura HMAC.
Configurar
- Portal Notare → Webhooks → Novo endpoint
- URL HTTPS pública do seu ERP
- Escolher eventos de interesse
- Salvar o signing secret — usado pra validar a assinatura
Eventos
nfse.autorizadaeventNFS-e foi autorizada pela prefeitura.
nfse.rejeitadaeventValidação ou regra de negócio falhou. Veja
erros[].nfse.canceladaeventCancelamento confirmado.
nfse.substituidaeventSubstituição efetivada (nova nota emitida).
nfse.falha_definitivaeventEsgotou retries automáticos sem sucesso.
Payload
POST {sua URL}
{
"event": "nfse.autorizada",
"timestamp": "2026-06-13T22:00:00Z",
"idIntegracao": "OS-30046339",
"nfse": {
"id": "5fcb9e1d-...",
"status": "autorizada",
"numero": "20119",
"chaveAcesso": "20119-7493120626...",
"numeroRps": "33472",
"serieRps": "13",
"urls": {
"pdf": "https://api.notare.nexosos.com/v1/nfse/5fcb9e1d-.../pdf",
"xml": "https://api.notare.nexosos.com/v1/nfse/5fcb9e1d-.../xml"
}
}
}
Use
idIntegracao pra correlacionarO idIntegracao está sempre no payload (quando você informou na emissão). Use-o como chave no seu ERP — você não precisa guardar o id UUID da Notare.
Headers do webhook
| Header | Valor |
|---|---|
Content-Type | application/json |
User-Agent | Notare-Webhook/1.0 |
X-Notare-Event | Ex: nfse.autorizada |
X-Notare-Delivery-Id | UUID único da entrega (útil pra log) |
X-Notare-Signature | HMAC-SHA256 — ver abaixo |
Validação HMAC
O header X-Notare-Signature traz o timestamp e a assinatura:
X-Notare-Signature: t=1718289600,v1=5257a869e7ecebeda32affa62cdca3fa...
Node.js
import crypto from 'crypto';
function validarAssinatura(rawBody, header, secret) {
const [tPart, v1Part] = header.split(',');
const timestamp = tPart.split('=')[1];
const assinatura = v1Part.split('=')[1];
// Rejeita webhooks com mais de 5 min (replay protection)
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const payload = `${timestamp}.${rawBody}`;
const esperada = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(assinatura),
Buffer.from(esperada),
);
}
Python
import hmac, hashlib, time
def validar_assinatura(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split('=', 1) for p in header.split(','))
timestamp = parts['t']
assinatura = parts['v1']
# Replay protection (5 min)
if abs(time.time() - int(timestamp)) > 300:
return False
payload = f"{timestamp}.{raw_body.decode()}".encode()
esperada = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(assinatura, esperada)
Retry automático
Se seu endpoint responder com HTTP ≠ 2xx, a Notare retenta com backoff exponencial:
| Tentativa | Atraso desde a anterior |
|---|---|
| 1 | imediato |
| 2 | +30s |
| 3 | +2min |
| 4 | +5min |
| 5 | +30min |
| 6 | +2h |
| 7 | +6h |
| 8 | +24h |
Após 8 tentativas, marca como delivery_failed. Reentrega manual via portal ou POST /v1/webhooks/{id}/replay.
Idempotência no seu ERP
A Notare pode entregar o mesmo evento mais de uma vez (timeout do seu lado, retry, etc.). Trate idempotentemente:
async function handle(payload) {
const chave = `${payload.event}:${payload.nfse.id}`;
const jaProcessado = await db.eventos.findUnique({ where: { eventoId: chave } });
if (jaProcessado) return; // OK, ignora
// ... processa ...
await db.eventos.create({ data: { eventoId: chave } });
}
Boas práticas
- Responda em < 5s — faça processamento pesado em fila depois de gravar recebimento
- Log o
X-Notare-Delivery-Id— suporte rastreia entregas por ele - Use HTTPS — webhooks HTTP são rejeitados em produção
- IP allowlist (opcional) — webhooks saem de
212.85.0.117/32. Consulte a status page pra mudanças.