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 Configuri un URL del tuo server nella Dashboard di WAzion
- 2 Selezioni quali eventi vuoi ricevere
- 3 Quando si verifica un evento, WAzion invia un POST al tuo URL
- 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
PrincipaleSi 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
Dopo il tentativo 6, il webhook viene segnalato come errore permanente.
Risposte accettate
Qualsiasi codice 2xx è considerato un successo.
Codici che attivano il ritentativo
Codici senza ritento (errore permanente)
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
I webhook NON seguono i reindirizzamenti. Il tuo endpoint deve rispondere direttamente senza reindirizzare.
6. Codice di Esempio Completo
<?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");
}
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.