Exemplos: Hotmart, Kiwify, Shopify e formulários
O que a Hotmart, a Kiwify e a Shopify mandam por webhook e os caminhos de telefone, e-mail, nome, produto e status. E um formulário do site sem expor o link.
Hotmart, Kiwify e Shopify avisam cada venda por webhook. O caminho é o mesmo nas três: o webhook da plataforma manda os dados para um webhook de entrada da NexTags, que acha ou cria o contato e inicia o fluxo. Para cada plataforma, este guia mostra um trecho do JSON da documentação oficial e o caminho (JSONPath) de cada dado. No fim, um formulário do seu site que manda os dados pelo seu servidor, sem expor o link.
Antes de começar
- Um webhook de entrada para cada plataforma, com o link copiado em Obter Link. Veja Webhook de entrada: receber dados de outro sistema.
- O fluxo de cada aviso, publicado. Para quem não falou com você nas últimas 24 horas, um fluxo de WhatsApp começa com um Modelo de Mensagem aprovado. Veja Modelo de mensagem do WhatsApp no fluxo.
- Um envio de teste de cada plataforma, para conferir os caminhos com dados reais.
Passo a passo
Passo 1: Hotmart: compras e outros eventos
Na Hotmart, em Ferramentas › Ver todas › Webhook (API e notificações), clique em + Cadastrar Webhook, marque os eventos e cole o link da NexTags como URL. Os dados da venda vêm em data, e o nome do evento, em event. Trecho do exemplo oficial da Hotmart (evento de compra, versão 2.0.0), encurtado:
{
"id": "1234567890123456789",
"creation_date": 12345678,
"event": "PURCHASE_APPROVED",
"version": "2.0.0",
"data": {
"product": {
"id": 213344,
"name": "Product Name"
},
"buyer": {
"email": "buyer@email.com",
"name": "Buyer Name",
"first_name": "Buyer",
"last_name": "Name",
"checkout_phone": "999999999",
"checkout_phone_code": "31"
},
"purchase": {
"status": "STARTED",
"transaction": "HP02316330308193"
}
}
}| Dado | JSONPath | No exemplo |
|---|---|---|
| Telefone | $.data.buyer.checkout_phone | 999999999 |
| DDD (só compradores do Brasil) | $.data.buyer.checkout_phone_code | 31 |
$.data.buyer.email | buyer@email.com | |
| Nome | $.data.buyer.name | Buyer Name |
| Produto | $.data.product.name | Product Name |
| Status da compra | $.data.purchase.status | STARTED |
| Evento | $.event | PURCHASE_APPROVED |
| Código da transação | $.data.purchase.transaction | HP02316330308193 |
No exemplo oficial, o telefone vem sem o código do país e sem o DDD, que chega à parte em checkout_phone_code. A Hotmart diz que, nas vendas para fora do Brasil, o código do país vem junto ao telefone. Como a tela do webhook de entrada usa um caminho só para o telefone, confira um envio real no Histórico da Hotmart. Se o número vier incompleto, identifique o contato pelo e-mail e guarde o DDD e o telefone em campos, ou receba o webhook num servidor seu, monte o número completo (55 + DDD + número) e repasse para a NexTags, como no formulário do último passo.
A Hotmart manda em todas as requisições o cabeçalho X-HOTMART-HOTTOK, que identifica a sua conta. A tela do webhook de entrada não tem onde conferir esse cabeçalho: para conferir, receba o webhook num servidor seu. Fonte: Eventos de pedidos do Webhook da Hotmart. A configuração na NexTags, passo a passo: Hotmart.
Passo 2: Kiwify: compra aprovada, Pix e boleto
Na Kiwify, em Apps › Webhooks, crie um webhook, cole o link da NexTags como URL, escolha os produtos e os eventos. Cada evento chega por POST com o JSON no corpo. A Kiwify acrescenta ao link o parâmetro signature, uma assinatura do corpo; a tela do webhook de entrada não tem onde conferir, mas um servidor seu pode. O exemplo oficial da Kiwify, que omite os dados do cliente:
{
"order_id": "9b1c2d3e-0000-4000-8000-000000000001",
"order_status": "paid",
"webhook_event_type": "order_approved"
}| Dado | JSONPath |
|---|---|
| Telefone, e-mail e nome | No bloco Customer. Os exemplos oficiais omitem esse bloco: confira os nomes num Testar Webhook, em Ver logs, e escolha os caminhos com a lupa do editor. |
| Produto | Confira no envio real, em Ver logs. |
| Status do pedido | $.order_status |
| Evento | $.webhook_event_type |
| ID do pedido | $.order_id |
O gatilho que você marca na Kiwify e o valor que chega em webhook_event_type nem sempre têm o mesmo nome:
| Gatilho na Kiwify | webhook_event_type | order_status |
|---|---|---|
compra_aprovada | order_approved | paid |
pix_gerado | pix_created | waiting_payment |
boleto_gerado | billet_created | waiting_payment |
compra_recusada | order_rejected | refused |
compra_reembolsada | order_refunded | refunded |
chargeback | chargeback | chargedback |
O carrinho abandonado chega em outro formato, sem webhook_event_type nem order_status: use um webhook de entrada só para ele. A Kiwify espera uma resposta de 200 a 299 em até 40 segundos, tenta de novo quando falha e pode mandar o mesmo evento mais de uma vez. Fonte: Receber e validar webhooks, da Kiwify. A configuração na NexTags, passo a passo: Kiwify.
Passo 3: Shopify: pedidos
No admin da Shopify, em Configurações › Notificações › Webhooks, clique em Criar webhook. Em Evento, escolha o aviso, como Criação de pedido; deixe o Formato em JSON e cole o link da NexTags em URL. O evento não muda depois: crie um webhook para cada aviso, como Pagamento de pedido e Cancelamento de pedido. Trecho do exemplo oficial do tópico orders/create, encurtado:
{
"id": 820982911946154508,
"email": "jon@example.com",
"phone": null,
"name": "#9999",
"order_status_url": "https://jsmith.myshopify.com/548380009/orders/123456abcd/authenticate?key=abcdefg",
"financial_status": "voided",
"fulfillment_status": null,
"total_price": "404.95",
"billing_address": {
"name": "Steve Shipper",
"phone": "555-555-SHIP"
},
"customer": {
"first_name": "John",
"last_name": "Smith",
"email": "john@example.com",
"phone": null
},
"line_items": [
{ "title": "Aviator sunglasses", "quantity": 1, "price": "89.99" }
]
}| Dado | JSONPath | No exemplo |
|---|---|---|
| Telefone | $.billing_address.phone | 555-555-SHIP |
$.email | jon@example.com | |
| Nome | $.billing_address.name | Steve Shipper |
| Produto (o primeiro do pedido) | $.line_items[0].title | Aviator sunglasses |
| Status do pagamento | $.financial_status | voided |
| Status do envio | $.fulfillment_status | null |
| Número do pedido | $.name | #9999 |
| Link de acompanhamento | $.order_status_url | o endereço da página do pedido |
O telefone depende de onde a sua loja pede o número: no endereço de cobrança, no de entrega ($.shipping_address.phone) ou no cliente ($.customer.phone). É um texto livre, digitado pelo cliente, e pode vir vazio: use o e-mail para identificar o contato quando o telefone não for confiável. A Shopify apaga o webhook quando o endereço responde com erro muitas vezes seguidas. Fontes: Criar webhooks, na Central de Ajuda da Shopify e tópicos de webhook da Shopify. A configuração na NexTags, passo a passo: Shopify.
Passo 4: Formulário do seu site
O navegador não deve mandar os dados direto para o link do webhook de entrada: o link ficaria no código da página, à vista de qualquer um. O formulário manda os dados para o seu servidor, e o servidor manda o JSON para a NexTags, com o link guardado na variável de ambiente NEXTAGS_WEBHOOK_URL.
O formulário:
<form method="post" action="/lead">
<input name="nome" placeholder="Seu nome" required>
<input name="email" type="email" placeholder="Seu e-mail">
<input name="telefone" type="tel" placeholder="WhatsApp com DDD" required>
<button type="submit">Quero falar no WhatsApp</button>
</form>O servidor, que recebe o formulário, deixa o telefone só com números, com o 55 na frente, e repassa:
// servidor.mjs (Node 18 ou mais novo): node servidor.mjs
import http from 'node:http';
http.createServer(async (req, res) => {
if (req.method !== 'POST' || req.url !== '/lead') {
res.writeHead(404).end();
return;
}
let corpo = '';
for await (const parte of req) corpo += parte;
const form = new URLSearchParams(corpo);
const digitos = (form.get('telefone') || '').replace(/\D/g, '');
const telefone = digitos.length <= 11 ? `55${digitos}` : digitos; // 55 + DDD + número
const resposta = await fetch(process.env.NEXTAGS_WEBHOOK_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
origem: 'formulario-site',
lead: {
nome: form.get('nome') || '',
email: form.get('email') || '',
telefone,
},
}),
});
if (resposta.ok) {
res.writeHead(303, { Location: '/obrigado.html' }).end();
} else {
res.writeHead(502).end('Não deu certo. Tente de novo em alguns minutos.');
}
}).listen(3000);
# servidor.py (Flask): flask --app servidor run --port 3000
import os
import re
import requests
from flask import Flask, redirect, request
app = Flask(__name__)
@app.post("/lead")
def lead():
digitos = re.sub(r"\D", "", request.form.get("telefone", ""))
telefone = "55" + digitos if len(digitos) <= 11 else digitos # 55 + DDD + número
resposta = requests.post(
os.environ["NEXTAGS_WEBHOOK_URL"],
json={
"origem": "formulario-site",
"lead": {
"nome": request.form.get("nome", ""),
"email": request.form.get("email", ""),
"telefone": telefone,
},
},
timeout=30,
)
if resposta.ok:
return redirect("/obrigado.html", code=303)
return "Não deu certo. Tente de novo em alguns minutos.", 502
<?php
// lead.php: no formulário, use action="/lead.php"
$digitos = preg_replace('/\D/', '', $_POST['telefone'] ?? '');
$telefone = strlen($digitos) <= 11 ? '55' . $digitos : $digitos; // 55 + DDD + número
$ch = curl_init(getenv('NEXTAGS_WEBHOOK_URL'));
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'origem' => 'formulario-site',
'lead' => [
'nome' => $_POST['nome'] ?? '',
'email' => $_POST['email'] ?? '',
'telefone' => $telefone,
],
]),
CURLOPT_TIMEOUT => 30,
]);
curl_exec($ch);
$codigo = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($codigo >= 200 && $codigo < 300) {
header('Location: /obrigado.html', true, 303);
} else {
http_response_code(502);
echo 'Não deu certo. Tente de novo em alguns minutos.';
}
| Dado | JSONPath |
|---|---|
| Telefone | $.lead.telefone |
$.lead.email | |
| Nome | $.lead.nome |
| Origem | $.origem |
No formulário, peça a permissão para mandar mensagens pelo WhatsApp, com um texto claro ao lado do botão. Veja Permissão para mensagens (opt-in) e descadastro.
Pronto: como conferir
- Faça um envio de teste em cada plataforma: na Hotmart, pela aba Minhas configurações ou pelo reenvio no Histórico; na Kiwify, com Testar Webhook; na Shopify, com um pedido de teste. Depois do envio, o contato aparece em Contatos, com os campos preenchidos.
- Cole o JSON de um envio real em Sample JSON, clique em Load sample JSON e confira o
Result:de cada caminho. - No formulário, envie com o seu número: o fluxo chega no seu WhatsApp.
Se não funcionou
O contato foi criado com o telefone incompleto
O caminho do telefone pegou um número sem o código do país ou sem o DDD, como o checkout_phone do exemplo da Hotmart. Identifique o contato pelo e-mail ou monte o número completo num servidor seu antes de mandar para a NexTags.
A plataforma desativou ou apagou o webhook
Hotmart e Shopify desativam ou apagam o webhook quando o endereço responde com erro. Confira se o webhook de entrada foi salvo e está Ativo e se o link está inteiro. Depois, reative ou recrie o webhook do lado da plataforma.
O mesmo aviso iniciou o fluxo duas vezes
As plataformas podem mandar o mesmo evento mais de uma vez. No fluxo, aplique uma etiqueta e confira com uma condição antes de mandar a mensagem. Veja Segurança, testes e novas tentativas.
O formulário não chega
Confira o log do seu servidor: a variável NEXTAGS_WEBHOOK_URL tem o link inteiro? O servidor recebeu a resposta da NexTags? Veja Meu webhook de entrada não recebe os dados.
Faça com a IA
Copie o pedido e cole no ChatGPT ou no Claude.
Vou ligar [Hotmart / Kiwify / Shopify / o formulário do meu site] a um webhook de entrada da NexTags. Leia este guia: https://docs.nextags.com.br/webhooks/exemplos-de-plataformas.md
Este é o JSON de um envio de teste, sem dados reais: [cole aqui]. Me diga os caminhos (JSONPath só com .chave, ["chave"] e [0]) do telefone, do e-mail, do nome, do produto e do status, os campos personalizados que devo criar e se o telefone já vem completo, com o código do país e o DDD.
Relacionados
Este conteúdo ajudou?
Obrigado! Sua resposta ajuda a melhorar a central.