CENTRO DE AJUDA

Webhooks

Receba notificações HTTP automáticas no seu servidor quando ocorrerem eventos importantes na WAzion.

1. Introdução

Os webhooks permitem que seu servidor receba notificações automáticas quando eventos específicos ocorrem no WAzion. Em vez de consultar periodicamente se há novidades, o WAzion envia uma requisição HTTP POST ao seu servidor quando algo relevante acontece.

Como funciona?

  1. 1 Você configura uma URL do seu servidor no Painel de Controle da WAzion
  2. 2 Você seleciona quais eventos deseja receber
  3. 3 Quando ocorre um evento, a WAzion envia um POST para o seu URL
  4. 4 Seu servidor processa o evento e responde com HTTP 200

Casos de uso

  • Sincronizar novos contatos com o seu CRM
  • Criar leads automaticamente
  • Disparar automações em Zapier/Make
  • Notificar aos sistemas internos

2. Eventos Disponíveis

phone.detected

Principal

Dispara-se quando é detectado um novo número de telefone em uma conversa. Útil para capturar leads automaticamente.

Payload de exemplo

{
  "event_type": "phone.detected",
  "phone": "+34612345678",
  "detected_at": "2025-01-15T14:30:00Z",
  "shop_id": 123,
  "conversation_hash": "a1b2c3d4e5f6..."
}
Campos do payload
event_type Tipo de evento
phone Número detectado (E.164)
detected_at Data/hora ISO 8601
shop_id ID da sua loja na WAzion
conversation_hash Hash único da conversa

followup.detected

Acompanhamento

Dispara quando o sistema de Acompanhamento Inteligente detecta uma conversa com intenção de compra. Inclui a mensagem de acompanhamento sugerida e o resultado da análise.

Payload de exemplo

{
  "event": "followup.detected",
  "event_type": "followup.detected",
  "shop_id": 123,
  "phone": "+34612345678",
  "intent_level": "high",
  "product_mentioned": "Funda iPhone 15 negro",
  "customer_objection": "Preguntó por el precio de envío",
  "outcome": "abandoned",
  "suggested_message": "Hola, vi que te interesaba la funda...",
  "shopify_order_found": false,
  "crm_order_found": false,
  "attempt_number": 1,
  "timestamp": "2026-02-14T10:00:00Z"
}
Campos do payload
intent_level Nível de intenção: médio ou alto
product_mentioned Produto mencionado (pode ser nulo)
outcome Estado: abandonado, incerto, convertido, apenas_suporte
suggested_message Mensagem de acompanhamento gerada pela IA
attempt_number Número de tentativa (1-3)

followup.replied

Acompanhamento

Dispara quando um cliente responde à mensagem de acompanhamento enviada pelo Acompanhamento Inteligente.

Payload de exemplo

{
  "event": "followup.replied",
  "event_type": "followup.replied",
  "phone": "+34612345678",
  "intent_level": "high",
  "product_mentioned": "Funda iPhone 15 negro",
  "attempt_number": 1,
  "followup_message": "Hola, vi que te interesaba la funda...",
  "days_to_reply": 2.5
}
Campos do payload
intent_level Nível de intenção: médio ou alto
product_mentioned Produto mencionado (pode ser nulo)
followup_message Mensagem de acompanhamento que foi enviada ao cliente
days_to_reply Dias decorridos desde o acompanhamento até a resposta

followup.converted

Acompanhamento

Dispara quando um cliente realiza uma compra após receber uma mensagem de acompanhamento. A compra é detectada via Shopify, WooCommerce, PrestaShop, VTEX ou seu endpoint CRM de verificação.

Payload de exemplo

{
  "event": "followup.converted",
  "event_type": "followup.converted",
  "phone": "+34612345678",
  "intent_level": "high",
  "product_mentioned": "Funda iPhone 15 negro",
  "attempt_number": 1,
  "followup_message": "Hola, vi que te interesaba la funda...",
  "days_to_reply": 3.0
}
Campos do payload
intent_level Nível de intenção: médio ou alto
product_mentioned Produto mencionado (pode ser nulo)
followup_message Mensagem de acompanhamento que foi enviada ao cliente
days_to_reply Dias decorridos desde o acompanhamento até a compra

plugin_chat.session_closed

Chat Web

Dispara quando uma sessão do widget de chat web termina. Inclui um resumo gerado por IA, o histórico completo de mensagens e a pontuação de satisfação do cliente.

Payload de exemplo

{
  "event_type": "plugin_chat.session_closed",
  "shop_id": 123,
  "session_id": "chat_abc123",
  "ai_summary": "Customer asked about shipping times and return policy. Satisfied with the answers provided.",
  "messages": [
    { "role": "user", "content": "How long does shipping take?", "timestamp": "2026-01-15T14:30:00Z" },
    { "role": "assistant", "content": "Shipping usually takes 3-5 business days.", "timestamp": "2026-01-15T14:30:05Z" }
  ],
  "satisfaction_score": 4.5,
  "closed_at": "2026-01-15T14:35:00Z"
}
Campos do payload
event_type Tipo de evento
shop_id ID da sua loja na WAzion
session_id Identificador único da sessão de chat
ai_summary Resumo da conversa gerado por IA
messages Histórico completo de mensagens (papel, conteúdo, timestamp)
satisfaction_score Pontuação de satisfação do cliente (1-5)
closed_at Data/hora ISO 8601 de fechamento da sessão

test

Evento de teste que você pode acionar manualmente a partir do Painel para verificar se seu endpoint está funcionando corretamente.

{
  "event_type": "test",
  "message": "This is a test webhook from WAzion",
  "timestamp": "2025-01-15T14:30:00+01:00",
  "shop_id": 123,
  "test": true
}

3. Cabeçalhos HTTP

WAzion envia os seguintes headers em cada requisição webhook:

Header Descrição Exemplo
Content-Type Tipo de conteúdo application/json
User-Agent Identificador da WAzion WAzion-Webhooks/1.0
X-Webhook-ID ID único do webhook (formato wh_XXXXXXXX) wh_00012345
X-Webhook-Event Tipo de evento phone.detected
X-Webhook-Attempt Número de tentativa (1-6) 1
X-Webhook-Timestamp Unix timestamp de envio 1705329000
X-Webhook-Signature Assinatura HMAC-SHA256 hex (se houver segredo) a1b2c3d4e5f6...

4. Assinatura HMAC-SHA256

Se você configurar um webhook secret, a WAzion assinará cada pedido usando HMAC-SHA256. Isso permite que você verifique se o pedido realmente vem da WAzion.

Como se gera a assinatura

// 1. Concatenar timestamp + "." + payload JSON
signature_payload = timestamp + "." + payload_json

// 2. Calcular HMAC-SHA256 y convertir a hexadecimal
signature = HMAC-SHA256(signature_payload, webhook_secret).toHex()

// 3. El header X-Webhook-Signature contiene la firma directamente:
"a1b2c3d4e5f6789..."  // Sin prefijo, solo el hash hex

Verificação em PHP

<?php
function verificarFirmaWebhook($payload, $signatureRecibida, $secret, $timestamp) {
    // Verificar que el timestamp no sea muy antiguo (5 min)
    if (abs(time() - intval($timestamp)) > 300) {
        return false;
    }

    // Construir payload firmado
    $signedPayload = $timestamp . '.' . $payload;

    // Calcular firma esperada
    $expectedSignature = hash_hmac('sha256', $signedPayload, $secret);

    // Comparar de forma segura (timing-safe)
    return hash_equals($expectedSignature, $signatureRecibida);
}

// Uso:
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$secret = 'tu-webhook-secret';

if (!verificarFirmaWebhook($payload, $signature, $secret, $timestamp)) {
    http_response_code(401);
    exit('Invalid signature');
}

Verificação em Node.js

const crypto = require('crypto');

function verificarFirmaWebhook(payload, signatureRecibida, secret, timestamp) {
    // Verificar que el timestamp no sea muy antiguo (5 min)
    const currentTime = Math.floor(Date.now() / 1000);
    if (Math.abs(currentTime - parseInt(timestamp)) > 300) {
        return false;
    }

    // Construir payload firmado
    const signedPayload = `${timestamp}.${payload}`;

    // Calcular firma esperada
    const expectedSignature = crypto
        .createHmac('sha256', secret)
        .update(signedPayload)
        .digest('hex');

    // Comparar de forma segura (timing-safe)
    return crypto.timingSafeEqual(
        Buffer.from(signatureRecibida),
        Buffer.from(expectedSignature)
    );
}

Importante: Também deves verificar que o timestamp não seja muito antigo (ex: máximo 5 minutos) para prevenir ataques de replay.

5. Especificações Técnicas

Tempos limite

Tempo esgotado por solicitação 180 segundos
Máximo na fila 100 webhooks

Repetições

Máximo de tentativas 6
Estratégia Recuo exponencial

Calendário de novas tentativas

Tento 1
Imediato
Tento 2
1 min
Tento 3
5 min
Tento 4
15 min
Tento 5
1 hora
Tento 6
4 horas

Após a tentativa 6, o webhook é marcado como falha permanente.

Respostas aceitas

200 OK 201 Created 202 Accepted 204 No Content

Qualquer código 2xx é considerado sucesso.

Códigos que ativam nova tentativa

408 Timeout 429 Rate Limit 5xx Server Error

Códigos sem reintento (falha permanente)

400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found

Desduplicação automática

WAzion evita enviar o mesmo número de telefone duas vezes. É armazenado um hash SHA256 de cada telefone enviado. Se o telefone já foi notificado anteriormente, não é enviado novamente.

Limitação de Taxa

Máximo 100 webhooks na fila por loja. Se o limite for ultrapassado, os novos webhooks serão descartados. O processador envia no máximo 50 webhooks por execução com 1 segundo de atraso entre cada um.

Configuração de conexão

Tempo limite total: 180s
Tempo limite de conexão: 5s
SSL verificado: Sim
Redirecionamentos: Não

Os webhooks NÃO seguem redirecionamentos. Seu endpoint deve responder diretamente sem redirecionar.

6. Código de Exemplo Completo

PHP
<?php
// webhook-handler.php

header('Content-Type: application/json');

// Configuración
$webhookSecret = 'tu-webhook-secret'; // Deja vacío si no usas firma

// Obtener datos
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$eventType = $_SERVER['HTTP_X_WEBHOOK_EVENT'] ?? '';

// Verificar firma (si hay secret configurado)
if ($webhookSecret) {
    $expectedSignature = hash_hmac(
        'sha256',
        $timestamp . '.' . $payload,
        $webhookSecret
    );

    if (!hash_equals($expectedSignature, $signature)) {
        http_response_code(401);
        echo json_encode(['error' => 'Invalid signature']);
        exit;
    }

    // Verificar timestamp (máximo 5 minutos de antigüedad)
    if (abs(time() - intval($timestamp)) > 300) {
        http_response_code(401);
        echo json_encode(['error' => 'Timestamp too old']);
        exit;
    }
}

// Decodificar payload
$data = json_decode($payload, true);

// Procesar según tipo de evento
switch ($eventType) {
    case 'phone.detected':
        $phone = $data['phone'];
        $shopId = $data['shop_id'];

        // Guardar en tu base de datos, CRM, etc.
        guardarNuevoLead($phone, $shopId);

        echo json_encode(['status' => 'ok', 'message' => 'Lead saved']);
        break;

    case 'followup.detected':
        // Seguimiento Inteligente: cliente con intención detectada
        $phone = $data['phone'];
        $intentLevel = $data['intent_level']; // "high" o "medium"
        $product = $data['product_mentioned'] ?? null;
        $suggestedMsg = $data['suggested_message'] ?? null;

        // Ejemplo: crear tarea en tu CRM
        procesarSeguimiento($phone, $intentLevel, $product, $suggestedMsg);

        echo json_encode(['status' => 'ok', 'message' => 'Follow-up processed']);
        break;

    case 'followup.replied':
        // El cliente respondió al mensaje de seguimiento
        error_log("Cliente {$data['phone']} respondió al seguimiento (día {$data['days_to_reply']})");
        echo json_encode(['status' => 'ok', 'message' => 'Reply tracked']);
        break;

    case 'followup.converted':
        // El cliente compró tras recibir el seguimiento
        error_log("Conversión: {$data['phone']} compró tras seguimiento (día {$data['days_to_reply']})");
        echo json_encode(['status' => 'ok', 'message' => 'Conversion tracked']);
        break;

    case 'plugin_chat.session_closed':
        // Sesión del chat web finalizada
        $summary = $data['ai_summary'];
        $messages = $data['messages'];
        $score = $data['satisfaction_score'];

        // Guardar resumen y puntuación en tu sistema
        error_log("Chat cerrado (score: $score): $summary");

        echo json_encode(['status' => 'ok', 'message' => 'Chat session logged']);
        break;

    case 'test':
        // Evento de prueba
        echo json_encode(['status' => 'ok', 'message' => 'Test received']);
        break;

    default:
        echo json_encode(['status' => 'ok', 'message' => 'Event not handled']);
}

function guardarNuevoLead($phone, $shopId) {
    // Tu lógica aquí: guardar en BD, enviar a CRM, etc.
    error_log("Nuevo lead: $phone de shop $shopId");
}

function procesarSeguimiento($phone, $intentLevel, $product, $suggestedMsg) {
    // Tu lógica aquí: crear tarea, notificar al equipo, etc.
    error_log("Follow-up $intentLevel: $phone" . ($product ? " - $product" : ""));
}
Node.js / Express
const express = require('express');
const crypto = require('crypto');

const app = express();
const WEBHOOK_SECRET = 'tu-webhook-secret'; // Deja vacío si no usas firma

// Middleware para raw body
app.use('/webhook', express.raw({ type: 'application/json' }));

app.post('/webhook', (req, res) => {
    const payload = req.body.toString();
    const signature = req.headers['x-webhook-signature'] || '';
    const timestamp = req.headers['x-webhook-timestamp'] || '';
    const eventType = req.headers['x-webhook-event'] || '';

    // Verificar firma (si hay secret)
    if (WEBHOOK_SECRET) {
        const expectedSignature = crypto
            .createHmac('sha256', WEBHOOK_SECRET)
            .update(`${timestamp}.${payload}`)
            .digest('hex');

        const sigBuffer = Buffer.from(signature);
        const expectedBuffer = Buffer.from(expectedSignature);

        if (sigBuffer.length !== expectedBuffer.length ||
            !crypto.timingSafeEqual(sigBuffer, expectedBuffer)) {
            return res.status(401).json({ error: 'Invalid signature' });
        }

        // Verificar timestamp
        if (Math.abs(Date.now() / 1000 - parseInt(timestamp)) > 300) {
            return res.status(401).json({ error: 'Timestamp too old' });
        }
    }

    const data = JSON.parse(payload);

    // Procesar evento
    switch (eventType) {
        case 'phone.detected':
            console.log(`Nuevo lead: ${data.phone} de shop ${data.shop_id}`);
            // Tu lógica aquí
            break;

        case 'followup.detected':
            console.log(`Follow-up ${data.intent_level}: ${data.phone}`);
            if (data.product_mentioned) console.log(`Producto: ${data.product_mentioned}`);
            // Crear tarea en tu CRM, notificar al equipo, etc.
            break;

        case 'followup.replied':
            console.log(`Cliente ${data.phone} respondió al seguimiento (día ${data.days_to_reply})`);
            // Actualizar estado del lead en tu CRM
            break;

        case 'followup.converted':
            console.log(`Conversión: ${data.phone} compró tras seguimiento (día ${data.days_to_reply})`);
            // Registrar conversión, calcular ROI, etc.
            break;

        case 'plugin_chat.session_closed':
            console.log(`Chat cerrado (score: ${data.satisfaction_score}): ${data.ai_summary}`);
            console.log(`Mensajes: ${data.messages.length}`);
            // Guardar resumen y puntuación en tu sistema
            break;

        case 'test':
            console.log('Test webhook received');
            break;
    }

    res.json({ status: 'ok' });
});

app.listen(3000, () => console.log('Webhook server running on port 3000'));

Histórico de alterações

Sem alterações recentes nesta documentação.

Artigos relacionados

Assistente WAzion

Informações comerciais e suporte técnico

Olá! Sou o assistente da WAzion. Posso ajudá-lo com informações sobre preços e planos, dúvidas técnicas, configuração ou qualquer pergunta sobre o nosso produto. Como posso ajudá-lo?
Desenvolvido com WAzion AI