Webhooks

Übersicht

Mit Webhooks ruft Veeting Ihr System auf, sobald in einem Meeting etwas geschieht. So müssen Sie die API nicht laufend abfragen. Tritt ein Event ein, sendet die Plattform einen HTTP-Aufruf an eine URL, die Sie konfiguriert haben. Im JSON-Body steht das betroffene Objekt.

Sie konfigurieren Webhooks an zwei Stellen. Beide lösen beim selben Event aus:

  • Auf Ebene der White-Label-Instanz, im Abschnitt «WebHooks» der Systemkonfiguration. Löst für jedes Konto der Instanz aus.
  • Auf Kontoebene, pro Konto. Löst nur für dieses Konto aus.

Konfigurieren Sie beide, ruft die Plattform Ihren Endpunkt beim selben Event zweimal auf, einmal pro Ebene.

Verwendung mit einem KI-Assistenten

Wenn Sie mit einem KI-Coding-Assistenten arbeiten, können Sie den folgenden Text in Ihren Assistenten kopieren und mit einem Satz ergänzen, der beschreibt, was Sie programmieren möchten.

Der Prompt verweist auf unsere Entwicklerdokumentation, die wir für Coding-Assistenten in einer separaten, token-optimierten KI-Version ausliefern.

Lies zuerst diese Seite, bevor Du Code schreibst:
https://www.veeting.com/de/developer-documentation/web-hooks

Sie dokumentiert die Webhooks von Veeting Rooms. Halte Dich an
diese fünf Regeln:

1. Veeting versucht die Zustellung genau einmal. Es gibt keine
   Wiederholung. Bestätige schnell, lege die Arbeit in eine Queue
   und gleiche bei Bedarf über die REST API ab.
2. Keine Payload ist in responseCode und data verpackt, auch
   onMeetingSummaryCreated nicht.
3. Manche Events senden ein Array statt eines Objekts. Alle fünf
   Reminder und onMeetingDeleted tun das. Prüfe das, bevor Du
   Felder liest.
4. Die Authentifizierung ist ein einziger optionaler
   X-API-KEY-Header. Wenn ich den Endpunkt schützen will, prüfe
   diesen Header und weise alles andere ab.
5. Frag mich, ob der Webhook auf Instanzebene oder auf Kontoebene
   konfiguriert ist. Beide können aktiv sein, was zwei Aufrufe pro
   Event bedeutet.

Was ich bauen möchte:

Regeln für jeden Webhook

  1. Wir versuchen die Zustellung genau einmal. Es gibt keine Wiederholung, kein Backoff und keine Dead-Letter-Queue. Ist Ihr Endpunkt nicht erreichbar oder antwortet er mit einem Fehlerstatus, loggen wir das Event auf unserer Seite. Auf Ihrer Seite ist es verloren. Richten Sie sich darauf ein: rasch bestätigen, die Arbeit intern in eine Queue legen und, wenn Sie sicher sein müssen, das Meeting mit GET /meeting/{id} erneut abrufen, statt darauf zu vertrauen, dass jedes Event angekommen ist.

  2. Antworten Sie schnell. Für die Zustellung ist kein Timeout konfiguriert. Ein langsamer Endpunkt hält die Verbindung deshalb offen, statt schnell abzubrechen. Geben Sie ein 2xx zurück, sobald Sie die Payload angenommen haben, und erledigen Sie die Arbeit danach.

  3. Der Body ist das Objekt selbst, kein Wrapper. Anders als bei der REST API steckt eine Webhook-Payload nicht in responseCode und data. Zwei Events weichen ab: onMeetingCancelled verschachtelt das Meeting zusammen mit day im Body, und onMeetingRecordingCreated kommt als Objekt an, in dem das Meeting steckt, statt als Meeting selbst. Beide sind unten beschrieben.

  4. Prüfen Sie die Form der Payload, bevor Sie Felder daraus lesen. Manche Events senden ein einzelnes Objekt, andere ein Array. Welche das sind, steht unten.

Konfiguration

Sie konfigurieren jeden Webhook einzeln, mit vier Einstellungen:

EinstellungBedeutung
enabledOb dieser Webhook überhaupt auslöst
urlDer Endpunkt, den wir aufrufen
methodPOST, PUT oder DELETE. Sonst gilt POST. Auf Kontoebene ausdrücklich setzen.
apiKeyOptional. Siehe unten.

Ihren Endpunkt schützen

Setzen Sie apiKey, sendet die Plattform ihn bei jedem Aufruf als X-API-KEY-Header. So kann Ihr Endpunkt alle Aufrufe ohne diesen Header abweisen. Lassen Sie das Feld leer, sendet die Plattform keinen solchen Header.

Mehr Authentifizierung hat ein Webhook-Aufruf nicht. Einen Endpunkt ohne konfigurierten API-Key ruft jeder auf, der seine URL kennt.

content-type: application/json sendet die Plattform immer mit.

Die Events

Es gibt 16 Events.

EventLöst aus, wennPayload
onMeetingScheduledEin Meeting erstellt wirdMeeting
onMeetingUpdatedEin Meeting geändert wirdMeeting
onMeetingClosedEin Meeting geschlossen wird, automatisch oder manuellMeeting
onMeetingDeletedMeetings gelöscht werdenArray von Meeting
onMeetingCancelledEin einzelner Termin einer Serie abgesagt wird{ meeting, day }
onMeetingJoinedJemand einem Meeting beitrittMeetingParticipantsStatus
onMeetingLeftJemand ein Meeting verlässtMeetingParticipantsStatus
onMeetingSummaryCreatedEine Summary erstellt wird, üblicherweise rund fünf Minuten nach dem Schliessen des MeetingsMeetingSummary
onMeetingRecordingCreatedEin aufgezeichnetes Meeting zusammengefügt wurdeAufzeichnung
onChatMessageCreatedEine Chat-Nachricht in einem Meeting gesendet wirdChatMessage mit IDs
onChatMessageModeratedSich der Moderationsstatus einer Chat-Nachricht ändertChatMessage mit IDs
onMeetingsReminder15MinutesBefore15 Minuten vor Meeting-BeginnArray von Meeting
onMeetingsReminder30MinutesBefore30 Minuten vor Meeting-BeginnArray von Meeting
onMeetingsReminder60MinutesBefore60 Minuten vor Meeting-BeginnArray von Meeting
onMeetingsReminder24HoursBefore24 Stunden vor Meeting-BeginnArray von Meeting
onMeetingsReminder48HoursBefore48 Stunden vor Meeting-BeginnArray von Meeting

Zwei Dinge zu den Remindern werden leicht übersehen:

  • Sie stellen ein Array zu, denn ein Reminder-Lauf umfasst alle Meetings, die zu diesem Zeitpunkt anstehen. Auch ein einzelnes Meeting kommt als Array mit einem Eintrag an.
  • Der Umfang hängt von der Ebene ab. Ein Reminder auf Ebene der White-Label-Instanz enthält jedes anstehende Meeting der Instanz, ein Reminder auf Kontoebene nur die Meetings dieses Kontos. Derselbe Lauf sendet also unterschiedliche Arrays an unterschiedliche Endpunkte.

Payloads

Meeting

Diese Payload senden onMeetingScheduled, onMeetingUpdated und onMeetingClosed. Es ist dasselbe Objekt, das die REST API bei POST /meeting zurückgibt, hier gekürzt:

{
  "id": "5e945c36b863b82cefdddf54",
  "meetingId": "9974-7653-8886-0485",
  "topic": "My Meeting Topic",
  "startTime": "2026-09-07T09:00:00.000Z",
  "endTime": "2026-09-07T10:00:00.000Z",
  "duration": 60,
  "type": "standard",
  "roomId": "5e9459c5b863b82cefdddf50",
  "isRecurring": false,
  "isRecorded": false,
  "isDialin": false,
  "invitedParticipants": [],
  "agenda": "",
  "whitelabelId": "5c737902b377b0f7fbf81fce",
  "accountId": "5e9459c5b863b82cefdddf4f",
  "addedByUserId": "5e9459c5b863b82cefdddf4e",
  "addedByUserEmail": "test-user@example.com",
  "addedByUserName": "Test User",
  "timezone": "Europe/Zurich",
  "isActive": true,
  "isOpen": false,
  "isClosed": false
}

Die beiden IDs verhalten sich genau wie in der REST API: Verwenden Sie id, wenn Sie die API aufrufen, und meetingId, wenn Sie einen Link bauen oder das Meeting jemandem anzeigen.

Array von Meeting

Diese Payload senden onMeetingDeleted und alle fünf Reminder. Es ist dasselbe Objekt wie oben, in einem Array:

[
  { "id": "5e945c36b863b82cefdddf54", "meetingId": "9974-7653-8886-0485", "topic": "My Meeting Topic" }
]

Bei onMeetingDeleted kann jedes Meeting zusätzlich ein Feld cancellationMessage mitführen. Darin steht der Text, den der Organisator beim Löschen angegeben hat.

Abgesagter Termin

Diese Payload sendet onMeetingCancelled, wenn jemand einen einzelnen Termin einer Serie absagt und nicht die ganze Serie. Das Meeting steckt verschachtelt im Body, und day bezeichnet den Termin:

{
  "meeting": { "id": "5e945c36b863b82cefdddf54", "meetingId": "9974-7653-8886-0485", "isRecurring": true },
  "day": "2026-09-14"
}

MeetingParticipantsStatus

Diese Payload senden onMeetingJoined und onMeetingLeft:

{
  "meetingId": "5df78199c015b37195230596",
  "meetingToken": "0000-0000-0000-0000",
  "isNamedRoom": false,
  "participant": {
    "name": "Joe Doe",
    "email": "joe@example.com",
    "meetingParticipantId": "4edaf0d2-cb6c-42f1-a266-4ae7d971f2e6",
    "isModerator": false
  },
  "totalNumberOfGuests": 3,
  "totalNumberOfModerators": 1,
  "whitelabelId": "5c737902b377b0f7fbf81fce",
  "accountId": "5e9459c5b863b82cefdddf4f"
}

Hinweis: Hier enthält meetingId die 24 Zeichen lange id des Meetings, und meetingToken die Nummer mit den Bindestrichen. Das ist genau umgekehrt zur Benennung überall sonst. Lesen Sie hier also besonders genau.

MeetingSummary

Diese Payload sendet onMeetingSummaryCreated. Wie jede andere Payload ist der Body die Summary selbst, ohne Wrapper:

{
  "id": "5f7d49eb62e8f9a43b23f986",
  "meetingId": "5f7d49e662e8f9a43b23f983",
  "meetingType": "boardroom",
  "agenda": "",
  "minutes": "",
  "participants": [
    {
      "name": "Participant 1",
      "durations": [
        { "action": "joined", "timestamp": 1602046445811 },
        { "action": "left", "timestamp": 1602046670808 }
      ],
      "info": {
        "browserName": "Chrome",
        "browserVersion": "82.0.4062.0",
        "osName": "macOS",
        "osVersion": "10.15.6",
        "platformType": "desktop",
        "platformVendor": "Apple"
      }
    }
  ],
  "documents": [],
  "pdf": null,
  "accountId": "5c73790ab377b0f7fbf81fde",
  "isNamedRoom": false,
  "isRecordingPrepared": false,
  "sfuHostname": "ch-01-sfu-02.wlvmr.net",
  "meetingQuestions": [],
  "meetingConversationDuration": 25,
  "meetingConversationStartToEndDuration": 25,
  "meetingParticipantsStartToEndDuration": 35,
  "meetingStartToEndDuration": 60
}

Die vier Dauer-Felder messen Unterschiedliches. Alle Angaben in Sekunden:

FeldWas es misst
meetingConversationDurationWie lange mindestens zwei Personen gleichzeitig im Raum waren
meetingConversationStartToEndDurationVom ersten Moment, in dem zwei Personen zusammen im Raum waren, bis zum letzten
meetingParticipantsStartToEndDurationVom Beitritt der ersten Person bis zum Verlassen der letzten
meetingStartToEndDurationDie geplante Dauer, einschliesslich Verlängerung

Aufzeichnung

Diese Payload sendet onMeetingRecordingCreated. Sie ist ein Wrapper: Meeting und Summary stecken darin, statt selbst den Body zu bilden.

{
  "meeting": { "id": "5e945c36b863b82cefdddf54", "meetingId": "9974-7653-8886-0485", "topic": "My Meeting Topic" },
  "summary": { "id": "5f48a701ee0c388a879bffa7" },
  "mergedRecordingUrl": "https://<recording-host>/prepared/<meeting>/<token>.webm",
  "recordingUrls": [
    "video-file-1.webm",
    "video-file-2.webm",
    "video-file-3.webm"
  ],
  "emailNotificationRecipients": ["organizer@example.com"]
}
EigenschaftBedeutung
meetingDas vollständige Meeting-Objekt, wie oben beschrieben
summaryDie Summary, zu der diese Aufzeichnung gehört
mergedRecordingUrlDie einzelne, zusammengefügte Aufzeichnung
recordingUrlsDie einzelnen Teile, aus denen die Aufzeichnung zusammengefügt wurde, so wie der Aufzeichnungs-Host sie meldet
emailNotificationRecipientsWen die Plattform gleich per E-Mail über diese Aufzeichnung informiert

Hinweis: Lesen Sie die IDs des Meetings aus meeting.id und meeting.meetingId. Auf oberster Ebene dieser Payload gibt es kein Feld meetingId.

ChatMessage

Diese Payload senden onChatMessageCreated und onChatMessageModerated. Der Body ist ein Objekt mit vier Feldern: meetingId, whitelabelId, accountId und chatMessage, das die Chat-Nachricht enthält.

Häufige Fehler

  • Darauf bauen, dass wir eine fehlgeschlagene Zustellung wiederholen. Wir tun es nicht.
  • Die Payload eines Reminders als einzelnes Meeting behandeln. Alle fünf Reminder senden ein Array.
  • Erwarten, dass onMeetingSummaryCreated in responseCode und data verpackt ankommt. Es ist ein unverpacktes Objekt wie jede andere Payload.
  • meetingId in den Join- und Leave-Payloads als Nummer mit Bindestrichen lesen. Dort ist es die 24 Zeichen lange id, und meetingToken ist die mit den Bindestrichen.
  • Denselben Endpunkt auf Instanz- und auf Kontoebene konfigurieren und den doppelten Aufruf dann für einen Fehler halten.
  • apiKey bei einem Endpunkt leer lassen, der Daten verändert.
  • Erst die langsame Arbeit erledigen und dann antworten. Niemand bricht den Aufruf für Sie ab.

Sind Sie nicht sicher, wie Sie Ihr Projekt am Besten umsetzen sollen?

Sprechen Sie mit unserem Team über Ihre Pläne.