Boekingswidget SDK
Bouw je eigen boekingsflow op je website met de JavaScript-SDK van Online boeken, of roep de publieke widget-API rechtstreeks aan.
Laatst bijgewerkt op
De gewone widget zet je met één script-tag op je site: zie Online boeken via je website. Wil je de boekingsflow zelf tekenen, met een eigen kalender, eigen accommodatietegels en een eigen afrekenpagina, dan gebruik je de boekingswidget-SDK. Die praat met dezelfde publieke API als de widget: beschikbaarheid, prijzen, extra's, kortingscodes en het aanmaken van de boeking komen uit Tillor, de opmaak is van jou.
Laden
<script src="https://tillor.eu/booking-sdk.js"></script>
<script>
const booking = Tillor.booking({
orgId: "org_abc123",
widgetKey: "bwk_xxx",
locale: "nl",
});
</script>| Optie | Verplicht | Uitleg |
|---|---|---|
orgId | ja | Je organisatie-ID, zoals op de integratiepagina Online boeken |
widgetKey | ja | De publieke widgetsleutel (bwk_…) van dezelfde pagina |
appUrl | nee | Waar Tillor draait; standaard de oorsprong waar booking-sdk.js vandaan komt |
locale | nee | Taal van namen en meldingen; standaard de taal van de browser |
dataLayer | nee | false om events niet naar window.dataLayer / gtag te sturen (standaard true) |
De sleutel is publiek
De widgetsleutel staat in de browser, net als bij de widget. Hij geeft alleen toegang tot wat een bezoeker toch al mag: beschikbaarheid, prijzen en het aanmaken van een boeking. Beperk hem met Toegestane domeinen op de integratiepagina; aanroepen vanaf een andere site krijgen dan een 403. Per bezoeker gelden 120 leesaanroepen en 10 boekingen per minuut.
Voor TypeScript staat er een declaratiebestand naast het script: booking-sdk.d.ts.
Methodes
Alle methodes geven een Promise terug en gooien een Tillor.BookingError bij een fout (zie Fouten). Datums zijn altijd yyyy-MM-dd in de kalender van het park.
| Methode | Beschrijving |
|---|---|
getConfig() | Naam, logo, boekingsmodus (request of instant), valuta, tijdzone, regels (minimale vooruitboektijd, voorschot, toeristenbelasting, welke gastgegevens verplicht zijn), de accommodaties en de extra's |
getHolidays(from, to) | School- en feestdagen van het land van het park in die periode |
getAvailability({ from, to, terrainTypeId? }) | Per accommodatietype en per nacht: hoeveel eenheden vrij zijn, de nachtprijs en of de nacht gesloten is. Ook getAvailability(from, to, terrainTypeId?) |
quote(input) | Prijst een verblijf zoals de widget: nachten, toeslagen per persoon, extra's, toeristenbelasting en kortingscode. Geeft issues wanneer een regel het verblijf blokkeert en alternatives wanneer het volzet is |
createBooking(input, { redirect? }) | Maakt de boeking aan. Bij Direct boeken komt er een checkoutUrl (Mollie) terug; met redirect: true stuurt de SDK de bezoeker daar meteen heen |
getBooking(reference, manageToken) | Status, verblijf, prijs, factuur en betaallink van een boeking; beide waarden komen uit createBooking |
requestCancellation(reference, manageToken, reason?) | Vraagt annulering aan; je team bevestigt in Tillor |
on(event, handler) | Luistert naar een tillor_booking_*-event of "*"; geeft een functie terug die weer afmeldt |
track(event, data) | Stuurt zelf een event, bijvoorbeeld wanneer de bezoeker data kiest in jouw kalender |
dates.addDays(date, n), dates.nightsBetween(in, out), dates.today(timeZone) | Datumhulpjes zonder tijdzoneverrassingen |
Invoer van quote en createBooking
{
terrainTypeId: "tty_chalet", // id uit getConfig().accommodations
checkIn: "2026-07-10",
checkOut: "2026-07-17",
party: { adults: 2, children: 1, toddlers: 0, animals: 0 },
extras: [{ extraId: "bk_extra_linnen", quantity: 3 }], // optioneel
partySegments: [], // optioneel: ander gezelschap voor een deel van het verblijf
promoCode: "ZOMER10", // optioneel
}createBooking vraagt daarbovenop:
| Veld | Uitleg |
|---|---|
guest | firstName, lastName, email; phone wanneer config.requirePhone aanstaat; address, zipCode, city, country wanneer config.askAddress aanstaat |
licensePlates | Kentekens, als config.askLicensePlates aanstaat |
acceptTerms | Moet true zijn |
expectedTotal | Het total van de laatste quote. Is de prijs intussen veranderd, dan weigert Tillor de boeking: vraag een nieuwe prijsopgave en toon die aan de gast |
note, consentToMarketing, returnUrl | Optioneel. returnUrl is waar Mollie de gast na het betalen heen stuurt; standaard de boekingspagina van Tillor |
idempotencyKey | Optioneel; de SDK maakt er zelf een, zodat een dubbele klik nooit twee boekingen maakt |
Het antwoord bevat bookingReference, manageToken, manageUrl (de boekingspagina van de gast), status, checkoutUrl (alleen bij Direct boeken) en holdExpiresAt.
Events
De SDK stuurt dezelfde events als de widget, naar je on-handlers en standaard ook naar window.dataLayer (Google Tag Manager) en gtag (GA4).
| Event | Wie stuurt het | Gegevens |
|---|---|---|
tillor_booking_view | de SDK, bij de eerste getConfig() | mode |
tillor_booking_select_dates | jij, via track | checkIn, checkOut, nights |
tillor_booking_select_accommodation | jij, via track | terrainTypeId |
tillor_booking_begin_checkout | jij, via track | terrainTypeId, total |
tillor_booking_request_sent | de SDK, na createBooking in de modus aanvraag | reference, status |
tillor_booking_payment_started | de SDK, na createBooking in de modus direct boeken | reference, status |
booking.on("*", (data, name) => console.log(name, data));
booking.track("tillor_booking_select_dates", { checkIn, checkOut, nights: 7 });Voorbeeld: beschikbaarheid en prijs tonen
Een pagina met accommodatietegels, een datumkeuze en een prijsopgave die live meerekent. Geen framework, alleen de SDK.
<div id="accommodations"></div>
<label>Aankomst <input id="check-in" type="date" /></label>
<label>Vertrek <input id="check-out" type="date" /></label>
<label>Volwassenen <input id="adults" type="number" min="1" value="2" /></label>
<pre id="quote"></pre>
<script src="https://tillor.eu/booking-sdk.js"></script>
<script>
const booking = Tillor.booking({ orgId: "org_abc123", widgetKey: "bwk_xxx", locale: "nl" });
const state = { terrainTypeId: null, config: null };
const money = (amount) =>
new Intl.NumberFormat("nl", { style: "currency", currency: state.config.currency }).format(amount);
async function start() {
state.config = await booking.getConfig();
const today = booking.dates.today(state.config.timeZone);
document.getElementById("check-in").value = booking.dates.addDays(today, 14);
document.getElementById("check-out").value = booking.dates.addDays(today, 17);
const list = document.getElementById("accommodations");
for (const accommodation of state.config.accommodations) {
const button = document.createElement("button");
button.textContent = `${accommodation.name} · vanaf ${money(accommodation.fromPricePerNight ?? 0)} per nacht`;
button.addEventListener("click", () => {
state.terrainTypeId = accommodation.id;
booking.track("tillor_booking_select_accommodation", { terrainTypeId: accommodation.id });
refresh();
});
list.appendChild(button);
}
}
async function refresh() {
const checkIn = document.getElementById("check-in").value;
const checkOut = document.getElementById("check-out").value;
const adults = Number(document.getElementById("adults").value);
const output = document.getElementById("quote");
if (!state.terrainTypeId || !checkIn || !checkOut) return;
try {
const quote = await booking.quote({
terrainTypeId: state.terrainTypeId,
checkIn,
checkOut,
party: { adults },
});
if (!quote.available) {
output.textContent = `Volzet. Vrij: ${quote.alternatives.map((a) => `${a.checkIn} → ${a.checkOut}`).join(", ")}`;
return;
}
if (!quote.valid) {
output.textContent = `Niet mogelijk: ${quote.issues.map((issue) => issue.code).join(", ")}`;
return;
}
output.textContent = quote.lines
.map((line) => `${line.label ?? line.kind}: ${money(line.total)}`)
.concat(`Totaal: ${money(quote.total)}`)
.join("\n");
} catch (error) {
output.textContent = error.message;
}
}
for (const id of ["check-in", "check-out", "adults"]) {
document.getElementById(id).addEventListener("change", refresh);
}
start();
</script>Een boeking maak je daarna met de gastgegevens uit je eigen formulier:
const result = await booking.createBooking(
{
terrainTypeId: state.terrainTypeId,
checkIn,
checkOut,
party: { adults },
guest: { firstName: "Jan", lastName: "Janssens", email: "jan@voorbeeld.be", phone: "+32 470 12 34 56" },
acceptTerms: true,
expectedTotal: quote.total,
},
{ redirect: true }, // bij Direct boeken meteen naar Mollie
);
// Bij een aanvraag: toon result.manageUrl, de boekingspagina van de gastFouten
Elke mislukte aanroep gooit een Tillor.BookingError met status, code, message en, bij een 400, issues per veld.
| Status | code | Betekenis |
|---|---|---|
| 400 | BAD_REQUEST | Ongeldige invoer (issues zegt welk veld), of een prijs die niet meer klopt |
| 401 | UNAUTHORIZED | Verkeerde widgetsleutel, of Online boeken staat uit |
| 403 | FORBIDDEN | De website staat niet bij de toegestane domeinen |
| 429 | TOO_MANY_REQUESTS | Te veel aanroepen van deze bezoeker; retryAfter zegt hoelang wachten |
| 0 | NETWORK | Tillor was niet bereikbaar |
try {
await booking.quote(input);
} catch (error) {
if (error instanceof Tillor.BookingError && error.status === 400) {
console.log(error.issues); // [{ field: "checkOut", message: "checkOut must be after checkIn" }]
}
}Zonder de SDK
De SDK is een dunne laag over de publieke widget-API. Vanuit een andere taal of een server roep je dezelfde endpoints aan met de header X-Tillor-Booking-Widget-Key. Stuur ook een Origin- of Referer-header van een toegestaan domein wanneer Toegestane domeinen ingesteld staat.
| Methode | Endpoint |
|---|---|
GET | /api/orgs/{orgId}/booking/widget/config?locale=nl |
GET | /api/orgs/{orgId}/booking/widget/holidays?from=…&to=…&locale=nl |
GET | /api/orgs/{orgId}/booking/widget/availability?from=…&to=…&terrainTypeId=… |
POST | /api/orgs/{orgId}/booking/widget/quote |
POST | /api/orgs/{orgId}/booking/widget/reservations |
GET | /api/orgs/{orgId}/booking/widget/reservations/{bookingReference}?manageToken=… |
POST | /api/orgs/{orgId}/booking/widget/reservations/{bookingReference}/cancellation-request |
POST https://tillor.eu/api/orgs/org_abc123/booking/widget/quote
X-Tillor-Booking-Widget-Key: bwk_xxx
Content-Type: application/json
{ "terrainTypeId": "tty_chalet", "checkIn": "2026-07-10", "checkOut": "2026-07-17", "party": { "adults": 2 } }