# Webhook: inviare le prenotazioni ad Airtable

> Versione web : https://www.calenoo.ch/it/help/webhooks
> Ultimo aggiornamento : 2026-11-05
> [Français](https://www.calenoo.ch/fr/help/webhooks.md) · [Deutsch](https://www.calenoo.ch/de/help/webhooks.md) · [English](https://www.calenoo.ch/en/help/webhooks.md) · [Italiano](https://www.calenoo.ch/it/help/webhooks.md)

Un webhook invia automaticamente ogni prenotazione, spostamento e annullamento delle Sue pagine Calenoo a un altro strumento: una base Airtable, o qualsiasi strumento in grado di ricevere un webhook HTTPS (Zapier, Make, n8n…). Questa pagina spiega come impostarlo, che cosa invia Calenoo e che cosa succede in caso di guasto.

## A che cosa servono i webhook

Un webhook invia automaticamente all'indirizzo web da Lei scelto i dati di ogni prenotazione, di ogni spostamento e di ogni annullamento, non appena avvengono.

Lo strumento che li riceve può inserirli in una tabella, avvisare qualcuno o avviare altre azioni. Calenoo si limita all'invio: il resto si imposta in quello strumento.

L'impostazione si fa una sola volta, nelle Sue [Impostazioni](https://www.calenoo.ch/it/app/account#webhooks), e vale per tutte le Sue pagine di prenotazione. Può registrare al massimo 3 indirizzi.

Le basta un account Calenoo: la voce «Webhooks» si trova nelle Sue Impostazioni, come le pagine di prenotazione.

I webhook si abbinano alla convalida delle richieste e alle sessioni di gruppo, altre due funzioni avanzate delle pagine di prenotazione. Vedi la [guida alle funzioni avanzate](https://www.calenoo.ch/it/guides/reservation-validation-groupes-webhooks).

## Passo dopo passo: Airtable

Questi passaggi seguono la documentazione di Airtable sul trigger «When webhook received» ([fonte](https://support.airtable.com/docs/en/when-webhook-received-trigger), letta il 2 novembre 2026). Se la schermata di Airtable è diversa da quella descritta qui, fa fede la sua pagina di aiuto.

1. Nella Sua base Airtable, apra «Automations» e crei un'automazione.
2. Aggiunga un trigger e scelga «When webhook received». Airtable mostra un indirizzo web univoco: lo copi.
3. In Calenoo, apra [Impostazioni › Webhooks](https://www.calenoo.ch/it/app/account#webhooks), incolli l'indirizzo in «Indirizzo di ricezione» e clicchi su «Aggiungi».
4. Lasci deselezionata la casella «Firmare le trasmissioni»: Airtable non sa verificare una firma.
5. Clicchi su «Invia un test». Calenoo invia un esempio di prenotazione completo, senza campi vuoti, e mostra «Test ricevuto» quando Airtable accetta la trasmissione.
6. In Airtable, avvii il test del trigger (pulsante «Run test»). Airtable usa la trasmissione di test come esempio per i passaggi successivi.
7. Aggiunga le azioni che useranno questi dati e vi associ i campi ricevuti. Per ritrovare un appuntamento spostato o annullato, usi `booking.id` come identificativo.
8. Provi ogni passaggio, poi attivi l'automazione (da OFF a ON). Da quel momento, ogni prenotazione delle Sue pagine parte verso Airtable.

### Che cosa dice Airtable su questo trigger

Letto sulla [pagina di aiuto di Airtable](https://support.airtable.com/docs/en/when-webhook-received-trigger) il 2 novembre 2026. Questi limiti sono quelli di Airtable e possono cambiare.

- Sono accettate solo richieste POST, con un'intestazione `Content-Type` impostata su `application/json` per il JSON. È ciò che invia Calenoo.
- Una trasmissione non deve superare 100 kb, e il trigger accetta al massimo cinque richieste al secondo.
- Ogni trasmissione che avvia un'automazione conta nei limiti di esecuzione del Suo piano Airtable. Il trigger è disponibile in tutti i tipi di piano, con limiti diversi.
- Airtable non supporta la verifica della firma: tenga disattivata la firma di Calenoo per Airtable.
- Chiunque conosca l'indirizzo del trigger può avviare la Sua automazione. Non lo condivida.

## Gli eventi inviati

Calenoo invia tutti gli eventi della tabella, senza possibilità di selezione: il Suo strumento legge il campo `event` e tiene quelli che gli servono.

| Evento | Quando viene inviato |
| --- | --- |
| `booking.created` | Un ospite prenota su una pagina senza convalida. |
| `booking.requested` | Un ospite invia una richiesta su una pagina «con convalida» (`status` è `pending`, `request_expires_at` indica la scadenza). |
| `booking.accepted` | Lei accetta la richiesta (`status` diventa `confirmed`, `previous_status` è `pending`; il link della videoconferenza viene aggiunto se ce n'è uno). |
| `booking.declined` | Lei rifiuta la richiesta (`status` è `declined`). |
| `booking.expired` | La richiesta non ha ricevuto risposta prima della scadenza (`status` è `expired`). |
| `booking.rescheduled` | L'ospite sposta il suo appuntamento o la sua richiesta (`previous_start` indica l'inizio precedente; una richiesta resta `pending`). |
| `booking.cancelled` | L'ospite annulla il suo appuntamento o ritira la sua richiesta, oppure Lei annulla da Calenoo (`previous_status` indica lo stato precedente). |

Ogni trasmissione riguarda un solo appuntamento. Per una pagina di gruppo (più posti per slot), ogni partecipante ha una trasmissione distinta, con il proprio `booking.id` e il `booking.session_id` della sua sessione, uguale per tutti i partecipanti.

## Il formato dei dati

Ogni trasmissione è una richiesta `POST` il cui corpo è JSON (versione 1). Ecco un esempio. Il pulsante «Invia un test» invia lo stesso formato, con `test` su `true` e tutti i campi compilati.

```json
{
  "version": 1,
  "event": "booking.created",
  "event_id": "5d0c6b0e-6b0a-4a52-9d5c-2f1d3c9a7e11",
  "occurred_at": "2026-11-02T18:00:00Z",
  "test": false,
  "booking": {
    "id": "b3f1c2a4-8d7e-4c6b-9a10-5e2f4d8c1a77",
    "status": "confirmed",
    "previous_status": null,
    "start": "2026-11-07T09:00:00Z",
    "end": "2026-11-07T10:00:00Z",
    "start_local": "2026-11-07T10:00:00+01:00",
    "end_local": "2026-11-07T11:00:00+01:00",
    "timezone": "Europe/Zurich",
    "duration_minutes": 60,
    "previous_start": null,
    "location": "Bern",
    "meeting_url": null,
    "language": "de",
    "request_expires_at": null,
    "seats": null,
    "session_id": null,
    "invitee": {
      "name": "Jeanne Fit",
      "email": "jeanne@example.ch",
      "note": "Looking forward to it"
    }
  },
  "page": {
    "id": "9a4e7d52-3c1b-4f08-8e66-0b7d2a5c9f33",
    "slug": "personal-training",
    "title": "Personal Training"
  }
}
```

### I campi principali

- `event`: l'evento (vedi la tabella qui sopra). `event_id`: un identificativo comune a tutti gli indirizzi che ricevono lo stesso evento. `occurred_at`: l'ora dell'evento, in tempo universale (UTC).
- `booking.id`: l'identificativo dell'appuntamento. È la chiave da usare per ritrovarlo.
- `booking.status` e `booking.previous_status`: lo stato dell'appuntamento e il suo stato precedente (`null` se non ce n'è uno).
- `booking.start` e `booking.end`: inizio e fine in tempo universale (UTC). `booking.start_local` e `booking.end_local`: gli stessi orari, con l'ora e lo scarto del fuso orario della pagina (`booking.timezone`). `booking.duration_minutes`: la durata in minuti.
- `booking.previous_start`: l'inizio precedente, in caso di spostamento; altrimenti `null`.
- `booking.location`, `booking.language` e `booking.meeting_url`: il luogo della pagina, la lingua della prenotazione e il link della videoconferenza (`null` se non ce n'è uno). Una richiesta in attesa non ha ancora un link: arriva con `booking.accepted`.
- `booking.request_expires_at`: la scadenza di una richiesta da convalidare (al massimo 48 ore, mai dopo l'inizio dell'appuntamento), altrimenti `null`. Resta compilata dopo la decisione.
- `booking.seats`: il numero di posti per slot di una pagina di gruppo, `null` per un appuntamento individuale.
- `booking.session_id`: l'identificativo della sessione di una pagina di gruppo, uguale per tutti i partecipanti, `null` per un appuntamento individuale. Resta compilato dopo un annullamento; uno spostamento indica quello della nuova sessione.
- `booking.invitee`: il nome, l'indirizzo email e il messaggio (`note`, `null` se non ce n'è uno) dell'ospite.
- `page`: l'identificativo, l'indirizzo breve (`slug`) e il titolo della pagina di prenotazione.

### Le intestazioni

- `Content-Type: application/json`
- `User-Agent: Calenoo-Webhook/1`
- `Calenoo-Event`: l'evento, per esempio `booking.created`.
- `Calenoo-Delivery`: l'identificativo della trasmissione. Resta identico a ogni nuovo tentativo: lo usi per ignorare un duplicato.
- `Calenoo-Signature`: presente solo se la firma è attivata (vedi sotto).

La struttura della versione 1 non cambia: qualsiasi modifica passerebbe da una versione 2.

## Verificare la firma

Questo passaggio è facoltativo. Se seleziona «Firmare le trasmissioni», Calenoo aggiunge l'intestazione `Calenoo-Signature` a ogni trasmissione: il Suo strumento può così verificare che la trasmissione provenga davvero da Calenoo e non sia stata modificata.

L'intestazione ha la forma `t=<secondi>,v1=<firma>`. `t` è l'ora di invio in secondi (ora Unix). `v1` è un HMAC-SHA256 in esadecimale, calcolato con il Suo segreto sul testo `t.corpo`: `t`, un punto, poi il corpo della richiesta esattamente come è stato ricevuto.

Il segreto inizia con `whsec_`: lo usi per intero, prefisso compreso. Calenoo lo mostra una sola volta, alla creazione: lo copi subito. Se lo perde, ne crei uno nuovo con «Nuovo segreto».

Ecco un esempio in JavaScript (Node.js). Calcoli sempre la firma sul testo grezzo ricevuto, prima di qualsiasi `JSON.parse`. Rifiuti anche una trasmissione il cui `t` è troppo vecchio: qui, più di 5 minuti.

```js
import crypto from "node:crypto";

function isValidSignature(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((part) => part.split("=")));
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  const received = Buffer.from(parts.v1 ?? "", "hex");
  const computed = Buffer.from(expected, "hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  return fresh && received.length === computed.length && crypto.timingSafeEqual(received, computed);
}
```

Node.js scrive i nomi delle intestazioni in minuscolo: l'intestazione si legge con `req.headers['calenoo-signature']`.

## In caso di guasto

Una trasmissione riesce quando il Suo strumento risponde con un codice 2xx entro 10 secondi. Calenoo non legge mai il contenuto della risposta e non segue nessun reindirizzamento: una risposta 3xx conta come un fallimento.

- Risposta 408, 425, 429 o 5xx, tempo scaduto o interruzione di rete: Calenoo riprova, al massimo 8 tentativi (il primo compreso), dopo 1, 2, 5, 15 e 30 minuti, poi 1 ora e 2 ore. Se la risposta contiene un'intestazione `Retry-After` (in secondi) più lunga di questa attesa, Calenoo la rispetta, fino a un massimo di 6 ore.
- Qualsiasi altra risposta 4xx (404 o 401, per esempio): fallimento definitivo, nessun nuovo tentativo.
- Le trasmissioni verso uno stesso indirizzo partono in ordine: una trasmissione in attesa di un nuovo tentativo trattiene quelle successive.
- Dopo 5 trasmissioni consecutive in fallimento definitivo, Calenoo disattiva l'indirizzo e Le invia un'email. Una trasmissione riuscita azzera il contatore.
- Le trasmissioni perse non vengono reinviate quando riattiva l'indirizzo. Risolto il problema, clicchi su «Riattiva» in [Impostazioni › Webhooks](https://www.calenoo.ch/it/app/account#webhooks).

Una stessa trasmissione può arrivare due volte, per esempio se il Suo strumento l'ha ricevuta ma la sua risposta si è persa. Poiché `Calenoo-Delivery` è identico a ogni tentativo, il Suo strumento può ignorare un duplicato.

Il registro «Ultime trasmissioni» in [Impostazioni › Webhooks](https://www.calenoo.ch/it/app/account#webhooks) mostra l'evento, il codice di risposta e lo stato di ogni trasmissione. Viene conservato 30 giorni, senza il contenuto delle trasmissioni.

## Sicurezza e dati personali

- Calenoo invia solo in `https`, sulla porta 443. Un indirizzo con nome utente o password, o su un'altra porta, viene rifiutato.
- Gli indirizzi privati o locali (rete interna, `localhost`…) vengono rifiutati al salvataggio, poi ricontrollati prima di ogni trasmissione.
- Mai nelle trasmissioni: i link e i token per gestire l'appuntamento, l'impronta IP, il Suo indirizzo email e i riferimenti della Sua agenda.
- Il contenuto di ogni trasmissione viene cancellato non appena la trasmissione è terminata.
- Il controllo resta Suo: può mettere in pausa un indirizzo, firmarlo, cambiarne il segreto o eliminarlo in qualsiasi momento.

Ogni trasmissione contiene dati personali: il nome, l'indirizzo email e il messaggio dell'ospite, con l'orario dell'appuntamento. Vanno allo strumento da Lei scelto, che li tratta sotto la propria responsabilità e secondo le proprie condizioni. Vedi l'[informativa sulla privacy](https://www.calenoo.ch/it/legal/privacy).

## Domande frequenti

### Serve un piano Airtable a pagamento?

Airtable indica che il trigger «When webhook received» è disponibile in tutti i tipi di piano, con limiti diversi. Ogni trasmissione che avvia un'automazione conta nei limiti di esecuzione del Suo piano: li verifichi nel Suo account Airtable ([fonte](https://support.airtable.com/docs/en/when-webhook-received-trigger)).

### Il mio strumento è in panne: si perdono delle prenotazioni?

No, le prenotazioni restano in Calenoo. Riguarda solo l'invio verso il Suo strumento: Calenoo riprova fino a 8 volte, con attese da 1 minuto a 2 ore. Dopo 5 trasmissioni consecutive in fallimento definitivo, l'indirizzo viene disattivato e le trasmissioni perse non vengono reinviate.

### Posso ricevere solo alcuni eventi?

No: Calenoo invia tutti gli eventi. Il Suo strumento legge il campo `event` (o l'intestazione `Calenoo-Event`) e tiene quelli che vuole trattare.

### Come evito i duplicati nella mia tabella?

Usi `booking.id` come chiave: uno spostamento o un annullamento riguarda lo stesso appuntamento e può quindi aggiornare la stessa riga. L'intestazione `Calenoo-Delivery` identifica ogni trasmissione e resta identica a ogni nuovo tentativo.

### Posso inviare a un server della mia rete interna o su un'altra porta?

No. Calenoo invia solo in HTTPS, sulla porta 443, verso un indirizzo pubblico.
