Webhooks sind der produktive Echtzeit-Kanal der Plattform: Bei jedem abonnierten Domain-Event sendet sie einen signierten POST an deine Endpoints. Diese Seite ist die technische Referenz; die Schritt-für-Schritt-Einrichtung steht im Help-Center unter Webhooks einrichten.
Endpoints und Abonnements
- Endpoints werden pro Workspace unter Einstellungen > Webhooks verwaltet (Berechtigung erforderlich; standardmäßig Owner und Admins).
- Jeder Endpoint abonniert eine Auswahl von Event-Typen oder per Wildcard
*alle aktuellen und künftigen Events. - Pro Endpoint können eigene HTTP-Header hinterlegt werden (etwa ein API-Key deines Zielsystems). Die Signatur-Header der Plattform sind reserviert und nicht überschreibbar.
- Das Signing-Secret (
whsec_+ Base64url) wird beim Anlegen genau einmal angezeigt und ist über Secret rotieren ersetzbar; das alte Secret wird dabei sofort ungültig.
Zustell-Envelope
Jede Zustellung ist ein POST mit Content-Type: application/json:
| Feld | Bedeutung |
|---|---|
id | Eindeutige Zustellungs-ID; identisch über alle Wiederholungs-Versuche. Als Idempotenz-Schlüssel verwenden. |
type | Event-Typ, wortgleich zum Katalog unten. |
createdAt | Entstehungszeitpunkt des Events (ISO 8601, UTC). |
data | Event-spezifische Nutzdaten. Beispiel-Payloads zeigt der Event-Picker in der Verwaltung. |
Signatur prüfen (Standard-Webhooks)
Drei Header begleiten jede Zustellung:
| Header | Inhalt |
|---|---|
webhook-id | Zustellungs-ID (gleich id im Body) |
webhook-timestamp | Unix-Sekunden des Versands |
webhook-signature | v1,<Base64-HMAC> |
Die Signatur ist HMAC-SHA256 über die Zeichenkette {id}.{timestamp}.{body}. Der Schlüssel ist der Base64url-dekodierte Teil des Secrets nach dem Präfix whsec_.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyWebhook(
secret: string, // "whsec_..."
id: string, // webhook-id header
timestamp: string, // webhook-timestamp header
signature: string, // webhook-signature header, "v1,<base64>"
rawBody: string, // unveraenderter Request-Body als String
): boolean {
const key = Buffer.from(secret.slice("whsec_".length), "base64url");
const expected = createHmac("sha256", key)
.update(`${id}.${timestamp}.${rawBody}`)
.digest("base64");
const given = signature.replace(/^v1,/, "");
const a = Buffer.from(expected);
const b = Buffer.from(given);
return a.length === b.length && timingSafeEqual(a, b);
}Fertige Standard-Webhooks-Bibliotheken existieren für alle gängigen Sprachen und übernehmen diese Schritte.
Zustellung, Wiederholungen, Auto-Pause
- Erwartet wird eine Antwort mit Status 2xx innerhalb des Zustell-Timeouts. Alles andere gilt als Fehlversuch.
- Wiederholungs-Zeitplan nach Fehlversuchen: 1, 5, 30, 120, 720 Minuten (maximal 5 Versuche pro Zustellung).
- Nach 15 fehlgeschlagenen Zustellungen in Folge (ohne zwischenzeitlichen Erfolg) pausiert der Endpoint automatisch und muss nach Behebung manuell reaktiviert werden.
- Das Zustell-Log speichert pro Versuch Status, Dauer und die ersten 2.048 Zeichen des Antwort-Bodys. Einzelne Zustellungen lassen sich manuell erneut zustellen.
- Test-Events: generisch (
webhook.test) oder pro Event-Typ mit dessen Beispiel-Payload.
Event-Katalog
Wortgleiche Typen, wie sie im type-Feld ankommen. Die Verwaltung zeigt zu jedem Event Beschreibung und Beispiel-Payload.
| Gruppe | Typ | Auslöser |
|---|---|---|
| Objekte | property.created | Neues Immobilien-Objekt angelegt |
| Objekte | property.updated | Objekt geändert |
| Objekte | property.sync.completed | Objekt-Import aus onOffice durchgelaufen |
| Kontakte | contact.sync.completed | Adressbuch-Import aus onOffice durchgelaufen |
| Tools | tool.installed | Tool im Workspace installiert |
| Tools | tool.uninstalled | Tool deinstalliert |
| Tools | tool.run.completed | Tool-Durchlauf erfolgreich beendet |
| Tools | tool.run.failed | Tool-Durchlauf mit Fehler abgebrochen |
| Zugangsdaten-Tresor | credential.created | Zugangsdaten-Eintrag erstellt |
| Zugangsdaten-Tresor | credential.updated | Eintrag geändert |
| Zugangsdaten-Tresor | credential.deleted | Eintrag entfernt |
| Zugangsdaten-Tresor | credential.shared | Zugang intern geteilt |
| Zugangsdaten-Tresor | credential.external_shared | Zugang per externem Link geteilt |
| Zugangsdaten-Tresor | credential.revealed | Geheimnis im Klartext angezeigt |
| Zugangsdaten-Tresor | credential.imported | Zugangsdaten importiert |
| Zugangsdaten-Tresor | credential.template_provisioned | Zugangsdaten-Vorlage eingerichtet |
| DB-Leads | db-lead.created | Neuer Lead erfasst |
| DB-Leads | db-lead.moved | Lead hat die Pipeline-Stufe gewechselt |
| DB-Leads | db-lead.qualified | Lead als qualifiziert markiert |
| DB-Leads | db-lead.scan.completed | Datenbank-Scan durchgelaufen |
| DB-Leads | db-lead.sync.completed | Lead-Import aus onOffice durchgelaufen |
| Homestaging | homestaging.generated | Homestaging-Bild generiert |
| Website | website_lead.received | Anfrage über ein Website-Formular eingegangen |
| Website | website_page.published | Website-Seite live geschaltet |
| Website | website_page.rolled_back | Seite auf frühere Version zurückgesetzt |
| Website | website_domain.verified | Website-Domain verifiziert |
| Website | website_onboarding.completed | Website-Ersteinrichtung abgeschlossen |
| Termin-Booking | booking.created | Termin gebucht (Buchungsseite oder Team) |
| Termin-Booking | booking.updated | Status oder Details eines Termins geändert |
| Termin-Booking | booking.cancelled | Termin abgesagt (Gast oder Team) |
| Termin-Booking | booking.rescheduled | Termin auf neuen Zeitpunkt verschoben |
| Workspace und Abrechnung | tenant.plan_changed | Abrechnungsplan gewechselt |
| Workspace und Abrechnung | tenant.credit_pack_purchased | Credit-Paket gekauft |
| Workspace und Abrechnung | tenant.recurring_pack_changed | Wiederkehrendes Credit-Paket angepasst |
| Workspace und Abrechnung | tenant.extended_usage_changed | Limit für erweiterte Nutzung angepasst |
| Workspace und Abrechnung | tenant.domain_verified | Custom-Domain des Workspace verifiziert |
| Workspace und Abrechnung | tenant.seat_subscribed | Zusätzlicher Team-Sitzplatz gebucht |
Betriebs-Empfehlungen
- Signatur immer prüfen und
webhook-idals Idempotenz-Schlüssel verwenden. - Schnell mit 2xx antworten und die eigentliche Verarbeitung asynchron erledigen (Queue), damit der Zustell-Timeout nicht reißt.
- Reihenfolge nicht voraussetzen: bei Wiederholungen können Events außer der Reihe ankommen;
createdAtnutzen. - Endpoint-Pausierung überwachen (Zustell-Log) und nach Behebung reaktivieren.