UC-008 — Tracking della Consegna con Webhooks
| Campo | Valore |
|---|---|
| ID | UC-008 |
| Obiettivo | Registrare un webhook, ricevere callback di delivery/read/inbound e gestire il ciclo di vita |
| Canale | Tutti (SMS, RCS, WhatsApp) |
| Complessità | ⭐⭐ Intermedio |
| Tempo stimato | 20 minuti |
| API coinvolte | POST /api/partner-gateway/v1/webhooks/delivery-status, GET /webhooks/delivery-status, PUT /webhooks/delivery-status, DELETE /webhooks/delivery-status |
Scenari reali
- LogisticaExpress — Dashboard real-time: Il pannello operativo mostra in tempo reale lo stato di migliaia di notifiche di spedizione. Ogni webhook aggiorna il contatore di consegnati/falliti.
- AssicuraPlus — SLA monitoring: Il sistema misura il tempo tra invio e consegna per ogni canale, generando alert se il tempo supera la soglia contrattuale di 60 secondi.
- FarmaOnline — Audit log: Ogni evento di delivery, lettura e risposta viene registrato nel database per compliance normativa e analisi post-campagna.
Flusso webhook
Il diagramma illustra il ciclo completo: registri il webhook, invii un messaggio, il carrier consegna, e la tua app riceve la notifica in tempo reale.
Step 1 — Registra il webhook
Registra il tuo endpoint HTTPS per ricevere le notifiche di delivery.
curl -X POST https://lora-api.agiletelecom.com/api/partner-gateway/v1/webhooks/delivery-status \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"callbackUrl": "https://api.logisticaexpress.it/webhooks/delivery"
}'
Response — Webhook registrato
{
"id": "wh-7f8a9b0c",
"callbackUrl": "https://api.logisticaexpress.it/webhooks/delivery",
"status": "ACTIVE",
"createdAt": "2026-04-09T09:00:00Z"
}
:::info Un solo webhook attivo E consentito un solo webhook di delivery-status per account. Crearne uno nuovo sostituisce automaticamente la configurazione precedente. :::
Verifica la configurazione attuale
curl -X GET https://lora-api.agiletelecom.com/api/partner-gateway/v1/webhooks/delivery-status \
-H "X-Api-Key: YOUR_API_KEY"
{
"id": "wh-7f8a9b0c",
"callbackUrl": "https://api.logisticaexpress.it/webhooks/delivery",
"status": "ACTIVE",
"createdAt": "2026-04-09T09:00:00Z"
}
Step 2 — Invia un messaggio di test
Invia un messaggio con simulation: true per testare il flusso senza costi reali.
curl -X POST https://lora-api.agiletelecom.com/api/message-server/whatsapp/send \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"destination": "+393401234567",
"phoneNumberId": 5,
"template": {
"id": 42
},
"placeholders": {
"nome": "Giulia"
},
"enableNotification": true,
"simulation": true,
"messageId": "test-webhook-001"
}'
Response — Messaggio di test accettato
{
"messageId": "test-webhook-001",
"simulation": true,
"results": {
"whatsapp": {
"accepted": true
},
"rcs": null,
"sms": null
}
}
:::tip Test locale con ngrok
Per testare i webhook in locale, usa ngrok per esporre il tuo server: ngrok http 3000. Registra la URL HTTPS generata come callback.
:::
Step 3 — Ricevi le callback
Il sistema invia POST request al tuo callbackUrl per ogni cambio di stato. Ecco i payload per i tre principali tipi di evento.
Payload DELIVERY
Notifica di consegna (o mancata consegna) del messaggio:
{
"eventType": "DELIVERY",
"messageId": "test-webhook-001",
"customerMessageId": "test-webhook-001",
"destination": "+393401234567",
"statusCode": 3,
"description": "Message delivered",
"channel": "WHATSAPP",
"timestamp": "2026-04-09T10:30:00Z"
}
Payload READ (solo WhatsApp/RCS)
Notifica di lettura — il destinatario ha aperto il messaggio:
{
"eventType": "READ",
"messageId": "test-webhook-001",
"destination": "+393401234567",
"channel": "WHATSAPP",
"timestamp": "2026-04-09T10:31:15Z"
}
Payload INBOUND
Il destinatario ha risposto al messaggio:
{
"eventType": "INBOUND",
"source": "+393401234567",
"destination": "+393209998877",
"text": "Si, confermo l'appuntamento di giovedi alle 15:30",
"channel": "WHATSAPP",
"messageType": "TEXT",
"timestamp": "2026-04-09T10:32:00Z"
}
Codici di stato
| statusCode | Significato |
|---|---|
3 | Consegnato |
6 | Non recapitabile |
8 | Scaduto |
Step 4 — Aggiorna il webhook
Modifica la URL di callback senza eliminare e ricreare la configurazione.
curl -X PUT https://lora-api.agiletelecom.com/api/partner-gateway/v1/webhooks/delivery-status \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"callbackUrl": "https://api.logisticaexpress.it/webhooks/v2/delivery"
}'
Response — Webhook aggiornato
{
"id": "wh-7f8a9b0c",
"callbackUrl": "https://api.logisticaexpress.it/webhooks/v2/delivery",
"status": "ACTIVE",
"updatedAt": "2026-04-09T11:00:00Z"
}
Step 5 — Revoca il webhook
Quando non hai più bisogno delle notifiche in tempo reale, elimina la configurazione.
curl -X DELETE https://lora-api.agiletelecom.com/api/partner-gateway/v1/webhooks/delivery-status \
-H "X-Api-Key: YOUR_API_KEY"
Response — Webhook eliminato
{
"deleted": true
}
Dopo la revoca, le notifiche di delivery non verranno più inviate. Potrai comunque consultare lo stato dei messaggi tramite polling.
Esempio — Webhook handler Node.js
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/delivery', (req, res) => {
const { eventType, messageId, statusCode, channel, destination } = req.body;
switch (eventType) {
case 'DELIVERY':
if (statusCode === 3) {
console.log(`[DELIVERED] ${messageId} via ${channel} a ${destination}`);
// Aggiorna il database: segna il messaggio come consegnato
// db.messages.update(messageId, { status: 'delivered', channel });
} else if (statusCode === 6) {
console.log(`[FAILED] ${messageId} via ${channel} a ${destination}`);
// Gestisci il fallimento: riprova o notifica l'operatore
// alertService.notify(`Messaggio ${messageId} non recapitabile`);
} else if (statusCode === 8) {
console.log(`[EXPIRED] ${messageId} via ${channel}`);
// Il messaggio e scaduto prima della consegna
}
break;
case 'READ':
console.log(`[READ] ${messageId} letto dal destinatario`);
// db.messages.update(messageId, { readAt: req.body.timestamp });
break;
case 'INBOUND':
console.log(`[INBOUND] Da ${req.body.source}: ${req.body.text}`);
// Processa la risposta: auto-reply, inoltra al CRM, ecc.
// crmService.createTicket(req.body.source, req.body.text);
break;
default:
console.log(`[UNKNOWN] Evento non gestito: ${eventType}`);
}
// Rispondi sempre velocemente - elabora in modo asincrono se necessario
res.sendStatus(200);
});
app.listen(3000, () => {
console.log('Webhook handler in ascolto sulla porta 3000');
});
Polling vs Webhook
| Aspetto | Polling | Webhook |
|---|---|---|
| Latenza | Dipende dalla frequenza di polling | Quasi real-time |
| Complessità | Semplice (solo richieste GET) | Richiede endpoint HTTPS pubblico |
| Scalabilita | Aumenta le chiamate API a volume alto | Push-based, nessuna chiamata extra |
| Costi API | Ogni poll consuma una chiamata | Nessun consumo aggiuntivo |
| Affidabilita | Nessun rischio di perdita eventi | Richiede gestione retry e idempotenza |
| Ideale per | Basso volume, controlli ad-hoc | Alto volume, dashboard real-time |
Dietro le quinte — Meccanismo di delivery dei webhook
Il sistema di webhook funziona con un modello at-least-once delivery:
- Registrazione: Quando registri un webhook, il sistema verifica che la URL sia raggiungibile con un test ping. Se il ping fallisce, la registrazione viene comunque accettata ma riceverai un warning.
- Dispatch: Ad ogni cambio di stato del messaggio (accettato, consegnato, letto, fallito), il sistema accoda un evento per il tuo webhook.
- Retry: Se la tua callback URL risponde con un codice diverso da
2xx, il sistema ritenta fino a 5 volte con backoff esponenziale (1s, 5s, 30s, 2min, 10min). - Circuit breaker: Se il tuo endpoint fallisce per oltre 50 chiamate consecutive, il webhook viene temporaneamente disattivato e riattivato dopo 30 minuti.
- Ordinamento: Gli eventi sono inviati in ordine cronologico per singolo messaggio, ma eventi di messaggi diversi possono arrivare in ordine sparso.
Best practices
- Usa HTTPS — La callback URL deve usare HTTPS per la sicurezza dei dati in transito.
- Rispondi rapidamente — Restituisci
200 OKentro pochi secondi. Elabora il payload in modo asincrono se serve. - Gestisci i retry — Il sistema ritenta in caso di errore. Rendi l'elaborazione idempotente usando il
messageIdcome chiave. - Valida la sorgente — Verifica che le richieste webhook provengano effettivamente da Qlara Platform API.
- Monitora i fallimenti — Se il tuo endpoint fallisce costantemente, il circuit breaker lo disattivera temporaneamente.
Risultato atteso
| Step | Azione | Risultato |
|---|---|---|
| 1 | POST /webhooks/delivery-status | Webhook registrato con stato ACTIVE |
| 2 | POST /whatsapp/send con simulation: true | Messaggio di test accettato |
| 3 | Callback ricevuta | Payload DELIVERY, READ o INBOUND |
| 4 | PUT /webhooks/delivery-status | URL di callback aggiornata |
| 5 | DELETE /webhooks/delivery-status | Webhook revocato, notifiche interrotte |
Prossimi passi
- Guida Webhooks: Approfondisci configurazione, testing locale e best practices
- UC-005 — Multi-Channel Fallback: Combina fallback multi-canale con tracking webhook
- Panoramica canali: Scopri quali eventi sono disponibili per ogni canale