Wofür die API da ist
Die API kennt zwei Arten von Token. Jede stellt jemand anderes aus und jede darf etwas anderes.
- Ein Kundentoken stellt der Kunde in seinem Konto aus, und er handelt nur für ihn: freie Termine, Anlegen, Auflisten, Lesen, Verschieben und Stornieren seiner eigenen Buchungen — nach denselben Regeln wie in der Anwendung, einschließlich der Stornofrist, die das Unternehmen festlegt. Er ist kostenlos, und das API-Modul des Unternehmens schaltet ihn nicht ab.
- Ein Firmentoken stellt das Unternehmen in der Verwaltung aus, und er handelt für das Unternehmen: Er legt Kunden des Unternehmens an, etwa beim Umzug aus einem anderen System. Auf Buchungen und freie Termine hat er keinen Zugriff — die gehören immer einer bestimmten Person.
Was die API noch nicht kann: Leistungen auflisten, Zahlungen, Webhooks, Massenoperationen und Buchungen löschen (eine stornierte Buchung bleibt im System).
So erhalten Sie einen Token
Firmentoken
- Melden Sie sich als Administrator des Betriebs in der Verwaltung an.
- Öffnen Sie im Menü Klienti den Punkt Firemní přístup přes API (Firmenzugang über die API).
- Schreiben Sie in das Feld Popis (Beschreibung), wofür der Token gedacht ist (zum Beispiel „Kundenimport aus Excel“), und klicken Sie auf Vydat (ausstellen).
- Der Token wird nur einmal angezeigt, direkt nach dem Ausstellen. Kopieren Sie ihn: Wir speichern nur seinen Hash und können ihn nicht erneut anzeigen.
Die Liste unter dem Formular zeigt dann die Beschreibung, den Anfang des Tokens und die letzte Verwendung. Die Schaltfläche Zneplatnit (widerrufen) stoppt einen Token sofort; einen neuen stellen Sie genauso aus wie den ersten.
Die Seite ist verfügbar, solange das Unternehmen das API-Modul eingeschaltet hat (standardmäßig ist es eingeschaltet). Ein ausgeschaltetes Modul stoppt alle Firmentoken, Kundentoken nicht.
Tokenverwaltung in der Verwaltung öffnen
Kundentoken
Ein Kunde stellt den Token selbst aus: Konto → API-Zugang → Token ausstellen. Es gilt dasselbe: Er wird einmal angezeigt und kann jederzeit widerrufen werden.
Authentifizierung
Jede Anfrage trägt den Token im Header Authorization mit dem Wort Bearer. Der Token beginnt mit rzv_live_.
Authorization: Bearer rzv_live_…
Rufen Sie immer über https auf. Die Basisadresse der Schnittstelle:
https://reservuj.cz/api/v1
Ein fehlender, ungültiger oder widerrufener Token liefert 401 unauthorized. Ein Kundentoken verliert seine Gültigkeit auch, wenn das Unternehmen das Konto des Kunden sperrt.
Erster Aufruf
Beginnen Sie mit dem Endpunkt GET /me. Er ändert nichts, er sagt nur, wem der Token gehört: Typ, Unternehmen, Berechtigungen und Limits. Er funktioniert mit beiden Arten von Token.
curl -s "https://reservuj.cz/api/v1/me" \
-H "Authorization: Bearer $RESERVUJ_TOKEN"
const response = await fetch('https://reservuj.cz/api/v1/me', {
headers: { Authorization: `Bearer ${process.env.RESERVUJ_TOKEN}` },
});
const body = await response.json();
if (!response.ok) {
throw new Error(`${response.status} ${body.error.code}: ${body.error.message}`);
}
console.log(body.data.type, body.data.scopes);
Die Antwort für einen Firmentoken:
{
"data": {
"type": "company",
"organization_id": 42,
"client_id": null,
"scopes": [
"clients"
],
"rate_limit": {
"limit_per_minute": 60,
"availability_cost": 5
}
}
}
Firmentoken: Kunden anlegen
POST /clients mit einem Body vom Typ application/json. Pflicht sind E-Mail und Nachname, optional Vorname, Telefon und Notiz (E-Mail, Vor- und Nachname höchstens 50 Zeichen, Telefon 20, Notiz 255).
curl -s -X POST "https://reservuj.cz/api/v1/clients" \
-H "Authorization: Bearer $RESERVUJ_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: import-2026-jana-novakova" \
-d '{"email":"jana.novakova@example.com","surname":"Nováková","name":"Jana","phone":"+420777123456"}'
import { randomUUID } from 'node:crypto';
const idempotencyKey = randomUUID();
const response = await fetch('https://reservuj.cz/api/v1/clients', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.RESERVUJ_TOKEN}`,
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey,
},
body: JSON.stringify({ email: 'jana.novakova@example.com', surname: 'Nováková', name: 'Jana' }),
});
const body = await response.json();
console.log(response.status, body);
Die Antwort ist 201. Eine bekannte E-Mail legt keinen zweiten Kunden an, sie ergänzt nur fehlende Angaben; das Feld created sagt, was passiert ist.
{
"data": {
"email": "jana.novakova@example.com",
"created": true
}
}
Ein angelegter Kunde erhält keine E-Mail. Informieren Sie Ihre Kunden vorab, dass Sie ihre Daten in ein neues System übertragen.
Kundentoken: freie Termine und Buchungen
Freie Termine suchen Sie für eine Leistung, höchstens 31 Tage auf einmal. Die Leistungs-ID (service_id) ist die ID der Preislistenzeile: Das Unternehmen sieht sie in der Verwaltung in der Preisliste (Ceník) in der Spalte ID und gibt sie dem Kunden weiter. Eine Liste der Leistungen liefert die API noch nicht.
curl -s "https://reservuj.cz/api/v1/availability?service_id=123&date_from=2026-11-16&date_to=2026-11-22" \
-H "Authorization: Bearer $RESERVUJ_TOKEN"
{
"data": [
{
"start": "2026-11-17T10:00:00+01:00",
"end": "2026-11-17T11:00:00+01:00",
"service_id": 123,
"provider_id": 7,
"branch_id": 3
}
]
}
Die Buchung legen Sie auf einen Beginn aus der Antwort an. Mit provider_id und branch_id aus dem freien Termin entsteht sie genau bei diesem Mitarbeiter und an diesem Standort; ohne sie weist das Unternehmen jemanden zu, der frei ist.
curl -s -X POST "https://reservuj.cz/api/v1/reservations" \
-H "Authorization: Bearer $RESERVUJ_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 8d0c6a52-5d0e-4f4e-9a49-2f6b0c3e7a11" \
-d '{"service_id":123,"start":"2026-11-17T10:00:00+01:00","provider_id":7,"branch_id":3,"note":"Poprvé"}'
Zeiten sind im Format ISO 8601 mit Zeitversatz; eine Zeit mit anderem Versatz wird in die Serverzeit (Europe/Prague) umgerechnet. Zwischen der Abfrage freier Termine und der Buchung kann ein Termin verschwinden — dann kommt 409.
Paginierung
GET /reservations liefert die eigenen Buchungen des Kunden nach Beginn sortiert, seitenweise. Der Parameter limit ist 1 bis 200 (Standard 50). Enthält die Antwort next_cursor, senden Sie ihn im Parameter cursor der nächsten Anfrage mit demselben Filter (from, to, status); null bedeutet die letzte Seite. Ein Cursor aus einem anderen Filter liefert 400 invalid_cursor.
let cursor = null;
do {
const url = new URL('https://reservuj.cz/api/v1/reservations');
url.searchParams.set('from', '2026-11-16T00:00:00Z');
url.searchParams.set('limit', '200');
if (cursor) {
url.searchParams.set('cursor', cursor);
}
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.RESERVUJ_TOKEN}` },
});
const page = await response.json();
for (const reservation of page.data) {
console.log(reservation.id, reservation.status, reservation.start);
}
cursor = page.next_cursor;
} while (cursor);
Das Lesen von Buchungen liefert einen ETag-Header. Senden Sie ihn in If-None-Match zurück, und unveränderte Daten kommen als 304 ohne Body.
Fehler und Codes
Ein Fehler hat immer dieselbe Form. Auf den Code (error.code) ist Verlass, der Text (error.message) ist für Menschen und kann sich ändern. Einen Code, den Sie nicht kennen, beurteilen Sie nach dem HTTP-Status.
{
"error": {
"code": "conflict",
"message": "Služba se platí zálohou online; objednejte ji v rezervačním portálu."
}
}
| Code | HTTP | Wann er kommt |
|---|---|---|
unauthorized |
401 | Der Token fehlt, ist ungültig oder widerrufen, hat für diese Operation keine Berechtigung, oder das Unternehmen hat das API-Modul ausgeschaltet (Firmentoken). |
bad_request |
400 | Ein falscher oder fehlender Parameter, ein falsch geformter Idempotency-Key. |
invalid_json |
400 | Der Body des Schreibzugriffs fehlt, ist kein gültiges JSON oder kein Objekt. |
invalid_cursor |
400 | Der Cursor ist ungültig oder gehört zu einem anderen Filter. |
idempotency_key_required |
400 | Verschieben einer Buchung ohne den Header Idempotency-Key. |
not_found |
404 | Der Datensatz existiert nicht oder der Token hat keinen Zugriff darauf; ein unbekannter Pfad. |
conflict |
409 | Der Termin ist belegt, es ist zu spät zum Stornieren oder Verschieben, die Buchung lässt sich nicht verschieben, das Unternehmen ist gesperrt oder nimmt noch keine Buchungen an, die Leistung wird mit einer Online-Anzahlung bezahlt. |
idempotency_in_progress |
409 | Eine Anfrage mit demselben Idempotency-Key wird noch verarbeitet. |
unsupported_media_type |
415 | Der Body des Schreibzugriffs ist kein application/json. |
unprocessable |
422 | Die Eingabe ist in Ordnung, kann aber nicht angenommen werden: Die Leistung wird dem Kunden nicht angeboten, der Termin liegt außerhalb der Öffnungszeiten, ist zu früh oder wird beim Verschieben nicht angeboten, Mitarbeiter oder Standort gehören nicht zur Leistung. |
idempotency_key_reused |
422 | Derselbe Idempotency-Key mit einem anderen Anfrage-Body. |
rate_limited |
429 | Das Anfragelimit wurde überschritten. |
method_not_allowed |
405 | Der Pfad kennt diese Methode nicht. |
server_error |
500 | Ein Fehler auf unserer Seite; error.details.incident enthält eine Kennung für den Support. |
409 bei einer Leistung mit Anzahlung. Eine Leistung, die das Unternehmen mit einer Online-Anzahlung abrechnet, kann die API nicht buchen: Sie antwortet 409 conflict und schreibt nichts. Schicken Sie den Kunden in das Buchungsportal des Unternehmens, wo er die Anzahlung bezahlt.
409 kommt auch bei einem belegten Termin (jemand anderes hat ihn inzwischen genommen — laden Sie die freien Termine neu), bei verspätetem Stornieren oder Verschieben, bei einem gesperrten Unternehmen und bei einem Unternehmen, das noch keine Buchungen annimmt.
Ein fremder Datensatz antwortet wie ein nicht existierender (404), nicht 403 — aus der Antwort darf nicht hervorgehen, was existiert.
Bei 500 server_error nennen Sie dem Support die Kennung aus error.details.incident. Einen Schreibzugriff können Sie mit demselben Idempotency-Key wiederholen; ist er schon durchgegangen, wird er nicht doppelt geschrieben.
Anfragelimits
Jeder Token hat 60 Punkte pro gleitender Minute. Eine gewöhnliche Anfrage kostet einen Punkt, die Suche nach freien Terminen 5 Punkte, weil sie rechenintensiv ist.
Jede Antwort trägt RateLimit-Limit, RateLimit-Remaining und RateLimit-Reset. Über dem Limit kommt 429 rate_limited mit dem Header Retry-After: Warten Sie die angegebene Anzahl Sekunden und machen Sie dann weiter.
HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 60
RateLimit-Remaining: 0
RateLimit-Reset: 17
Retry-After: 17
Anfragen ohne gültigen Token werden nach IP-Adresse gezählt, damit das Ausprobieren von Token nicht kostenlos ist.
Sichere Wiederholung (Idempotency-Key)
Schreibzugriffe (POST und DELETE) können Sie mit dem Header Idempotency-Key senden — einem eindeutigen Wert für eine Operation, etwa einer UUID. Bricht die Verbindung ab und wissen Sie nicht, ob der Schreibzugriff durchgegangen ist, senden Sie dieselbe Anfrage mit demselben Schlüssel erneut: Sie erhalten die gespeicherte Antwort mit dem Header Idempotency-Replayed: true, und nichts wird doppelt geschrieben.
- Der Schlüssel hat 1 bis 100 sichtbare ASCII-Zeichen, unterscheidet Groß- und Kleinschreibung und gilt 24 Stunden, nur für denselben Token.
- Derselbe Schlüssel mit einem anderen Body liefert 422 idempotency_key_reused, eine Wiederholung während der Verarbeitung 409 idempotency_in_progress.
- Beim Verschieben einer Buchung (POST /reservations/{id}/move) ist der Header Pflicht; ohne ihn kommt 400 idempotency_key_required.
- Gespeichert werden Erfolg und Ablehnungen mit 409 oder 422. Fehlerhafte Eingaben und Ablehnungen wegen des momentanen Zustands des Unternehmens werden nicht gespeichert, ein korrigierter oder späterer Versuch geht also durch.
Versionen und Änderungen
Die Version der Schnittstelle steht in der Adresse (/api/v1). Neue Felder, Aufzählungswerte oder Endpunkte kommen ohne neue Version in der Adresse hinzu; ignorieren Sie daher unbekannte Felder und Werte und behandeln Sie einen unbekannten Buchungsstatus als beendete Buchung, die nicht stattgefunden hat. Das Entfernen oder eine Bedeutungsänderung würde /api/v2 bedeuten.
Sicherheit
- Ein Token gehört nicht in den Browser. Den Code einer Webseite oder App kann jeder lesen, der sie öffnet, und mit dem Token dann in Ihrem Namen handeln. Deshalb sendet die API keine CORS-Header, und der Browser blockiert Aufrufe von einer fremden Webseite. Rufen Sie sie von Ihrem Server auf.
- Speichern Sie den Token als Geheimnis (Umgebungsvariable, Passworttresor), nicht im Quellcode oder in einem Repository.
- Stellen Sie für jede Integration einen eigenen Token mit einer treffenden Beschreibung aus; bei einem Leck widerrufen Sie nur diesen einen.
- Bei Verdacht auf ein Leck widerrufen Sie den Token sofort. Der Widerruf wirkt sofort.
- Geben Sie den Firmentoken nicht an Kunden weiter — die stellen sich in ihrem Konto einen eigenen Token aus.
Spezifikation (Swagger UI) · Website-Widget · Erste Schritte