# Webhooks: Buchungen an Airtable senden

> Webversion : https://www.calenoo.ch/de/help/webhooks
> Zuletzt aktualisiert : 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)

Ein Webhook sendet jede Buchung, Verschiebung und Absage Ihrer Calenoo-Seiten automatisch an ein anderes Tool: eine Airtable-Base oder jedes Tool, das einen HTTPS-Webhook empfangen kann (Zapier, Make, n8n …). Diese Seite erklärt, wie Sie das einrichten, was Calenoo sendet und was bei einem Ausfall passiert.

## Wozu Webhooks dienen

Ein Webhook sendet die Angaben jeder Buchung, jeder Verschiebung und jeder Absage automatisch an die Webadresse Ihrer Wahl, sobald sie stattfinden.

Das empfangende Tool kann sie zum Beispiel in eine Tabelle eintragen, jemanden benachrichtigen oder weitere Aktionen auslösen. Calenoo übernimmt nur den Versand: Alles Weitere stellen Sie in diesem Tool ein.

Die Einrichtung erfolgt einmal in Ihren [Einstellungen](https://www.calenoo.ch/de/app/account#webhooks) und gilt für alle Ihre Buchungsseiten. Sie können höchstens 3 Adressen speichern.

Sie brauchen nur ein Calenoo-Konto: Die Rubrik „Webhooks“ finden Sie in Ihren Einstellungen, wie die Buchungsseiten.

Webhooks lassen sich mit der Prüfung von Anfragen und mit Gruppenterminen kombinieren, zwei weiteren erweiterten Funktionen der Buchungsseiten. Siehe den [Ratgeber zu den erweiterten Funktionen](https://www.calenoo.ch/de/guides/reservation-validation-groupes-webhooks).

## Schritt für Schritt: Airtable

Diese Schritte folgen der Dokumentation von Airtable zum Trigger „When webhook received“ ([Quelle](https://support.airtable.com/docs/en/when-webhook-received-trigger), gelesen am 2. November 2026). Wenn der Bildschirm von Airtable anders aussieht als hier beschrieben, gilt dessen Hilfeseite.

1. Öffnen Sie in Ihrer Airtable-Base „Automations“ und erstellen Sie eine Automatisierung.
2. Fügen Sie einen Trigger hinzu und wählen Sie „When webhook received“. Airtable zeigt eine einmalige Webadresse an: Kopieren Sie sie.
3. Öffnen Sie in Calenoo [Einstellungen › Webhooks](https://www.calenoo.ch/de/app/account#webhooks), fügen Sie die Adresse unter „Empfangsadresse“ ein und klicken Sie auf „Hinzufügen“.
4. Lassen Sie das Kästchen „Übermittlungen signieren“ ungewählt: Airtable kann keine Signatur prüfen.
5. Klicken Sie auf „Test senden“. Calenoo sendet eine vollständige Beispielbuchung ohne leere Felder und zeigt „Test empfangen“ an, wenn Airtable die Übermittlung annimmt.
6. Starten Sie in Airtable den Test des Triggers (Schaltfläche „Run test“). Airtable verwendet die Testübermittlung als Beispiel für die nächsten Schritte.
7. Fügen Sie die Aktionen hinzu, die diese Daten verwenden, und ordnen Sie ihnen die empfangenen Felder zu. Um einen verschobenen oder abgesagten Termin wiederzufinden, verwenden Sie `booking.id` als Kennung.
8. Testen Sie jeden Schritt und aktivieren Sie dann die Automatisierung (von OFF auf ON). Ab dann geht jede Buchung Ihrer Seiten an Airtable.

### Was Airtable zu diesem Trigger sagt

Gelesen auf der [Hilfeseite von Airtable](https://support.airtable.com/docs/en/when-webhook-received-trigger) am 2. November 2026. Diese Grenzen stammen von Airtable und können sich ändern.

- Es werden nur POST-Anfragen akzeptiert, bei JSON mit dem Header `Content-Type` auf `application/json`. Genau das sendet Calenoo.
- Eine Übermittlung darf höchstens 100 kb gross sein, und der Trigger akzeptiert höchstens fünf Anfragen pro Sekunde.
- Jede Übermittlung, die eine Automatisierung startet, zählt zu den Ausführungslimits Ihres Airtable-Plans. Der Trigger ist in allen Plantypen verfügbar, mit unterschiedlichen Limits.
- Airtable unterstützt keine Signaturprüfung: Lassen Sie die Signatur von Calenoo für Airtable ausgeschaltet.
- Jede Person, die die Adresse des Triggers kennt, kann Ihre Automatisierung auslösen. Geben Sie sie nicht weiter.

## Die gesendeten Ereignisse

Calenoo sendet alle Ereignisse der Tabelle, eine Auswahl ist nicht möglich: Ihr Tool liest das Feld `event` und behält die Ereignisse, die es braucht.

| Ereignis | Wann es gesendet wird |
| --- | --- |
| `booking.created` | Ein Gast bucht auf einer Seite ohne Bestätigung. |
| `booking.requested` | Ein Gast sendet eine Anfrage auf einer Seite „mit Bestätigung“ (`status` ist `pending`, `request_expires_at` gibt die Frist an). |
| `booking.accepted` | Sie nehmen die Anfrage an (`status` wird `confirmed`, `previous_status` ist `pending`; der Videokonferenz-Link wird ergänzt, falls es einen gibt). |
| `booking.declined` | Sie lehnen die Anfrage ab (`status` ist `declined`). |
| `booking.expired` | Die Anfrage wurde vor Fristende nicht beantwortet (`status` ist `expired`). |
| `booking.rescheduled` | Der Gast verschiebt seinen Termin oder seine Anfrage (`previous_start` gibt den alten Beginn an; eine Anfrage bleibt `pending`). |
| `booking.cancelled` | Der Gast sagt seinen Termin ab oder zieht seine Anfrage zurück, oder Sie sagen in Calenoo ab (`previous_status` gibt den früheren Status an). |

Jede Übermittlung betrifft genau einen Termin. Bei einer Gruppenseite (mehrere Plätze pro Termin) wird jede teilnehmende Person einzeln übermittelt, mit eigener `booking.id` und der `booking.session_id` ihres Gruppentermins, die für alle Teilnehmenden gleich ist.

## Das Datenformat

Jede Übermittlung ist eine `POST`-Anfrage, deren Inhalt JSON ist (Version 1). Hier ein Beispiel. Die Schaltfläche „Test senden“ sendet dasselbe Format, mit `test` auf `true` und allen ausgefüllten Feldern.

```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"
  }
}
```

### Die wichtigsten Felder

- `event`: das Ereignis (siehe Tabelle oben). `event_id`: eine Kennung, die für alle Adressen gleich ist, die dasselbe Ereignis erhalten. `occurred_at`: der Zeitpunkt des Ereignisses in Weltzeit (UTC).
- `booking.id`: die Kennung des Termins. Sie ist der Schlüssel, um ihn wiederzufinden.
- `booking.status` und `booking.previous_status`: der Status des Termins und sein vorheriger Status (`null`, wenn es keinen gibt).
- `booking.start` und `booking.end`: Beginn und Ende in Weltzeit (UTC). `booking.start_local` und `booking.end_local`: dieselben Zeiten mit Uhrzeit und Versatz der Zeitzone der Seite (`booking.timezone`). `booking.duration_minutes`: die Dauer in Minuten.
- `booking.previous_start`: der alte Beginn bei einer Verschiebung, sonst `null`.
- `booking.location`, `booking.language` und `booking.meeting_url`: der Ort der Seite, die Sprache der Buchung und der Videokonferenz-Link (`null`, wenn es keinen gibt). Eine ausstehende Anfrage hat noch keinen Link: Er kommt mit `booking.accepted`.
- `booking.request_expires_at`: die Frist einer zu bestätigenden Anfrage (höchstens 48 Stunden, nie nach Beginn des Termins), sonst `null`. Sie bleibt nach dem Entscheid ausgefüllt.
- `booking.seats`: die Anzahl Plätze pro Termin einer Gruppenseite, `null` bei einem Einzeltermin.
- `booking.session_id`: die Kennung des Gruppentermins, für alle Teilnehmenden gleich, `null` bei einem Einzeltermin. Sie bleibt nach einer Absage erhalten; bei einer Umbuchung steht hier die Kennung des neuen Gruppentermins.
- `booking.invitee`: Name, E-Mail-Adresse und Nachricht (`note`, `null`, wenn es keine gibt) des Gastes.
- `page`: die Kennung, die Kurzadresse (`slug`) und der Titel der Buchungsseite.

### Die Header

- `Content-Type: application/json`
- `User-Agent: Calenoo-Webhook/1`
- `Calenoo-Event`: das Ereignis, zum Beispiel `booking.created`.
- `Calenoo-Delivery`: die Kennung der Übermittlung. Sie bleibt bei jedem neuen Versuch gleich: Nutzen Sie sie, um ein Duplikat zu ignorieren.
- `Calenoo-Signature`: nur vorhanden, wenn die Signatur eingeschaltet ist (siehe unten).

Die Struktur der Version 1 ändert sich nicht: Jede Änderung würde über eine Version 2 erfolgen.

## Die Signatur prüfen

Dieser Schritt ist freiwillig. Wenn Sie „Übermittlungen signieren“ ankreuzen, fügt Calenoo jeder Übermittlung den Header `Calenoo-Signature` hinzu: Ihr Tool kann so prüfen, dass die Übermittlung wirklich von Calenoo stammt und nicht verändert wurde.

Der Header hat die Form `t=<Sekunden>,v1=<Signatur>`. `t` ist der Sendezeitpunkt in Sekunden (Unix-Zeit). `v1` ist ein HMAC-SHA256 in Hexadezimal, berechnet mit Ihrem Schlüssel über den Text `t.Inhalt`: `t`, ein Punkt, dann der Inhalt der Anfrage genau so, wie er empfangen wurde.

Der Schlüssel beginnt mit `whsec_`: Verwenden Sie ihn vollständig, mit Präfix. Calenoo zeigt ihn nur ein einziges Mal bei der Erstellung an: Kopieren Sie ihn sofort. Wenn Sie ihn verlieren, erstellen Sie mit „Neuer Schlüssel“ einen neuen.

Hier ein Beispiel in JavaScript (Node.js). Berechnen Sie die Signatur immer über den empfangenen Rohtext, vor jedem `JSON.parse`. Lehnen Sie auch eine Übermittlung ab, deren `t` zu alt ist: hier mehr als 5 Minuten.

```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 schreibt die Header-Namen klein: Der Header wird mit `req.headers['calenoo-signature']` gelesen.

## Bei einem Ausfall

Eine Übermittlung gilt als erfolgreich, wenn Ihr Tool innerhalb von 10 Sekunden mit einem 2xx-Code antwortet. Calenoo liest den Inhalt der Antwort nie und folgt keiner Weiterleitung: Eine 3xx-Antwort zählt als Fehlschlag.

- Antwort 408, 425, 429 oder 5xx, Zeitüberschreitung oder Netzunterbruch: Calenoo versucht es erneut, höchstens 8 Versuche (der erste eingeschlossen), nach 1, 2, 5, 15 und 30 Minuten, dann nach 1 und 2 Stunden. Enthält die Antwort einen Header `Retry-After` (in Sekunden), der länger ist als diese Wartezeit, wartet Calenoo so lange, höchstens 6 Stunden.
- Jede andere 4xx-Antwort (zum Beispiel 404 oder 401): endgültiger Fehlschlag, kein neuer Versuch.
- Die Übermittlungen einer Adresse erfolgen der Reihe nach: Eine Übermittlung, die auf einen neuen Versuch wartet, hält die folgenden zurück.
- Nach 5 endgültig fehlgeschlagenen Übermittlungen in Folge deaktiviert Calenoo die Adresse und sendet Ihnen eine E-Mail. Eine erfolgreiche Übermittlung setzt den Zähler auf null zurück.
- Verpasste Übermittlungen werden nicht nachgesendet, wenn Sie die Adresse reaktivieren. Klicken Sie nach der Behebung des Problems unter [Einstellungen › Webhooks](https://www.calenoo.ch/de/app/account#webhooks) auf „Reaktivieren“.

Dieselbe Übermittlung kann zweimal eintreffen, zum Beispiel wenn Ihr Tool sie erhalten hat, die Antwort aber verloren ging. Da `Calenoo-Delivery` bei jedem Versuch gleich ist, kann Ihr Tool ein Duplikat ignorieren.

Das Protokoll „Letzte Übermittlungen“ unter [Einstellungen › Webhooks](https://www.calenoo.ch/de/app/account#webhooks) zeigt Ereignis, Antwortcode und Status jeder Übermittlung. Es wird 30 Tage aufbewahrt, ohne den Inhalt der Übermittlungen.

## Sicherheit und Personendaten

- Calenoo sendet nur über `https`, auf Port 443. Eine Adresse mit Benutzername oder Passwort oder mit einem anderen Port wird abgelehnt.
- Private oder lokale Adressen (internes Netzwerk, `localhost` …) werden bei der Speicherung abgelehnt und vor jeder Übermittlung erneut geprüft.
- Nie in den Übermittlungen enthalten: Links und Schlüssel zur Verwaltung des Termins, der IP-Fingerabdruck, Ihre E-Mail-Adresse und die Referenzen Ihres Kalenders.
- Der Inhalt jeder Übermittlung wird gelöscht, sobald die Übermittlung abgeschlossen ist.
- Sie behalten die Kontrolle: Sie können eine Adresse jederzeit pausieren, signieren, ihren Schlüssel ändern oder sie löschen.

Jede Übermittlung enthält Personendaten: Name, E-Mail-Adresse und Nachricht des Gastes, mit der Uhrzeit des Termins. Sie gehen an das Tool, das Sie gewählt haben; dieses bearbeitet sie in eigener Verantwortung und nach seinen eigenen Bedingungen. Siehe die [Datenschutzerklärung](https://www.calenoo.ch/de/legal/privacy).

## Häufige Fragen

### Brauche ich einen kostenpflichtigen Airtable-Plan?

Airtable gibt an, dass der Trigger „When webhook received“ in allen Plantypen verfügbar ist, mit unterschiedlichen Limits. Jede Übermittlung, die eine Automatisierung startet, zählt zu den Ausführungslimits Ihres Plans: Prüfen Sie diese in Ihrem Airtable-Konto ([Quelle](https://support.airtable.com/docs/en/when-webhook-received-trigger)).

### Mein Tool ist ausgefallen: Gehen Buchungen verloren?

Nein, die Buchungen bleiben in Calenoo. Betroffen ist nur die Übermittlung an Ihr Tool: Calenoo versucht es bis zu 8 Mal, mit Wartezeiten von 1 Minute bis 2 Stunden. Nach 5 endgültig fehlgeschlagenen Übermittlungen in Folge wird die Adresse deaktiviert, und verpasste Übermittlungen werden nicht nachgesendet.

### Kann ich nur bestimmte Ereignisse erhalten?

Nein: Calenoo sendet alle Ereignisse. Ihr Tool liest das Feld `event` (oder den Header `Calenoo-Event`) und behält die Ereignisse, die es verarbeiten will.

### Wie vermeide ich Duplikate in meiner Tabelle?

Verwenden Sie `booking.id` als Schlüssel: Eine Verschiebung oder Absage betrifft denselben Termin und kann daher dieselbe Zeile aktualisieren. Der Header `Calenoo-Delivery` kennzeichnet jede Übermittlung und bleibt bei jedem neuen Versuch gleich.

### Kann ich an einen Server in meinem internen Netzwerk oder auf einem anderen Port senden?

Nein. Calenoo sendet nur über HTTPS, auf Port 443, an eine öffentliche Adresse.
