Sviluppatori

Webhook di fine chiamata

A fine telefonata l'agente vocale manda al tuo server un JSON firmato con i dati della chiamata, il contatto e le informazioni estratte.

Vedi come Markdown

#A cosa serve

Con il webhook di fine chiamata puoi:

  • salvare ogni chiamata, con esito e riepilogo, nel tuo CRM o gestionale;
  • aprire una pratica o un ticket con i dati che l'agente ha raccolto;
  • passare la chiamata a Zapier o Make, e da lì a centinaia di app.
La chiamata finisceL'analisi prepara esito, riepilogo e dati estratti.
→
SupportFast inviaUna richiesta POST con il JSON, firmata con la tua chiave.
→
Il tuo server riceveControlla la firma, risponde e salva i dati.

Prima di iniziare

  • La funzione «Webhook di fine chiamata» del piano.
  • Un indirizzo HTTPS raggiungibile da Internet che accetti richieste POST e risponda entro 10 secondi.

#Attivarlo

  1. Apri il passo Post Chiamata

    Nell'agente vocale vai in Profilo AgenteModifica ProfiloPost Chiamata: è il quarto passo della configurazione. Ci arrivi anche dal riepilogo del Profilo Agente, con il pulsante dell'area Azioni Post Chiamata, che si chiama Modifica, o Configura se l'area è ancora da impostare.

  2. Accendi il webhook

    Nel riquadro Webhook attiva Attiva il webhook e scrivi l'URL webhook, che deve iniziare con https://.

  3. Scegli le opzioni

    Invia solo per chiamate completate, acceso di serie, manda il webhook solo per le chiamate riuscite, spiegate più sotto. Includi URL della registrazione audio, acceso di serie, aggiunge il link dell'audio. Se la registrazione è spenta nel passo Generali, il link resta vuoto.

  4. Salva e copia la chiave

    Premi Salva modifiche: la chiave viene creata in quel momento. Nel riquadro Chiave Segreta Webhook (Secret HMAC) premi Mostra la chiave o Copia la chiave e mettila nella configurazione del tuo server.

Con Rigenera la chiave ne crei una nuova. La conferma avvisa che «Le firme fatte con la chiave attuale non saranno più valide: aggiorna il sistema che riceve il webhook.». Lettura e rigenerazione valgono subito, senza salvare.

Passo Post Chiamata dell'agente vocale: webhook acceso con l'URL https://www.esempio.it/webhook/fine-chiamata, le opzioni Invia solo per chiamate completate e Includi URL della registrazione accese, e la chiave segreta nascosta con Mostra, Copia e Rigenera.
Nel passo Post Chiamata accendi il webhook, scrivi l’URL e copia la chiave segreta.

#Quando parte

Il webhook parte dopo l'analisi di fine chiamata: esito, riepilogo, sentiment e variabili estratte sono già pronti nel JSON.

Una chiamata è riuscita quando qualcuno ha risposto e ha detto almeno una parola, oppure quando è stata trasferita a un collega. Una segreteria, o una risposta senza parole, non conta. È la stessa definizione di Chiamate: elenco e dettaglio, e nel JSON corrisponde a call.reached uguale a true.

  • Con Invia solo per chiamate completate acceso, il webhook parte solo per le chiamate riuscite, comprese le trasferite: nonostante il nome, non conta lo stato «Completata».
  • Con l'opzione spenta parte per ogni chiamata conclusa, anche mai risposta o finita in segreteria. In quei casi call.reached vale false e call.endReason dice perché la chiamata si è chiusa.

#Cosa riceve il tuo server

Una richiesta POST con il corpo in JSON e questi header:

HeaderValore
Content-Typeapplication/json
X-Webhook-Signaturesha256= seguito dalla firma HMAC SHA-256 del corpo, in esadecimale.
X-Webhook-Call-IdL'identificativo della chiamata, lo stesso di call.id.

JSON · esempio con dati di prova

{
  "call": {
    "customerNumber": "+390212345678",
    "agentNumber": "+390612345678",
    "direction": "inbound",
    "startedAt": "19/10/2026 10:12",
    "endedAt": "19/10/2026 10:15",
    "duration": "3 min 5 s",
    "reached": true,
    "result": "appointment_booked",
    "resultText": "Il cliente ha prenotato un tagliando.",
    "sentiment": "positive",
    "summary": "Mario Rossi chiede un tagliando. L'agente propone giovedì 22 ottobre alle 9:30 e il cliente accetta.",
    "transcript": "AI: Buongiorno, Officina Rossi, come posso aiutarla?\n\nHUMAN: Vorrei prenotare un tagliando.",
    "transferName": null,
    "recordingUrl": "https://www.esempio.it/registrazione-di-esempio.mp3",
    "appointment": {
      "startsAt": "22/10/2026 09:30",
      "calendarName": "Tagliando",
      "startsAtIso": "2026-10-22T07:30:00.000Z",
      "uid": "prenotazione-esempio-123"
    },
    "variables": { "nome_cliente": "Mario Rossi", "targa": "AB123CD" },
    "id": "chiamata-esempio-001",
    "conversationId": "conversazione-esempio-001",
    "status": "ended",
    "endReason": "agent_endcall",
    "errorDetail": null,
    "durationSec": 185,
    "startedAtIso": "2026-10-19T08:12:04.000Z",
    "endedAtIso": "2026-10-19T08:15:09.000Z"
  },
  "messages": [
    { "role": "assistant", "content": "Buongiorno, Officina Rossi, come posso aiutarla?" },
    { "role": "user", "content": "Vorrei prenotare un tagliando." }
  ],
  "contact": {
    "fullName": "Mario Rossi",
    "email": "mario.rossi@esempio.it",
    "phone": "+390212345678"
  },
  "event": "telephone.ended",
  "variables": { "nome_cliente": "Mario Rossi", "targa": "AB123CD" }
}
event Testo

Sempre telephone.ended.

call Oggetto

I dati della chiamata, con gli stessi nomi delle variabili {{call.*}}: cosa contiene ogni campo è in Dati della chiamata. I campi leggibili sono nel fuso del workspace; quelli con Iso sono date in UTC. I campi in codice hanno questi valori:

  • direction: inbound o outbound;
  • reached: true se la chiamata è riuscita, come spiegato in Quando parte, altrimenti false;
  • result: resolved, unresolved, abandoned, appointment_booked, transferred, no_interaction, no_answer, voicemail o spam;
  • sentiment: positive, neutral o negative;
  • status: ended, failed o transferred;
  • endReason, il motivo di chiusura: agent_endcall (chiusa dall'agente), user_hangup (riagganciata dal cliente), timeout (durata massima raggiunta), silence_timeout (silenzio prolungato), transfer (trasferita), voicemail (segreteria telefonica), no_answer (nessuna risposta), busy (occupato), rejected (rifiutata), invalid_number (numero non valido), blocked (bloccata dalla compagnia telefonica), sip_error (errore di linea) o error (errore tecnico).

La durata si conta dalla risposta, senza lo squillo: per una chiamata mai risposta durationSec vale 0 e duration è vuoto.

messages Lista

La conversazione, messaggio per messaggio: role vale assistant per l'agente e user per il cliente.

contact Oggetto o null

Nome completo, email e telefono del contatto. È null quando alla chiamata non è legato un contatto, per esempio se chi chiama ha il numero nascosto.

variables Oggetto

Le variabili estratte dalla chiamata, con i nomi che hai scelto in Estrazione Dati e Variabili. Vuoto se l'estrazione è spenta. Lo stesso oggetto è anche in call.variables.

Un dato che manca arriva null o vuoto: per esempio l'appuntamento, se non è stato preso, o il link dell'audio, se nessuno ha risposto o la registrazione non è ancora pronta.

#Verificare la firma

La firma ti dice che la richiesta arriva davvero da SupportFast e non è stata modificata. Calcola l'HMAC SHA-256 del corpo con la chiave segreta, sul testo così come lo ricevi e prima di trasformarlo in oggetto: anche uno spazio diverso cambia la firma. Scrivilo in esadecimale con davanti sha256= e confrontalo con l'header X-Webhook-Signature.

Node.js con Express

const crypto = require("crypto");
const express = require("express");

const app = express();
const SEGRETO = process.env.SUPPORTFAST_WEBHOOK_SECRET;

app.post("/webhook/fine-chiamata", express.raw({ type: "application/json" }), (req, res) => {
  const ricevuta = req.get("X-Webhook-Signature") || "";
  const attesa = "sha256=" + crypto.createHmac("sha256", SEGRETO).update(req.body).digest("hex");
  const valida = ricevuta.length === attesa.length &&
    crypto.timingSafeEqual(Buffer.from(ricevuta), Buffer.from(attesa));
  if (!valida) return res.sendStatus(401);

  res.sendStatus(200);
  const dati = JSON.parse(req.body.toString("utf8"));
  // salva dati.call, dati.contact e dati.variables nel tuo gestionale
});

app.listen(3000);

PHP

<?php
$segreto = getenv('SUPPORTFAST_WEBHOOK_SECRET');
$corpo = file_get_contents('php://input');
$ricevuta = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$attesa = 'sha256=' . hash_hmac('sha256', $corpo, $segreto);

if (!hash_equals($attesa, $ricevuta)) {
    http_response_code(401);
    exit;
}

http_response_code(200);
$dati = json_decode($corpo, true);
// salva $dati['call'], $dati['contact'] e $dati['variables'] nel tuo gestionale

#Se il tuo server non risponde

  • Se la connessione non riesce, se passano i 10 secondi o se rispondi con un codice 5xx, riprova dopo 1, 3 e 10 secondi. In tutto, al massimo quattro invii.
  • Con un codice 4xx non riprova.
  • Non segue i reindirizzamenti: un codice 3xx conta come errore e non si ritenta.
  • Non invia a indirizzi interni o privati, solo a server raggiungibili da Internet.
  • Dopo l'ultimo tentativo l'invio si ferma. La chiamata resta comunque nella pagina Chiamate dell'agente.

Rispondi subito, lavora dopo. Rispondi 200 appena hai verificato la firma e fai dopo le operazioni lente. Per non salvare due volte la stessa chiamata dopo un nuovo tentativo, controlla X-Webhook-Call-Id.

#Come provarlo

  1. Prepara chi riceve

    Usa il tuo server o un indirizzo di prova che ti mostri le richieste in arrivo, come quelli di Zapier o Make. Mettilo in URL webhook e salva.

  2. Fai una chiamata vera

    Chiama l'agente dal tuo cellulare, parla qualche secondo e chiudi. Con Invia solo per chiamate completate acceso, una chiamata in cui non dici nulla non fa partire il webhook.

  3. Controlla cosa è arrivato

    Dopo l'analisi di fine chiamata arriva la richiesta. Verifica la firma e controlla i campi che ti servono.

#Con Zapier, Make o il tuo gestionale

In Zapier usa il trigger Catch Hook dell'app Webhooks by Zapier; in Make il modulo Custom webhook. Copia l'indirizzo che ti danno in URL webhook: dopo la prima chiamata i campi del JSON sono disponibili nei passi successivi. Questi strumenti non verificano la firma da soli: tieni riservato l'indirizzo.

Per il tuo gestionale chiedi al fornitore se ha un indirizzo adatto, o fallo preparare a uno sviluppatore con l'esempio qui sopra.

#In alternativa: un'automazione sugli eventi di telefonia

Se ti servono solo alcuni dati, o vuoi mandarli in più posti, usa un'automazione con un evento della categoria Telefonia, come Chiamata terminata. Con il nodo API chiami il tuo server con i campi che scegli; con gli altri nodi mandi un'email o un WhatsApp.

Webhook di fine chiamataAutomazione
Dove si impostaNel passo Post Chiamata dell'agente vocale, riquadro WebhookNelle automazioni del workspace
Dati inviatiSempre tutti, nello stesso JSONQuelli che scegli tu
Quando parteA fine chiamata, anche per le trasferiteAll'evento scelto: Chiamata terminata non parte per le trasferite
SicurezzaFirma HMAC con la chiaveGli header che imposti nel nodo API
ConsumiNessuna esecuzioneOgni partenza conta nelle esecuzioni del mese
Piano«Webhook di fine chiamata»Le automazioni e, per il nodo API, «API e webhook»

Vedi Eventi di partenza (trigger) e Chiamare un'API esterna.

#Domande frequenti

La firma non corrisponde mai.

Controlla di usare la chiave attuale e che la firma sia in esadecimale minuscolo, con davanti sha256=. Il calcolo è spiegato in Verificare la firma.

Posso mandare i dati a più indirizzi?

Il webhook ha un solo indirizzo per agente. Per più destinazioni passa da Zapier o Make, oppure usa un'automazione.

#Leggi anche