Webhook: cos’è e come collegare l’AI a form, email e fogli
Webhook spiegato in modo semplice e pratico: ricevi dati con FastAPI, classificali con l'AI e salvali su foglio di calcolo. Anche con Make e Zapier.
👁 113 letture
Un webhook è un URL verso cui un’applicazione invia automaticamente dati a un’altra nel momento esatto in cui accade qualcosa: un utente compila un form, arriva un’email o viene registrato un pagamento. Invece di interrogare continuamente il sistema chiedendo “ci sono novità?”, aspetti semplicemente che qualcuno bussi alla tua porta. Di seguito vediamo un webhook nella pratica: un server locale in Python (FastAPI) riceve una richiesta da un form, un modello di intelligenza artificiale la classifica e genera una bozza di risposta, e infine il risultato viene salvato in un foglio di calcolo. Poi vedremo la stessa procedura su Make e Zapier. Questo testo fa parte del percorso Agenti AI da zero, dedicato all’automazione.
Situazione al 4 ottobre 2026
- Test: Windows 11, Python 3.13, FastAPI 0.142.2, uvicorn 0.54.0, modello
openai/gpt-5.6-lunatramite OpenRouter. - Singola richiesta: 1,7-2,1 s dall’invio alla risposta, costo di circa 0,00016 USD (circa 0,00014 EUR; tasso di riferimento BCE del 12/10/2026: 1 EUR = 1,1181 USD).
- Make: il piano gratuito include 1.000 crediti al mese e 2 scenari attivi. Zapier: 100 task al mese, solo Zap a due passaggi, mentre i webhook richiedono piani a pagamento (da 19,99 USD al mese con fatturazione annuale, circa 17,88 EUR; fonte BCE del 12/10/2026).
- Codice: pacchetto archivio-iniziale-agenti, file
06_webhook.pye06_test_webhook.py.
Cos’è un webhook in parole semplici
Esistono due modi principali con cui un programma può scoprire se ci sono nuovi dati:
- interrogazione periodica (polling): il tuo software controlla ogni 5 minuti la casella di posta o un foglio di calcolo per vedere se ci sono nuovi elementi. È un metodo semplice, ma introduce ritardi e consuma chiamate inutili;
- webhook: fornisci a un’altra applicazione il tuo indirizzo (ad esempio
https://tuodominio.it/webhook/form), e quest’ultima invia una richiesta HTTP POST con i dati in formato JSON non appena si verifica l’evento.
Un webhook è essenzialmente una normale chiamata API, ma a parti invertite: non sei tu a chiamare il servizio esterno, è il servizio che chiama te. Se le richieste HTTP e il formato JSON sono argomenti nuovi per te, puoi iniziare dalla guida alle API.
Sorgenti tipiche di webhook:
- moduli e form (Tally, Typeform, Google Moduli tramite script, form su WordPress);
- pagamenti (Stripe o PayPal che notificano una transazione riuscita);
- piattaforme e-commerce (WooCommerce, Shopify: notifica di un nuovo ordine);
- strumenti di produttività (GitHub, Slack, Trello, Calendly);
- piattaforme di automazione (Make, Zapier e n8n includono moduli predefiniti del tipo “ricevi webhook”).
Cosa ho costruito
- Un form di contatto invia un JSON contenente: nome, e-mail e messaggio.
- Il server verifica un token segreto nell’header (per evitare che utenti non autorizzati inviino dati spazzatura).
- Il modello AI riceve il messaggio e restituisce un JSON strutturato con: categoria, urgenza da 1 a 3, riassunto e bozza di risposta.
- Il server registra la riga in un file CSV (equivalente locale di un foglio Google) e restituisce il risultato.
Il codice del server webhook
Installazione dei pacchetti necessari: pip install fastapi uvicorn openai python-dotenv. Nel file .env inserisci le chiavi OPENROUTER_API_KEY e WEBHOOK_SECRET (una stringa casuale sufficientemente lunga).
"""06 - Webhook: il form invia la richiesta -> il server interroga il modello -> il risultato finisce nel foglio (CSV) e torna in risposta.
Avvio: uvicorn 06_webhook:app --port 8000 (oppure: python 06_webhook.py)
Test: curl -X POST http://127.0.0.1:8000/webhook/formularz -H "X-Webhook-Secret: ..." -H "Content-Type: application/json"
-d "{\"imie\":\"Ola\",\"email\":\"ola@example.com\",\"wiadomosc\":\"Partita IVA errata in fattura\"}"
"""
import csv, datetime, json, os, pathlib
from dotenv import load_dotenv
from fastapi import FastAPI, Header, HTTPException
from openai import OpenAI
from pydantic import BaseModel
load_dotenv()
SEKRET = os.getenv("WEBHOOK_SECRET", "cambia-questo-valore") # la stessa stringa da inserire in Make/Zapier/form
MODEL = os.getenv("MODEL", "openai/gpt-5.6-luna")
ARKUSZ = pathlib.Path("richieste.csv")
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=os.environ["OPENROUTER_API_KEY"])
app = FastAPI()
class Richiesta(BaseModel): # schema del JSON inviato dal form
imie: str
email: str
wiadomosc: str
@app.post("/webhook/formularz")
def ricevi(z: Richiesta, x_webhook_secret: str = Header(default="")):
if x_webhook_secret != SEKRET: # senza controllo, chiunque conosca l'URL potrebbe saturare le tue chiamate API
raise HTTPException(status_code=401, detail="secret non valido")
r = client.chat.completions.create(model=MODEL, response_format={"type": "json_object"}, messages=[
{"role": "system", "content": "Sei un assistente per il supporto clienti. Restituisci un JSON con i campi: kategoria (faktury|reklamacja|"
"oferta|inne), pilnosc (1-3, dove 3 = urgente), streszczenie (max 12 parole), szkic_odpowiedzi (2-3 frasi, tono diretto)."},
{"role": "user", "content": f"Da: {z.imie}\nMessaggio: {z.wiadomosc}"}])
wynik = json.loads(r.choices[0].message.content)
nuovo = not ARKUSZ.exists()
with ARKUSZ.open("a", newline="", encoding="utf-8-sig") as f:
w = csv.writer(f, delimiter=";")
if nuovo: w.writerow(["timestamp", "nome", "email", "categoria", "urgenza", "riassunto", "bozza_risposta"])
w.writerow([datetime.datetime.now().isoformat(timespec="seconds"), z.imie, z.email, wynik.get("kategoria"),
wynik.get("pilnosc"), wynik.get("streszczenie"), wynik.get("szkic_odpowiedzi")])
return {"ok": True, **wynik, "koszt_usd": getattr(r.usage, "cost", None)}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
Elementi chiave da notare:
class Richiesta(BaseModel)definisce la struttura attesa del JSON. FastAPI respinge in automatico le richieste prive dei campi obbligatori con un errore 422, prima ancora di inoltrare la chiamata al modello;- il token nell’header
X-Webhook-Secret: un tentativo privo di credenziali corrette riceve un errore 401 e non consuma crediti AI; response_format={"type": "json_object"}obbliga il modello a restituire un JSON valido, pronto per essere convertito nelle colonne del file;- la codifica
utf-8-sigassicura che programmi come Microsoft Excel interpretino correttamente i caratteri accentati.
Avvio e test pratico
In una prima finestra del terminale avvio il server: uvicorn 06_webhook:app --port 8000. Nella seconda eseguo lo script di simulazione, che invia tre richieste lecite e una con token errato (abbiamo svolto il test con testi originali in polacco):
"""Invia 3 richieste di esempio al webhook locale (simulando il comportamento di un form o di Make/Zapier)."""
import os, time, requests
URL = "http://127.0.0.1:8000/webhook/formularz"
SEKRET = os.getenv("WEBHOOK_SECRET", "cambia-questo-valore")
print("secret errato ->", requests.post(URL, json={"imie": "X", "email": "x@example.com", "wiadomosc": "test"},
headers={"X-Webhook-Secret": "tentativo_casuale"}).status_code)
# Nota: i messaggi di test originali sono stati inseriti in lingua polacca
for imie, tresc in [("Ola", "[Traduzione dal polacco originale: Buongiorno, sulla fattura FV/2026/09/027 c'è il codice fiscale errato della mia azienda. Ho bisogno di una rettifica entro venerdì, perché chiudo il mese.]"),
("Marek", "[Traduzione dal polacco originale: Quanto costa l'implementazione di un chatbot sul sito di un negozio con 300 prodotti? Mi interessa una scadenza per novembre.]"),
("Kasia", "[Traduzione dal polacco originale: Il cavo ordinato è arrivato danneggiato, lo spinotto è rotto. Voglio un rimborso.]")]:
t = time.time()
r = requests.post(URL, json={"imie": imie, "email": f"{imie.lower()}@example.com", "wiadomosc": tresc},
headers={"X-Webhook-Secret": SEKRET}, timeout=60)
print(f"\n{imie}: {tresc}\n-> HTTP {r.status_code} in {time.time() - t:.1f} s: {r.json()}")
Risultato ottenuto il 4 ottobre alle 14:11 (riportiamo l’output del test originario eseguito in polacco, con traduzione a seguire):
> python 06_test_webhook.py
secret errato -> 401
Ola: [Traduzione dal polacco originale: Buongiorno, sulla fattura FV/2026/09/027 c'è il codice fiscale errato della mia azienda. Ho bisogno di una rettifica entro venerdì, perché chiudo il mese.]
-> HTTP 200 in 2.1 s: {'ok': True, 'categoria': 'fatture', 'urgenza': 3, 'riassunto': '[Traduzione dal polacco originale: Codice fiscale errato sulla fattura, necessaria rettifica entro venerdì]', 'bozza_risposta': '[Traduzione dal polacco originale: Grazie per il messaggio - controlleremo la fattura FV/2026/09/027 e prepareremo la rettifica con il codice fiscale corretto. Ce ne occuperemo con urgenza affinché il documento sia pronto entro venerdì.]', 'costo_usd': 0.0001746}
Marek: [Traduzione dal polacco originale: Quanto costa l'implementazione di un chatbot sul sito di un negozio con 300 prodotti? Mi interessa una scadenza per novembre.]
-> HTTP 200 in 1.8 s: {'ok': True, 'categoria': 'offerta', 'urgenza': 2, 'riassunto': '[Traduzione dal polacco originale: Richiesta sul costo e sui tempi di implementazione di un chatbot per il negozio]', 'bozza_risposta': '[Traduzione dal polacco originale: Ciao Marek, prepareremo un preventivo per l'implementazione del chatbot per il negozio con 300 prodotti e verificheremo la disponibilità per novembre. Invia cortesemente l'indirizzo del negozio e specifica quali funzioni dovrebbe supportare il chatbot.]', 'costo_usd': 0.000162}
Kasia: [Traduzione dal polacco originale: Il cavo ordinato è arrivato danneggiato, lo spinotto è rotto. Voglio un rimborso.]
-> HTTP 200 in 1.7 s: {'ok': True, 'categoria': 'reclamo', 'urgenza': 2, 'riassunto': '[Traduzione dal polacco originale: Cavo danneggiato, spinotto rotto; la cliente richiede il rimborso.]', 'bozza_risposta': '[Traduzione dal polacco originale: Ci dispiace che il cavo sia arrivato danneggiato. Ti preghiamo di inviare le foto del danno e il numero d'ordine, e ci occuperemo del rimborso.]', 'costo_usd': 0.0001504}
> type richieste.csv
timestamp;nome;email;categoria;urgenza;riassunto;bozza_risposta
2026-10-04T14:11:16;Ola;ola@example.com;fatture;3;[Traduzione dal polacco originale: Codice fiscale errato sulla fattura, necessaria rettifica entro venerdì];[Traduzione dal polacco originale: Grazie per il messaggio - controlleremo la fattura FV/2026/09/027 e prepareremo la rettifica con il codice fiscale corretto. Ce ne occuperemo con urgenza affinché il documento sia pronto entro venerdì.]
2026-10-04T14:11:18;Marek;marek@example.com;offerta;2;[Traduzione dal polacco originale: Richiesta sul costo e sui tempi di implementazione di un chatbot per il negozio];[Traduzione dal polacco originale: Ciao Marek, prepareremo un preventivo per l'implementazione del chatbot per il negozio con 300 prodotti e verificheremo la disponibilità per novembre. Invia cortesemente l'indirizzo del negozio e specifica quali funzioni dovrebbe supportare il chatbot.]
2026-10-04T14:11:19;Kasia;kasia@example.com;reclamo;2;"[Traduzione dal polacco originale: Cavo danneggiato, spinotto rotto; la cliente richiede il rimborso.]";[Traduzione dal polacco originale: Ci dispiace che il cavo sia arrivato danneggiato. Ti preghiamo di inviare le foto del danno e il numero d'ordine, e ci occuperemo del rimborso.]
Traduzione dei contenuti testati:
- Messaggio di Ola: “Buongiorno, sulla fattura FV/2026/09/027 la Partita IVA della mia azienda è errata. Ho bisogno della rettifica entro venerdì per la chiusura del mese.” Classificazione: fatture, urgenza 3. Bozza generata: conferma della presa in carico urgente con indicazione del numero di fattura e scadenza a venerdì.
- Messaggio di Marek: “Quanto costa implementare un chatbot per un e-commerce da 300 prodotti? Mi interessa una consegna entro novembre.” Classificazione: preventivo, urgenza 2. Bozza generata: richiesta di dettagli sulle funzioni necessarie e verifica disponibilità.
- Messaggio di Kasia: “Il cavo ordinato è arrivato danneggiato, il connettore è spaccato. Desidero il rimborso.” Classificazione: reclamo, urgenza 2. Bozza generata: scuse per il disguido e richiesta di foto del danno e del numero d’ordine.
Osservazioni sul test:
- la chiamata con token errato è stata subito bloccata con codice 401, senza attivare il modello;
- tutte le categorie sono state assegnate in modo esatto. Il livello massimo di urgenza (3) è stato riservato solo alla richiesta vincolata a una data imminente (“entro venerdì”);
- le bozze di risposta sono pertinenti: nel caso del reclamo, il modello ha chiesto autonomamente fotografie del danno e numero di ordine;
- il costo per singola chiamata è rimasto intorno a 0,00015-0,00017 USD (circa 0,00013-0,00015 EUR; tasso BCE del 12/10/2026).
Un intoppo riscontrato durante le prove: l’utility curl su Git Bash per Windows, con caratteri accentati inseriti direttamente in -d '...', ha inviato una codifica non valida provocando l’errore 400 (“There was an error parsing the body”). Per evitare il problema è bastato salvare il payload JSON su file e caricarlo tramite --data-binary @file.json, oppure affidarsi allo script Python mostrato sopra.
Come esporre il webhook su Internet
Un server in ascolto su 127.0.0.1 è raggiungibile soltanto dalla tua macchina. Perché un form pubblico possa recapitare i dati, hai bisogno di un endpoint accessibile dall’esterno:
- per sviluppo e test: puoi usare un tunnel come ngrok o Cloudflare Tunnel, che assegna un URL HTTPS temporaneo reindirizzato alla tua porta 8000;
- in produzione: una piattaforma di hosting per applicazioni come Railway o un server VPS dedicato, dove eseguire lo script e ottenere un dominio stabile;
- senza gestire server: puoi ricevere il webhook direttamente all’interno di Make, Zapier o n8n ed eseguire la chiamata al modello da lì.
Imposta sempre connessioni HTTPS e un token di verifica. Servizi più strutturati (come Stripe o GitHub) firmano ogni richiesta con un hash HMAC: in quel caso, invece di confrontare una stringa fissa, calcoli la firma del payload ricevuto e verifichi che coincida con l’header inviato dal mittente.
La stessa logica su Make (senza codice)
- Crea un nuovo scenario e imposta come primo modulo Webhooks, Custom webhook. Make genererà un indirizzo URL dedicato.
- Incolla l’indirizzo nelle impostazioni del tuo form (Tally, Typeform, WordPress) e invia una compilazione di prova. Make leggerà in automatico la struttura del JSON.
- Aggiungi un modulo AI: OpenAI (oppure un modulo HTTP collegato a OpenRouter) inserendo il prompt di classificazione con output in JSON.
- Aggiungi il modulo JSON, Parse JSON per suddividere l’output nelle singole variabili.
- Aggiungi il modulo Google Sheets, Add a row e associa i campi alle rispettive colonne.
- Opzionale: imposta un filtro per le richieste con “urgenza = 3” e aggiungi un modulo per inviare una notifica immediata.
Ogni operazione eseguita su Make consuma crediti (in linea generale uno per ciascun modulo attraversato). Il profilo gratuito include 1.000 operazioni mensili e 2 scenari attivi. Per maggiori dettagli puoi consultare la guida Make: come funziona e costi.
La stessa logica su Zapier
- Trigger: Webhooks by Zapier, Catch Hook (fornisce l’URL da inserire nel form) oppure il trigger nativo del servizio di form, se supportato.
- Azione: ChatGPT o AI by Zapier con le istruzioni per categorizzare il testo.
- Azione: Google Sheets, Create Spreadsheet Row.
Il modulo Webhooks by Zapier è considerato una funzionalità premium e richiede un abbonamento (piano Professional a partire da 19,99 USD al mese con pagamento annuale, pari a circa 17,88 EUR, oppure 29,99 USD con rinnovo mensile, pari a circa 26,82 EUR; conversioni stimate su tasso BCE del 12/10/2026: 1 EUR = 1,1181 USD). Inoltre il flusso richiede 3 passaggi, superando il limite del piano gratuito. Il confronto dettagliato tra queste piattaforme è disponibile nell’articolo Zapier: cos’è e quanto costa.
Codice, Make o Zapier: quale soluzione scegliere?
| Metodo | Costo fisso | Requisiti | A chi è adatto |
|---|---|---|---|
| Server dedicato (FastAPI) | Costo di hosting, da 0 EUR su computer locale con tunnel | Basi di Python | A chi desidera pieno controllo sull’infrastruttura senza pagare per singola operazione |
| n8n (self-hosted) | Edizione Community gratuita, si paga solo l’infrastruttura server | Capacità di configurare n8n su un VPS | A chi gestisce volumi elevati di automazioni e vuole mantenere i dati sui propri sistemi |
| Make | Da 0 USD (1.000 operazioni incluse) | Solo browser web | A chi cerca flussi di media complessità con diramazioni logiche |
| Zapier | Webhook inclusi a partire dal piano Professional | Solo browser web | A chi necessita della massima compatibilità con app terze e di un setup immediato |
Domande frequenti
Cos’è un webhook in parole semplici?
È un indirizzo web fornito a un’applicazione esterna affinché questa possa trasmettere automaticamente una notifica contenente dati utili non appena si verifica un determinato evento.
Che differenza c’è tra un’API e un webhook?
Una classica API è una porta a cui bussi quando hai bisogno di prelevare o inviare informazioni. Un webhook è un campanello che suona sul tuo server quando accade qualcosa dall’altra parte. Dal punto di vista tecnico, un webhook è comunque una chiamata API indirizzata al tuo endpoint.
Un webhook è sicuro?
Il livello di sicurezza dipende dalle misure applicate. I requisiti minimi prevedono l’uso obbligatorio di HTTPS e la verifica di un token segreto nell’header. Sistemi avanzati integrano la firma HMAC del contenuto, la validazione del timestamp contro attacchi di replay e vincoli sulla dimensione massima del payload.
Come testare un webhook senza un server pubblico?
Puoi effettuare chiamate direttamente da uno script in locale verso 127.0.0.1 (come nell’esempio mostrato) oppure creare un tunnel provvisorio che reindirizzi un dominio pubblico verso la porta del tuo computer.
Cosa succede se il mio server è spento o non risponde?
Il comportamento dipende dal mittente. Piattaforme strutturate come Stripe o GitHub riprovano la trasmissione a intervalli regolari per un certo lasso di tempo. Sistemi più semplici potrebbero scartare definitivamente il messaggio. Per questo è consigliabile che l’endpoint risponda immediatamente con un codice di conferma, delegando le elaborazioni pesanti (come l’interrogazione dell’AI) a code in background in presenza di traffico sostenuto.
È possibile collegare un webhook a un agente AI?
Sì, è una delle modalità operative più comuni: l’evento viene intercettato tramite webhook e, invece di una singola chiamata standard al modello, avvia il ciclo operativo di un agente dotato di strumenti esterni. Per approfondire l’implementazione pratica puoi leggere la guida su come creare un agente AI in Python.
Google · Le tue fontiPiù in alto su Google con un solo clicAggiungi alle fonti preferite →Fonti e dettagli della verifica
- Listini consultati: Make, Zapier, n8n (dati verificati il 4 ottobre 2026). Conversione USD/EUR stimata secondo il tasso ufficiale della Banca Centrale Europea del 12/10/2026 (1 EUR = 1,1181 USD, fonte BCE).
- Documentazione ufficiale di FastAPI.
- Test eseguito su macchina locale il 4 ottobre 2026 alle ore 14:11 con dati fittizi.


