De Palma Labs, distributore per l'Italia della piattaforma AYROMEX, Meta Tech Provider

Webhook

Ogni cliente può avere un URL di webhook, che riceve una POST JSON per ogni evento.

curl -X PUT https://api.depalmalabs.it/v1/customers/cus_xxxxxxxxxxxx/webhook \
  -H "Authorization: Bearer $DPL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://tuo-gestionale.it/whatsapp/webhook"}'

# Il secret (whsec_...) è restituito solo alla prima configurazione o con "rotate_secret": true

Eventi

TipoQuando
message.statusCambio di stato di un messaggio inviato: sent, delivered, read, failed. Include categoria e se è a pagamento.
message.receivedMessaggio in arrivo dal destinatario (testo, media, risposta a pulsante). Apre la finestra di 24 ore.
template.statusTemplate approvato, rifiutato (con motivo), in pausa o disattivato da Meta.
customer.qualityVariazione della qualità del numero (GREEN, YELLOW, RED) e del limite di invio. Con RED il marketing va in pausa automatica.
customer.statusCliente collegato, in pausa, bloccato o di nuovo attivo; pagamento Meta mancante o ripristinato.

Firma

Ogni richiesta ha l'header X-DPL-Signature: t=<unix>,v1=<hex>, dove v1 è l'HMAC-SHA256 di t + "." + corpo calcolato con il secret del webhook. Verifica la firma sul corpo grezzo, confronta in tempo costante e rifiuta timestamp più vecchi di 5 minuti.

<?php
// Webhook De Palma Labs: verifica firma, poi elabora. Rispondi 2xx entro 10 secondi.
$secret = getenv('DPL_WEBHOOK_SECRET'); // whsec_...
if (!$secret) { http_response_code(500); exit; } // senza secret ogni firma sarebbe falsificabile
$body = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_DPL_SIGNATURE'] ?? '';

parse_str(str_replace(',', '&', $header), $parts); // t=...&v1=...
$t = (int)($parts['t'] ?? 0);
$expected = hash_hmac('sha256', $t . '.' . $body, $secret);

if (!$t || abs(time() - $t) > 300 || !hash_equals($expected, (string)($parts['v1'] ?? ''))) {
    http_response_code(401);
    exit;
}

$event = json_decode($body, true);
// Deduplica: lo stesso evento può arrivare più di una volta (consegna "almeno una volta").
// if (giaElaborato($event['id'])) { http_response_code(200); exit; }

switch ($event['type']) {
    case 'message.status':   // sent, delivered, read, failed
        aggiornaStato($event['data']['message_id'], $event['data']['status']);
        break;
    case 'message.received': // risposta del cliente
        salvaRisposta($event['data']['from'], $event['data']['text']);
        break;
    case 'template.status':  // APPROVED, REJECTED...
        aggiornaTemplate($event['data']['name'], $event['data']['status']);
        break;
    case 'customer.quality': // GREEN, YELLOW, RED e limite di invio
    case 'customer.status':  // attivo, in pausa, pagamento Meta mancante...
        aggiornaCliente($event['customer_id'], $event['type'], $event['data']);
        break;
}
http_response_code(200);

Consegna e tentativi

  • Rispondi con un codice 2xx entro 10 secondi; se ti serve più tempo, elabora in coda.
  • In caso di errore ritentiamo dopo 30 s, 2 min, 10 min, 30 min, 1 h, 3 h, 6 h e 12 h (circa un giorno in tutto). Dopo l'ultimo tentativo l'evento resta visibile nel portale come non consegnato.
  • Consegna "almeno una volta": deduplica sull'id dell'evento. L'ordine non è garantito: usa created_at e tieni lo stato più avanzato.
  • Solo URL https pubblici (porta 443 o 8443). I redirect non vengono seguiti.