Mit der Veeting REST API erstellen Sie aus Ihrer eigenen Software heraus Konten, planen Meetings im Namen Ihrer User und verwalten diese Meetings über den ganzen Lebenszyklus. Es ist eine ganz normale JSON-API über HTTPS, Sie können also jeden HTTP-Client einsetzen. Die Beispiele unten verwenden curl.
Basis-URL: https://<DOMAIN-NAME>/api/v6
<DOMAIN-NAME> ist Ihre eigene Meeting-Domain. Einen gemeinsamen oder globalen Host gibt es nicht: Nehmen Sie die Domain, unter der Ihre Instanz läuft.
Authentifizierung: ein X-API-KEY-Header bei jedem Aufruf.
Content-Type: application/json bei jedem POST und PUT.
Viele Integrationen entstehen heute mit einem KI-Assistenten. Arbeiten Sie ebenso, dann kopieren Sie den folgenden Text in Ihren Assistenten und schreiben Sie einen Satz dazu, was Sie bauen möchten.
Der Text schickt den Assistenten zuerst auf diese Seite und warnt ihn vor den wenigen Dingen, die er sonst gerne falsch macht.
Lies zuerst diese Seite, bevor Du Code schreibst:
https://www.veeting.com/de/developer-documentation/api-usage
Sie dokumentiert die Veeting Rooms REST API. Halte Dich an diese
fünf Regeln:
1. Frage mich nach meiner Veeting-Domain. Rate nicht.
2. Der API-Key ist ein serverseitiges Geheimnis. Frage ich nach
einer Browser-Integration, dann baue stattdessen ein Backend
dafür und sage mir warum.
3. Ein Aufruf war nur dann erfolgreich, wenn der HTTP-Status 200
ist UND der Body responseCode 0 enthält. Prüfe beides.
4. Ein Meeting hat zwei IDs. Verwende die 24-stellige «id» in
API-Aufrufen und die «meetingId» mit den Bindestrichen im
Meeting-Link. Vertausche sie nie.
5. Frage mich, ob mein API-Key auf Kontoebene oder auf
Instanzebene liegt. Das ändert, welche Endpunkte und Header
gelten.
Was ich bauen möchte:Diese vier Regeln gelten für jeden Endpunkt auf dieser Seite. Regel 2 überrascht die meisten: Lesen Sie sie auch dann, wenn Sie den Rest überspringen.
Der API-Key ist ein serverseitiges Geheimnis. Wer ihn hat, steuert Ihre ganze Meeting-Plattform. Legen Sie ihn deshalb nie in Browser-JavaScript, in ein Mobile-App-Bundle oder in ein öffentliches Repository. Wollen Sie Meetings aus dem Browser heraus starten, dann rufen Sie die API von Ihrem eigenen Backend aus auf und geben nur das Ergebnis an die Seite weiter.
Ein erfolgreicher Aufruf liefert HTTP 200 und responseCode: 0. Die meisten Endpunkte antworten auch dann mit HTTP 200, wenn der Aufruf fehlgeschlagen ist. Das tatsächliche Ergebnis steht in einem negativen responseCode. Wer nur den HTTP-Status prüft, hält Fehler für Erfolge. Prüfen Sie immer beides.
Die Payload liegt immer unter data. Lesen Sie Ihre Werte aus data, nie von der obersten Ebene. Eine Fehlerantwort enthält gar kein data-Feld.
Alle Zeitstempel sind ISO 8601 in UTC, mit einem Z am Ende. Das Feld timezone eines Meetings steuert nur, wie Einladungen die Zeiten anzeigen. Es verschiebt weder startTime noch endTime.
Es gibt zwei Arten. Welche Sie haben, entscheidet darüber, was Sie tun können.
| Kontoebene | Instanzebene | |
|---|---|---|
| Erstellt unter | Kontoeinstellungen | White-Label-Einstellungen |
| Kann Konten erstellen | nein | ja |
| Kann Meetings verwalten | ja, innerhalb des eigenen Kontos | ja, für jeden User der Instanz |
| Berechtigungen | immer nur Meetings | wählbar: Konten, User, Meetings, Reporting, Branding |
| Wer ihn haben darf | der Kontoinhaber | ausschliesslich der Betreiber der White-Label-Instanz |
Hinweis: Geben Sie Keys auf Instanzebene nie an Kunden weiter. Wer einen solchen Key hat, erstellt Konten und handelt für jeden User der Instanz.
Ein Key auf Kontoebene erreicht immer nur die Meeting-Endpunkte, ganz gleich, welche Berechtigungen Sie ihm sonst zu geben versuchen. Die Plattform erzwingt diese Grenze selbst, sie ist keine blosse Konvention. Einen Key auf Kontoebene können Sie deshalb gefahrlos einem Integrator geben, der nur Meetings planen muss.
Ausserdem müssen Sie auf der White-Label-Instanz die Funktion accountLevelApiKeys einschalten. Ist sie aus, weist die Plattform jeden Aufruf mit einem Key auf Kontoebene ab.
Standardmässig handelt ein Key als synthetischer API-User, der zu keiner Person gehört. Wollen Sie stattdessen als echter Meeting-Organisator handeln, senden Sie einen dieser Header:
X-USER-ID: <USER-ID>
X-USER-EMAIL: <USER-EMAIL>Senden Sie beide, gewinnt X-USER-ID und die Plattform ignoriert X-USER-EMAIL. Senden Sie deshalb nur einen davon.
Das funktioniert mit beiden Key-Arten. Ein Key auf Instanzebene handelt als jeder beliebige User der Instanz, ein Key auf Kontoebene nur als Organisator im eigenen Konto. Jedes andere Ziel weist die Plattform ab.
Ein Meeting hat zwei IDs, und die beiden sind nicht austauschbar. Wer sie verwechselt, macht den häufigsten Fehler im Umgang mit dieser API.
| Name | Sieht aus wie | Wofür sie da ist |
|---|---|---|
data.id | 5e945c36b863b82cefdddf54 | 24 Zeichen hexadezimal. Verwenden Sie sie in jedem API-Aufruf. |
data.meetingId | 9974-7653-8886-0485 | Ziffern mit Bindestrichen. Verwenden Sie sie im Meeting-Link und überall dort, wo Sie das Meeting Personen anzeigen. |
Faustregel: Was Bindestriche hat, ist für Menschen. Was 24 hexadezimale Zeichen hat, ist für die API.
PUT /meetingakzeptiert zufällig beides, alle übrigen Endpunkte nicht. Nehmen Sie deshalb überalldata.id, dann liegen Sie immer richtig.
Drei weitere IDs erscheinen in Antworten:
| Name | Quelle |
|---|---|
| Konto-ID | data.id von POST /account |
| User-ID | data[].id von GET /account/admin/{accountId} |
| Raum-ID | data.roomId eines Meetings, wird zusammen mit dem Konto erstellt |
Mit diesem Aufruf erstellen Sie ein Konto und dessen ersten Administrator in einem Schritt. Nur mit Keys auf Instanzebene.
| Feld | Typ | Pflicht | Hinweise |
|---|---|---|---|
| adminEmail | string | ja | Muss auf der ganzen Instanz eindeutig sein. Jede E-Mail-Adresse gibt es nur einmal, auch über verschiedene Konten hinweg. |
| adminFirstname | string | ja | |
| adminLastname | string | ja | |
| adminPreferredLanguage | string | ja | ISO 639-1, zum Beispiel en oder de |
| accountType | string | ja | trial, standard, business, professional, confidential, boardroom, classroom, boardroomClassroom, school, payAsYouGo, internal |
| sendPassword | boolean | ja | true sendet ein Passwort per E-Mail an adminEmail. Beim Testen false setzen, damit Sie keine echten Personen anschreiben. |
| paidUntil | ISO 8601 | nein | Nach diesem Zeitpunkt funktioniert das Konto nicht mehr. |
curl 'https://<DOMAIN-NAME>/api/v6/account' \
-X POST \
-H 'X-API-KEY: <API-KEY>' \
-H 'content-type: application/json' \
--data-binary '{"sendPassword":true,"accountType":"trial","adminFirstname":"Test","adminLastname":"User","adminEmail":"test-user@example.com","adminPreferredLanguage":"en","paidUntil":"2026-12-31T23:59:59.000Z"}'{
"responseCode": 0,
"data": {
"id": "5e9459c5b863b82cefdddf4f",
"name": "test-user@example.com",
"accountType": "trial",
"numberOfMeetingRooms": 1,
"paidUntil": "2026-12-31T23:59:59.000Z",
"accountRooms": []
}
}data.id ist die Konto-ID. Merken Sie sich den Wert, der nächste Aufruf braucht ihn.
Die Antwort beschreibt das Konto, nicht den soeben erstellten Administrator. Dessen User-ID fehlt darin also. Die holen Sie mit dem nächsten Aufruf.
Dieser Aufruf liefert die Administratoren eines Kontos. Nur mit Keys auf Instanzebene. So kommen Sie an die User-ID des Administrators, den POST /account erstellt hat.
curl 'https://<DOMAIN-NAME>/api/v6/account/admin/<ACCOUNT-ID>' \
-X GET \
-H 'X-API-KEY: <API-KEY>'{
"responseCode": 0,
"data": [
{
"id": "5e9459c5b863b82cefdddf4e",
"email": "test-user@example.com",
"firstName": "Test",
"lastName": "User",
"preferredLanguage": "en",
"timezone": "Europe/Zurich"
}
]
}data ist ein Array. Bei einem frisch erstellten Konto steht darin ein Eintrag, ein Konto im laufenden Betrieb kann mehrere haben. Suchen Sie deshalb den Eintrag mit der erwarteten E-Mail-Adresse, statt blind data[0] zu nehmen.
Pflicht sind nur zwei Felder, alles Übrige ist optional. Was Sie weglassen, füllt die Plattform mit den Vorgaben des Kontos. Das Beispiel unten sendet trotzdem den vollständigen Satz, und meistens wollen Sie genau das: Ein Meeting ohne endTime oder duration ist selten so gemeint.
| Feld | Typ | Pflicht | Hinweise |
|---|---|---|---|
| topic | string | ja | |
| startTime | ISO 8601 | ja | |
| endTime | ISO 8601 | nein | |
| duration | number | nein | Minuten. Die Plattform leitet den Wert nicht aus den Zeiten ab. Achten Sie also selbst darauf, dass er dazu passt. |
| type | string | nein | standard, offTheRecord, boardroom, classroom, audiobridge |
| isRecurring | boolean | nein | |
| recurring | object | nein | Senden Sie {}, wenn isRecurring auf false steht. Das Schema steht weiter unten. |
| isRecorded | boolean | nein | Nur bestimmte Meeting-Typen lassen sich aufzeichnen. |
| isDialin | boolean | nein | |
| invitedParticipants | array | nein | [] für keine. Den Aufbau eines Eintrags finden Sie weiter unten. |
| meetingPermissionId | string oder null | nein | null nimmt die Vorgabe des Kontos. |
Mit einem Key auf Instanzebene ergänzen Sie X-USER-ID oder X-USER-EMAIL, um im Namen dieses Organisators zu planen.
curl 'https://<DOMAIN-NAME>/api/v6/meeting' \
-X POST \
-H 'X-API-KEY: <API-KEY>' \
-H 'X-USER-ID: <USER-ID>' \
-H 'content-type: application/json' \
--data-binary '{"topic":"My Meeting Topic","startTime":"2026-09-07T09:00:00.000Z","endTime":"2026-09-07T10:00:00.000Z","duration":60,"type":"standard","isRecurring":false,"isRecorded":false,"isDialin":false,"invitedParticipants":[],"recurring":{},"meetingPermissionId":null}'{
"responseCode": 0,
"data": {
"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",
"type": "standard",
"roomId": "5e9459c5b863b82cefdddf50",
"isActive": false,
"isOpen": false,
"isClosed": true,
"accountId": "5e9459c5b863b82cefdddf4f",
"addedByUserId": "5e9459c5b863b82cefdddf4e",
"timezone": "Europe/Zurich"
}
}So lesen Sie die Antwort:
data.id brauchen Sie in jedem späteren Aufruf.data.meetingId zeigen Sie Personen an, und sie gehört in den Meeting-Link.isClosed steht bei einem soeben erstellten Meeting auf true. Das ist normal. Es heisst nur, dass noch niemand beitreten kann. Für Moderatoren öffnet ein Meeting rund eine Stunde vor Beginn (isPreOpen), für alle anderen rund 15 Minuten vorher (isOpen). Werten Sie das nicht als fehlgeschlagenen Aufruf und wiederholen Sie ihn nicht.Jeder Eintrag in invitedParticipants ist ein Objekt:
| Feld | Typ | Pflicht | Hinweise |
|---|---|---|---|
| string | ja | Muss eine gültige E-Mail-Adresse sein. | |
| name | string | nein | Erscheint in der Einladung. |
| sendInvite | boolean | ja | true schickt dieser Person eine Einladung mit dem Meeting-Link. false fügt sie dem Meeting hinzu, ohne ihr zu schreiben. |
"invitedParticipants": [
{ "email": "anna@example.com", "name": "Anna Meier", "sendInvite": true }
]Setzen Sie sendInvite beim Testen auf false, aus demselben Grund wie sendPassword beim Anlegen eines Kontos: Es ist der Unterschied zwischen einem Testlauf und einer E-Mail an eine echte Person.
Steht isRecurring auf false, senden Sie {}. Steht es auf true, gelten diese Felder:
| Feld | Werte |
|---|---|
| frequencyType | never, daily, weekly, monthly, yearly |
| frequency | Zahl. Wie viele Einheiten zwischen zwei Wiederholungen liegen, 2 mit weekly also jede zweite Woche. Maximal 52 wöchentlich, 12 monatlich, 10 jährlich. |
| weekDays | An welchen Tagen eine wöchentliche Serie stattfindet, als zweibuchstabige Codes: MO, TU, WE, TH, FR, SA, SU. |
| monthlyPattern | day wiederholt am selben Tag des Monats, nthWeekDay am selben Wochentag des Monats, zum Beispiel am zweiten Dienstag. |
| endsType | never, after, on |
| endsAfter | Anzahl Wiederholungen, wenn endsType auf after steht. Die erste zählt mit, 12 ergibt also 12 Meetings insgesamt. Maximal 365. |
| endsOn | ISO-8601-Datum, wenn endsType auf on steht |
| exclude | Array von ISO-8601-Daten, die entfallen |
weekDayserwartetMO, nichtMonday. Einen Wert, den wir nicht kennen, verwerfen wir ohne Fehlermeldung. Bleibt einer wöchentlichen Serie danach kein erkannter Tag, wiederholt sie sich schlicht an dem Tag, auf den ihrestartTimefällt. Nichts schlägt fehl, es lohnt sich also, das gleich richtig zu treffen.
Ein wöchentliches Standup, jeden Montag, 12-mal:
{
"isRecurring": true,
"recurring": {
"frequencyType": "weekly",
"frequency": 1,
"weekDays": ["MO"],
"endsType": "after",
"endsAfter": 12
}
}Die API liefert keinen Link zurück. Bauen Sie ihn selbst aus der meetingId mit den Bindestrichen zusammen:
https://<DOMAIN-NAME>/meeting/<data.meetingId>Für das Beispiel oben ist das https://<DOMAIN-NAME>/meeting/9974-7653-8886-0485.
Es ist derselbe Link, den Veeting in seine Einladungs-E-Mails und Kalendereinträge schreibt. Zwei Dinge dazu:
meetingId mit den Bindestrichen. Die 24-stellige id funktioniert hier nicht.<DOMAIN-NAME>, unter der Sie die API aufrufen. Eine White-Label-Instanz kann aber eine andere Domain hinterlegt haben. Lesen Sie den Wert deshalb aus Ihrer eigenen Konfiguration, statt ihn zu raten.Ist das Meeting passwortgeschützt, hängt der Einladungslink zusätzlich den Passwort-Hash an. So muss der Empfänger das Passwort nicht eintippen:
https://<DOMAIN-NAME>/meeting/<data.meetingId>?meetingAccessHash=<data.passwordHash>An einen Meeting-Link hängen Sie zusätzlich Query-Parameter an. Damit geben Sie den Teilnehmernamen vor, überspringen den Gerätetest, wählen ein Layout und einiges mehr.
curl 'https://<DOMAIN-NAME>/api/v6/meeting/5e945c36b863b82cefdddf54' \
-X GET \
-H 'X-API-KEY: <API-KEY>' \
-H 'X-USER-EMAIL: <USER-EMAIL>'Liefert dasselbe Meeting-Objekt zurück wie POST /meeting. Erwartet ausschliesslich die 24-stellige id.
Sie brauchen das öfter, als es aussieht, denn PUT ersetzt und ergänzt nicht: Meeting lesen, die gewünschten Felder ändern, das vollständige Objekt zurücksenden. Ebenso gleichen Sie damit ab, wenn Ihnen ein Webhook entgangen sein könnte, denn die Zustellung wird genau einmal versucht, ohne Wiederholung.
Der Aufrufer muss Organisator des Kontos sein, dem das Meeting gehört, oder in dessen invitedParticipants stehen.
PUT /api/v6/meeting/<MEETING-ID>/<SEND-UPDATE-TO-PARTICIPANTS>Das zweite Pfadsegment ist ein Boolean und nicht optional:
true schickt allen unter invitedParticipants eine aktualisierte Einladung.false ändert das Meeting stillschweigend.Dieser Aufruf ersetzt das Meeting, er ist kein Patch. Senden Sie deshalb das vollständige Meeting-Objekt, samt einem id-Feld mit der Meeting-ID. Was Sie weglassen, geht verloren.
curl 'https://<DOMAIN-NAME>/api/v6/meeting/5e945c36b863b82cefdddf54/false' \
-X PUT \
-H 'X-API-KEY: <API-KEY>' \
-H 'X-USER-EMAIL: <USER-EMAIL>' \
-H 'content-type: application/json' \
--data-binary '{"id":"5e945c36b863b82cefdddf54","topic":"My Updated Meeting Topic","startTime":"2026-09-07T09:30:00.000Z","endTime":"2026-09-07T10:30:00.000Z","duration":60,"type":"standard","isRecurring":false,"isRecorded":false,"isDialin":false,"invitedParticipants":[],"recurring":{},"meetingPermissionId":null}'Meetings schliessen sich von selbst, sobald sie zu Ende sind. Wollen Sie eines sofort beenden, schliessen Sie es vorzeitig. Die Plattform entfernt dann alle, die noch im Raum sind.
Schliessen ist nicht Löschen: Der Meeting-Datensatz bleibt bestehen.
curl 'https://<DOMAIN-NAME>/api/v6/meeting/close' \
-X POST \
-H 'X-API-KEY: <API-KEY>' \
-H 'X-USER-EMAIL: <USER-EMAIL>' \
-H 'content-type: application/json' \
--data-binary '{"meetingId":"5e945c36b863b82cefdddf54"}'Hinweis: Trotz seines Namens erwartet das Feld
meetingIdin diesem Body die 24-stelligedata.id, nicht diedata.meetingIdmit den Bindestrichen.
curl 'https://<DOMAIN-NAME>/api/v6/meeting/5e945c36b863b82cefdddf54' \
-X DELETE \
-H 'X-API-KEY: <API-KEY>' \
-H 'X-USER-EMAIL: <USER-EMAIL>'Mehrere Meetings löschen Sie in einem Aufruf, indem Sie die IDs mit Kommas trennen:
DELETE /api/v6/meeting/<ID-1>,<ID-2>,<ID-3>Im Body senden Sie optional eine cancellationMessage mit. Diesen Text übernimmt die Absage an die eingeladenen Teilnehmer.
Eine Fehlerantwort führt einen negativen responseCode und eine responseMessage mit, aber kein data-Feld. Denken Sie daran: Der HTTP-Status lautet meistens trotzdem 200.
{
"responseCode": -44,
"responseMessage": "Input validation failed",
"errors": []
}| responseCode | Bedeutung | Was zu tun ist |
|---|---|---|
| 0 | Erfolg | |
| -1 | Die Plattform befindet sich in einem Wartungsfenster | Später erneut versuchen |
| -11 | Nicht gefunden | ID prüfen, nicht erneut versuchen |
| -22 | Anwendungsfehler | Nicht blind wiederholen. Der Aufruf hat nicht getan, worum Sie gebeten haben |
| -33 | API-Fehler | Aufbau des Aufrufs prüfen |
| -44 | Eingabevalidierung fehlgeschlagen | Payload korrigieren. errors nennt den Grund |
| -55 | Abrechnungsfehler | Das Konto darf diese Aktion nicht ausführen |
| -99 | Authentifizierung fehlgeschlagen | Falscher, inaktiver oder nicht berechtigter API-Key, oder ein User-Header, für den der Key nicht handeln darf. Nicht erneut versuchen |
Pro IP-Adresse dürfen Sie 50 Aufrufe pro Sekunde senden, mit einer kurzen Reserve für Spitzen. Darüber antwortet die Plattform mit HTTP 429 statt mit einem responseCode.
responseCode zu prüfen.meetingId mit den Bindestrichen dort übergeben, wo die 24-stellige id gefragt ist.id statt mit meetingId zusammenbauen.X-USER-ID zusammen mit X-USER-EMAIL senden und erwarten, dass die Plattform die E-Mail-Adresse nimmt.POST /account mit einem Key auf Kontoebene aufrufen.duration senden, die nicht zu startTime und endTime passt.Z am Ende senden.isClosed: true bei einem soeben erstellten Meeting als Fehler deuten.PUT /meeting nur die geänderten Felder senden und damit alle übrigen löschen.So richten Sie mit einem Key auf Instanzebene einen Kunden ein und planen dessen erstes Meeting:
POST /account -> data.id Konto-ID
GET /account/admin/<ACCOUNT-ID> -> data[0].id User-ID
POST /meeting + X-USER-ID -> data.id für API-Aufrufe
data.meetingId für den Meeting-Link
PUT /meeting/<ID>/<true|false> aktualisieren
POST /meeting/close vorzeitig schliessen
DELETE /meeting/<ID> entfernenMit einem Key auf Kontoebene überspringen Sie die ersten beiden Schritte und senden direkt an /meeting.
Sprechen Sie mit unserem Team über Ihre Pläne.