Pular para conteúdo

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:

  1. Detecta carrinhos abandonados no Shopify em tempo real
  2. Envia primeira mensagem via WhatsApp após 1 hora
  3. Envia email de follow-up após 24 horas
  4. Oferece cupom de desconto após 48 horas
  5. Rastreia conversões e ROI
  6. 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

  1. Acesse o Lumina Flow Builder
  2. Crie um novo flow:
  3. Nome: "Recuperação de Carrinhos Abandonados"
  4. Descrição: "Sistema multicanal para recuperar vendas perdidas"
  5. Tags: "shopify", "ecommerce", "whatsapp", "email"

1.2. Obter URL do Webhook

  1. Adicione um nó Webhook Trigger
  2. Copie a URL gerada (ex: https://api.lumina.app.br/v1/webhooks/wh_cart_abc123)
  3. Anote esta URL - vamos configurá-la no Shopify

1.3. Configurar Webhook no Shopify

  1. No admin do Shopify, vá para SettingsNotifications
  2. Role até Webhooks e clique em Create webhook
  3. Configure:
  4. Event: Carts update
  5. Format: JSON
  6. URL: Cole a URL do webhook do Lumina
  7. API Version: 2024-01 (ou mais recente)
  8. 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

  1. No Shopify, crie outro webhook:
  2. Event: Orders create
  3. 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:

  1. Acesse Meta Business Manager
  2. Vá para WhatsApp → Message Templates
  3. Verifique se o template está "Approved"
  4. Se rejeitado, revise e reenvie para aprovação

Problema 2: Email vai para spam

Solução:

  1. Configure SPF, DKIM e DMARC no seu domínio
  2. Use serviço confiável (SendGrid, Mailgun)
  3. Evite palavras como "grátis", "urgente" no assunto
  4. Inclua link de unsubscribe

Problema 3: Cupom não funciona no checkout

Causa: Price rule mal configurada Solução:

  1. Verifique se a price rule foi criada com sucesso
  2. Confirme que o customer_id está correto
  3. 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:

  1. Verifique se o webhook "Orders create" está ativo
  2. Confirme que o cart_token está sendo capturado
  3. Teste manualmente atualizando o status no DB

Problema 5: Muitos carrinhos abandonados falsos

Causa: Bots ou usuários apenas navegando Solução:

  1. Aumente o valor mínimo do carrinho
  2. Adicione delay maior antes da primeira mensagem
  3. 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

  1. Taxa de Recuperação Geral: Meta 15-25%
  2. Taxa de Abertura WhatsApp: Meta >80%
  3. Taxa de Abertura Email: Meta >20%
  4. Conversão com Cupom: Meta >30%
  5. ROI do Sistema: Receita recuperada / Custo de operação
  6. 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:

  1. Campanha Multicanal - Coordene múltiplos canais
  2. CRM Lead Scoring - Pontue e qualifique leads
  3. Feedback Loop - Colete e analise feedback

Recursos Adicionais

Precisa de Ajuda?


Última atualização: Janeiro 2025 Versão do tutorial: 1.0