Validação externa (gatilho de checkout): libere ou bloqueie compras com regras do seu sistema
Conecte o checkout a um sistema seu para decidir, em tempo real, quem pode comprar: seguir, avisar ou bloquear.
1. O QUE É A VALIDAÇÃO EXTERNA?
A validação externa (também chamada de gatilho de checkout) permite que o seu próprio sistema decida, em tempo real, se uma pessoa pode ou não comprar no seu checkout.
Funciona assim: quando alguém informa o e-mail ou tenta finalizar a compra, a EngagED consulta um endereço (URL) que você configurou, envia os dados dessa compra e espera a sua resposta. Com base nela, o checkout faz uma de três coisas:
Seguir: a compra continua normalmente.
Avisar: a compra não é impedida, e o comprador vê uma mensagem sua (a seção 8 mostra em que momento).
Bloquear: a compra é impedida e o comprador vê uma mensagem sua explicando o motivo. Nada é cobrado.

Esse recurso é útil quando a regra de quem pode comprar está fora da EngagED. Alguns exemplos:
Um curso exclusivo para associados, sócios ou membros de uma entidade, em que só quem está na sua lista pode se inscrever;
Uma turma liberada apenas para alunos com matrícula ativa no seu sistema acadêmico;
Impedir que a mesma pessoa compre duas vezes um produto que só pode ser adquirido uma vez;
Mostrar ao comprador um aviso sobre alguma condição do curso (por exemplo, "este curso exige conhecimento prévio em X").
Importante: este recurso exige integração técnica.
A EngagED faz a pergunta, mas quem responde é um sistema seu. Você vai precisar de um endpoint (uma URL que recebe e responde requisições) criado pelo seu time de desenvolvimento ou montado em uma ferramenta de automação que consiga devolver uma resposta personalizada, como o n8n ou o Make. As seções 6, 7, 9, 10 e 11 deste artigo trazem o que a pessoa responsável pela integração precisa saber.
2. COMO FUNCIONA, PASSO A PASSO
Imagine um curso que só associados podem comprar. Você tem uma lista com os e-mails dos associados no seu sistema.
1. O comprador abre o checkout e informa o e-mail.
2. A EngagED envia para a sua URL uma requisição com os dados daquela compra: qual checkout, qual e-mail, qual produto, entre outros.
3. O seu sistema procura esse e-mail na lista de associados.
4. O seu sistema responde para a EngagED: "pode seguir" (se encontrou) ou "bloqueie e mostre esta mensagem" (se não encontrou).
5. O checkout aplica a resposta na hora. Se for bloqueio, o comprador vê a sua mensagem logo depois de sair do campo de e-mail, assim que o seu sistema responde. Com o evento de finalização também marcado (seção 3), ele não consegue concluir a compra.
Tudo isso acontece em poucos segundos, enquanto o comprador está na página.

3. EM QUAIS MOMENTOS A EngagED CONSULTA O SEU SISTEMA
Ao criar o gatilho, você escolhe em quais momentos (eventos) a EngagED deve chamar a sua URL. Pode marcar um ou os dois:
E-mail informado (captura de lead ou formulário): a consulta acontece assim que o comprador informa o e-mail. No formulário do checkout, se a resposta for bloquear, a mensagem aparece assim que o seu sistema responde, logo depois que o comprador sai do campo de e-mail (no nosso teste, cerca de 1 segundo), mesmo com o resto dos dados ainda em branco. É a forma de mostrar o bloqueio logo no início. Uma resposta de aviso neste evento só é exibida em checkouts com captura de lead, quando o comprador salva os dados do lead; no formulário comum, o aviso do e-mail não aparece para o comprador.
Tentativa de finalizar a compra: a consulta acontece quando o comprador clica em "Finalizar compra". Nesse momento a EngagED já sabe o nome, o telefone e o CPF ou CNPJ digitados, a forma de pagamento, o código de cupom digitado e o valor da compra com os order bumps escolhidos, antes do desconto do cupom. Se a resposta for bloquear, a compra não é criada e nada é cobrado. Se a resposta for avisar, a compra é criada e o comprador vê a sua mensagem logo depois de clicar em "Finalizar compra". No cartão aprovado, em produtos gratuitos e no início de um período de teste gratuito, ao clicar no botão da mensagem ele vai para a tela de boas-vindas. No Pix e no boleto, a mensagem aparece sobre a tela com o QR Code ou o boleto. Se o cartão for recusado, o aviso não é exibido.
Importante: para regras de "quem pode comprar", marque os dois eventos.
O bloqueio no evento de e-mail mostra a sua mensagem logo no início, mas sozinho não trava a compra: o comprador pode fechar a janela e continuar preenchendo. Se o evento "Tentativa de finalizar a compra" não estiver marcado, a EngagED não consulta o seu sistema quando ele clica em "Finalizar compra", e a compra é concluída. É esse segundo evento que impede a compra de fato, inclusive se a pessoa trocar de e-mail no meio do caminho.
4. COMO ATIVAR NO SEU CHECKOUT
1. Acesse seu admin da EngagED em admin.online.engaged.com.br
2. Vá até a seção de checkouts e crie ou edite um checkout.
3. Localize o bloco "Integrações" (Validações externas durante a compra) e ative o toggle "Validação externa (gatilho de checkout)".

4. No campo "Gatilho de checkout", escolha um gatilho que já exista na sua organização ou clique em "Criar gatilho" para cadastrar um novo (veja a próxima seção).

5. Salve o checkout.
No checkout inteiro ou só em uma opção de venda?
Você pode ativar a validação externa no checkout como um todo (vale para todas as opções de venda) ou em opções de venda específicas, dentro da configuração de cada uma. Não é possível usar os dois ao mesmo tempo: ao ativar em uma opção de venda, a validação do checkout é desligada, e vice-versa. A plataforma avisa antes de fazer essa troca.

Um mesmo gatilho pode ser usado em vários checkouts da sua organização. Se você alterar o gatilho (a URL, por exemplo), a mudança vale para todos os checkouts que o utilizam.
5. COMO CRIAR O GATILHO
Ao clicar em "Criar gatilho", a janela "Novo gatilho de checkout" pede:
Nome interno: um nome para você identificar o gatilho. Exemplo: "Validação de associados". O comprador não vê esse nome.
URL do seu endpoint: o endereço que a EngagED vai chamar. Precisa começar com https://. Exemplo: https://sua-api.com.br/engaged/checkout
Quando chamar: os eventos descritos na seção 3. Os dois já vêm marcados; desmarque o que não quiser usar, mantendo pelo menos um.
Cabeçalhos personalizados (opcional): informações enviadas em toda chamada. O uso mais comum é autenticação, por exemplo um cabeçalho
Authorizationcom uma chave que só o seu sistema conhece.

Clique em "Criar gatilho". O gatilho é criado já ativo e a janela continua aberta, agora com mais três blocos:
Segredo de assinatura: um código gerado pela EngagED que permite ao seu sistema confirmar que a chamada veio mesmo da EngagED (detalhes na seção 9). Use o botão "Copiar" para levar o valor ao seu sistema.
Testar integração: confere se o seu endpoint está respondendo do jeito certo (seção 10).
Gatilho ativo: desligue e clique em "Salvar" para pausar o gatilho sem apagá-lo. A mudança só vale depois de salvar. Um gatilho inativo não faz nenhuma consulta e as compras seguem normalmente.

Como trocar o segredo. Se o segredo vazar, use "Gerar novo segredo". Ao confirmar, a EngagED gera um novo segredo e passa a assinar as chamadas com ele na hora; o antigo deixa de valer. Como o novo valor só aparece depois da confirmação, não dá para atualizar o seu sistema antes. Para trocar com segurança:
1. Prepare o seu endpoint para aceitar a assinatura feita com qualquer um de dois segredos: o atual e o novo, que você vai cadastrar no passo 3.
2. Clique em "Gerar novo segredo", confirme e copie o novo valor.
3. Cadastre o novo segredo no seu endpoint logo em seguida. Quando tudo estiver funcionando, remova o segredo antigo.
Atenção durante a troca: entre os passos 2 e 3, as chamadas chegam assinadas com o segredo novo, que o seu sistema ainda não conhece. Se o seu endpoint recusar essas chamadas com um status de erro (fora da faixa 200 a 299), a EngagED trata como falha e a compra segue sem validação (seção 11). Faça a troca num horário de pouco movimento e mantenha essa janela o mais curta possível.
6. O QUE A EngagED ENVIA PARA O SEU SISTEMA
Esta seção e as seções 7, 9, 10 e 11 são para a pessoa que vai desenvolver o endpoint.
A EngagED faz uma requisição POST para a sua URL, com o corpo em JSON. Estes são os cabeçalhos enviados:
Content-Type: application/jsonUser-Agent: EngagED-Checkout-Triggers/1X-Engaged-Event: o evento que gerou a chamada, com o mesmo valor do campotriggerTypedo corpo (purchase.email_submittedoupurchase.submitted).X-Engaged-Delivery: um identificador único de cada chamada, igual ao campodeliveryId.X-Engaged-Signature: a assinatura do corpo (veja a seção 9).Os cabeçalhos personalizados que você cadastrou no gatilho (até 20). Eles não podem usar os nomes dos cabeçalhos acima.
O corpo segue o mesmo padrão dos webhooks da EngagED: todo registro da EngagED (gatilho, organização, checkout, opção de venda, produto, preço, desconto, pessoa e compra) traz o seu _id, os valores em dinheiro vêm em centavos (149900 equivale a R$ 1.499,00) e um campo sem valor não vem no JSON, em vez de vir como null. Os objetos de configuração e os dados digitados no checkout não têm _id. Trate como opcional todo campo, exceto triggerType, sentAt, deliveryId, trigger, organization, checkout e user.email, que vêm sempre. Os comentários abaixo dizem quando cada campo opcional vem.
Os dois exemplos abaixo trazem um comentário em cada campo. Os comentários estão aqui só para explicar: o JSON enviado não tem comentários.
Evento "E-mail informado" (purchase.email_submitted), com o comprador já cadastrado na sua organização e um checkout com quatro opções de venda (uma delas inativa):
{
/**
* Evento que gerou a chamada. Possíveis valores:
* • purchase.email_submitted (e-mail informado)
* • purchase.submitted (tentativa de finalizar a compra)
*
* O mesmo valor vai no header X-Engaged-Event.
*
* No botão "Testar integração" (seção 10), a chamada sai com o triggerType do
* evento escolhido e com "isTest": true a mais. Em eventos reais, isTest não vem.
*/
"triggerType": "purchase.email_submitted",
// Data e hora do envio, ISO 8601 em UTC.
"sentAt": "2026-10-28T20:12:40.205Z",
/**
* Id único desta chamada, igual ao header X-Engaged-Delivery.
*
* Muda a cada chamada, inclusive numa nova tentativa da mesma compra.
*/
"deliveryId": "6720a1f4c3b2a1d0e9f8a7b6",
// Gatilho que fez a chamada.
"trigger": {
// Id do gatilho.
"_id": "6720a1c4e5b3f2a1d0c9b8a7",
// Nome interno do gatilho, definido no admin.
"name": "Validação de associados"
},
// Organização dona do checkout.
"organization": {
// Id da organização.
"_id": "65aa3f9c2d1e4b5a6c7d8e9f"
},
/**
* Id da opção de venda escolhida.
*
* Serve para achar a opção em checkout.purchaseChoices.choices.
*
* No evento de e-mail da captura de lead, quando o checkout não fixa a opção, vem a
* opção padrão (default: true). A opção definitiva chega em purchase.submitted.
*/
"checkoutPurchaseChoiceId": "66cc5f9b2d3e4f5a6b7c8d9e",
// Checkout em que a compra está acontecendo.
"checkout": {
// Id do checkout.
"_id": "66bb4e8a1c2d3e4f5a6b7c8d",
// Id público do checkout, o do link (/p/checkout/<sharedId>).
"sharedId": "abc123xyz",
// Nome do checkout no admin.
"description": "Curso exclusivo para associados",
// Opções de venda do checkout, no mesmo formato dos webhooks da EngagED.
"purchaseChoices": {
/**
* Todas as opções de venda do checkout, ativas e inativas.
*
* A escolhida é a que tem _id igual a checkoutPurchaseChoiceId.
*
* Checkout com uma opção só tem 1 item.
*/
"choices": [
/**
* Opção escolhida (_id igual a checkoutPurchaseChoiceId): curso + taxa
* avulsa + desconto da opção.
*/
{
// Id da opção de venda.
"_id": "66cc5f9b2d3e4f5a6b7c8d9e",
// Como a opção aparece para o comprador.
"appearance": {
// Nome da opção mostrado no checkout.
"name": "Pagamento à vista"
},
/**
* Possíveis valores:
* • true (opção que já vem selecionada ao abrir o checkout)
* • false (demais opções)
*/
"default": true,
/**
* Status da opção. Possíveis valores:
* • ACTIVE (aparece para o comprador)
* • INACTIVE (não aparece, mas vem na lista)
*/
"status": "ACTIVE",
/**
* Itens que compõem a opção (produtos e quantidades).
*
* Não inclui order bumps.
*/
"invoiceItems": [
{
/**
* Tipo do item. Possíveis valores:
* • PRODUCT (produto cadastrado; vem com productPrice e
* product)
* • INLINE (item avulso criado direto na opção de venda;
* vem só com name, quantity e amount)
*/
"type": "PRODUCT",
// Quantidade do item.
"quantity": 1,
/**
* Id do preço usado neste item, o mesmo de
* product.price._id. Só em item PRODUCT.
*/
"productPrice": "66ff9c3d5e6f7a8b9c0d1e2f",
// Produto do item. Só em item PRODUCT.
"product": {
// Id do produto.
"_id": "66ee8b2c4d5e6f7a8b9c0d1e",
/**
* Tipo do produto. Possíveis valores:
* • ACCESS (dá acesso a curso, turma ou comunidade)
* • OTHER (qualquer outro produto: físico, serviço,
* ingresso, entre outros)
*/
"type": "ACCESS",
// Nome do produto.
"name": "Curso de Gestão para Associados",
// Preço do produto usado neste item.
"price": {
// Id do preço.
"_id": "66ff9c3d5e6f7a8b9c0d1e2f",
// Moeda. Hoje BRL.
"currency": "BRL",
// Regra de cálculo do valor.
"billingConfig": {
/**
* Forma de cálculo. Hoje é sempre PER_UNIT
* (valor por unidade).
*/
"type": "PER_UNIT",
/**
* Valor unitário em centavos (149900 = R$
* 1.499,00).
*
* Valor do item = unitAmount x quantity.
*/
"unitAmount": 149900,
/**
* Valor percebido cadastrado no preço, em
* centavos. Só vem quando cadastrado.
*/
"perceivedUnitAmount": 199900
},
// Como o preço é cobrado.
"paymentConfig": {
/**
* Possíveis valores:
* • ONE_TIME (pagamento único)
* • RECURRING (assinatura)
*/
"type": "ONE_TIME"
}
}
}
},
{
"type": "INLINE",
// Nome do item avulso. Só em item INLINE.
"name": "Taxa de inscrição",
"quantity": 1,
// Valor do item avulso, em centavos. Só em item INLINE.
"amount": 5000
}
],
/**
* Descontos configurados na própria opção de venda (tipo MANUAL).
*
* Só vem quando a opção tem desconto.
*/
"invoiceDiscounts": [
{
// Id do desconto.
"_id": "6a7d8e9f0a1b2c3d4e5f6a7b",
// Tipo do desconto. Na opção de venda, MANUAL.
"type": "MANUAL",
/**
* Valor do desconto, em centavos. Sempre vem: quando o
* desconto foi cadastrado em porcentagem, é o valor já
* calculado.
*/
"amount": 20000,
// Descrição do desconto, definida no admin.
"description": "Desconto para associados"
}
],
// Soma dos itens da opção, em centavos.
"invoiceItemsAmount": 154900,
/**
* Desconto configurado na própria opção, em centavos (0 quando não
* há).
*
* Não inclui cupom.
*/
"invoiceDiscountedAmount": 20000,
/**
* Total da opção, em centavos: invoiceItemsAmount menos
* invoiceDiscountedAmount.
*/
"invoiceTotalAmount": 134900
},
/**
* Assinatura mensal: produto de acesso com preço RECURRING, sem
* desconto (por isso sem invoiceDiscounts).
*/
{
"_id": "66cc6a0c3e4f5a6b7c8d9e0f",
"appearance": {
"name": "Assinatura mensal"
},
"default": false,
"status": "ACTIVE",
"invoiceItems": [
{
"type": "PRODUCT",
"quantity": 1,
"productPrice": "6a6c7d8e9f0a1b2c3d4e5f6a",
"product": {
"_id": "6a5b6c7d8e9f0a1b2c3d4e5f",
"type": "ACCESS",
"name": "Mentoria Mensal para Associados",
"price": {
"_id": "6a6c7d8e9f0a1b2c3d4e5f6a",
"currency": "BRL",
"billingConfig": {
"type": "PER_UNIT",
"unitAmount": 19900
},
"paymentConfig": {
"type": "RECURRING",
/**
* De quanto em quanto tempo a assinatura cobra.
* Só em RECURRING.
*/
"interval": {
/**
* Quantas unidades entre uma cobrança e
* outra (1 com MONTH = todo mês).
*/
"amount": 1,
/**
* Unidade do intervalo. Possíveis valores:
* • DAY (dia)
* • WEEK (semana)
* • MONTH (mês)
* • YEAR (ano)
*/
"unit": "MONTH"
},
/**
* Número máximo de cobranças da assinatura (com
* intervalo de 1 MONTH, 12 = 12 meses). Só em
* RECURRING, e só quando a assinatura tem
* limite.
*/
"chargeLimit": 12
}
}
}
}
],
"invoiceItemsAmount": 19900,
"invoiceDiscountedAmount": 0,
"invoiceTotalAmount": 19900
},
/**
* Assinatura anual: produto do tipo OTHER com preço RECURRING e
* desconto em porcentagem na opção.
*/
{
"_id": "66cc8c2e5a6b7c8d9e0f1a2b",
"appearance": {
"name": "Clube anual"
},
"default": false,
"status": "ACTIVE",
"invoiceItems": [
{
"type": "PRODUCT",
"quantity": 1,
"productPrice": "6aa0b1c2d3e4f5a6b7c8d9e0",
"product": {
"_id": "6a9fa0b1c2d3e4f5a6b7c8d9",
"type": "OTHER",
"name": "Clube de Benefícios para Postos",
"price": {
"_id": "6aa0b1c2d3e4f5a6b7c8d9e0",
"currency": "BRL",
"billingConfig": {
"type": "PER_UNIT",
"unitAmount": 49900
},
"paymentConfig": {
"type": "RECURRING",
"interval": {
"amount": 1,
"unit": "YEAR"
},
"chargeLimit": 3
}
}
}
}
],
"invoiceDiscounts": [
{
"_id": "6aa1c2d3e4f5a6b7c8d9e0f1",
"type": "MANUAL",
"amount": 4990,
/**
* Desconto em fração (0.1 = 10%). Só vem quando o desconto
* foi cadastrado em porcentagem.
*/
"percent": 0.1,
"description": "Desconto de lançamento"
}
],
"invoiceItemsAmount": 49900,
"invoiceDiscountedAmount": 4990,
"invoiceTotalAmount": 44910
},
// Opção inativa: não aparece para o comprador, mas vem na lista.
{
"_id": "66cc7b1d4f5a6b7c8d9e0f1a",
"appearance": {
"name": "Turma de 2025"
},
"default": false,
"status": "INACTIVE",
"invoiceItems": [
{
"type": "PRODUCT",
"quantity": 1,
"productPrice": "66ff9c3d5e6f7a8b9c0d1e2f",
"product": {
"_id": "66ee8b2c4d5e6f7a8b9c0d1e",
"type": "ACCESS",
"name": "Curso de Gestão para Associados",
"price": {
"_id": "66ff9c3d5e6f7a8b9c0d1e2f",
"currency": "BRL",
"billingConfig": {
"type": "PER_UNIT",
"unitAmount": 149900,
"perceivedUnitAmount": 199900
},
"paymentConfig": {
"type": "ONE_TIME"
}
}
}
}
],
"invoiceItemsAmount": 149900,
"invoiceDiscountedAmount": 0,
"invoiceTotalAmount": 149900
}
]
}
},
// Quem está informando o e-mail, com o que se sabe até agora.
"user": {
/**
* Id da pessoa na organização.
*
* Só vem se ela já existe (já deixou lead, já comprou ou já tentou comprar).
* Pessoa nova não tem.
*/
"_id": "66dd7a1b3c4d5e6f7a8b9c0d",
/**
* Nome da pessoa. Quando vem:
* • Captura de lead: o nome digitado ou, se não houver, o do cadastro.
* • E-mail no formulário: só se a pessoa já existe.
*
* Na captura de lead, se o comprador sai do campo de e-mail e espera antes de
* clicar em Continuar, a consulta é feita nesse momento, ainda sem o nome
* digitado: vem o do cadastro, se a pessoa já existe (seção 11).
*/
"name": "Maria Souza",
// E-mail informado. Sempre presente.
"email": "maria@exemplo.com.br",
/**
* Telefone com DDI (+5541999999999). Quando vem:
* • Captura de lead: o digitado ou, se não houver, o do cadastro. Pode faltar
* se o lead não pede telefone e a pessoa é nova, ou no caso descrito em
* user.name.
* • E-mail no formulário: só se a pessoa já existe.
*/
"phoneNumber": "+5541999999999",
/**
* País do telefone, código ISO de 2 letras (BR, PT, US, entre outros).
*
* Vem junto com phoneNumber.
*/
"phoneNumberCountry": "BR"
},
// Última compra desta pessoa neste checkout. Só vem se ela já tem compra aqui.
"purchase": {
// Id da compra.
"_id": "6a2c3d4e5f6a7b8c9d0e1f2a",
/**
* Status da compra. Possíveis valores, entre outros:
* • VOID (Pix ou boleto cancelado)
* • DRAFT (só deixou o lead)
* • OPEN (aguardando pagamento)
* • REFUSED (cartão recusado)
* • EXPIRED (Pix ou boleto vencido)
* • PAID (já comprou)
*/
"status": "VOID"
},
/**
* Parâmetros do link do checkout (UTMs e parâmetros próprios). Quando vem:
* • Captura de lead: sim, salvo no caso da consulta feita ao sair do campo de
* e-mail, descrito em user.name.
* • E-mail no formulário: não vem.
*/
"queryParams": {
// Cada parâmetro do link vira uma chave, com o valor em texto.
"utm_source": "newsletter",
// Outro parâmetro de UTM, se houver no link.
"utm_campaign": "associados-2026",
// Parâmetro próprio do cliente, se houver no link. O nome é livre.
"indicacao": "sindicato-rs"
}
}
Evento "Tentativa de finalizar a compra" (purchase.submitted). Traz os mesmos campos do evento de e-mail e mais os dados digitados no formulário de pagamento: o nome, o telefone, os dados fiscais do pagador (userPaymentProfile, com CPF ou CNPJ), o cupom digitado e a forma de pagamento escolhida. As opções de venda vêm iguais às do exemplo anterior e foram recolhidas aqui para não repetir:
{
/**
* Evento que gerou a chamada. Possíveis valores:
* • purchase.email_submitted (e-mail informado)
* • purchase.submitted (tentativa de finalizar a compra)
*
* O mesmo valor vai no header X-Engaged-Event.
*
* No botão "Testar integração" (seção 10), a chamada sai com o triggerType do
* evento escolhido e com "isTest": true a mais. Em eventos reais, isTest não vem.
*/
"triggerType": "purchase.submitted",
// Data e hora do envio, ISO 8601 em UTC.
"sentAt": "2026-10-28T20:41:12.874Z",
/**
* Id único desta chamada, igual ao header X-Engaged-Delivery.
*
* Muda a cada chamada, inclusive numa nova tentativa da mesma compra.
*/
"deliveryId": "6720a2b5d4c3b2a1f0e9d8c7",
// Gatilho que fez a chamada.
"trigger": {
// Id do gatilho.
"_id": "6720a1c4e5b3f2a1d0c9b8a7",
// Nome interno do gatilho, definido no admin.
"name": "Validação de associados"
},
// Organização dona do checkout.
"organization": {
// Id da organização.
"_id": "65aa3f9c2d1e4b5a6c7d8e9f"
},
/**
* Id da opção de venda escolhida.
*
* Serve para achar a opção em checkout.purchaseChoices.choices.
*
* No evento de e-mail da captura de lead, quando o checkout não fixa a opção, vem a
* opção padrão (default: true). A opção definitiva chega em purchase.submitted.
*/
"checkoutPurchaseChoiceId": "66cc5f9b2d3e4f5a6b7c8d9e",
// Checkout em que a compra está acontecendo.
"checkout": {
// Id do checkout.
"_id": "66bb4e8a1c2d3e4f5a6b7c8d",
// Id público do checkout, o do link (/p/checkout/<sharedId>).
"sharedId": "abc123xyz",
// Nome do checkout no admin.
"description": "Curso exclusivo para associados",
/**
* Mesmo conteúdo do exemplo anterior: todas as opções de venda do checkout, com
* itens, produtos, preços e descontos.
*/
"purchaseChoices": {
"choices": [ ... ]
}
},
// Quem está comprando, com os dados digitados no formulário.
"user": {
/**
* Id da pessoa na organização.
*
* Só vem se ela já existe (já deixou lead, já comprou ou já tentou comprar).
* Pessoa nova não tem.
*/
"_id": "66dd7a1b3c4d5e6f7a8b9c0d",
// Nome digitado no formulário ou, se não houver, o do cadastro.
"name": "Maria Souza",
// E-mail informado. Sempre presente.
"email": "maria@exemplo.com.br",
/**
* Telefone com DDI (+5541999999999), como digitado. Se o checkout não pede
* telefone, vem o do cadastro, quando a pessoa já existe; senão não vem.
*/
"phoneNumber": "+5541999999999",
/**
* País do telefone, código ISO de 2 letras (BR, PT, US, entre outros).
*
* Vem junto com phoneNumber.
*/
"phoneNumberCountry": "BR"
},
/**
* Dados fiscais do pagador, digitados no formulário.
*
* Só no evento purchase.submitted.
*/
"userPaymentProfile": {
// País do pagador, código ISO de 2 letras.
"country": "BR",
// Moeda da compra. Hoje BRL.
"currency": "BRL",
/**
* Tipo de pessoa do pagador. Possíveis valores:
* • INDIVIDUAL (pessoa física)
* • COMPANY (empresa)
*/
"type": "COMPANY",
// Nome da pessoa física ou razão social da empresa.
"name": "Posto Exemplo Ltda",
// Documentos do pagador. Normalmente 1 item.
"taxIds": [
{
/**
* Tipo do documento. Possíveis valores:
* • BR_CPF (pessoa física no Brasil)
* • BR_CNPJ (empresa no Brasil)
* • PASSPORT e documentos de outros países (pagador estrangeiro)
*/
"type": "BR_CNPJ",
// País do documento, código ISO de 2 letras.
"country": "BR",
// Número do documento.
"value": "12345678000199"
}
]
},
// Última compra desta pessoa neste checkout. Só vem se ela já tem compra aqui.
"purchase": {
// Id da compra.
"_id": "6a2c3d4e5f6a7b8c9d0e1f2a",
/**
* Status da compra. Possíveis valores, entre outros:
* • DRAFT (só deixou o lead)
* • OPEN (aguardando pagamento)
* • REFUSED (cartão recusado)
* • VOID (Pix ou boleto cancelado)
* • EXPIRED (Pix ou boleto vencido)
* • PAID (já comprou; com a recompra desligada, a EngagED recusa esta
* finalização logo depois, veja a seção 11)
*/
"status": "VOID"
},
/**
* Cupom digitado nesta tentativa, ainda não validado.
*
* Vem sempre que houver cupom digitado, mesmo que uma tentativa anterior já tenha
* usado cupom. Sem cupom, a chave não vem.
*/
"voucherCode": "ASSOCIADO10",
/**
* Tentativa de pagamento que está acontecendo agora. Ainda não foi gravada, por
* isso não tem _id, status nem datas.
*
* Compra gratuita: não vem.
*/
"payment": {
/**
* Forma de pagamento escolhida agora. Possíveis valores:
* • CREDIT_CARD (cartão de crédito)
* • PIX
* • BANK_SLIP (boleto)
*/
"paymentMethod": "PIX",
/**
* Número de parcelas escolhido.
*
* Pix e boleto: sempre 1.
*/
"installments": 1,
/**
* Valor em centavos: total da opção escolhida mais os order bumps marcados.
* Neste exemplo, 134900 da opção + 9700 de um order bump. Os order bumps não
* vêm em outro campo: a diferença entre payment.amount e o invoiceTotalAmount
* da opção é o valor deles.
*
* Antes do cupom e do desconto por forma de pagamento, que são aplicados depois
* do gatilho. Sem juros de parcelamento.
*/
"amount": 144600,
// Moeda. Hoje BRL.
"currency": "BRL"
},
// Parâmetros do link do checkout (UTMs e parâmetros próprios).
"queryParams": {
// Cada parâmetro do link vira uma chave, com o valor em texto.
"utm_source": "newsletter",
// Outro parâmetro de UTM, se houver no link.
"utm_campaign": "associados-2026",
// Parâmetro próprio do cliente, se houver no link. O nome é livre.
"indicacao": "sindicato-rs"
}
}
Atenção: os campos personalizados do checkout não são enviados nesta chamada. O CPF ou CNPJ só chega na tentativa de finalizar a compra, em userPaymentProfile.taxIds. No evento de e-mail, monte a sua regra a partir do e-mail ou de um parâmetro próprio no link do checkout (queryParams).
7. O QUE O SEU SISTEMA DEVE RESPONDER
O seu endpoint deve responder com status HTTP 2xx (200 a 299; o mais comum é 200) e um JSON neste formato:
{
"action": { "code": "block" },
"reason": "nao_associado",
"message": {
"title": "Inscrição exclusiva para associados",
"body": "Não encontramos este e-mail na nossa lista de associados. Se você é associado, use o e-mail cadastrado na entidade ou fale com a nossa equipe.",
"format": "text",
"buttonLabel": "Entendi"
}
}
Os campos da resposta:
action.code (obrigatório):
nonepara seguir,warnpara avisar oublockpara bloquear.message (opcional): a mensagem exibida ao comprador quando a ação é
warnoublock.title (obrigatório se enviar
message): título da janela, até 120 caracteres.body (obrigatório se enviar
message): texto da mensagem, até 5.000 caracteres.format:
text(padrão) ouhtml. Em HTML, apenas marcações simples de texto são mantidas, como parágrafos, quebras de linha, negrito, itálico, sublinhado, listas, subtítulos (h3, h4) e links (http, https ou mailto, que abrem em nova aba). Qualquer outra marcação é removida por segurança; se não sobrar texto, a EngagED mostra a mensagem padrão.buttonLabel: texto do botão da janela, até 40 caracteres.
reason (opcional): um código interno seu, até 64 caracteres, para você identificar o motivo da decisão. O comprador não vê esse campo.
Os valores de action.code e format são aceitos em maiúsculas ou minúsculas.
Atenção: se a resposta tiver qualquer campo fora dessas regras (por exemplo, message sem title, ou um texto acima do limite), a EngagED considera a resposta inválida e deixa a compra seguir, mesmo que action.code seja block. Use o botão "Testar integração" (seção 10) para conferir.
Para deixar a compra seguir, basta responder:
{ "action": { "code": "none" } }
Uma resposta sem corpo com status entre 200 e 299 (por exemplo, 204) também é entendida como "seguir".
Se você bloquear ou avisar sem enviar message, a EngagED mostra um texto padrão:
Bloqueio: título "Não foi possível continuar", texto "Esta compra não pode ser concluída no momento." e botão "Fechar".
Aviso: título "Atenção", texto "Verifique as informações antes de continuar." e botão "Entendi".
Se você enviar a mensagem sem buttonLabel, o botão usa esses mesmos textos padrão.
Dica: sempre envie a sua própria mensagem no bloqueio. Explique o motivo e diga o que a pessoa pode fazer (usar outro e-mail, falar com o suporte, etc.). Um bloqueio sem explicação gera dúvida e chamados.
8. COMO O COMPRADOR VÊ CADA RESPOSTA
Veja abaixo como cada resposta do seu sistema aparece no checkout. No bloqueio, a mensagem é a do exemplo da seção 7. No aviso, usamos a mensagem "Antes de continuar", com o botão "Continuar".
Bloquear. A janela com a sua mensagem aparece assim que o seu sistema responde, logo depois que o comprador preenche o e-mail e sai do campo, antes mesmo de ele informar nome, telefone ou dados de pagamento.

Bloquear, no celular. No celular a mesma janela aparece sobre o formulário, no mesmo momento.

Bloquear, na finalização. A mesma janela aparece logo depois do clique em "Finalizar compra", sobre a tela de pagamento. A compra não é criada e nada é cobrado.
Avisar. Na tentativa de finalizar, o aviso aparece logo depois que o comprador clica em "Finalizar compra", com a compra já criada. No cartão aprovado (como no print abaixo), em produtos gratuitos e no início de um período de teste gratuito, ao clicar no botão da mensagem ele vai para a tela de boas-vindas. No Pix e no boleto, a mensagem aparece sobre a tela com o QR Code ou o boleto.

Seguir. Nenhuma janela aparece: o comprador continua preenchendo o checkout normalmente, sem perceber a validação.

9. SEGURANÇA: COMO CONFIRMAR QUE A CHAMADA VEIO DA EngagED
Como a sua URL fica acessível pela internet, é uma boa prática conferir se cada chamada veio mesmo da EngagED. Há duas formas, e você pode usar as duas juntas:
1. Cabeçalho personalizado. Cadastre no gatilho um cabeçalho como Authorization: Bearer sua-chave-secreta e, no seu endpoint, recuse chamadas que não tragam esse valor. Lembre-se de que qualquer recusa (por exemplo, status 401) é tratada como falha e a compra segue (seção 11). Confira com o "Testar integração" depois de configurar.
2. Assinatura. Toda chamada traz o cabeçalho X-Engaged-Signature no formato sha256=<assinatura>. A assinatura é um HMAC SHA-256 do corpo da requisição, exatamente como foi recebido, usando o segredo de assinatura do gatilho como chave. O seu endpoint calcula o mesmo valor e compara. Exemplo em Node.js:
const crypto = require('crypto');
function assinaturaValida(corpoBruto, assinaturaRecebida, segredo) {
if (typeof assinaturaRecebida !== 'string') return false;
const esperada = 'sha256=' + crypto
.createHmac('sha256', segredo)
.update(corpoBruto)
.digest('hex');
const a = Buffer.from(assinaturaRecebida);
const b = Buffer.from(esperada);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Use o corpo bruto (o texto recebido, antes de converter para objeto). Se você converter o JSON e gerar o texto de novo, a assinatura pode não bater. No Express, receba o corpo com express.raw({ type: 'application/json' }) para ter o texto original.
10. TESTANDO A INTEGRAÇÃO
Na tela do gatilho, no bloco "Testar integração", escolha em "Evento a testar" qual evento quer simular e clique em "Enviar teste". Só os eventos marcados em "Quando chamar" podem ser escolhidos; os outros aparecem desabilitados, com a instrução para marcá-los primeiro.

A EngagED envia para a sua URL uma chamada no mesmo formato do evento escolhido (seção 6), com o mesmo triggerType e com o campo "isTest": true a mais. Use esse campo no seu endpoint para responder normalmente sem gravar nada como se fosse uma compra real. Os dados são fictícios:
comprador
teste@exemplo.com, com o nome "Pessoa de Teste";um checkout de exemplo (
sharedIdexemplo-checkout) com uma opção de venda de exemplo e um item avulso de R$ 99,00 (9900);no evento "Tentativa de finalizar a compra", também um telefone, os dados fiscais de uma pessoa física com CPF fictício e um pagamento por Pix de R$ 99,00.
Depois, a tela mostra:
se a resposta foi válida ou não;
o status HTTP e o tempo de resposta;
a ação entendida (seguir, avisar ou bloquear) e a mensagem que o comprador veria;
o conteúdo que o seu endpoint devolveu.

O teste usa a última versão salva do gatilho. Se houver alterações não salvas, a plataforma oferece salvar antes de testar. Os testes não entram na regra de consultas repetidas (seção 11): cada clique em "Enviar teste" faz uma chamada nova.
Quando a resposta não é válida, a tela mostra o motivo. Os mais comuns:
Tempo limite excedido: o seu endpoint demorou mais de 5 segundos para responder.
Status HTTP inesperado: o endpoint respondeu com um status fora da faixa 200 a 299 (por exemplo, 301, 401, 404 ou 500). Redirecionamentos não são seguidos.
Resposta fora do padrão esperado: o JSON não segue o formato da seção 7 (por exemplo, falta
action.codeou um texto passou do limite de caracteres).Falha de rede ao chamar a URL: a URL não está acessível pela internet, o certificado é inválido ou a resposta passou de 64 KB.
URL não permitida: a URL não usa https ou aponta para um endereço interno da sua rede.
11. REGRAS IMPORTANTES
Se o seu sistema falhar, a venda segue. Se a sua URL não responder, demorar mais de 5 segundos, devolver erro ou responder fora do padrão, a EngagED deixa a compra seguir normalmente. Essa escolha protege as suas vendas de uma instabilidade no seu sistema. Por isso, mantenha o endpoint estável e rápido, principalmente se o bloqueio for essencial para o seu negócio.
Sem nova tentativa. Como o comprador está esperando na página, cada consulta é feita uma única vez. Quando o banco pede a confirmação do cartão (3D Secure), a nova tentativa depois da confirmação não gera outra consulta: vale a resposta da primeira.
Responda rápido. O limite é de 5 segundos. Procure responder em menos de 1 segundo para o comprador não perceber espera.
Consultas repetidas de e-mail. Se o mesmo e-mail for informado de novo no mesmo checkout em até 1 minuto, a EngagED reaproveita a última resposta válida do seu sistema em vez de consultá-lo de novo. Isso vale também para consultas que chegam ao mesmo tempo: o seu sistema recebe uma chamada só. Se a consulta anterior falhou, uma nova é feita. A opção de venda não entra nessa regra: trocar de opção dentro do mesmo minuto não gera nova consulta.
A consulta vem antes das validações da EngagED. Nos dois eventos, o seu sistema é consultado antes de a EngagED conferir, entre outras coisas, vagas, cupom, se a pessoa já comprou ou tem um teste gratuito em andamento e se há um pagamento anterior ainda em processamento. Por isso, o seu endpoint pode receber uma chamada que a EngagED recusa logo depois por um desses motivos. Não trate a chamada como compra confirmada: para isso, use os webhooks de compra da EngagED.
Aviso na finalização. Quando a resposta for "avisar" no momento de finalizar a compra, a compra é criada e o comprador vê a sua mensagem logo depois de clicar em "Finalizar compra". No cartão aprovado, em produtos gratuitos e no início de um período de teste gratuito, o botão da mensagem leva à tela de boas-vindas; no Pix e no boleto, a mensagem aparece sobre a tela de pagamento. Se o cartão for recusado, o aviso não é exibido. Se a compra não pode acontecer, use "bloquear".
Aviso no evento de e-mail. Só aparece em checkouts com captura de lead, quando o comprador salva os dados do lead. No formulário comum, um aviso no evento de e-mail não é exibido.
12. EXEMPLO COMPLETO: CURSO EXCLUSIVO PARA ASSOCIADOS
Uma entidade vai lançar um curso que só os associados podem comprar. Ela tem uma planilha com o e-mail de cada associado.
Configuração na EngagED
1. Cria um gatilho chamado "Validação de associados", com a URL do endpoint dela, marcando os eventos "E-mail informado" e "Tentativa de finalizar a compra".
2. Ativa a validação externa no checkout do curso e seleciona esse gatilho.
Lógica do endpoint
1. Recebe a chamada e confere a assinatura.
2. Lê o campo user.email do JSON recebido.
3. Procura esse e-mail na lista de associados (a planilha pode ser importada para um banco de dados ou consultada por uma ferramenta de automação).
4. Se encontrar, responde { "action": { "code": "none" } }.
5. Se não encontrar, responde com block e uma mensagem explicando que a inscrição é exclusiva para associados e como falar com a entidade.
Resultado para o comprador
O associado informa o e-mail e compra normalmente, sem perceber a validação.
Quem não é associado vê a mensagem logo depois de informar o e-mail. Como o evento de finalização também está marcado, essa pessoa não consegue concluir a compra.
13. PERGUNTAS FREQUENTES
Preciso saber programar para usar?
Alguém precisa criar o endpoint que responde à EngagED. Pode ser o seu time de tecnologia ou uma ferramenta de automação que receba uma requisição e devolva uma resposta personalizada em até 5 segundos, como o n8n ou o Make.
No n8n, por exemplo, bastam três nós: Webhook (método POST, respondendo pelo nó de resposta) → Code → Respond to Webhook (devolvendo o item recebido, com status 200). O nó Code decide a resposta. Um exemplo simples, que libera quem está na lista e bloqueia os demais:
const body = $input.first().json.body || {};
const email = String(body.user?.email || '').toLowerCase();
const associados = ['maria@exemplo.com.br', 'joao@exemplo.com.br'];
if (associados.includes(email)) {
return [{ json: { action: { code: 'none' } } }];
}
return [{ json: {
action: { code: 'block' },
reason: 'nao_associado',
message: {
title: 'Inscrição exclusiva para associados',
body: 'Não encontramos este e-mail na nossa lista de associados. Fale com a nossa equipe.',
format: 'text',
buttonLabel: 'Entendi'
}
} }];
Em um caso real, a lista de e-mails costuma vir de uma planilha ou de um banco de dados consultado antes do nó Code. Esse exemplo não confere a assinatura. No n8n, a forma mais simples de proteger o endpoint é o cabeçalho personalizado (seção 9, item 1), conferido no próprio nó Webhook em "Authentication: Header Auth".
Posso validar pelo CPF ou CNPJ?
Sim, na tentativa de finalizar a compra: o documento digitado chega em userPaymentProfile.taxIds (seção 6). No evento de e-mail o documento ainda não foi informado, então ali a chave é o e-mail.
Onde personalizo a mensagem que o comprador vê?
A mensagem não é configurada na tela do checkout. Ela vem na resposta do seu endpoint, nos campos title, body e buttonLabel (seção 7). Assim, você pode mostrar mensagens diferentes para cada situação.
O que acontece se o meu sistema sair do ar?
As compras seguem normalmente enquanto ele estiver fora (seção 11).
Posso usar o mesmo gatilho em vários checkouts?
Sim. Use os campos checkout e checkoutPurchaseChoiceId do JSON recebido se precisar aplicar regras diferentes para cada um.
Posso desligar a validação temporariamente?
Sim. Desative o toggle no checkout e salve o checkout, ou desmarque "Gatilho ativo" no gatilho e clique em "Salvar". Com o gatilho inativo, nenhuma consulta é feita e as compras seguem normalmente em todos os checkouts que o usam.