Webhook: segnalare eventi ai tuoi sistemi

Dopo aver letto questo articolo avrai configurato un webhook che, per gli eventi che hai scelto, richiama il tuo indirizzo — con chiave di firma, consegna di prova, registro e nuovi tentativi automatici in caso di errore.

Un webhook inverte la direzione: non è il tuo sistema a chiedere informazioni a Univents, ma Univents che si fa sentire dal tuo sistema non appena succede qualcosa — arriva una richiesta, un preventivo viene accettato o viene emessa una fattura. Così programmi, script o servizi di automazione propri si collegano al tuo workspace, senza che nessuno debba controllare periodicamente.

Si configura nel workspace stesso, in Integrazioni → Sviluppatori → Webhook. Configurazione, chiave e registro sono visibili solo al ruolo Titolare — lì è indicato infatti quali indirizzi Univents richiama.

Quando conviene un webhook

Usa un webhook quando il tuo sistema deve reagire subito: riportare una richiesta nel tuo CRM, far partire la spedizione in un sistema di magazzino, registrare una fattura nella tua contabilità, aggiornare un tabellone informativo. Per tutto ciò che deve restare all’interno di Univents, il posto giusto sono invece le Automazioni.

Aggiungere un endpoint

  1. Clicca su Integrazioni nel menu principale a sinistra, poi sulla categoria Sviluppatori a sinistra e apri la scheda Webhook.
  2. Clicca in alto a destra su Aggiungi webhook.
  3. Inserisci in Nome qualcosa che riconoscerai in seguito, ad esempio CRM-Sync.
  4. Inserisci in Indirizzo l’indirizzo completo https:// del tuo destinatario. Solo https — gli indirizzi non cifrati vengono rifiutati. Univents rifiuta anche gli indirizzi nella tua rete locale, su localhost o in una rete interna.
  5. Seleziona gli Eventi che questo destinatario deve ricevere.
  6. Salva. Univents ti mostra ora la chiave di firma (Una tantum).

La chiave di firma

La chiave inizia con whsec_. Viene mostrata esattamente una volta — alla creazione e alla rigenerazione. Dopo vedi solo l’inizio, così nel workspace resta chiaro quale chiave è registrata, senza che il valore stesso giri in chiaro. Copiala quindi subito nel tuo sistema (variabile d’ambiente, secret store).

Con Rigenera chiave di firma ne ottieni una nuova in qualsiasi momento. Subito dopo, la vecchia non è più valida — sostituiscila dal tuo lato nello stesso momento, altrimenti il tuo destinatario rifiuterà i messaggi in arrivo.

Verificare la firma

Univents invia con ogni consegna tre header:

HeaderContenuto
X-Univents-Signaturet=<Zeitstempel>,v1=<Signatur>
X-Univents-Eventla chiave del trigger, ad esempio inquiry_created
X-Univents-Delivery-Idl’identificativo di questa consegna

La firma è un HMAC-SHA256 sul testo ${t}.${Body} — quindi timestamp, un punto e il body grezzo, byte per byte esattamente come arriva. Importante: verifica il body grezzo, non uno riserializzato. Un JSON.parse seguito da JSON.stringify cambia l’ordine dei campi e gli escape, e la verifica fallisce anche se il messaggio è a posto.

Ecco come appare la verifica in Node:

const crypto = require('node:crypto');
const express = require('express');

const app = express();

// Roher Body — NICHT express.json() davor.
app.post('/webhooks/dein-system', express.raw({ type: '*/*' }), (req, res) => {
  const header = req.get('X-Univents-Signature') ?? '';
  const parts = Object.fromEntries(
    header.split(',').map((piece) => piece.trim().split('=')),
  );

  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(`${parts.t}.${req.body.toString('utf8')}`)
    .digest('hex');

  const received = parts.v1 ?? '';

  // Länge zuerst prüfen: timingSafeEqual wirft bei ungleicher Länge.
  const ok =
    received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));

  if (!ok) return res.status(400).send('signature mismatch');

  // Der Zeitstempel liegt unter der Signatur — so kann niemand eine alte
  // Meldung erneut einspielen.
  const ageSeconds = Math.abs(Date.now() / 1000 - Number(parts.t));
  if (ageSeconds > 300) return res.status(400).send('stale');

  const event = JSON.parse(req.body.toString('utf8'));
  console.log(req.get('X-Univents-Event'), event);

  res.sendStatus(200);
});

Rispondi con 200 (o un’altra risposta nell’intervallo 2xx) non appena hai accettato il messaggio. Qualsiasi altra risposta, o una risposta che impiega più di dieci secondi, conta come tentativo fallito.

Consegne: cosa succede in caso di errore

La consegna avviene almeno una volta — non esattamente una volta. Un’interruzione della connessione lungo il percorso può far sì che la stessa operazione ti arrivi una seconda volta. Costruisci quindi il tuo destinatario in modo che una doppia chiamata non rompa nulla: confronta X-Univents-Delivery-Id con gli ultimi identificativi elaborati prima di creare qualcosa.

Se il tuo destinatario non risponde o risponde con un errore, Univents riprova. In totale sono previsti otto tentativi, a intervalli crescenti:

TentativoIntervallo dal precedente
1immediato
21 minuto
35 minuti
430 minuti
52 ore
66 ore
712 ore
824 ore

Dopodiché il messaggio è considerato definitivamente fallito e non viene più ritentato. Se il tuo sistema è in manutenzione programmata, hai così quasi due giorni per tornare raggiungibile.

Consegna di prova

Con Invia test invii un messaggio a un endpoint senza aspettare un evento reale. Così, dopo la configurazione, verifichi che indirizzo, verifica della firma e percorso di risposta funzionino. La consegna di prova compare poi nel registro, così non la confondi con un messaggio reale.

Un endpoint In pausa non riceve consegne di prova — attivalo prima. E poiché la consegna di prova è l’unico modo con cui puoi far chiamare al nostro server un indirizzo da te scelto, vale un limite di dieci consegne di prova per workspace e ora. Dopo, l’interfaccia ti avvisa di riprovare più tardi.

Registro e nuova consegna

Sotto ogni endpoint trovi le ultime 50 consegne. Per ogni riga vedi:

  • lo stato — in attesa, in corso, consegnato o definitivamente fallito,
  • l’evento,
  • il numero di tentativi (ad esempio 3/8),
  • lo stato della risposta del tuo destinatario.

Con Mostra contenuto vedi il messaggio inviato, con Dettagli l’ultimo errore e il momento del prossimo tentativo. Univents conserva la risposta del tuo destinatario in forma abbreviata come materiale diagnostico.

Invia di nuovo reinvia un messaggio consegnato o definitivamente fallito — utile se il tuo sistema è stato assente solo per poco. Finché una consegna è in corso o ancora in attesa, il pulsante è bloccato, in modo che non nascano due chiamate parallele.

Mettere in pausa ed eliminare

L’interruttore sull’endpoint lo mette In pausa: Univents non recapita più nulla subito, ma la configurazione resta — ideale per un sistema in ristrutturazione. Univents legge lo stato a ogni consegna, quindi la pausa ha effetto subito e non solo al prossimo evento.

Elimina rimuove l’endpoint insieme al suo registro. Univents chiede conferma prima.

Eventi validi

Si può scegliere tra gli stessi eventi che conoscono anche le automazioni. La lista nella finestra cresce: non appena Univents segnala un evento in un nuovo punto, è disponibile anche per i webhook. Un evento senza spunta non viene consegnato.

Cosa contiene il messaggio

Ogni messaggio ha la stessa busta:

{
  "id": "48213",
  "type": "inquiry_created",
  "created_at": "2026-09-28T10:12:33.000Z",
  "data": { "…": "…" }
}
  • id — l’identificativo di questa consegna, identico all’header X-Univents-Delivery-Id. Usalo come chiave di idempotenza.
  • type — la chiave del trigger (inquiry_created, event_confirmed, …).
  • created_at — quando è stata costruita la busta, non quando è avvenuto l’evento.
  • data — il contenuto di dominio, per ogni evento secondo un elenco fisso.

In data c’è solo ciò che è in questo elenco. I campi interni e tutto ciò che potrebbe contenere credenziali o testo libero vengono omessi — tra cui legacy_*, stripe_* (identificativi di pagamento), *token*, *secret*, *_hash*, pdf_url, campi e-mail e telefono, note/descrizioni e interruttori interni (is_*, created_by, deleted_at). Un campo senza valore viene omesso, non inviato come null. Le nuove colonne delle nostre tabelle non compaiono qui da sole; le aggiungiamo deliberatamente una per una.

Ogni versione di data contiene entity_type e entity_id — così puoi assegnare il messaggio anche quando l’evento non ha un record proprio.

Richieste

EventoCampi in data
inquiry_createdid, event_id, contact_id, location_id, booking_page_id, booking_request_state, event_name, event_date, event_end_date, guest_count, estimated_value, account_id, created_at, updated_at
quote_acceptedi campi di inquiry_created e quelli di un preventivo (vedi Documenti finanziari)

Eventi

EventoCampi in data
event_confirmed, event_cancelledid, name, guest_count, budget, start_date, end_date, date_is_confirmed, language, event_state, location_id, room_id, contact_id, account_id, created_at, updated_at
event_t_minuscome sopra, in aggiunta days_before, weekday
event_t_pluscome sopra, in aggiunta days_after

Documenti finanziari

Vale per invoice_overdue, invoice_paid, quote_sent, quote_expired e quote_rejected; per quote_rejected in aggiunta source.

id, event_id, contact_id, doc_type, document_number, title, version, quote_state, invoice_state, issue_date, due_date, sent_date, accepted_date, rejected_date, expires_at, net_total, gross_total, tax_total, discount_total, deposit_amount, currency, linked_order_id, prior_version_id, account_id, created_at, updated_at

Altri eventi

EventoCampi in data
payment_receivedid, booking_request_id, event_id, contact_id, booking_order_state, total_net, total_gross, total_tax, discount_total, currency, payment_method, paid_at, account_id, created_at, updated_at
contract_signedid, event_id, contact_id, title, contract_doc_type, contract_state, version, sent_at, expires_at, has_signed_pdf, account_id, created_at, updated_at
email_receivedcontact_id, received_at, has_attachments, has_contact_match — nessun contenuto della mail, nessun oggetto
schedule_monthlyday_of_month, target_date, period_start, period_end, period_label, account_id

La consegna di prova usa la stessa busta con type: "webhook_test" e data: { test, endpoint_id, sent_at }.

Un evento per il quale qui non c’è un elenco arriva con data vuoto: meglio una busta vuota ma visibile che un record grezzo.

Limiti

  • Solo https, nessun reindirizzamento: se il tuo server risponde con un reindirizzamento, conta come tentativo fallito. Inserisci l’indirizzo definitivo.
  • Dieci secondi per tentativo. Il lavoro più lungo va dopo la risposta: accetta il messaggio, rispondi con 200 e poi elaboralo.
  • Il Contenuto del messaggio descrive il record che ha scatenato l’evento — trattalo come dati clienti del tuo workspace e non registrarlo altrove senza controllo.

Newsletter di Univents

Novità di prodotto e consigli pratici per attività di eventi e catering. Circa una volta al mese, non di più.

La newsletter viene inviata in inglese.