Pular para o conteúdo

Webhooks

Como avisar automaticamente outro sistema quando um aluno é criado, ganha acesso ou perde acesso na sua plataforma.

Nesta página

Um webhook é um aviso automático: quando algo acontece na sua plataforma (um aluno novo, um acesso liberado, um acesso retirado), ela envia na hora uma requisição POST com os dados do evento para um endereço do seu sistema. Assim o seu CRM, sua automação de WhatsApp ou seu ERP ficam sabendo sem precisar consultar a API.

Exige conhecimento técnico

Para receber webhooks você precisa de um endereço na internet preparado para receber requisições (um sistema próprio ou uma ferramenta de automação que ofereça "webhook de entrada").

Criar um webhook

  1. No painel, acesse Configurações › Webhooks.
  2. Clique em Novo webhook.
  3. Em Para onde enviar, preencha o Endpoint (o endereço que vai receber os avisos, começando com http:// ou https://) e, se quiser, uma Descrição (ex.: "automação de boas-vindas").
  4. Em Quando enviar, marque os eventos que devem gerar um aviso. Use Marcar todos para marcar um grupo inteiro.
  5. Em O que cada envio leva, escolha quanto dado pessoal vai em cada aviso (veja Dados pessoais no envio).
  6. Clique em Criar webhook.

Logo após criar, a plataforma mostra o Segredo de assinatura (começa com whsec_). Ele aparece só nessa hora: copie e guarde, porque é com ele que o seu sistema confere se o aviso veio mesmo da sua plataforma (veja Verificar a assinatura).

Gerenciar os webhooks

Na aba Endpoints, cada webhook mostra o endereço, os eventos marcados e o status (Ativo ou Desativado). No menu ··· de cada um você pode:

  • Editar: mudar endereço, descrição, eventos, nível de dados e ligar ou desligar o webhook (caixa Ativo). Na mesma tela, Gerar novo segredo de assinatura troca o segredo (o anterior deixa de valer na hora).
  • Enviar teste: manda um aviso de teste para o endereço (veja Testar).
  • Desativar / Ativar: pausa os envios sem apagar a configuração.
  • Excluir: remove o webhook.

Eventos disponíveis

Hoje a plataforma dispara estes eventos:

EventoNome na telaQuando dispara
user.createdAluno criadoUm aluno novo é cadastrado por uma venda (postback de qualquer gateway ou a rota de Entrega da API) ou pelo cadastro gratuito
enrollment.createdAcesso liberadoO aluno ganha acesso: por uma venda aprovada, pelo cadastro gratuito, por liberação manual no painel ou por importação
enrollment.revokedAcesso revogadoUm acesso é encerrado: venda cancelada, reembolsada, com chargeback ou em disputa, assinatura cancelada ou em atraso, ou retirada manual no painel
Informação

Alunos criados à mão no painel ou pela importação ainda não disparam user.created. O acesso liberado para eles dispara enrollment.created normalmente.

Outros eventos da tela

A tela de criação já lista os eventos abaixo, mas eles ainda não são enviados. Se você marcar algum, nada é disparado por enquanto.

  • Cadastro: Tags do aluno atualizadas (user.tags_updated), Campo personalizado atualizado (user.custom_field_updated).
  • Acesso: Primeiro acesso (user.first_accessed), Acesso suspenso ou reativado (enrollment.updated), Acesso restaurado (enrollment.restored).
  • Progresso: Curso iniciado (product_progress.started), Aula concluída (lesson_progress.completed), Marco de progresso atingido (product_progress.milestone_reached), Progresso atualizado (product_progress.updated), Curso concluído (product_progress.completed).
  • Provas: Prova concluída (exam_attempt.completed), Tentativa de prova reaberta (exam_attempt.reopened).
  • Certificados: Certificado emitido (certificate.issued), Certificado revogado (certificate.revoked).
  • Termos: Termo aceito (term_acceptance.created).
  • Pontos: Pontos creditados (score.created), Pontos ajustados (score.adjusted).
  • Atendimento: Chamado aberto (ticket.created), Chamado respondido (ticket.reply_created), Chamado encerrado (ticket.closed), Dúvida enviada (question.created), Dúvida respondida (question.reply_published), Comentário enviado (comment.created), Comentário respondido (comment.reply_created).

Formato do envio

Cada aviso é um POST com corpo JSON e estes cabeçalhos:

CabeçalhoValor
Content-Typeapplication/json
User-AgentASALA-Hub-Webhooks/1.0
X-Webhook-Signaturesha256=<assinatura> (veja Verificar a assinatura)

O corpo sempre tem o mesmo envelope:

CampoDescrição
idIdentificador único do envio. Use para ignorar avisos repetidos
typeNome do evento (ex.: enrollment.created)
created_atQuando o evento aconteceu (ISO-8601, horário de Brasília)
instanceIdentificador da sua plataforma (o subdomínio)
dataOs dados do evento

O objeto user

Os três eventos trazem o aluno em data.user:

CampoDescrição
idID do aluno (o mesmo usado na API)
name, email, document, phoneDados do cadastro
magic_login_urlLink de login automático do aluno
access.first_at / access.last_atPrimeiro e último login (null se nunca entrou), cada um com unix e iso
created.onOrigem do cadastro: sale (venda), api, import, free (cadastro gratuito) ou manual
created.atData do cadastro, com unix e iso

Exemplo: user.created

{
  "id": "01JA9K3V2M4F7C1D8E2B5A6C9D",
  "type": "user.created",
  "created_at": "2026-10-15T10:42:07-03:00",
  "instance": "escola",
  "data": {
    "user": {
      "id": 1234,
      "name": "Maria Silva",
      "email": "maria@email.com",
      "document": "242.863.810-64",
      "phone": "+5521999990000",
      "magic_login_url": "https://escola.asalahub.com/auth/login?crstk=Xy7...",
      "access": { "first_at": null, "last_at": null },
      "created": {
        "on": "sale",
        "at": { "unix": "1792064527", "iso": "2026-10-15T10:42:07-03:00" }
      }
    },
    "created": { "on": "sale" }
  }
}

No cadastro gratuito, data.created vem como { "on": "free", "method": "form" }.

Exemplo: enrollment.created (por venda)

Quando o acesso vem de uma venda, o aviso é enviado uma vez por entrega e traz a entrega e a venda:

{
  "id": "01JA9K3V2N8H2K4M6P1Q3R5S7T",
  "type": "enrollment.created",
  "created_at": "2026-10-15T10:42:07-03:00",
  "instance": "escola",
  "data": {
    "user": {
      "id": 1234,
      "name": "Maria Silva",
      "email": "maria@email.com",
      "document": "242.863.810-64",
      "phone": "+5521999990000",
      "magic_login_url": "https://escola.asalahub.com/auth/login?crstk=Xy7...",
      "access": { "first_at": null, "last_at": null },
      "created": {
        "on": "sale",
        "at": { "unix": "1792064527", "iso": "2026-10-15T10:42:07-03:00" }
      }
    },
    "delivery": {
      "id": 21,
      "type": "single",
      "name": "Atendimento que Encanta - Kiwify",
      "config_url": "https://escola.asalahub.com/dashboard/sales/deliveries/21/edit",
      "external_ids": ["a1b2c3d4"]
    },
    "created": { "on": "sale", "by": null },
    "sale": { "id": 845, "code": "PEDIDO-1001", "engine": "kiwify" }
  }
}

delivery.type é single (venda única) ou subscription (assinatura). created.on pode ser sale ou api (quando a venda veio pela rota /entrega/enviar).

Exemplo: enrollment.created (manual, gratuito ou importação)

Quando o acesso é liberado no painel, pelo cadastro gratuito ou pela importação, o aviso é enviado uma vez por curso e traz o curso e o acesso:

{
  "id": "01JA9K7B4C6D8E0F2G4H6J8K0M",
  "type": "enrollment.created",
  "created_at": "2026-10-15T11:05:30-03:00",
  "instance": "escola",
  "data": {
    "user": { "id": 1234, "name": "Maria Silva", "email": "maria@email.com", "...": "..." },
    "product": {
      "id": 12,
      "order": 1,
      "name": "Atendimento que Encanta",
      "offer_url": null,
      "group": { "id": 4, "order": 1, "name": "Treinamentos" }
    },
    "enrollment": {
      "id": 3310,
      "product_id": 12,
      "delivery_id": null,
      "source": "manual",
      "started_at": "2026-10-15T11:05:30-03:00",
      "ends_at": "2027-10-15T11:05:30-03:00"
    },
    "created": { "on": "manual", "by": 1 }
  }
}

enrollment.source (e created.on) é manual, free ou import. ends_at nulo significa acesso vitalício. Em created.by vem o ID do administrador que liberou à mão. product.group é a vitrine do curso.

Exemplo: enrollment.revoked

Enviado uma vez para cada acesso encerrado:

{
  "id": "01JA9M2Q8R0S2T4V6W8X0Y2Z4A",
  "type": "enrollment.revoked",
  "created_at": "2026-10-20T09:12:44-03:00",
  "instance": "escola",
  "data": {
    "user": { "id": 1234, "name": "Maria Silva", "email": "maria@email.com", "...": "..." },
    "product": {
      "id": 12,
      "order": 1,
      "name": "Atendimento que Encanta",
      "offer_url": null,
      "group": { "id": 4, "order": 1, "name": "Treinamentos" }
    },
    "enrollment": {
      "id": 3309,
      "product_id": 12,
      "delivery_id": 21,
      "ended_at": "2026-10-20T09:12:44-03:00",
      "reason": "refunded"
    }
  }
}

Valores comuns de reason: refunded (reembolso), canceled (cancelamento), chargeback, dispute (reembolso solicitado ou disputa), subscription_canceled (assinatura cancelada), subscription_late (assinatura em atraso) e admin (retirado à mão no painel).

Dados pessoais no envio

Em O que cada envio leva você escolhe, para todos os eventos daquele webhook, como os dados do aluno chegam:

OpçãoO que acontece em data.user
Só identificadores, sem dados pessoais (padrão)Nome, e-mail, documento, telefone e link de login não são enviados. Chegam o id, as datas de acesso e a origem do cadastro
Com dados pessoais mascaradosNome, e-mail, documento e telefone chegam escondidos em parte (ex.: ma**@e**.com, Ma** Si**). O link de login não é enviado
Completo, com dados pessoaisTodos os dados como estão no cadastro, incluindo magic_login_url

Os dados que não são pessoais (nome do curso, da entrega, códigos da venda) sempre vão completos. Os exemplos acima usam a opção Completo.

Dica

Se o seu sistema só precisa saber que algo aconteceu, fique com Só identificadores e busque o restante pela API usando o id do aluno. É a opção mais segura.

Verificar a assinatura

Todo envio leva o cabeçalho X-Webhook-Signature com o valor sha256= seguido de um HMAC-SHA256 do corpo da requisição exatamente como chegou, calculado com o seu segredo de assinatura e escrito em hexadecimal. Se a assinatura que você calcular for igual à recebida, o aviso veio da sua plataforma e não foi alterado no caminho.

Exemplo em Node.js (Express):

import express from 'express';
import crypto from 'node:crypto';

const app = express();
const SEGREDO = process.env.WEBHOOK_SECRET; // o whsec_... mostrado ao criar o webhook

// Importante: ler o corpo cru, antes de qualquer conversão para JSON
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const recebida = String(req.get('X-Webhook-Signature') || '');
  const esperada = 'sha256=' + crypto.createHmac('sha256', SEGREDO).update(req.body).digest('hex');

  const a = Buffer.from(recebida);
  const b = Buffer.from(esperada);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).send('assinatura inválida');
  }

  const evento = JSON.parse(req.body.toString('utf8'));
  // Use evento.id para não processar o mesmo aviso duas vezes
  console.log(evento.type, evento.data);

  res.sendStatus(200);
});

app.listen(3000);
Atenção

Calcule a assinatura sobre o corpo bruto. Se você converter para objeto e depois para texto de novo, a ordem ou os espaços podem mudar e a assinatura não vai bater. A assinatura não inclui data e hora, por isso guarde os id já processados e ignore repetidos.

Respostas, novas tentativas e histórico

  • O seu endereço deve responder com um código 2xx (por exemplo, 200) em até 10 segundos. Qualquer outro código, falta de resposta ou redirecionamento (3xx) conta como falha.
  • Em caso de falha, a plataforma tenta de novo automaticamente, até 5 tentativas no total: a 2ª cerca de 30 segundos depois, a 3ª após 2 minutos, a 4ª após 10 minutos e a 5ª após 1 hora. Os envios pendentes ficam guardados e continuam mesmo se o servidor reiniciar.
  • Desativar ou excluir o webhook cancela os envios que ainda estavam esperando nova tentativa.

Na aba Histórico ficam os envios dos últimos 60 dias, com data, evento, endereço, código de resposta e número de tentativas. Filtre por Sucesso, Pendentes ou Com falha e clique em Ver envio para ver o corpo enviado e a resposta do seu sistema.

Testar um webhook

No menu ··· do webhook, clique em Enviar teste. A plataforma manda um aviso para o endereço com o primeiro evento marcado, assinado com o seu segredo, e o resultado aparece no Histórico. O teste é enviado uma vez só (sem novas tentativas) e tem este formato:

{
  "id": "TESTE1792064527000",
  "type": "user.created",
  "test": true,
  "data": {}
}

Use o campo test para não tratar esse aviso como um evento real.

Webhook legado

O botão Novo webhook legado cria um webhook no formato antigo (versão 2), para integrações que já esperam esse formato. Nele os dados vão sempre completos e o corpo é:

{
  "version": "2",
  "event": "delivery.add",
  "event_id": "01JA9K3V2N8H2K4M6P1Q3R5S7T",
  "payload": { "user": { }, "delivery": { }, "created": { }, "sale": { } }
}

O conteúdo de payload é o mesmo descrito nos exemplos acima. A assinatura, as tentativas e o histórico funcionam do mesmo jeito.

Eventos legados que são enviados hoje:

Evento legadoCorresponde a
user.created (Aluno criado)user.created
user.created.free (Aluno gratuito criado)user.created
delivery.add (Entrega adicionada)enrollment.created
Aluno gratuito criado

No momento, user.created.free é enviado para todo aluno novo, não só os do cadastro gratuito. Para saber se o aluno é gratuito, confira payload.user.created.on (vale free no cadastro gratuito).

Outros eventos legados

Aluno progresso (user.progress), Aula concluída (class.concluded), Curso iniciado (course.initiated), Curso concluído (course.concluded), Curso progresso (course.progress), Curso 50%, 75% e 90% (course.progress.50, .75, .90), Prova aprovado (exam.approved), Prova reprovado (exam.reproved), Certificado emitido (certificado.emitido) e Termo assinado (user.term.signed) ainda não são enviados.

Avisos de vendas recebidas (postback)

Os webhooks desta tela são de saída: a sua plataforma avisando outro sistema. Para o caminho contrário, quando um gateway de pagamento avisa a sua plataforma sobre uma venda, cada entrega tem o seu próprio endereço de postback, mostrado na tela da entrega:

https://<sua-plataforma>/api/postback/<gateway>/<token>

Veja como configurar em Entregas e integrações e, para integrações próprias, em Entrega Customizada.

Esta página ajudou?

Artigos relacionados