Ajuda / API e programadores

Webhooks de saída

Formato dos eventos enviados para os seus sistemas e como verificar a assinatura.

Formato

POST com Content-Type: application/jsonjson
{
  "event": "purchase.approved",
  "id": "hotmart:HP123456789",
  "app_id": "9b1f…",
  "occurred_at": "2026-10-12T10:31:00.000Z",
  "contact": {
    "email": "ana@exemplo.pt",
    "first_name": "Ana",
    "last_name": "Silva",
    "phone": null,
    "consent": true,
    "tags": [
      "cliente"
    ]
  },
  "product_ids": [
    "4c2e…"
  ],
  "lesson_id": null,
  "amount_cents": 9700,
  "currency": "EUR",
  "checkout_url": null,
  "data": {}
}

Cabeçalhos: X-Event (o tipo do evento), X-Signature (se definiu um segredo) e User-Agent: Zurian-Webhooks/1.0. O campo id é estável: use-o para ignorar entregas repetidas.

Verificar a assinatura

Node.jsts
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(rawBody: string, header: string | null, secret: string) {
  const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  return !!header && header.length === expected.length &&
    timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}

Calcule o HMAC sobre o corpo tal como chegou, antes de o converter de JSON.

Entregas e repetições

Responda com 2xx em menos de 15 segundos. Erros 5xx, 408, 429 e falhas de rede são repetidos com espera crescente; outros 4xx não. Os redirecionamentos não são seguidos e o URL tem de ser HTTPS público.

Eventos

EventoQuando
member.joinedNovo aluno entrou na app
purchase.approvedCompra aprovada
access.revokedAcesso retirado (reembolso/chargeback)
lesson.completedAula concluída
course.completedCurso concluído
quiz.passedQuiz aprovado
quiz.failedQuiz reprovado
member.inactiveAluno inativo há X dias
tag.addedEtiqueta adicionada
checkout.abandonedCarrinho abandonado
payment.pendingPagamento pendente (transferência, voucher ou referência)
payment.failedPagamento recusado/expirado
subscription.past_dueSubscrição com pagamento em atraso
subscription.canceledSubscrição cancelada
subscription.plan_changedMudança de plano
access.expiringAcesso a terminar
post.createdPublicação na comunidade
funnel.completedQuiz de captação concluído