Zum Inhalt springen
Jetzt Gründungspartner werden: Gründungspartner zahlen 30 % weniger für ihren Tarif, solange ihr Abonnement läuft.
Dokumentation, API & Webhooks

Webhook-Events

Benachrichtigungen an Ihre Systeme bei Ereignissen zu Reservierungen, Gästen und Treuekarten.

Zuletzt aktualisiert am 16. Oktober 2026

Mit einem Webhook ruft platzeo eine Adresse Ihrer Wahl auf, sobald im Restaurant etwas passiert. Den Aufbau der Anfrage mit einem vollständigen Beispiel finden Sie in der API-Referenz.

Einrichten

Im Admin unter Webhooks → Neuer Webhook:

  1. Name und URL eintragen. Die Adresse muss öffentlich erreichbar sein und https verwenden.
  2. Ereignisse auswählen.
  3. Bei Bedarf Methode (POST, PUT oder PATCH), Payload-Format, eigene Header, Timeout und Wiederholungen anpassen.

Nach dem Erstellen zeigt platzeo das Secret des Webhooks an. Es beginnt mit whsec_. Kopieren Sie es gleich. Mit Testereignis senden schicken Sie eine Probe-Zustellung, unter Letzte Zustellungen sehen Sie jeden Versuch.

Ereignisse

  • reservation.created – Reservierung erstellt, online oder von Ihrem Team
  • reservation.updated – Tag, Uhrzeit, Personenzahl, Tisch oder Wünsche geändert
  • reservation.confirmed – Reservierung bestätigt
  • reservation.cancelled – Reservierung storniert oder Anfrage abgelehnt
  • reservation.seated – Gast am Tisch
  • reservation.completed – Besuch abgeschlossen
  • reservation.no_show – Gast nicht erschienen
  • guest.created – neuer Gast
  • guest.updated – Gast geändert
  • loyalty.card.created – Treuekarte erstellt
  • loyalty.stamp.added – Stempel oder Punkte gutgeschrieben
  • loyalty.reward.redeemed – Belohnung eingelöst

Welche Ereignisse eine Änderung sendet

Ändert eine Reservierung ihren Status, kommt genau ein Ereignis: das des neuen Status. reservation.updated kommt dann nicht zusätzlich; es steht für alle anderen Änderungen.

Beginnt eine Reservierung in einem Status mit eigenem Ereignis, folgt es direkt auf reservation.created: reservation.confirmed bei einer sofort bestätigten Online-Buchung und bei einer Reservierung, die Ihr Team einträgt, reservation.seated bei Laufkundschaft. Wer alle bestätigten Reservierungen braucht, abonniert also nur reservation.confirmed.

Gehören mehrere Ereignisse zu einer Aktion, sendet platzeo sie nacheinander in fester Reihenfolge: erst der Gast, dann reservation.created, dann der Status. Eine Wiederholung nach einem Fehlschlag kann später ankommen als das nächste Ereignis.

Aufbau einer Zustellung

Dies ist Version 2 der Inhalte. Im Standard-Format liegt um die Daten ein Umschlag:

{
  "id": "0b6f6a52-6c1e-4c0e-9a51-3d8f3f1f2a77",
  "event": "reservation.created",
  "version": 2,
  "timestamp": 1791475200,
  "restaurantId": "9b206577-1a4b-4497-9378-48b66f49c18c",
  "data": {
    "reservation": {
      "id": "3a4b5c6d-7e8f-4a1b-9c2d-3e4f5a6b7c8d",
      "status": "confirmed",
      "date": "2026-04-20",
      "time": "19:30",
      "partySize": 4,
      "durationMinutes": 120,
      "tables": [
        {
          "id": "5f0c2f6e-9d1b-4c1e-8a57-2b1f0c3d4e5f",
          "name": "Tisch 4"
        }
      ],
      "source": "online",
      "occasion": "Geburtstag",
      "notes": "Bitte ein Tisch am Fenster.",
      "customFields": [
        {
          "label": "Kinderstuhl",
          "value": "Ja"
        }
      ],
      "createdAt": "2026-04-15T14:30:00.000Z",
      "updatedAt": "2026-04-15T14:30:00.000Z"
    },
    "guest": {
      "id": "0d9c8b7a-6f5e-4d3c-9b2a-1f0e9d8c7b6a",
      "firstName": "Anna",
      "lastName": "Beispiel",
      "email": "anna.beispiel@example.com",
      "phone": "+49 30 1234567"
    }
  }
}
  • id – Kennung der Zustellung, bei jedem Versuch dieselbe
  • event – Name des Ereignisses
  • version – Version der Inhalte, auch im Header X-platzeo-Payload-Version
  • timestamp – Zeitpunkt des Ereignisses in Sekunden seit 1970
  • restaurantId – Kennung Ihres Restaurants
  • data – der Inhalt, je nach Ereignis

Mit dem Payload-Format Raw (nur Daten) besteht der Body nur aus dem Inhalt von data.

Gesendet werden genau die Felder, die hier stehen. Neue Felder können jederzeit dazukommen. Fällt ein Feld weg oder ändert es seine Bedeutung, steigt die Version.

Reservierungen

Alle Ereignisse, die mit reservation. beginnen, tragen reservation und guest.

Felder von reservation:

  • id – Kennung der Reservierung
  • status – pending, confirmed, seated, completed, cancelled oder no_show
  • date – Tag der Reservierung, JJJJ-MM-TT
  • time – Uhrzeit, HH:MM
  • partySize – Personenzahl
  • durationMinutes – geplante Dauer in Minuten
  • tables – zugewiesene Tische mit id und name; leer, solange kein Tisch zugewiesen ist
  • source – wie die Reservierung entstand, zum Beispiel online, phone oder walkin
  • occasion – Anlass, sonst null
  • notes – Wünsche des Gastes zu dieser Reservierung, sonst null
  • customFields – Antworten auf Ihre eigenen Fragen im Buchungsformular, je label und value
  • createdAt – angelegt, ISO 8601
  • updatedAt – zuletzt geändert, ISO 8601

Felder von guest:

  • id – Kennung des Gastes
  • firstName – Vorname
  • lastName – Nachname
  • email – E-Mail-Adresse, null wenn keine bekannt ist
  • phone – Telefonnummer, null wenn keine bekannt ist

guest ist null bei Laufkundschaft ohne Namen. Interne Notizen zum Gast werden nicht gesendet.

reservation.cancelled nennt zusätzlich cancelledBy: guest (über den Link in der E-Mail), restaurant oder expired (der Gast hat seine E-Mail-Adresse nicht rechtzeitig bestätigt).

{
  "data": {
    "reservation": {
      "id": "3a4b5c6d-7e8f-4a1b-9c2d-3e4f5a6b7c8d",
      "status": "cancelled",
      "date": "2026-04-20",
      "time": "19:30",
      "partySize": 4,
      "durationMinutes": 120,
      "tables": [
        {
          "id": "5f0c2f6e-9d1b-4c1e-8a57-2b1f0c3d4e5f",
          "name": "Tisch 4"
        }
      ],
      "source": "online",
      "occasion": "Geburtstag",
      "notes": "Bitte ein Tisch am Fenster.",
      "customFields": [
        {
          "label": "Kinderstuhl",
          "value": "Ja"
        }
      ],
      "createdAt": "2026-04-15T14:30:00.000Z",
      "updatedAt": "2026-04-15T14:30:00.000Z"
    },
    "guest": {
      "id": "0d9c8b7a-6f5e-4d3c-9b2a-1f0e9d8c7b6a",
      "firstName": "Anna",
      "lastName": "Beispiel",
      "email": "anna.beispiel@example.com",
      "phone": "+49 30 1234567"
    },
    "cancelledBy": "guest"
  }
}

Gäste

guest.created und guest.updated tragen guest mit den Feldern oben und zusätzlich:

  • marketingConsent – true, wenn der Gast Werbung zugestimmt hat
  • createdAt – angelegt, ISO 8601
{
  "data": {
    "guest": {
      "id": "0d9c8b7a-6f5e-4d3c-9b2a-1f0e9d8c7b6a",
      "firstName": "Anna",
      "lastName": "Beispiel",
      "email": "anna.beispiel@example.com",
      "phone": "+49 30 1234567",
      "marketingConsent": false,
      "createdAt": "2026-03-02T09:15:00.000Z"
    }
  }
}

Treueprogramm

Alle Ereignisse, die mit loyalty. beginnen, tragen card:

  • id – Kennung der Karte
  • cardNumber – Kartennummer, wie sie auf der Karte steht
  • guestId – Kennung des Gastes
  • programId – Kennung des Treueprogramms
  • status – active oder blocked
  • stamps – Stempel auf der Karte
  • points – Punkte auf der Karte
  • rewardsRedeemed – bisher eingelöste Belohnungen
  • issuedAt – ausgestellt, ISO 8601
  • expiresAt – Ablauf, ISO 8601, sonst null

Der QR-Code der Karte wird nie gesendet. loyalty.stamp.added trägt zusätzlich delta, die gutgeschriebenen Stempel oder Punkte. loyalty.reward.redeemed trägt delta (abgezogen, negativ) und reward mit id und title; id ist null bei einem Stempelprogramm, dessen Belohnung keine eigene Kennung hat.

{
  "data": {
    "card": {
      "id": "7c6b5a49-3827-4615-a0b9-c8d7e6f5a4b3",
      "cardNumber": "PLZ-4821-7730",
      "guestId": "0d9c8b7a-6f5e-4d3c-9b2a-1f0e9d8c7b6a",
      "programId": "2e3f4a5b-6c7d-4e8f-9a0b-1c2d3e4f5a6b",
      "status": "active",
      "stamps": 0,
      "points": 0,
      "rewardsRedeemed": 2,
      "issuedAt": "2026-03-02T09:15:00.000Z",
      "expiresAt": null
    },
    "delta": -10,
    "reward": {
      "id": "6b5a4938-2716-4504-b9a8-d7c6e5f4a3b2",
      "title": "Gratis Kaffee"
    }
  }
}

Testereignis

Testereignis senden schickt ein Beispiel des ersten Ereignisses, das der Webhook abonniert hat, mit erfundenen Daten in genau der Form oben. Der Body trägt zusätzlich "test": true (im Format raw steht es neben den Daten), die Anfrage den Header X-platzeo-Test: true. Prüfen Sie eines von beiden, bevor Sie die Daten verarbeiten. Ein Test wird nicht wiederholt und zählt im Admin nicht zu den zugestellten oder fehlgeschlagenen Ereignissen; dort zählt jedes Ereignis einmal, mit seinem Ergebnis nach allen Wiederholungen.

Umstieg von Version 1

Bis Oktober 2026 enthielt data ganze Datensätze ohne Versionsangabe. Wenn Ihr Empfänger aus dieser Zeit stammt, prüfen Sie:

  • Felder außerhalb der Listen oben gibt es nicht mehr, zum Beispiel restaurantId, tableId und guestId in der Reservierung (jetzt tables und guest.id), notes und noShowCount beim Gast, qrToken, stampCount und pointsBalance bei der Karte (jetzt stamps und points).
  • Uhrzeiten kommen als HH:MM ohne Sekunden.
  • Ein Statuswechsel sendet ein Ereignis statt zwei. Wer bisher nur reservation.updated abonniert hat, erfährt Statuswechsel jetzt über die Status-Ereignisse.
  • source bei reservation.cancelled heißt jetzt cancelledBy.
  • Das Testereignis hat die Form eines echten Ereignisses und trägt "test": true.

Signatur prüfen

Jede Anfrage trägt den Header X-platzeo-Signature in der Form t=<Zeitstempel>,v1=<Signatur>. Die Signatur ist der HMAC-SHA256 über <Zeitstempel>.<Body> mit dem Secret, hexadezimal geschrieben. Rechnen Sie mit dem Body genau so, wie er ankommt. Prüfung in Node:

// header: Wert des Headers X-platzeo-Signature, rawBody: Body als Text
const parts = Object.fromEntries(
  header.split(",").map((p) => p.split("=")),
);
const expected = crypto
  .createHmac("sha256", secret)
  .update(parts.t + "." + rawBody)
  .digest("hex");
const ok =
  parts.v1.length === expected.length &&
  crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));

Zustellung und Wiederholungen

Als zugestellt gilt eine Antwort mit Status 2xx innerhalb des Timeouts (Standard: 10 Sekunden). Weiterleitungen folgt platzeo nicht. Schlägt ein Versuch fehl, wiederholt platzeo die Zustellung nach 1 Minute, 5 Minuten, 30 Minuten und 2 Stunden, danach alle 6 Stunden. Voreingestellt sind drei Wiederholungen, einstellbar sind 0 bis 10. Nach der letzten gilt die Zustellung als fehlgeschlagen und steht so im Protokoll.

Alle Versuche einer Zustellung tragen dieselbe Kennung im Header X-platzeo-Delivery. Kommt eine Kennung ein zweites Mal an, ist es eine Wiederholung: Verarbeiten Sie jede nur einmal.

Noch Fragen?