AssinAPI

Webhooks

Eventos

envelope.created, envelope.sent, envelope.viewed, signer.authenticated, signer.signed, signer.declined, envelope.completed, envelope.cancelled, envelope.expired.

Assinatura

Cada requisição traz X-Signature: v1=<hex>, X-Event-Id e X-Timestamp. A assinatura é HMAC-SHA256 do texto {X-Event-Id}.{X-Timestamp}.{corpo bruto} com seu whsec_….

import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(raw: string, h: Headers, secret: string) {
  const ts = Number(h.get('x-timestamp'));
  if (Math.abs(Date.now() / 1000 - ts) > 300) return false; // replay
  const expected = 'v1=' + createHmac('sha256', secret).update(`${h.get('x-event-id')}.${ts}.${raw}`).digest('hex');
  const got = h.get('x-signature') ?? '';
  return got.length === expected.length && timingSafeEqual(Buffer.from(got), Buffer.from(expected));
}

Entrega e retries

  • Responda 2xx em até 10 s. Redirecionamentos não são seguidos.
  • Falhas são reenviadas com backoff exponencial; após o limite, a entrega vai para a dead letter (visível e reprocessável no dashboard).
  • Deduplique por X-Event-Id: o mesmo evento pode chegar mais de uma vez.
  • URLs precisam ser HTTPS públicas; endereços privados são bloqueados (proteção SSRF).

Payload

json
{
  "id": "whevt_…",
  "object": "event",
  "type": "envelope.completed",
  "environment": "SANDBOX",
  "createdAt": "2026-09-29T17:30:00.000Z",
  "data": {
    "envelope": { "id": "env_…", "status": "COMPLETED", "title": "Contrato", "externalId": "pedido-123" },
    "evidence": { "manifestHash": "d984ae…", "verificationCode": "K3M9-2QXA-7TZP", "verifyUrl": "https://assinapi.com.br/verify?code=…" },
    "documents": [{ "id": "doc_…", "filename": "contrato.pdf", "sha256": "…", "finalSha256": "…" }]
  }
}

Testando localmente

bash
npm install -g @assinapi/cli
assinapi login --token ask_test_…
assinapi listen --forward-to localhost:3000/api/webhooks/assinapi
assinapi trigger envelope.completed