external-id, atribuição de badge) e quais as decisões de design (chave natural, conflitos, idempotência) que você precisa tomar.
Cenário
Seu time de marketing usa HubSpot. Cada vez que um lead é qualificado e ganha a tagPago Anual lá, você quer que essa pessoa apareça na sua comunidade Cativa com o badge Premium (que dá acesso ao grupo VIP, ao curso pago, etc.). Quando a tag é removida (ou o cliente cancela), o badge precisa sair.
A Cativa é a fonte de verdade da comunidade (quem está no grupo, quem completou qual lição, quem postou o quê). O CRM é a fonte de verdade da relação comercial (lead, oportunidade, cliente, churn). A integração faz a ponte entre os dois.
Pré-requisitos
- API Key da Cativa: gerada no Console (Developers > API Keys). Veja Quick Start: API Key. Todas as chamadas abaixo usam
Authorization: Bearer cativa_live_.... - Acesso de API ao seu CRM: token do HubSpot, da RD Station, etc. A Cativa não fornece SDK pra esses CRMs; use os SDKs oficiais (
@hubspot/api-client,pipedrive-node-sdk, etc.). - Um servidor de integração rodando do seu lado: pode ser um worker Node, um Cloud Function/Lambda, um job no Airflow, qualquer coisa que execute código com acesso aos dois lados.
Mapeamento conceitual
A pergunta central é: como reconhecer que o contato1234 no HubSpot é o mesmo usuário 01HQ7Z3X4Y... na Cativa?
A chave natural recomendada é o
email (ela está dos dois lados desde o cadastro). Depois do primeiro casamento, cacheie o vínculo dos dois lados: guarde o Contact ID do CRM no usuário Cativa com PATCH /admin/users/{userId}/external-id e, se quiser, guarde o User ID Cativa numa propriedade custom do CRM (ex: cativa_user_id). Assim você para de depender de lookup por email a cada execução.
A Cativa tem um campo de ID externo no User, gravável via
PATCH /admin/users/{userId}/external-id. Use-o como a forma canônica de vincular a chave do seu CRM ao usuário Cativa. Com o vínculo persistido no próprio usuário, a tabela de mapeamento local (descrita abaixo) vira opcional (só uma otimização de cache do seu lado).Dois fluxos possíveis
- Pull-based (worker periódico)
- Webhook-based (event-driven)
Quando usar: você não tem webhook do CRM disponível, ou os volumes são pequenos (até alguns milhares de contatos), ou um delay de minutos/horas é aceitável.Um worker do seu lado roda numa frequência (ex: a cada 15 minutos), lê o estado do CRM, lê o estado da Cativa, calcula o diff e aplica.Prós: simples de implementar, fácil de debugar, fácil de fazer backfill.
Contras: delay (não é real-time), gasto desnecessário de API calls quando nada mudou.
Descobrir e vincular um usuário existente
O casamento pelo email é o coração da sincronização. Faça uma vez por contato e persista o vínculo no próprio usuário Cativa.1
Resolva o email para o usuário Cativa
GET /admin/users/email/{email} retorna o usuário com aquele email no tenant da sua chave. Este é o caminho canônico da sincronização CRM.200 OK (ilustrativa; schema completo na API Reference, tag Users):404 Not Found, trate esse caso como “criar usuário” (seção abaixo):2
Grave o Contact ID do CRM no usuário Cativa
Capture o Resposta Com o vínculo persistido no próprio usuário Cativa, os próximos syncs não precisam mais refazer o lookup por email.
id do retorno e, logo em seguida, grave o Contact ID do seu CRM no usuário com PATCH /admin/users/{userId}/external-id:200 OK (ilustrativa) com o vínculo já refletido:GET /tenant/api/v2/auth/me retorna o usuário dono da chave e o customer (tenant) ao qual ela pertence:
200 OK (ilustrativa):
Opcional: tabela de mapeamento local como cache
Oexternal-id no usuário Cativa já resolve o vínculo. Se ainda assim você quiser evitar toda ida à API da Cativa (por latência ou custo), mantenha uma tabela de cache do seu lado:
- Cadastre um listener Cativa pro evento
user_created. Toda vez que alguém entra na comunidade, o webhook chega comUser.IdeUser.Email. Insere/atualiza essa linha. - Cadastre um listener pro evento
user_received_badge. Toda mudança de badge atualiza a linha.
email para cativa_user_id localmente. Trate a tabela como cache: a fonte de verdade do vínculo continua sendo o external-id gravado no usuário Cativa.
Criar usuário a partir do CRM
Quando o contato do CRM ainda não existe na Cativa (o lookup por email retorna 404), crie o usuário comPOST /admin/users. Aproveite e já grave o externalId no mesmo request:
201 Created (ilustrativa), guarde o id (e o externalId já vinculado) para os próximos syncs:
Atribuir badge
A atribuição de badge por API Key administrativa está disponível. Há duas formas, sob/admin/membership/badges:
Por email (ideal pro CRM, dispensa resolver o userId antes):
userId Cativa em cache):
200 OK (ilustrativa; schema completo na API Reference, tag Badge):
DELETE no mesmo recurso por id:
A Cativa garante que atribuir o mesmo badge duas vezes é idempotente, o estado final é o mesmo (a segunda chamada responde sucesso sem duplicar). Mesma coisa pra remover badge que já não está atribuído. Isso simplifica retries no seu worker (ver Badges como permissão).
Esqueleto de worker pull-based
Com os endpoints de write disponíveis, o worker abaixo roda ponta a ponta. As funçõesassignBadge/removeBadge (definidas acima) já batem na API real.
Tratar conflitos
Erros comuns
O sync derrubou badges atribuídos por outras fontes
O sync derrubou badges atribuídos por outras fontes
Quando o seu worker é a única autoridade sobre um badge e ele não vê motivo no CRM, ele remove. Mas se o badge foi atribuído por outra fonte (compra paywall, importação manual), o sync apaga indevidamente.Solução: mantenha uma lista de “badges gerenciados pelo CRM” no seu worker. Só atribua e remova esses. Badges fora da lista são ignorados pelo diff.
Rate limit ao processar muitos contatos
Rate limit ao processar muitos contatos
Em syncs grandes (milhares de contatos), você pode bater rate limit no CRM ou na Cativa. Implemente:
- Backoff exponencial em 429 (a Cativa respeita
Retry-After). - Paginação no CRM (ex:
getPage(100, after, ...)no HubSpot). - Paralelismo controlado (ex:
p-limit(5)no Node).
Webhook do user_created não chegou, usuário ficou fora da tabela de mapeamento
Webhook do user_created não chegou, usuário ficou fora da tabela de mapeamento
Se você só popula
crm_cativa_mapping via webhook, qualquer disparo perdido vira lacuna. Por isso recomendamos:- Um reconcile job semanal que pega todos os contatos do CRM com email e tenta dar match com os usuários conhecidos.
- Logging do
X-Cativa-Execution-Idem todo webhook recebido, se faltar, dá pra abrir ticket de investigação.
Email diferente entre CRM e Cativa do mesmo usuário
Email diferente entre CRM e Cativa do mesmo usuário
Acontece quando o usuário usa email pessoal pra comprar e email corporativo pra entrar na comunidade (ou vice-versa). Não tem solução automática, você precisa uma propriedade adicional no CRM (ex:
community_email) e usar essa pro lookup em vez do email principal.Próximos passos
Subscrever webhooks Cativa
Configure listeners para
user_created e user_received_badge para manter sua tabela de mapeamento atualizada.Liberar acesso via compra externa
O caso específico de “compra externa → badge” tem padrões e cuidados próprios.
