Tutorial: Recuperação de Carrinhos Abandonados com Shopify
Aprenda a criar um sistema completo de recuperação de carrinhos abandonados usando Shopify, WhatsApp e Email, com sequências automatizadas e análise de conversão.
O Que Você Vai Construir
Um sistema de recuperação que:
- Detecta carrinhos abandonados no Shopify em tempo real
- Envia primeira mensagem via WhatsApp após 1 hora
- Envia email de follow-up após 24 horas
- Oferece cupom de desconto após 48 horas
- Rastreia conversões e ROI
- Para a sequência automaticamente se o cliente finalizar a compra
Tempo estimado: 45 minutos Nível: Intermediário ROI esperado: +15-25% de recuperação de carrinhos
O Que Você Vai Aprender
- Integração com webhooks do Shopify
- Criação de sequências multicanal (WhatsApp + Email)
- Uso de delays e condicionais para timing perfeito
- Rastreamento de conversões
- Cancelamento de flows em andamento
- Personalização de mensagens com dados do cliente
- Geração dinâmica de cupons de desconto
Pré-requisitos
- ✅ Conta no Shopify (qualquer plano)
- ✅ Conta WhatsApp Business API configurada
- ✅ Serviço de email configurado (SendGrid, Mailgun, etc)
- ✅ Acesso ao painel administrativo do Shopify
- ✅ Conhecimento básico de JSON e webhooks
Parte 1: Configurar Webhook no Shopify
1.1. Criar o Flow
- Acesse o Lumina Flow Builder
- Crie um novo flow:
- Nome: "Recuperação de Carrinhos Abandonados"
- Descrição: "Sistema multicanal para recuperar vendas perdidas"
- Tags: "shopify", "ecommerce", "whatsapp", "email"
1.2. Obter URL do Webhook
- Adicione um nó Webhook Trigger
- Copie a URL gerada (ex:
https://api.lumina.app.br/v1/webhooks/wh_cart_abc123) - Anote esta URL - vamos configurá-la no Shopify
1.3. Configurar Webhook no Shopify
- No admin do Shopify, vá para Settings → Notifications
- Role até Webhooks e clique em Create webhook
- Configure:
- Event:
Carts update - Format:
JSON - URL: Cole a URL do webhook do Lumina
- API Version:
2024-01(ou mais recente) - Clique em Save
Parte 2: Construir a Lógica de Detecção
2.1. Validar Dados do Carrinho
Adicione um nó If/Else conectado ao webhook:
// Condições para considerar carrinho abandonado:
{{
$trigger.body.abandoned_checkout_url && // Tem URL de recuperação
$trigger.body.email && // Tem email do cliente
$trigger.body.line_items && // Tem produtos
$trigger.body.line_items.length > 0 &&
$trigger.body.total_price && // Tem valor
parseFloat($trigger.body.total_price) > 0 &&
!$trigger.body.completed_at // Não foi finalizado
}}
Nome do nó: "Validar Carrinho Abandonado"
2.2. Filtrar Carrinhos de Baixo Valor (Opcional)
Adicione outro If/Else após o primeiro:
// Apenas carrinhos acima de R$ 50
{{
parseFloat($trigger.body.total_price) >= 50
}}
Nome do nó: "Filtrar Valor Mínimo"
2.3. Extrair e Formatar Dados
Adicione um nó Transform para estruturar os dados:
{{
{
"cartId": $trigger.body.id,
"token": $trigger.body.token,
"customer": {
"email": $trigger.body.email,
"phone": $trigger.body.phone || $trigger.body.billing_address?.phone,
"firstName": $trigger.body.customer?.first_name || $trigger.body.billing_address?.first_name,
"lastName": $trigger.body.customer?.last_name || $trigger.body.billing_address?.last_name
},
"cart": {
"items": $trigger.body.line_items.map(item => ({
"title": item.title,
"variant": item.variant_title,
"quantity": item.quantity,
"price": item.price,
"image": item.properties?.image_url || ""
})),
"itemCount": $trigger.body.line_items.length,
"subtotal": $trigger.body.subtotal_price,
"total": $trigger.body.total_price,
"currency": $trigger.body.currency || "BRL"
},
"recoveryUrl": $trigger.body.abandoned_checkout_url,
"createdAt": $trigger.body.created_at,
"updatedAt": $trigger.body.updated_at
}
}}
Nome do nó: "Estruturar Dados do Carrinho"
Parte 3: Salvar no Banco de Dados
3.1. Registrar Carrinho Abandonado
Adicione um nó Database Insert:
// Tabela: abandoned_carts
{
"table": "abandoned_carts",
"data": {
"cart_id": "{{ $nodes['estruturar-dados'].output.cartId }}",
"token": "{{ $nodes['estruturar-dados'].output.token }}",
"customer_email": "{{ $nodes['estruturar-dados'].output.customer.email }}",
"customer_phone": "{{ $nodes['estruturar-dados'].output.customer.phone }}",
"customer_name": "{{ $nodes['estruturar-dados'].output.customer.firstName }} {{ $nodes['estruturar-dados'].output.customer.lastName }}",
"cart_data": "{{ JSON.stringify($nodes['estruturar-dados'].output.cart) }}",
"recovery_url": "{{ $nodes['estruturar-dados'].output.recoveryUrl }}",
"total_value": "{{ parseFloat($nodes['estruturar-dados'].output.cart.total) }}",
"status": "abandoned",
"sequence_stage": 0,
"created_at": "{{ new Date().toISOString() }}"
}
}
Nome do nó: "Salvar Carrinho no DB"
Dica: Crie a tabela no seu PostgreSQL:
CREATE TABLE abandoned_carts (
id SERIAL PRIMARY KEY,
cart_id VARCHAR(255) UNIQUE NOT NULL,
token VARCHAR(255),
customer_email VARCHAR(255) NOT NULL,
customer_phone VARCHAR(50),
customer_name VARCHAR(255),
cart_data JSONB,
recovery_url TEXT,
total_value DECIMAL(10,2),
status VARCHAR(50) DEFAULT 'abandoned',
sequence_stage INT DEFAULT 0,
recovered_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_cart_status ON abandoned_carts(status);
CREATE INDEX idx_customer_email ON abandoned_carts(customer_email);
Parte 4: Sequência de Recuperação - Etapa 1 (WhatsApp)
4.1. Aguardar 1 Hora
Adicione um nó Delay:
- Delay Time: 3600000 (1 hora em milissegundos)
- Nome: "Aguardar 1 Hora"
4.2. Verificar se Carrinho Ainda Está Abandonado
Adicione um nó Database Query:
SELECT status, cart_id
FROM abandoned_carts
WHERE cart_id = '{{ $nodes['estruturar-dados'].output.cartId }}'
AND status = 'abandoned'
LIMIT 1;
Nome do nó: "Verificar Status do Carrinho"
4.3. Condição para Continuar
Adicione um nó If/Else:
{{
$nodes['verificar-status'].output.rows &&
$nodes['verificar-status'].output.rows.length > 0 &&
$nodes['verificar-status'].output.rows[0].status === 'abandoned'
}}
Nome do nó: "Carrinho Ainda Abandonado?"
4.4. Enviar Mensagem WhatsApp
Adicione um nó WhatsApp - Send Message:
Configure a credencial do WhatsApp Business API e depois:
{
"to": "{{ $nodes['estruturar-dados'].output.customer.phone }}",
"type": "template",
"template": {
"name": "abandoned_cart_reminder",
"language": {
"code": "pt_BR"
},
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "{{ $nodes['estruturar-dados'].output.customer.firstName || 'Cliente' }}"
},
{
"type": "text",
"text": "{{ $nodes['estruturar-dados'].output.cart.itemCount }}"
},
{
"type": "text",
"text": "{{ parseFloat($nodes['estruturar-dados'].output.cart.total).toFixed(2) }}"
}
]
},
{
"type": "button",
"sub_type": "url",
"index": 0,
"parameters": [
{
"type": "text",
"text": "{{ $nodes['estruturar-dados'].output.token }}"
}
]
}
]
}
}
Nome do nó: "Enviar WhatsApp 1h"
Template sugerido (crie no Meta Business Manager):
Olá {{1}}! 👋
Notamos que você deixou {{2}} item(ns) no carrinho, no valor de R$ {{3}}.
Que tal finalizar sua compra agora? Seus produtos estão te esperando! 🛍️
*Clique no botão abaixo para recuperar seu carrinho:*
Botão: "Ver Meu Carrinho" → URL dinâmica
4.5. Atualizar Stage no DB
Adicione um nó Database Update:
{
"table": "abandoned_carts",
"data": {
"sequence_stage": 1,
"updated_at": "{{ new Date().toISOString() }}"
},
"where": {
"cart_id": "{{ $nodes['estruturar-dados'].output.cartId }}"
}
}
Nome do nó: "Atualizar para Stage 1"
Parte 5: Sequência de Recuperação - Etapa 2 (Email)
5.1. Aguardar Mais 23 Horas
Adicione outro nó Delay:
- Delay Time: 82800000 (23 horas em milissegundos)
- Nome: "Aguardar 23 Horas"
5.2. Verificar Status Novamente
Repita a lógica de verificação (Database Query + If/Else)
5.3. Enviar Email de Follow-up
Adicione um nó Send Email (configure sua credencial do SendGrid/Mailgun):
{
"to": "{{ $nodes['estruturar-dados'].output.customer.email }}",
"from": "vendas@sualojaaqui.com.br",
"fromName": "Equipe Sua Loja",
"subject": "{{ $nodes['estruturar-dados'].output.customer.firstName }}, seu carrinho está esperando por você! 🛒",
"html": `
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #5c6ac4; color: white; padding: 20px; text-align: center; }
.product { border: 1px solid #ddd; padding: 15px; margin: 10px 0; border-radius: 5px; }
.product img { max-width: 100px; float: left; margin-right: 15px; }
.cta-button {
display: inline-block;
background: #5c6ac4;
color: white;
padding: 15px 30px;
text-decoration: none;
border-radius: 5px;
margin: 20px 0;
}
.footer { text-align: center; color: #666; font-size: 12px; margin-top: 30px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Não perca esses produtos! 🎁</h1>
</div>
<p>Olá, <strong>{{ $nodes['estruturar-dados'].output.customer.firstName }}</strong>!</p>
<p>Percebemos que você deixou alguns itens incríveis no seu carrinho. Eles ainda estão disponíveis e reservados para você!</p>
<h3>Seus Produtos:</h3>
{{ $nodes['estruturar-dados'].output.cart.items.map(item => `
<div class="product">
${item.image ? `<img src="${item.image}" alt="${item.title}">` : ''}
<strong>${item.title}</strong><br>
${item.variant ? `Variação: ${item.variant}<br>` : ''}
Quantidade: ${item.quantity}<br>
Preço: R$ ${parseFloat(item.price).toFixed(2)}
</div>
`).join('') }}
<p style="font-size: 18px; margin: 20px 0;">
<strong>Total: R$ {{ parseFloat($nodes['estruturar-dados'].output.cart.total).toFixed(2) }}</strong>
</p>
<div style="text-align: center;">
<a href="{{ $nodes['estruturar-dados'].output.recoveryUrl }}" class="cta-button">
Finalizar Minha Compra Agora
</a>
</div>
<p>Dúvidas? Responda este email ou entre em contato pelo WhatsApp!</p>
<div class="footer">
<p>Você recebeu este email porque iniciou uma compra em nossa loja.</p>
<p>Sua Loja © 2025 - Todos os direitos reservados</p>
</div>
</div>
</body>
</html>
`
}
Nome do nó: "Enviar Email 24h"
5.4. Atualizar Stage
{
"table": "abandoned_carts",
"data": {
"sequence_stage": 2,
"updated_at": "{{ new Date().toISOString() }}"
},
"where": {
"cart_id": "{{ $nodes['estruturar-dados'].output.cartId }}"
}
}
Parte 6: Sequência de Recuperação - Etapa 3 (Cupom)
6.1. Aguardar Mais 24 Horas
Adicione outro nó Delay:
- Delay Time: 86400000 (24 horas)
- Nome: "Aguardar 24 Horas"
6.2. Verificar Status (Última Vez)
Repita a verificação de status
6.3. Gerar Cupom de Desconto
Adicione um nó HTTP Request para criar cupom no Shopify:
{
"method": "POST",
"url": "https://{{ $vars.shopifyStore }}.myshopify.com/admin/api/2024-01/price_rules.json",
"authentication": "headerAuth",
"headers": {
"X-Shopify-Access-Token": "{{ $credentials.shopify.accessToken }}",
"Content-Type": "application/json"
},
"body": {
"price_rule": {
"title": "Recuperação Carrinho - {{ $nodes['estruturar-dados'].output.token }}",
"target_type": "line_item",
"target_selection": "all",
"allocation_method": "across",
"value_type": "percentage",
"value": "-10.0",
"customer_selection": "prerequisite",
"prerequisite_customer_ids": ["{{ $nodes['estruturar-dados'].output.customer.id }}"],
"once_per_customer": true,
"usage_limit": 1,
"starts_at": "{{ new Date().toISOString() }}",
"ends_at": "{{ new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString() }}"
}
}
}
Nome do nó: "Criar Price Rule"
6.4. Criar Código do Cupom
Adicione outro nó HTTP Request:
{
"method": "POST",
"url": "https://{{ $vars.shopifyStore }}.myshopify.com/admin/api/2024-01/price_rules/{{ $nodes['criar-price-rule'].output.data.price_rule.id }}/discount_codes.json",
"authentication": "headerAuth",
"headers": {
"X-Shopify-Access-Token": "{{ $credentials.shopify.accessToken }}",
"Content-Type": "application/json"
},
"body": {
"discount_code": {
"code": "VOLTE10-{{ $nodes['estruturar-dados'].output.token.substring(0, 8).toUpperCase() }}"
}
}
}
Nome do nó: "Criar Código do Cupom"
6.5. Enviar WhatsApp com Cupom
{
"to": "{{ $nodes['estruturar-dados'].output.customer.phone }}",
"type": "text",
"text": {
"preview_url": true,
"body": `🎉 *ÚLTIMA CHANCE!* 🎉
Olá, {{ $nodes['estruturar-dados'].output.customer.firstName }}!
Temos uma oferta *EXCLUSIVA* para você:
*10% DE DESCONTO* no seu carrinho! 🎁
Use o cupom: *{{ $nodes['criar-codigo'].output.data.discount_code.code }}*
⏰ Válido por apenas 7 dias!
Finalize agora: {{ $nodes['estruturar-dados'].output.recoveryUrl }}
Não perca essa oportunidade! 🚀`
}
}
Nome do nó: "Enviar Cupom WhatsApp"
6.6. Enviar Email com Cupom
Similar ao email anterior, mas incluindo o código do cupom em destaque.
6.7. Atualizar para Stage Final
{
"table": "abandoned_carts",
"data": {
"sequence_stage": 3,
"coupon_code": "{{ $nodes['criar-codigo'].output.data.discount_code.code }}",
"coupon_sent_at": "{{ new Date().toISOString() }}",
"updated_at": "{{ new Date().toISOString() }}"
},
"where": {
"cart_id": "{{ $nodes['estruturar-dados'].output.cartId }}"
}
}
Parte 7: Flow de Conversão (Detectar Compra)
Crie um segundo flow separado para detectar quando o cliente finaliza a compra:
7.1. Webhook de Order Created
- No Shopify, crie outro webhook:
- Event:
Orders create - URL: Nova URL do segundo flow
7.2. Verificar se Veio de Carrinho Abandonado
// If/Else
{{
$trigger.body.cart_token && // Tem token do carrinho
$trigger.body.customer?.email // Tem email
}}
7.3. Atualizar Status no DB
{
"table": "abandoned_carts",
"data": {
"status": "recovered",
"recovered_at": "{{ new Date().toISOString() }}",
"order_id": "{{ $trigger.body.id }}",
"order_name": "{{ $trigger.body.name }}",
"final_value": "{{ $trigger.body.total_price }}",
"updated_at": "{{ new Date().toISOString() }}"
},
"where": {
"token": "{{ $trigger.body.cart_token }}",
"status": "abandoned"
}
}
7.4. Enviar Mensagem de Agradecimento
{
"to": "{{ $trigger.body.customer.phone }}",
"type": "text",
"text": {
"body": `Obrigado pela sua compra, {{ $trigger.body.customer.first_name }}! 🎉
Pedido *{{ $trigger.body.name }}* confirmado com sucesso!
Você receberá atualizações sobre o envio em breve. 📦
Qualquer dúvida, estamos aqui! 💬`
}
}
Parte 8: Dashboard de Métricas
Crie queries para analisar o desempenho:
Query 1: Taxa de Recuperação
SELECT
COUNT(*) FILTER (WHERE status = 'abandoned') as abandonados,
COUNT(*) FILTER (WHERE status = 'recovered') as recuperados,
ROUND(
(COUNT(*) FILTER (WHERE status = 'recovered')::DECIMAL /
NULLIF(COUNT(*), 0)) * 100,
2
) as taxa_recuperacao
FROM abandoned_carts
WHERE created_at >= NOW() - INTERVAL '30 days';
Query 2: Receita Recuperada
SELECT
SUM(final_value) as receita_recuperada,
COUNT(*) as pedidos_recuperados,
AVG(final_value) as ticket_medio
FROM abandoned_carts
WHERE status = 'recovered'
AND recovered_at >= NOW() - INTERVAL '30 days';
Query 3: Performance por Stage
SELECT
sequence_stage,
COUNT(*) as total,
COUNT(*) FILTER (WHERE status = 'recovered') as recuperados,
ROUND(
(COUNT(*) FILTER (WHERE status = 'recovered')::DECIMAL /
NULLIF(COUNT(*), 0)) * 100,
2
) as taxa_conversao
FROM abandoned_carts
WHERE created_at >= NOW() - INTERVAL '30 days'
GROUP BY sequence_stage
ORDER BY sequence_stage;
Flow Completo (JSON)
{
"name": "Recuperação de Carrinhos Abandonados",
"description": "Sistema multicanal para recuperar vendas perdidas",
"version": "1.0",
"variables": {
"shopifyStore": "sualojaaqui",
"minCartValue": 50,
"discountPercentage": 10,
"couponValidDays": 7
},
"trigger": {
"id": "trigger_1",
"type": "webhook",
"name": "Shopify Cart Update",
"config": {
"method": "POST",
"path": "/shopify/cart-update"
}
},
"nodes": [
{
"id": "node_1",
"type": "if-else",
"name": "Validar Carrinho Abandonado",
"config": {
"condition": "{{ $trigger.body.abandoned_checkout_url && $trigger.body.email && $trigger.body.line_items && $trigger.body.line_items.length > 0 && $trigger.body.total_price && parseFloat($trigger.body.total_price) > 0 && !$trigger.body.completed_at }}"
},
"position": { "x": 100, "y": 100 }
},
{
"id": "node_2",
"type": "if-else",
"name": "Filtrar Valor Mínimo",
"config": {
"condition": "{{ parseFloat($trigger.body.total_price) >= $vars.minCartValue }}"
},
"position": { "x": 300, "y": 100 }
},
{
"id": "node_3",
"type": "transform",
"name": "Estruturar Dados do Carrinho",
"config": {
"transform": "{{ { cartId: $trigger.body.id, token: $trigger.body.token, customer: { email: $trigger.body.email, phone: $trigger.body.phone || $trigger.body.billing_address?.phone, firstName: $trigger.body.customer?.first_name || $trigger.body.billing_address?.first_name, lastName: $trigger.body.customer?.last_name || $trigger.body.billing_address?.last_name }, cart: { items: $trigger.body.line_items.map(item => ({ title: item.title, variant: item.variant_title, quantity: item.quantity, price: item.price, image: item.properties?.image_url || '' })), itemCount: $trigger.body.line_items.length, subtotal: $trigger.body.subtotal_price, total: $trigger.body.total_price, currency: $trigger.body.currency || 'BRL' }, recoveryUrl: $trigger.body.abandoned_checkout_url, createdAt: $trigger.body.created_at, updatedAt: $trigger.body.updated_at } }}"
},
"position": { "x": 500, "y": 100 }
},
{
"id": "node_4",
"type": "database-insert",
"name": "Salvar Carrinho no DB",
"config": {
"table": "abandoned_carts",
"data": "{{ { cart_id: $nodes['node_3'].output.cartId, token: $nodes['node_3'].output.token, customer_email: $nodes['node_3'].output.customer.email, customer_phone: $nodes['node_3'].output.customer.phone, customer_name: `${$nodes['node_3'].output.customer.firstName} ${$nodes['node_3'].output.customer.lastName}`, cart_data: JSON.stringify($nodes['node_3'].output.cart), recovery_url: $nodes['node_3'].output.recoveryUrl, total_value: parseFloat($nodes['node_3'].output.cart.total), status: 'abandoned', sequence_stage: 0, created_at: new Date().toISOString() } }}"
},
"position": { "x": 700, "y": 100 }
},
{
"id": "node_5",
"type": "delay",
"name": "Aguardar 1 Hora",
"config": {
"delayTime": 3600000
},
"position": { "x": 900, "y": 100 }
},
{
"id": "node_6",
"type": "database-query",
"name": "Verificar Status do Carrinho",
"config": {
"query": "SELECT status, cart_id FROM abandoned_carts WHERE cart_id = '{{ $nodes['node_3'].output.cartId }}' AND status = 'abandoned' LIMIT 1"
},
"position": { "x": 1100, "y": 100 }
},
{
"id": "node_7",
"type": "if-else",
"name": "Carrinho Ainda Abandonado?",
"config": {
"condition": "{{ $nodes['node_6'].output.rows && $nodes['node_6'].output.rows.length > 0 && $nodes['node_6'].output.rows[0].status === 'abandoned' }}"
},
"position": { "x": 1300, "y": 100 }
},
{
"id": "node_8",
"type": "whatsapp-send-message",
"name": "Enviar WhatsApp 1h",
"config": {
"credential": "whatsapp_business",
"to": "{{ $nodes['node_3'].output.customer.phone }}",
"template": "abandoned_cart_reminder"
},
"position": { "x": 1500, "y": 100 }
},
{
"id": "node_9",
"type": "database-update",
"name": "Atualizar para Stage 1",
"config": {
"table": "abandoned_carts",
"data": "{{ { sequence_stage: 1, updated_at: new Date().toISOString() } }}",
"where": "{{ { cart_id: $nodes['node_3'].output.cartId } }}"
},
"position": { "x": 1700, "y": 100 }
},
{
"id": "node_10",
"type": "delay",
"name": "Aguardar 23 Horas",
"config": {
"delayTime": 82800000
},
"position": { "x": 1900, "y": 100 }
}
],
"edges": [
{ "source": "trigger_1", "target": "node_1" },
{ "source": "node_1", "sourceHandle": "true", "target": "node_2" },
{ "source": "node_2", "sourceHandle": "true", "target": "node_3" },
{ "source": "node_3", "target": "node_4" },
{ "source": "node_4", "target": "node_5" },
{ "source": "node_5", "target": "node_6" },
{ "source": "node_6", "target": "node_7" },
{ "source": "node_7", "sourceHandle": "true", "target": "node_8" },
{ "source": "node_8", "target": "node_9" },
{ "source": "node_9", "target": "node_10" }
]
}
Teste do Sistema
1. Teste Simulado
# Simular carrinho abandonado
curl -X POST https://api.lumina.app.br/v1/webhooks/wh_cart_abc123 \
-H "Content-Type: application/json" \
-d '{
"id": 12345678901234,
"token": "test_abc123xyz",
"email": "cliente@example.com",
"phone": "+5511999999999",
"customer": {
"first_name": "Maria",
"last_name": "Silva"
},
"line_items": [
{
"title": "Camiseta Premium",
"variant_title": "P / Azul",
"quantity": 2,
"price": "79.90"
},
{
"title": "Calça Jeans",
"variant_title": "38",
"quantity": 1,
"price": "149.90"
}
],
"subtotal_price": "309.70",
"total_price": "309.70",
"currency": "BRL",
"abandoned_checkout_url": "https://sualojaaqui.com/cart/c/test_abc123xyz",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}'
2. Verificar Logs
Acesse o dashboard do Lumina e veja:
- Webhook recebido ✅
- Dados validados ✅
- Carrinho salvo no DB ✅
- Delay de 1h iniciado ✅
3. Acelerar Teste (Desenvolvimento)
Para testar sem esperar, altere temporariamente os delays:
- 1 hora → 60 segundos (60000ms)
- 23 horas → 120 segundos (120000ms)
- 24 horas → 180 segundos (180000ms)
Troubleshooting
Problema 1: WhatsApp não envia
Causa: Template não aprovado pelo Meta Solução:
- Acesse Meta Business Manager
- Vá para WhatsApp → Message Templates
- Verifique se o template está "Approved"
- Se rejeitado, revise e reenvie para aprovação
Problema 2: Email vai para spam
Solução:
- Configure SPF, DKIM e DMARC no seu domínio
- Use serviço confiável (SendGrid, Mailgun)
- Evite palavras como "grátis", "urgente" no assunto
- Inclua link de unsubscribe
Problema 3: Cupom não funciona no checkout
Causa: Price rule mal configurada Solução:
- Verifique se a price rule foi criada com sucesso
- Confirme que o customer_id está correto
- Teste o cupom manualmente no Shopify
Problema 4: Flow não para após compra
Causa: Flow de conversão não está funcionando Solução:
- Verifique se o webhook "Orders create" está ativo
- Confirme que o cart_token está sendo capturado
- Teste manualmente atualizando o status no DB
Problema 5: Muitos carrinhos abandonados falsos
Causa: Bots ou usuários apenas navegando Solução:
- Aumente o valor mínimo do carrinho
- Adicione delay maior antes da primeira mensagem
- Implemente filtro de email descartável:
// No nó de validação, adicione:
{{
!$trigger.body.email.includes('tempmail') &&
!$trigger.body.email.includes('guerrillamail') &&
!$trigger.body.email.includes('10minutemail')
}}
Otimizações e Melhorias
1. Personalização por Categoria
// No nó Transform, adicione análise de produtos:
{{
{
...dados,
"category": $trigger.body.line_items[0].product_type,
"isHighValue": parseFloat($trigger.body.total_price) > 500
}
}}
Depois, crie mensagens diferentes para cada categoria.
2. A/B Testing
Crie dois flows idênticos mas com:
- Diferentes % de desconto (5% vs 10%)
- Diferentes timings (1h vs 2h)
- Diferentes mensagens
Compare as taxas de conversão.
3. Segmentação por Histórico
-- Verificar se é cliente recorrente
SELECT COUNT(*) as previous_orders
FROM orders
WHERE customer_email = '{{ $nodes['estruturar-dados'].output.customer.email }}'
AND created_at < NOW() - INTERVAL '30 days';
Ofereça desconto maior para novos clientes.
4. Urgência e Escassez
Adicione na mensagem:
- "Últimas unidades em estoque!"
- "Preço válido apenas até hoje!"
- "5 pessoas estão vendo este produto agora"
5. Recomendações de Produtos
Use a Shopify API para buscar produtos relacionados:
// HTTP Request
GET https://{{ $vars.shopifyStore }}.myshopify.com/admin/api/2024-01/products.json?collection_id={{ $trigger.body.line_items[0].collection_id }}&limit=3
Inclua no email de 24h.
Métricas de Sucesso
KPIs para Acompanhar
- Taxa de Recuperação Geral: Meta 15-25%
- Taxa de Abertura WhatsApp: Meta >80%
- Taxa de Abertura Email: Meta >20%
- Conversão com Cupom: Meta >30%
- ROI do Sistema: Receita recuperada / Custo de operação
- Tempo Médio até Conversão: Menor é melhor
Dashboard Sugerido
// Crie um flow agendado (1x por dia) que gera relatório:
{
"metricas_30_dias": {
"carrinhos_abandonados": 450,
"recuperados": 85,
"taxa_recuperacao": "18.9%",
"receita_recuperada": "R$ 12.450,00",
"ticket_medio_recuperado": "R$ 146,47",
"roi": "8.5x",
"por_stage": {
"stage_1_whatsapp": "12% conversão",
"stage_2_email": "5% conversão",
"stage_3_cupom": "2% conversão"
}
}
}
Próximos Passos
Depois de dominar este tutorial, explore:
- Campanha Multicanal - Coordene múltiplos canais
- CRM Lead Scoring - Pontue e qualifique leads
- Feedback Loop - Colete e analise feedback
Recursos Adicionais
- Shopify Webhooks: https://shopify.dev/docs/api/admin-rest/2024-01/resources/webhook
- WhatsApp Templates: https://developers.facebook.com/docs/whatsapp/message-templates
- SendGrid Best Practices: https://sendgrid.com/docs/ui/sending-email/deliverability/
Precisa de Ajuda?
- Documentação: Integração Shopify
- Comunidade: Discord do Lumina
- Suporte: support@lumina.app.br
Última atualização: Janeiro 2025 Versão do tutorial: 1.0