CENTRO ASSISTENZA

Webhooks

Ricevi notifiche HTTP automatiche sul tuo server quando si verificano eventi importanti in WAzion.

1. Introduzione

I webhook permettono al tuo server di ricevere notifiche automatiche quando si verificano eventi specifici in WAzion. Invece di interrogare periodicamente se ci sono novità, WAzion invia una richiesta HTTP POST al tuo server quando accade qualcosa di rilevante.

Come funziona?

  1. 1 Configuri un URL del tuo server nella Dashboard di WAzion
  2. 2 Selezioni quali eventi vuoi ricevere
  3. 3 Quando si verifica un evento, WAzion invia un POST al tuo URL
  4. 4 Il tuo server elabora l’evento e risponde con HTTP 200

Casi d’uso

  • Sincronizzare nuovi contatti con il tuo CRM
  • Creare lead automaticamente
  • Attivare automazioni in Zapier/Make
  • Notificare ai sistemi interni

2. Eventi Disponibili

phone.detected

Principale

Si attiva quando viene rilevato un nuovo numero di telefono in una conversazione. Utile per acquisire automaticamente lead.

Payload di esempio

{
  "event_type": "phone.detected",
  "phone": "+34612345678",
  "detected_at": "2025-01-15T14:30:00Z",
  "shop_id": 123,
  "conversation_hash": "a1b2c3d4e5f6..."
}
Campi del payload
event_type Tipo di evento
phone Numero rilevato (E.164)
detected_at Data/ora ISO 8601
shop_id ID del tuo negozio su WAzion
conversation_hash Hash unico della conversazione

test

Evento di prova che puoi attivare manualmente dal Dashboard per verificare che il tuo endpoint funzioni correttamente.

{
  "event_type": "test",
  "message": "Este es un webhook de prueba desde WAzion",
  "timestamp": "2025-01-15T14:30:00+01:00",
  "shop_id": 123,
  "test": true
}

3. Header HTTP

WAzion invia i seguenti header in ogni richiesta webhook:

Header Descrizione Esempio
Content-Type Tipo di contenuto application/json
User-Agent Identificatore di WAzion WAzion-Webhooks/1.0
X-Webhook-ID ID univoco del webhook (formato wh_XXXXXXXX) wh_00012345
X-Webhook-Event Tipo di evento phone.detected
X-Webhook-Attempt Numero di tentativo (1-6) 1
X-Webhook-Timestamp Timestamp Unix di invio 1705329000
X-Webhook-Signature Firma HMAC-SHA256 esadecimale (se c’è un segreto) a1b2c3d4e5f6...

4. Firma HMAC-SHA256

Se configuri un webhook secret, WAzion firmerà ogni richiesta utilizzando HMAC-SHA256. Questo ti permette di verificare che la richiesta provenga realmente da WAzion.

Come si genera la firma

// 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 in 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('Firma inválida');
}

Verifica in 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: Dovresti anche verificare che il timestamp non sia troppo vecchio (es: massimo 5 minuti) per prevenire attacchi di replay.

5. Specifiche Tecniche

Timeout

Timeout per richiesta 10 secondi
Massimo in coda 100 webhooks

Ritenti

Numero massimo di tentativi 6
Strategia Backoff esponenziale

Calendario di ritentativi

Tentativo 1
Immediato
Tentativo 2
1 min
Tentativo 3
5 min
Tentativo 4
15 min
Tentativo 5
1 hora
Tentativo 6
4 horas

Dopo il tentativo 6, il webhook viene segnalato come errore permanente.

Risposte accettate

200 OK 201 Created 202 Accepted 204 No Content

Qualsiasi codice 2xx è considerato un successo.

Codici che attivano il ritentativo

408 Timeout 429 Rate Limit 5xx Server Error

Codici senza ritento (errore permanente)

400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found

De-duplicazione automatica

WAzion evita di inviare lo stesso numero di telefono due volte. Viene memorizzato un hash SHA256 di ogni telefono inviato. Se il telefono è stato già notificato in precedenza, non viene inviato di nuovo.

Limitazione della velocità

Massimo 100 webhook in coda per negozio. Se si supera il limite, i nuovi webhook vengono scartati. Il processore invia massimo 50 webhook per esecuzione con 1 secondo di intervallo tra ciascuno.

Configurazione della connessione

Timeout totale: 10s
Timeout di connessione: 5s
SSL verificato:
Reindirizzamenti: No

I webhook NON seguono i reindirizzamenti. Il tuo endpoint deve rispondere direttamente senza reindirizzare.

6. Codice di Esempio 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 '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");
}
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 'test':
            console.log('Test webhook received');
            break;
    }

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

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

Cronologia delle modifiche

Nessuna modifica recente in questa documentazione.

Articoli correlati

Assistente WAzion

Informazioni commerciali e supporto tecnico

Ciao! Sono l’assistente di WAzion. Posso aiutarti con informazioni su prezzi e piani, dubbi tecnici, configurazione o qualsiasi domanda sul nostro prodotto. Come posso aiutarti?
Sviluppato con WAzion AI