K čemu API je
API má dva druhy tokenu. Každý vydává někdo jiný a každý smí něco jiného.
- Klientský token vydává klient ve svém účtu a jedná jen za něj: volné termíny, založení, seznam, přečtení, přesun a zrušení jeho vlastních rezervací — podle stejných pravidel jako v aplikaci, včetně lhůty pro zrušení, kterou nastavuje firma. Je zdarma a modul API firmy ho nevypíná.
- Firemní token vydává firma v administraci a jedná za firmu: zakládá klienty firmy, třeba při převodu z jiného systému. K rezervacím ani k volným termínům přístup nemá — ty vždy patří konkrétnímu člověku.
Co API zatím neumí: vypsat služby, platby, webhooky, hromadné operace a mazání rezervací (zrušená rezervace v systému zůstává).
Jak získat token
Firemní token
- Přihlaste se do administrace jako správce podniku.
- V menu Klienti otevřete Firemní přístup přes API.
- Do pole Popis napište, k čemu token bude (třeba „Převod klientů z Excelu“), a klikněte na Vydat.
- Token se ukáže jen jednou, hned po vydání. Zkopírujte si ho: ukládáme jen jeho otisk a znovu ho zobrazit nejde.
V seznamu pod formulářem pak vidíte popis, začátek tokenu a kdy byl naposledy použit. Tlačítko Zneplatnit token okamžitě zastaví; nový vydáte stejně jako první.
Stránka je dostupná, dokud má firma zapnutý modul API (ve výchozím stavu zapnutý je). Vypnutý modul zastaví všechny firemní tokeny, klientské ne.
Otevřít správu tokenů v administraci
Klientský token
Klient si token vydá sám: Účet → Přístup přes API → Vydat token. Platí totéž: zobrazí se jednou a zneplatnit ho jde kdykoli.
Autentizace
Každý požadavek nese token v hlavičce Authorization se slovem Bearer. Token začíná rzv_live_.
Authorization: Bearer rzv_live_…
Volejte vždy přes https. Základní adresa rozhraní:
https://reservuj.cz/api/v1
Chybějící, neplatný nebo zneplatněný token vrací 401 unauthorized. Klientský token přestane platit i ve chvíli, kdy firma klientovi zablokuje účet.
První volání
Začněte koncovým bodem GET /me. Nic nemění, jen řekne, komu token patří: typ, firmu, rozsahy a limity. Funguje s oběma druhy tokenu.
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);
Odpověď pro firemní token:
{
"data": {
"type": "company",
"organization_id": 42,
"client_id": null,
"scopes": [
"clients"
],
"rate_limit": {
"limit_per_minute": 60,
"availability_cost": 5
}
}
}
Firemní token: založení klienta
POST /clients s tělem application/json. Povinný je e-mail a příjmení, volitelné jméno, telefon a poznámka (e-mail, jméno i příjmení nejvýš 50 znaků, telefon 20, poznámka 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);
Odpověď 201. Známý e-mail nezaloží druhého klienta, jen doplní chybějící údaje; pole created řekne, co se stalo.
{
"data": {
"email": "jana.novakova@example.com",
"created": true
}
}
Založený klient nedostane žádný e-mail. Dejte svým klientům předem vědět, že jejich údaje přenášíte do nového systému.
Klientský token: volné termíny a rezervace
Volné termíny hledáte pro jednu službu, nejvýš 31 dní najednou. ID služby (service_id) je ID řádku ceníku: firma ho vidí v administraci v Ceníku ve sloupci ID a klientovi ho předá. Seznam služeb API zatím nevydává.
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
}
]
}
Rezervaci založíte na začátek z odpovědi. S provider_id a branch_id z volného termínu vznikne přesně u toho pracovníka a na té pobočce; bez nich firma přiřadí kohokoli volného.
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é"}'
Časy jsou ve formátu ISO 8601 s časovým posunem; čas s jiným posunem se převede na čas serveru (Europe/Prague). Mezi dotazem na volné termíny a založením může termín zmizet — pak přijde 409.
Stránkování
GET /reservations vrací vlastní rezervace klienta seřazené podle začátku, po stránkách. Parametr limit je 1 až 200 (výchozí 50). Je-li v odpovědi next_cursor, pošlete ho v parametru cursor dalšího dotazu se stejným filtrem (from, to, status); null znamená poslední stránku. Kurzor z jiného filtru vrátí 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);
Čtení rezervací vrací hlavičku ETag. Pošlete ji zpět v If-None-Match a nezměněná data přijdou jako 304 bez těla.
Chyby a kódy
Chyba má vždy stejný tvar. Na kód (error.code) se dá spolehnout, text (error.message) je pro člověka a může se změnit. Kód, který neznáte, posuďte podle HTTP stavu.
{
"error": {
"code": "conflict",
"message": "Služba se platí zálohou online; objednejte ji v rezervačním portálu."
}
}
| Kód | HTTP | Kdy přijde |
|---|---|---|
unauthorized |
401 | Chybí token, je neplatný nebo zneplatněný, nemá na tuhle operaci rozsah, nebo firma vypnula modul API (firemní token). |
bad_request |
400 | Chybný nebo chybějící parametr, špatný tvar Idempotency-Key. |
invalid_json |
400 | Tělo zápisu chybí, není platný JSON nebo to není objekt. |
invalid_cursor |
400 | Kurzor je neplatný nebo patří k jinému filtru. |
idempotency_key_required |
400 | Přesun rezervace bez hlavičky Idempotency-Key. |
not_found |
404 | Záznam neexistuje nebo k němu token nemá přístup; neznámá cesta. |
conflict |
409 | Termín je obsazený, na zrušení nebo přesun je pozdě, rezervace se přesunout nedá, firma je pozastavená nebo zatím nepřijímá rezervace, služba se platí zálohou online. |
idempotency_in_progress |
409 | Požadavek se stejným Idempotency-Key se ještě zpracovává. |
unsupported_media_type |
415 | Tělo zápisu není application/json. |
unprocessable |
422 | Vstup je v pořádku, ale nedá se přijmout: služba není pro klienta v nabídce, termín je mimo provozní dobu, příliš brzy nebo se při přesunu nenabízí, pracovník či pobočka ke službě nepatří. |
idempotency_key_reused |
422 | Stejný Idempotency-Key s jiným tělem požadavku. |
rate_limited |
429 | Překročený limit požadavků. |
method_not_allowed |
405 | Cesta tuhle metodu nezná. |
server_error |
500 | Chyba na naší straně; v error.details.incident je značka pro podporu. |
409 u služby se zálohou. Službu, kterou firma účtuje zálohou online, API objednat neumí: odpoví 409 conflict a nic nezapíše. Pošlete klienta do rezervačního portálu firmy, kde zálohu zaplatí.
409 vrací také obsazený termín (mezitím ho vzal někdo jiný — načtěte volné termíny znovu), pozdní zrušení nebo přesun, pozastavená firma a firma, která zatím nepřijímá rezervace.
Cizí záznam odpovídá jako neexistující (404), ne 403 — z odpovědi nejde poznat, co existuje.
Při 500 server_error uveďte podpoře značku z error.details.incident. Zápis můžete zopakovat se stejným Idempotency-Key; pokud už proběhl, podruhé se nezapíše.
Limity požadavků
Každý token má 60 bodů za klouzavou minutu. Běžný požadavek stojí jeden bod, hledání volných termínů 5 bodů, protože je výpočetně náročné.
Každá odpověď nese RateLimit-Limit, RateLimit-Remaining a RateLimit-Reset. Po překročení přijde 429 rate_limited s hlavičkou Retry-After: počkejte uvedený počet sekund a pak pokračujte.
HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 60
RateLimit-Remaining: 0
RateLimit-Reset: 17
Retry-After: 17
Požadavky bez platného tokenu se počítají podle IP adresy, aby zkoušení tokenů nebylo zdarma.
Bezpečné opakování (Idempotency-Key)
Zápisy (POST a DELETE) můžete poslat s hlavičkou Idempotency-Key — jedinečnou hodnotou pro jednu operaci, třeba UUID. Když spojení spadne a nevíte, jestli zápis prošel, pošlete stejný požadavek znovu se stejným klíčem: dostanete uloženou odpověď s hlavičkou Idempotency-Replayed: true a nic se nezapíše dvakrát.
- Klíč má 1 až 100 viditelných znaků ASCII, rozlišuje velká a malá písmena a platí 24 hodin, jen pro týž token.
- Stejný klíč s jiným tělem vrátí 422 idempotency_key_reused, opakování během zpracování 409 idempotency_in_progress.
- U přesunu rezervace (POST /reservations/{id}/move) je hlavička povinná, bez ní přijde 400 idempotency_key_required.
- Uloží se úspěch a odmítnutí 409 nebo 422. Chybný vstup ani odmítnutí kvůli momentálnímu stavu firmy se neukládá, takže opravený nebo pozdější pokus projde.
Verze a změny
Verze rozhraní je v adrese (/api/v1). Nové pole, hodnota výčtu nebo koncový bod přibývají bez nové verze v adrese, proto neznámá pole a hodnoty ignorujte a neznámý stav rezervace berte jako ukončenou rezervaci, která se nekonala. Odebrání nebo změna významu by znamenala /api/v2.
Bezpečnost
- Token nepatří do prohlížeče. Kód webové stránky nebo aplikace si přečte každý, kdo ji otevře, a s tokenem pak jedná vaším jménem. API proto ani neposílá hlavičky CORS a volání z cizí webové stránky prohlížeč zablokuje. Volejte ho ze svého serveru.
- Token uložte jako tajný údaj (proměnná prostředí, trezor hesel), ne do zdrojového kódu ani do repozitáře.
- Pro každou integraci vydejte vlastní token s výstižným popisem; při úniku zneplatníte jen ten jeden.
- Při podezření na únik token hned zneplatněte. Zneplatnění platí okamžitě.
- Firemní token nedávejte klientům — ti si vydávají vlastní token ve svém účtu.