Voltar pro blog json-p.org

PilarRecuperação de vendas automática Publicado5 de agosto de 2026 AutorElton

Webhook Hotmart: todos os eventos e o que fazer com cada um

Todos os eventos do webhook Hotmart (aprovado, PIX pendente, recusa, reembolso, chargeback) e a regua certa pra disparar em cada um sem virar dev.

Cliente gerou o PIX às 14h. Não pagou. Ninguém falou com ele. Às 16h o código expirou e a venda evaporou. Você nem soube que ela existiu.

Cartão recusado por limite. Do lado da Hotmart isso é um estado bem definido. Do seu lado, é silêncio. O cara ia comprar, o banco travou, e ninguém mandou "tenta de novo com outro cartão ou PIX". Perdeu.

Pior: teve gente que pediu reembolso três semanas atrás e você continua mandando conteúdo de boas-vindas pra ela, porque a sua automação nunca ouviu falar de reembolso. Cada um desses momentos é um evento que a Hotmart te avisou em tempo real. Você só não estava escutando o evento certo.

O webhook da Hotmart é o cano por onde essas informações chegam. Reagir bem a cada evento é a diferença entre uma operação que recupera venda no automático e uma que só olha o dashboard no fim do dia pra ver o estrago.

A resposta curta

A Hotmart dispara um POST (payload versão 2.0.0) pra uma URL sua toda vez que o status de uma transação muda. O corpo é um JSON com um campo event (o nome do evento, tipo PURCHASE_APPROVED) e um campo data com os detalhes da compra. Reagir bem é fazer quatro coisas: identificar o evento certo, confiar só se o hottok bater, não processar o mesmo evento duas vezes, e disparar a régua do estado. O resto do texto é o detalhe de cada uma.

Webhook Hotmart eventos: o mapa de estados

Antes dos nomes, entenda o formato. Na versão 2.0.0 do webhook, todo evento chega com o mesmo envelope. Muda só o que vem dentro de data. Um payload de compra aprovada, mascarado, é mais ou menos assim:

{
  "id": "e8f9a0b1-2c3d-4e5f-6789-0abcdef12345",
  "creation_date": 1737120000000,
  "event": "PURCHASE_APPROVED",
  "version": "2.0.0",
  "data": {
    "product": {
      "id": 0000000,
      "name": "Seu Produto"
    },
    "buyer": {
      "email": "c****@email.com",
      "name": "Nome Mascarado",
      "checkout_phone": "+55XXXXXXXXXXX"
    },
    "purchase": {
      "transaction": "HP00000000000000",
      "status": "APPROVED",
      "approved_date": 1737120000000,
      "payment": {
        "type": "PIX",
        "installments_number": 1
      },
      "price": {
        "value": 197.00,
        "currency_value": "BRL"
      }
    },
    "affiliates": [],
    "commissions": []
  }
}

Os campos que você vai usar sempre: event (o que aconteceu), data.purchase.transaction (o ID único da compra, sua chave), data.purchase.status (o estado atual da transação) e data.buyer (pra quem falar). O id lá em cima é o ID do evento em si, e ele importa pra idempotência, que a gente vê mais pra frente.

Os eventos, um por um, e o que cada um quer dizer

Esses são os eventos de compra do webhook 2.0.0. Não invente nome, use exatamente estes:

E os de assinatura, se você vende recorrência: SUBSCRIPTION_CANCELLATION (assinatura cancelada), SWITCH_PLAN (troca de plano) e UPDATE_SUBSCRIPTION_CHARGE_DATE (mudança na data de cobrança). Tem ainda os de área de membros (CLUB_FIRST_ACCESS, CLUB_MODULE_COMPLETED), que servem mais pra engajamento do que pra recuperar venda.

Status da transação x nome do evento (não confunda)

Tem gente que roteia a régua pelo event e tem gente que roteia pelo data.purchase.status. Os dois existem e não são a mesma coisa. O event é a notificação ("mudou pra isso agora"). O status é o estado atual da transação. Os valores possíveis de status, na doc atual da Hotmart, são:

APPROVED            BLOCKED            CANCELLED
CHARGEBACK          COMPLETE           EXPIRED
NO_FUNDS            OVERDUE            PARTIALLY_REFUNDED
PRE_ORDER           PRINTED_BILLET     PROCESSING_TRANSACTION
PROTESTED           REFUNDED           STARTED
UNDER_ANALISYS      WAITING_PAYMENT

Dois detalhes que economizam horas: NO_FUNDS é a recusa por saldo/limite insuficiente (a "recusa de cartão" que você quer recuperar) e WAITING_PAYMENT é o PIX/boleto aguardando. E sim, UNDER_ANALISYS está escrito assim mesmo na Hotmart, com o erro de grafia. Copie exatamente, não "corrija" pra ANALYSIS, senão seu if nunca casa.

Como saber que o webhook é mesmo da Hotmart

Sua URL de webhook é pública. Qualquer um que descobrir o endereço pode mandar um POST falso dizendo "PURCHASE_APPROVED, libera o acesso". A Hotmart resolve isso com o hottok: um token único da sua conta. Na versão 2.0, ele chega no header X-HOTMART-HOTTOK. A regra é uma linha: se o hottok do header não for exatamente igual ao da sua configuração, você responde 200 e descarta, sem processar nada.

if request.header("X-HOTMART-HOTTOK") != MEU_HOTTOK:
    return 200        # responde ok, mas ignora
processa(payload)

Guarde o hottok como segredo de ambiente, nunca no código versionado. E responda 200 rápido mesmo pro que você descarta: webhook que recebe erro ou timeout entra em fila de retentativa, e aí o mesmo evento volta várias vezes (o que nos leva ao próximo problema).

Idempotência: o mesmo evento vai chegar mais de uma vez

Webhook não é entrega garantida uma vez só. É entrega garantida pelo menos uma vez. Se sua resposta demorou, se deu um 500, se a rede engasgou, a Hotmart reenvia. Se você não se proteger, o cliente recebe a mesma mensagem de "seu PIX está esperando" três vezes, e você parece um robô quebrado.

A defesa é idempotência: registre o id do evento (ou a dupla transaction + event) numa tabela antes de agir. Se já viu aquele id, ignora e responde 200.

-- pseudo-lógica
INSERT INTO eventos_vistos (evento_id) VALUES ($id)
ON CONFLICT (evento_id) DO NOTHING;
-- se não inseriu nada, é repetido: para aqui.

Sem isso, todo pico de tráfego (lançamento, dia de PIX em massa) vira disparo duplicado. É o erro que mais queima lista.

O que dá errado na prática

Os furos que eu mais vejo em operação de infoproduto na Hotmart:

A régua por estado: o que disparar pra cada evento

Aqui é onde o técnico vira dinheiro. Cada evento pede uma reação comercial diferente. Um mapa que funciona:

Quando essa régua sai no WhatsApp, que é onde o público brasileiro de infoproduto responde, vale decidir com cuidado por onde disparar. As opções e os trade-offs (bloqueio, custo, número oficial) estão em Evolution API vs Cloud API. E se você quiser saber de qual anúncio veio a venda que você recuperou, cuidado com a atribuição no meio do caminho: o motivo de o crédito se perder está em por que o UTM se perde no WhatsApp.

Fechando

O webhook da Hotmart não é integração de dev. É a lista de estados do seu funil chegando em tempo real. Quem só ouve "aprovado" opera no escuro pra tudo que dá errado antes e depois da venda. Quem mapeia os dez eventos, valida o hottok, garante idempotência e liga cada estado numa régua específica recupera venda enquanto dorme. O código é a parte fácil. A decisão de qual mensagem cada estado merece é o que separa uma operação que fatura de uma que só assiste o dashboard.