Pular para o conteúdo principal
Você vende um curso ou produto digital num gateway externo (Hotmart, Kiwify, Eduzz, Stripe Checkout) e quer que a compra automaticamente libere acesso a um grupo, curso ou espaço dentro da sua comunidade Cativa. Este guia mostra a arquitetura recomendada, o que dá pra fazer hoje e o que está em desenvolvimento.

Cenário

Cliente compra “Curso Premium” na Hotmart. Em segundos, ele recebe acesso ao grupo “Alunos Premium” e ao curso na sua comunidade Cativa. A ponte entre os dois lados é o conceito de badge como permissão: o badge Premium é configurado no Console como requisito de acesso ao grupo e ao curso. Quando o usuário ganha o badge, o acesso aparece automaticamente. Quando perde, some.

Pré-requisitos

  1. API Key da Cativa — gerada no Console (Developers > API Keys). Veja Quick Start: API Key.
  2. Badge configurado no Console — crie o badge Premium (ou o nome do seu produto) e configure-o como requisito de acesso no grupo/curso correspondente. Veja Badges como permissão.
  3. Webhook de compra do seu gateway — Hotmart, Kiwify, Eduzz e Stripe disparam webhook pra um endpoint do seu servidor quando uma compra é confirmada. Não aponte o webhook do gateway diretamente pra Cativa — você precisa de um servidor proxy que recebe, valida e traduz o evento.
  4. Email confiável do comprador — todos os gateways enviam o email no payload da compra. Esse é o ponto-de-junção com o usuário Cativa.

Arquitetura do fluxo

   Cliente compra na Hotmart


   Webhook Hotmart ──► Seu servidor (proxy) ──► API Cativa (atribui badge)


                                         Webhook Cativa user_received_badge


                                              Email/CRM/Analytics
Você é o intermediário entre o gateway e a Cativa. Isso te dá controle pra:
  • Validar o webhook do gateway (cada um tem assinatura própria — confira a doc do gateway).
  • Tratar idempotência (Hotmart pode disparar o mesmo webhook duas vezes).
  • Logar o purchase ID do gateway pra auditoria/conciliação.
  • Decidir o badge correto baseado em qual produto foi comprado.
E você também pode opcionalmente subscrever o webhook da Cativa user_received_badge pra disparar email de boas-vindas, atualizar CRM, ou registrar evento no analytics.

Implementação

1

Receber o webhook do gateway

Cada gateway tem seu próprio formato de webhook e seu próprio mecanismo de assinatura. Configure o webhook no painel do gateway apontando pra um endpoint do seu servidor (ex: https://meuapp.com/webhooks/hotmart).A Cativa não documenta o formato do webhook desses gateways — consulte a doc oficial:Esqueleto do receiver (Express, exemplo Hotmart):
import express from 'express';

const app = express();

app.post(
  '/webhooks/hotmart',
  express.json(),
  async (req, res) => {
    // 1. Verifique a assinatura do gateway aqui (seguindo a doc do gateway)

    const event = req.body;
    // Hotmart usa events do tipo "PURCHASE_APPROVED", "PURCHASE_CANCELED", etc.

    if (event.event === 'PURCHASE_APPROVED') {
      await handlePurchase({
        email: event.data.buyer.email,
        productId: event.data.product.id,
        purchaseId: event.data.purchase.transaction
      });
    }

    if (event.event === 'PURCHASE_CANCELED' ||
        event.event === 'PURCHASE_REFUNDED' ||
        event.event === 'PURCHASE_CHARGEBACK') {
      await handleCancellation({
        email: event.data.buyer.email,
        productId: event.data.product.id,
        purchaseId: event.data.purchase.transaction
      });
    }

    // Responda 2xx logo — processe em background se possível.
    res.status(200).send('ok');
  }
);
Sempre verifique a assinatura do webhook do gateway antes de confiar nos dados. Sem isso, qualquer um que descobrir sua URL pode liberar badges arbitrários.
2

Identificar o usuário na Cativa pelo email

Hoje há duas situações:Caso A — o comprador já tem conta na Cativa: você precisa achar o User.Id dele.
O endpoint público para parceiros buscarem usuário arbitrário pelo email está em desenvolvimento. Hoje, esse lookup usa um endpoint admin que não está disponível para API Keys de parceiro.Workaround recomendado: mantenha uma tabela local email → cativa_user_id populada pelo webhook user_created da Cativa. Toda vez que alguém entra na comunidade, você grava a linha. Quando a compra chegar, faz lookup local sem bater na API.
async function resolveCativaUserId(email) {
  const row = await db.userMap.findOne({ email });
  return row?.cativaUserId ?? null;
}
Caso B — o comprador ainda não tem conta na Cativa: você precisa fazer ele se cadastrar primeiro.
Criação de usuário direto via API Key de parceiro está em desenvolvimento. Workaround atual: envie um email com o link de cadastro do tenant + um lembrete de que o badge será atribuído assim que ele entrar. Quando ele se cadastrar, o webhook user_created chega no seu servidor e você completa o fluxo (ver step 4).
async function inviteBuyer({ email, productId, purchaseId }) {
  // Salva a intenção de atribuição pra completar quando user_created chegar
  await db.pendingGrants.insert({
    email,
    productId,
    purchaseId,
    createdAt: new Date()
  });

  // Envia email com link de cadastro do tenant
  await mailer.send({
    to: email,
    subject: 'Seu acesso ao Curso Premium',
    text: `Cadastre-se em https://{customerName}.cativa.digital/signup?email=${encodeURIComponent(email)} para liberar seu acesso.`
  });
}
3

Atribuir o badge

Mapeie o productId do gateway pro badgeId da Cativa configurado no Console.
A atribuição de badge via API Key de parceiro está em desenvolvimento. Hoje, atribuição/remoção de badges é feita no Console (manualmente ou via importação) ou via fluxos automáticos da plataforma. Quando o endpoint público estiver disponível, esta página será atualizada com o cURL completo. Pra liberar o seu caso enquanto isso, abra ticket em dev@cativa.digital.
Esquema mental do que vai parecer:
const PRODUCT_TO_BADGE = {
  'hotmart_product_123': 'badge_uuid_premium',
  'hotmart_product_456': 'badge_uuid_mentoria_2026'
};

async function handlePurchase({ email, productId, purchaseId }) {
  const badgeId = PRODUCT_TO_BADGE[productId];
  if (!badgeId) {
    console.warn(`No badge mapped for product ${productId}`);
    return;
  }

  const cativaUserId = await resolveCativaUserId(email);
  if (!cativaUserId) {
    await inviteBuyer({ email, productId, purchaseId });
    return;
  }

  // Quando o endpoint estiver disponível:
  const res = await fetch(
    `https://apis.cativalab.digital/tenant/v1/.../badges/${badgeId}/users/${cativaUserId}`,
    {
      method: 'POST',
      headers: { Authorization: `Bearer ${process.env.CATIVA_API_KEY}` }
    }
  );
  if (!res.ok) throw new Error(`Badge assign failed: ${res.status}`);

  await db.purchases.insert({
    purchaseId,
    cativaUserId,
    badgeId,
    grantedAt: new Date()
  });
}
A Cativa garante que atribuir o mesmo badge duas vezes é idempotente (ver Badges como permissão) — então retries causados por timeout ou re-disparo do gateway não criam efeito duplicado.
4

(Opcional) Reagir ao webhook user_received_badge

A Cativa dispara o webhook user_received_badge toda vez que um badge é atribuído (não importa se foi via API, Console, ou outro fluxo). Cadastre um listener para ele se você quer:
  • Mandar email de boas-vindas com link pra comunidade
  • Atualizar o status do contato no CRM
  • Registrar conversão no analytics
Veja Cadastrando e verificando webhooks para o passo-a-passo de cadastro do listener e verificação HMAC. O payload do evento está em user_received_badge.Esqueleto do receiver:
import express from 'express';
import crypto from 'crypto';

const SECRET = process.env.CATIVA_WEBHOOK_SECRET; // whsec_...

app.post(
  '/webhooks/cativa',
  express.raw({ type: 'application/json' }),
  async (req, res) => {
    // Verificação HMAC — ver /webhooks/subscribing-and-verifying
    const sigHeader = req.header('X-Cativa-Signature') ?? '';
    const parts = Object.fromEntries(
      sigHeader.split(',').map(p => p.split('='))
    );
    const ts = Number(parts.t);
    const sig = parts.v1;
    if (Math.abs(Date.now() / 1000 - ts) > 300) return res.status(400).end();
    const expected = crypto
      .createHmac('sha256', SECRET)
      .update(`${ts}.${req.body.toString('utf8')}`)
      .digest('hex');
    if (!crypto.timingSafeEqual(
      Buffer.from(expected, 'hex'),
      Buffer.from(sig, 'hex')
    )) return res.status(401).end();

    const event = JSON.parse(req.body.toString('utf8'));

    if (event.BadgeName === 'Premium') {
      await sendWelcomeEmail(event.User.Email, event.User.FirstName);
      await analytics.track('Purchase Activated', {
        email: event.User.Email,
        badge: event.BadgeName
      });
    }

    res.status(200).send('ok');
  }
);

Cancelamento e chargeback

Quando o gateway cancela ou faz chargeback, você quer remover o badge pra revogar o acesso.
async function handleCancellation({ email, productId, purchaseId }) {
  const badgeId = PRODUCT_TO_BADGE[productId];
  if (!badgeId) return;

  const cativaUserId = await resolveCativaUserId(email);
  if (!cativaUserId) return;

  // Quando o endpoint estiver disponível:
  await fetch(
    `https://apis.cativalab.digital/tenant/v1/.../badges/${badgeId}/users/${cativaUserId}`,
    {
      method: 'DELETE',
      headers: { Authorization: `Bearer ${process.env.CATIVA_API_KEY}` }
    }
  );

  await db.purchases.update({ purchaseId }, { revokedAt: new Date() });
}
A remoção de badge via API Key de parceiro está no mesmo waitlist da atribuição. Mesmo workaround: hoje é feito no Console; o endpoint público sai em breve.
Remover o badge é o suficiente — o acesso ao grupo, curso e espaços atrelados ao badge some imediatamente.

Erros comuns

Esse é o caso mais comum. O cliente comprou na Hotmart antes de entrar na sua comunidade.Solução: o step 2 acima cobre — registre a intenção em pendingGrants e dispare o convite. Subscreva o webhook user_created e, quando o cadastro acontecer, complete a atribuição:
// Listener do user_created
if (event.event === 'user_created') {
  const pending = await db.pendingGrants.findAll({ email: event.User.Email });
  for (const grant of pending) {
    await handlePurchase({
      email: event.User.Email,
      productId: grant.productId,
      purchaseId: grant.purchaseId
    });
    await db.pendingGrants.delete({ id: grant.id });
  }
}
Acontece quando o cliente compra com email pessoal e entra na comunidade com email corporativo. Não tem solução automática.Solução: ofereça uma página “Já comprei, mas estou logado com outro email” no seu app, onde o cliente informa o email da compra. Você valida o purchase ID localmente e atribui o badge pro usuário logado (não pro email da compra).
A atribuição de badge na Cativa é idempotente: aplicar o mesmo badge duas vezes resulta no mesmo estado final. Mas por garantia, salve o purchaseId do gateway numa tabela local e cheque antes:
if (await db.purchases.exists({ purchaseId })) {
  return; // já processado
}
Isso também ajuda a auditar/conciliar mais tarde (ex: relatório financeiro vs concessões).
Cada gateway dispara webhook quando a renovação é cobrada com sucesso (ex: Hotmart SUBSCRIPTION_CHARGE_SUCCESS). Trate como um handlePurchase idempotente — re-aplica o badge (sem efeito se já está). Se a renovação falha (ex: cartão recusado), trate como handleCancellation.
Você precisa reagir ao chargeback rapidamente — o webhook do gateway chega, você remove o badge na hora, acesso aos recursos atrelados some. Não confie em job batch noturno pra isso.
Mapeie um produto pra vários badges se necessário. Exemplo: produto Curso Premium libera tanto Premium (acesso ao curso) quanto Mentoria-2026 (acesso ao grupo de mentoria). Faça duas atribuições no handlePurchase.

Próximos passos

Webhooks da Cativa

Cadastre listeners para user_created (cobrir caso “comprou antes de cadastrar”) e user_received_badge (disparar ações pós-acesso).

Sincronizar membros do CRM

Se você também usa CRM, combine este fluxo com sincronização de tags pra ter um único hub de permissões.