Ricevere eventi webhook

Setup, struttura payload e esempio server
Visualizza come Markdown

Userbot invia eventi al tuo server tramite HTTP POST quando accadono cose rilevanti: un nuovo messaggio, una conversazione avviata, un cambio di stato, un aggiornamento variabili. Non serve fare polling — il tuo backend riceve una push ogni volta che qualcosa cambia.

Perché i webhook sono obbligatori

Le integrazioni Userbot non devono costruire la UI chat dalla risposta REST di POST /v1/bots/{bot_key}/messages/. L’agente può generare più messaggi per ogni input; dopo un’escalation i messaggi dell’operatore devono comparire in tempo reale. Tutto questo arriva sul webhook registrato in dashboard — in particolare tramite l’evento message.added.

In produzione:

  1. Registra un endpoint HTTPS in dashboard Userbot
  2. Invia messaggi REST con webhookOnly: true
  3. Aggiorna la UI solo dagli eventi webhook verificati (HMAC)

Il parametro webhookUrl nel body REST esiste solo per test locali — non sostituisce il webhook in dashboard.

Per il flusso completo passo passo, vedi Quickstart e Concetti chiave.

Per ricevere gli eventi devi esporre un endpoint HTTPS raggiungibile pubblicamente da internet. Userbot non consegna webhook a localhost o URL HTTP non sicuri.

Setup

La configurazione richiede tre passi:

  1. Crea un endpoint sul tuo server che accetti POST JSON e risponda 200 entro 5 secondi
  2. Registra l’URL nella dashboard Userbot — Userbot ti fornirà un Webhook Secret per la verifica delle firme (mostrato una sola volta in creazione, salvalo subito)
  3. Implementa la verifica HMAC — ogni richiesta va validata prima di processare il payload (Verifica firma HMAC)

Il tuo endpoint deve rispondere con status 200 entro 5 secondi. Se rispondi in ritardo o con errore, Userbot ritenta la consegna — e potresti ricevere lo stesso evento più volte. Per questo serve la deduplicazione per eventId.

Headers della richiesta

Ogni webhook include header che identificano l’evento e permettono la verifica della firma:

HeaderDescrizione
x-userbot-signatureFirma HMAC-SHA256 del payload (hex) — confrontala con il calcolo locale
x-userbot-timestampTimestamp Unix in secondi — verifica che sia entro ±5 minuti (anti-replay)
x-userbot-event-idID univoco dell’evento — usa per deduplicazione
x-userbot-attemptNumero del tentativo (1, 2, 3…) — utile per capire se è un retry

Struttura del body

Tutti gli eventi condividono una struttura comune. Il campo data varia in base al tipo di evento:

1{
2 "event": "message.added",
3 "eventId": "evt_abc123",
4 "sessionId": 48291,
5 "createdAt": 1715500800,
6 "data": { }
7}
CampoTipoDescrizione
eventstringTipo di evento — determina la struttura di data
eventIdstringIdentificativo univoco dell’evento — obbligatorio per deduplicazione
sessionIdnumberID numerico della sessione (opzionale per user.variables.created)
createdAtnumberTimestamp Unix di creazione dell’evento
dataobjectPayload specifico dell’evento — messaggio, stato, variabili, ecc.

Per tipi di evento, campi in data ed esempi di payload per ciascun evento, consulta Userbot Webhook Events nella tab API Reference.

I valori di state negli eventi webhook (agent_assigned, bot_assigned) differiscono dalla nomenclatura usata nelle API REST di aggiornamento stato (escalated, bot). Tieni conto del mapping nella tua integrazione — vedi Concetti chiave.

Esempio server

Gli esempi sotto mostrano un handler completo: verifica HMAC, controllo timestamp, deduplicazione implicita (log per evento) e risposta 200. Adattali al tuo framework e al tuo secret manager.

1const express = require("express");
2const crypto = require("crypto");
3
4const app = express();
5const WEBHOOK_SECRET = "il_tuo_webhook_secret";
6
7app.use(express.json({
8 verify: (req, _res, buf) => { req.rawBody = buf; },
9}));
10
11app.post("/webhook", (req, res) => {
12 const signature = req.headers["x-userbot-signature"];
13 const timestamp = req.headers["x-userbot-timestamp"];
14
15 if (!signature || !timestamp) {
16 return res.status(401).json({ error: "Missing signature headers" });
17 }
18
19 const now = Math.floor(Date.now() / 1000);
20 if (Math.abs(now - parseInt(timestamp, 10)) > 300) {
21 return res.status(401).json({ error: "Timestamp expired" });
22 }
23
24 const rawBody = req.rawBody.toString("utf8");
25 const expected = crypto
26 .createHmac("sha256", WEBHOOK_SECRET)
27 .update(`${timestamp}.${rawBody}`)
28 .digest("hex");
29
30 const sigBuf = Buffer.from(signature, "hex");
31 const expectedBuf = Buffer.from(expected, "hex");
32 if (sigBuf.length !== expectedBuf.length ||
33 !crypto.timingSafeEqual(sigBuf, expectedBuf)) {
34 return res.status(401).json({ error: "Invalid signature" });
35 }
36
37 const { event, sessionId, data } = req.body;
38 switch (event) {
39 case "message.added":
40 console.log(`Nuovo messaggio in sessione ${sessionId}:`, data);
41 break;
42 case "conversation.created":
43 console.log(`Nuova conversazione: ${sessionId}`);
44 break;
45 case "conversation.state_changed":
46 console.log(`Stato cambiato: ${sessionId}${data.state}`);
47 break;
48 case "conversation.archived":
49 console.log(`Conversazione archiviata: ${sessionId}`);
50 break;
51 case "user.variables.updated":
52 console.log(`Variabili aggiornate per sessione ${sessionId}:`, data);
53 break;
54 default:
55 console.log(`Evento sconosciuto: ${event}`);
56 }
57
58 res.status(200).json({ status: "ok" });
59});
60
61app.listen(3000);

Per un tutorial end-to-end che collega REST, webhook e HMAC, vedi Tutorial: prima integrazione.