Hlídání cen OTA pro rezervační systémy (booking engine) a hotelové skupiny.
Přímou cenu každého hotelu porovnáváme s cenami na Booking.com a Expedia a u prodejců v Google Hotels, které hotel sleduje. Disparitu zařadíme do fronty daného hotelu, jakmile se shodnou dvě načtení z Googlu. Podepsaný webhook níže je zdokumentovaný, ale zatím není v provozu.
Content-Type: application/json
webhook-id: ns4k8w2q7c1m9x5t3h6v0b2r8d7f4j1g
webhook-timestamp: 1791547324
webhook-signature: v1,Xq0c…e21a=
{
"version": 3,
"id": "ns4k8w2q7c1m9x5t3h6v0b2r8d7f4j1g",
"event": "disparity.opened",
"disparityId": "jn7cq2b1x9d8w4fzr3m6kt5v0h7s2e8a",
"hotel": { "id": "kd72mbx0vy4p9s1c6tqh8wf3gr5n2j7e", "name": "Hotel Aurora" },
"ota": "booking",
"stayDate": "2026-10-16",
"occupancy": 2,
"directPriceEurMinor": 18800,
"otaPriceEurMinor": 17900,
"deltaEurMinor": 900,
"deltaPct": 4.79,
"trust": "confirmed",
"findingKind": "rate_disparity",
"evidence": { … },
"detectedAt": "2026-10-09T12:02:00.000Z"
}Ilustrační hodnoty. Hotel Aurora neexistuje.
Bez vazby na jediný rezervační systém. BON je první systém, ze kterého ceny načítáme přímo. Hotely s jakýmkoli jiným systémem pokrýváme už dnes.
Přímo z rezervačního systému
Kde máme konektor pro rezervační systém hotelu, načítáme přímou cenu hotelu tak, jak ji host vidí na jeho vlastním webu. BON je první.
Nebo přes Google Hotels
U hotelu s jakýmkoli jiným systémem použijeme cenu, kterou Google Hotels ukazuje pro jeho oficiální web. Nic se neinstaluje a přístup do systému nepotřebujeme.
Další rezervační systém
Každý rezervační systém znamená jeden konektor; kontroly a upozornění zůstávají stejné. Pokud vyvíjíte rezervační systém nebo channel manager, promluvme si o tom, aby byl na řadě ten váš.
Jedna podepsaná událost, když se otevře potvrzená disparita. Jakmile bude v provozu, odešle se jako JSON na vlastní endpoint každého hotelu a verzi nese hned první pole.
Pole
- version
- Dnes vždy 3. Pod touto verzí mohou přibývat další pole, proto ta, která neznáte, ignorujte.
- id
- ID tohoto doručení. Každý opakovaný pokus o toto doručení nese stejné ID, v těle i v hlavičce webhook-id. Podle něj poznáte opakování a můžete je přeskočit.
- event
- Vždy disparity.opened: nová disparita. Jiná událost není.
- disparityId
- ID disparity.
- hotel
- ID a název hotelu ve StayParity.
- ota
- Kanál jako slug malými písmeny: booking, expedia nebo kterýkoli prodejce uvedený v Google Hotels.
- stayDate
- Noc ve formátu YYYY-MM-DD.
- occupancy
- Počet hostů.
- directPriceEurMinor
- Přímá cena hotelu v eurocentech.
- otaPriceEurMinor
- Cena kanálu v eurocentech.
- deltaEurMinor
- O kolik méně kanál účtuje, v eurocentech.
- deltaPct
- Tentýž rozdíl v procentech přímé ceny, na dvě desetinná místa.
- trust
- confirmed, pokud byla cena načtena na vlastní stránce kanálu; seen_twice, pokud se ceny ze dvou načtení z Google Hotels, pořízených s odstupem nejméně 30 minut a na stejném základě, od sebe liší nejvýše o 1 %.
- reason
- now_over_threshold, když disparita, která už byla otevřená pod vašimi prahy, je mezitím překročila. U nové disparity chybí.
- findingKind
- Vždy rate_disparity: stejný pokoj za stejných podmínek, levnější u prodejce.
- evidence
- První a nejnovější načtení disparity, každé s oběma nabídkami tak, jak byly načteny: cena, zdroj, čas, pokoj, stravování a storno podmínky.
- detectedAt
- Kdy byla disparita poprvé zjištěna, v UTC.
Jak se doručuje
- POST s Content-Type application/json na veřejnou https URL
- Doručeno, jakmile váš endpoint odpoví kódem 2xx do 10 sekund
- Při 5xx nebo vypršení časového limitu se doručení opakuje, pokaždé s delší prodlevou
- 4xx se bere jako odmítnutí a doručení se neopakuje
- Přesměrování se nesledují, proto uveďte konečnou URL
Které disparity se odesílají
- Pravidlo upozornění každého hotelu určuje minimální rozdíl, v eurech i v procentech
- Disparity u prodejců, které hotel sleduje, jakmile jsou potvrzené na vlastní stránce prodejce. Disparita zjištěná jen na Googlu zůstává ve frontě a v souhrnném e-mailu
- Jedna událost na disparitu, když se otevře, a dokud zůstává otevřená, už se znovu neodešle
- Každý hotel může odesílat na vlastní endpoint, nebo několik hotelů na stejný
HMAC-SHA256 nad ID, časovým razítkem a nezměněným tělem. Řídí se specifikací Standard Webhooks, takže ho oficiální knihovna umí ověřit ve většině programovacích jazyků.
webhook-id: <the payload's id>
webhook-timestamp: <unix seconds>
webhook-signature: v1,<base64 HMAC‑SHA256>- 1Přečtěte tři hlavičky: webhook-id, webhook-timestamp v sekundách unixového času a webhook-signature, což je v1, čárka a pak podpis v Base64.
- 2Spočítejte HMAC-SHA256 nad ID, tečkou, časovým razítkem, tečkou a pak tělem přesně tak, jak dorazilo. Klíčem je část vašeho podpisového klíče za whsec_, dekódovaná z Base64.
- 3Oba podpisy porovnejte v konstantním čase a odmítněte časové razítko, které je od aktuálního času příliš vzdálené, aby zachycený požadavek nešlo odeslat znovu.
- 4Každý opakovaný pokus si ponechá své webhook-id a je znovu podepsán, s vlastním časovým razítkem.
Jeden podpisový klíč na účet podepisuje doručení všech hotelů. Správce účtu ho vytvoří v portálu a uvidí ho jen jednou; vytvořením nového se ten původní nahradí.
Ověření v Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
// Or let the official library do it: npm install standardwebhooks, then
// new Webhook(secret).verify(rawBody, headers) throws unless it checks out.
// rawBody: the request body exactly as it arrived, before JSON.parse.
// headers: the request headers, with Node's lower-case names.
// secret: your whole signing secret, whsec_ prefix included.
function verify(rawBody, headers, secret, toleranceSeconds = 300) {
const id = headers["webhook-id"];
const timestamp = Number(headers["webhook-timestamp"]);
if (!id || !Number.isInteger(timestamp)) return false;
if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const expected = createHmac("sha256", key).update(`${id}.${timestamp}.${rawBody}`).digest();
return String(headers["webhook-signature"] ?? "").split(" ").some((entry) => {
const [version, signature = ""] = entry.split(",", 2);
const given = Buffer.from(signature, "base64");
return version === "v1" && given.length === expected.length && timingSafeEqual(given, expected);
});
}Python, PHP, opakované pokusy a testování v dokumentaci webhooků
Všechny hotely v jednom účtu. Pro hotelovou skupinu nebo pro hotely, které nám přivedete jako partner.
Každý hotel s vlastní frontou
Každý hotel má vlastní disparity a pravidlo upozornění a portál mezi hotely přepíná.
Váš tým s rolemi
Správci účtu spravují tým a podpisový klíč. Členové pracují s frontou a nastavují upozornění.
Nastavíme to my
Náš tým přidá každý hotel, jeho pravidlo upozornění a přístupy pro váš tým. Vaše hotely nic neinstalují.
Doporučte nám své hotely, nebo službu StayParity prodávejte dál. Řekněte nám, jak s hotely spolupracujete, a podmínky dohodneme společně.
Doporučovat
Představte nám hotely, se kterými spolupracujete. Nastavíme je a postaráme se o ně.
Prodávat dál
Nabízejte službu StayParity svým hotelům jako součást toho, co jim už prodáváte.
Na co se partneři ptají nejdřív. Na cokoli dalšího se nás zeptejte.
Ne. BON je první rezervační systém, ze kterého ceny načítáme přímo, nikoli podmínka. U hotelu s jakýmkoli jiným systémem použijeme cenu, kterou Google Hotels ukazuje pro jeho oficiální web.
Dnes přes frontu každého hotelu a jeho denní nebo týdenní souhrn e-mailem. Až bude webhook v provozu, pošle každou potvrzenou disparitu na váš endpoint ve chvíli, kdy se otevře. Pokud potřebujete něco, co webhook nenese, dejte nám vědět.
Pro kontroly a upozornění nic. Widget pro přímé rezervace je jeden tag script na vlastním webu hotelu, a to jen pokud ho hotel chce.
Zjistěte, kolik si OTA účtují za vaše pokoje na dnešní noc.
Bez karty, bez instalace. Příštích 30 nocí ve srovnání se všemi prodejci v Google Hotels.