Webhook
Inoltra gli eventi della tua organizzazione verso un sistema esterno con firma HMAC e nuovi tentativi automatici.
I webhook ti permettono di ricevere notifiche asincrone quando succede qualcosa nell'organizzazione SmartCQ. Sono utili se hai un CRM o un gestionale già in uso e vuoi mantenerlo allineato con quello che SmartCQ osserva, per esempio una disiscrizione o il completamento di una campagna. La consegna avviene tramite una coda e una finestra di elaborazione: non è un canale sincrono né una garanzia di latenza in tempo reale.
Quando usarli
- Hai un CRM esterno: vuoi che si aggiorni quando un contatto cambia stato email (bounce, unsubscribe, segnalazione spam).
- Hai una dashboard custom o un data warehouse: vuoi ricevere gli eventi senza fare polling continuo.
- Vuoi un audit trail interno: registri ogni cambio rilevante in un sistema terzo.
Eventi disponibili
| Evento | Quando si genera | Payload |
|---|---|---|
contact.created | Uno o più contatti vengono creati (a mano, da import, da API, da moduli esterni) | { records: [{ contactId, before: null, after, changedFields }] } |
contact.updated | Uno o più contatti cambiano: anagrafica, fase, stato email, assegnatario, archiviazione, ripristino, unione duplicati | { records: [{ contactId, before, after, changedFields }] } |
contact.deleted | Uno o più contatti vengono eliminati (cestino) o assorbiti da un'unione duplicati | { records: [{ contactId, before, after: null, changedFields }] } |
contact.email_status_changed | Lo stato email di un contatto cambia (bounce, unsubscribe, segnalazione spam, riattivazione) | { contactId, email, from, to, reason, webhookEventType? } |
campaign.completed | Una campagna ha finito il dispatch (tutti i send chiamati, qualunque esito) | { campaignId, name, kind, completedAt, stats } |
Eventi con diff — gli eventi
contact.created/updated/deletedviaggiano in batch: un evento può contenere più record (es. un import CSV ne genera uno per blocco, non uno per contatto). Per ogni record,beforeeaftercontengono i soli campi cambiati (changedFieldsli elenca);contact.createdha lo snapshot completo inafter,contact.deleteduno snapshot compatto inbefore. Per il record completo, interroga l'API con ilcontactId.
Nota —
contact.email_status_changedcopre in modo unificato bounce, segnalazione spam e disiscrizione. Il consumer leggefrometoper capire la transizione. Non ci sono eventi paralleliemail.bounced,email.complained,email.unsubscribed.
Puoi sottoscriverti a un evento specifico, a tutti gli eventi di uno scope (contact.*, campaign.*) o a tutti gli eventi (*).
Configurare un webhook
- Vai su Impostazioni → Integrazioni e API → Webhook.
- Clicca Aggiungi webhook.
- Scegli il tipo di evento, inserisci l'URL del tuo endpoint e un'etichetta opzionale.
- SmartCQ genera un secret HMAC unico per quel webhook. Copialo subito: dopo non sarà più mostrato.
La gestione dei webhook è riservata al Titolare. L'URL deve usare HTTPS, essere pubblicamente raggiungibile e accettare richieste POST con corpo JSON; indirizzi privati, locali o riservati vengono rifiutati.
Come arrivano gli eventi
SmartCQ invia una richiesta POST al tuo URL con questi header:
Content-Type: application/json
User-Agent: SmartCQ-Webhook/1
x-smartcq-signature: sha256=<hex>
x-smartcq-event-id: <uuid>
x-smartcq-event-type: <type>
x-smartcq-event-version: <number>
x-smartcq-delivery-id: <uuid>
Il body ha questa struttura:
{
"id": "<event-uuid>",
"type": "contact.email_status_changed",
"version": 1,
"tenantId": "<tenant-uuid>",
"createdAt": "2026-05-10T08:00:00.000Z",
"payload": {
"contactId": "...",
"email": "...",
"from": "active",
"to": "bounced",
"reason": "webhook",
"webhookEventType": "email.bounced"
}
}
Verifica firma HMAC
L'header x-smartcq-signature è calcolato come sha256=<hex(HMAC-SHA256(body, secret))> sul body raw della richiesta.
Esempio Node.js:
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody: string, header: string, secret: string): boolean {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(header);
if (a.length !== b.length) return false;
return timingSafeEqual(a, b);
}
Esempio Python (Flask):
import hmac, hashlib
def verify(raw_body: bytes, header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)
Rispondi con un 2xx entro 8 secondi: se vai oltre o rispondi con errore, SmartCQ riprova.
Gestire i duplicati
Ogni delivery ha un x-smartcq-delivery-id univoco. Se il tuo endpoint è retry-safe, puoi ignorarlo. Altrimenti, salva l'id ricevuto e scarta i duplicati che hanno lo stesso x-smartcq-event-id (uguale per tutti i tentativi sullo stesso evento).
Nuovi tentativi e finestra di consegna
Se la consegna fallisce per un errore di rete o una risposta diversa da 2xx, SmartCQ programma fino a quattro nuovi tentativi. Le attese indicate sono il momento dal quale il tentativo diventa disponibile, non un orario di consegna garantito:
| Tentativo | Attesa minima |
|---|---|
| 1° nuovo tentativo | non prima di 1 minuto |
| 2° nuovo tentativo | non prima di 5 minuti |
| 3° nuovo tentativo | non prima di 30 minuti |
| 4° nuovo tentativo | non prima di 2 ore |
Dopo cinque tentativi complessivi falliti — il primo invio e quattro nuovi tentativi — la consegna entra in stato parked e non viene più riprovata. La trovi nella sezione Ultime consegne della pagina Webhook.
Nota — Oggi SmartCQ elabora la coda una volta al giorno, nella finestra pianificata delle 06:02 UTC. Un tentativo già disponibile viene quindi eseguito nel primo giro giornaliero utile. I recapiti sono indipendenti per ogni webhook: un indirizzo lento o irraggiungibile non ritarda gli altri.
Disabilitare e cancellare
- Disabilita un webhook: gli eventi successivi non vengono più inoltrati a quell'URL. Il webhook resta in lista, riattivabile in qualsiasi momento.
- Elimina: rimuove definitivamente la configurazione. Gli eventi storici restano visibili per tracciabilità, ma non vengono consegnati ad altri indirizzi.
Buone pratiche
- Mantieni l'endpoint snello: rispondi 2xx velocemente e accoda l'elaborazione vera in secondo piano.
- Registra
x-smartcq-event-idper tracciare cosa hai ricevuto. - Non fidarti dell'ordine: gli eventi possono arrivare fuori ordine se uno è andato in retry. Riconcilia leggendo il timestamp
createdAt. - Controlla la versione: l'header
x-smartcq-event-versionindica lo schema del payload. SmartCQ aumenta la versione quando introduce una modifica incompatibile.
Vuoi farlo nella tua area SmartCQ?
Webhook si gestisce direttamente nella dashboard.