Avalik API ja broneerimisvidin

Public API & embeddable widget. Asenda näidetes APP oma rakenduse domeeniga ja uks-maja oma asukoha lühinimega (slug).

OpenAPI 3.1: /api/openapi.json · Reference

Üldist / Base URL

Baas-URL: https://APP/api/v1/{venue}/, kus {venue} on asukoha slug.

  • HTTPS on kohustuslik; kasuta alati https:// aadressi.
  • CORS: kõik lõpp-punktid vastavad päistega Access-Control-Allow-Origin: *, meetodid GET, POST, OPTIONS, päis Content-Type. Brauserist saab API-d otse kutsuda igalt domeenilt.
  • Rate limit (IP kohta, token bucket): catalog, rooms, availability 120 päringut minutis; estimate 60 minutis; requests 10 päringut minutis. Ületamisel vastus 429 {"error":"rate_limited"}.
  • Summad on sentides (*Cents). Kellaajad on asukoha ajavööndis (venue.timezone).
  • Isikuandmeid ei tagastata kunagi. Vastused sisaldavad ainult kataloogi, vabu aegu ja hinnaarvutust; kliendi andmeid (nimi, e-post, telefon) saab ainult saata (requests), mitte lugeda. Kui asukoht ei näita hindu avalikult, on hinnaväljad tühjad / null.

GET /catalog

Asukoha avalik kataloog: ruumid, broneeringutüübid, sündmusetüübid ja lisateenused (ainult aktiivsed). Vahemälu 60 s.

ParameeterKohustuslikKirjeldus
curl "https://APP/api/v1/uks-maja/catalog"
{
  "venue": { "name": "Üks Maja", "slug": "uks-maja", "currency": "EUR", "vatRate": 0.24,
             "pricesIncludeVat": false, "showPrices": true, "startStepMinutes": 30,
             "minBookingMinutes": 60, "maxBookingMinutes": 1440, "timezone": "Europe/Tallinn" },
  "rooms": [ { "id": "…", "name": "Saal", "description": "…", "capacity": 80, "areaM2": 120,
               "equipment": [], "restrictions": null, "openHours": {} } ],
  "bookingTypes": [ { "id": "…", "code": "private", "name": "…", "description": "…", "terms": "…",
                      "collectsCompany": false, "allowsPublicEvent": true } ],
  "eventTypes": [ { "id": "…", "name": "Sünnipäev", "bookingTypeIds": ["…"] } ],
  "extras": [ { "id": "…", "name": "Heli", "description": null, "unit": "per_booking",
                "amountCents": 5000, "bookingTypeIds": null } ]
}

GET /rooms

Ainult ruumide nimekiri (sama kuju mis catalog.rooms).

curl "https://APP/api/v1/uks-maja/rooms"
{ "rooms": [ { "id": "…", "name": "Saal", "capacity": 80, "areaM2": 120, "…": "…" } ] }

GET /availability

Vabad algusajad ühe päeva kohta ruumide kaupa. commonStarts on ajad, mil kõik valitud ruumid on korraga vabad.

ParameeterKohustuslikKirjeldus
datejahYYYY-MM-DD
durationjahKestus minutites, täisarv 15 kuni 20160
roomsjah*Ruumi id-d komadega eraldatud. *Pole vaja, kui wholeHouse=1
wholeHouseei0 või 1; 1 = kõik aktiivsed ruumid
eventTypeIdeiSündmusetüübi id (võib mõjutada konflikte)
curl "https://APP/api/v1/uks-maja/availability?date=2026-11-20&duration=180&rooms=ROOM_ID_1,ROOM_ID_2"
{
  "date": "2026-11-20",
  "rooms": [
    { "roomId": "ROOM_ID_1", "freeStarts": ["10:00", "10:30", "18:00"] },
    { "roomId": "ROOM_ID_2", "freeStarts": ["10:00", "18:00"] }
  ],
  "commonStarts": ["10:00", "18:00"]
}

POST /estimate

Hinnaarvutus ja saadavuse kontroll, midagi ei salvestata. JSON keha on sama mis requests, ilma customer väljata.

ParameeterKohustuslikKirjeldus
bookingTypeIdjahBroneeringutüübi id
eventTypeIdeiSündmusetüübi id
datejahYYYY-MM-DD
endDateeiYYYY-MM-DD, kui lõpp on teisel päeval
startTime / endTimejahHH:MM (lõpp võib olla 24:00)
roomIdsjah*Ruumi id-de massiiv. *Pole vaja, kui wholeHouse=true
wholeHouseeiboolean, vaikimisi false
guestsjahTäisarv 1 kuni 10000
extrasei[{ extraId, quantity }]
title, description, messageeiTekst (title kuni 200, teised kuni 4000 märki)
isPublicEventeiboolean
localeeiKliendi keel: et | en | ru | lv | lt (pakkumise leht ja e-kirjad kasutavad seda)
curl -X POST "https://APP/api/v1/uks-maja/estimate" \
  -H "Content-Type: application/json" \
  -d '{
    "bookingTypeId": "…",
    "date": "2026-11-20",
    "startTime": "18:00",
    "endTime": "22:00",
    "roomIds": ["…"],
    "guests": 40,
    "extras": [{ "extraId": "…", "quantity": 1 }]
  }'
{
  "available": true,
  "problems": [],
  "lines": [ { "label": "Saal", "quantity": 4, "unit": "hour", "unitPriceCents": 6000, "totalCents": 24000 } ],
  "netCents": 24000,
  "vatCents": 5760,
  "grossCents": 29760,
  "needsManualPrice": false,
  "flags": []
}

needsManualPrice: true tähendab, et lõpliku hinna annab pakkumine. problems sisaldab {code, message, field}.

POST /requests

Saadab uue broneeringutaotluse. Ei kinnita kunagi automaatselt, personal vaatab taotluse üle ja saadab pakkumise. Keha nagu estimate pluss kohustuslik customer. Valikuline päringuparameeter ?source=widget märgib allika vidinaks (muidu api).

ParameeterKohustuslikKirjeldus
customer.namejah2 kuni 200 märki
customer.emailjahKehtiv e-posti aadress
customer.phoneeiKuni 40 märki
customer.companyeiEttevõtte nimi
customer.regCodeeiRegistrikood
customer.newsletterConsenteiboolean, vaikimisi false
curl -X POST "https://APP/api/v1/uks-maja/requests?source=widget" \
  -H "Content-Type: application/json" \
  -d '{
    "bookingTypeId": "…",
    "date": "2026-11-20",
    "startTime": "18:00",
    "endTime": "22:00",
    "roomIds": ["…"],
    "guests": 40,
    "customer": { "name": "Mari Maasikas", "email": "mari@example.ee", "newsletterConsent": false }
  }'
HTTP 201
{ "bookingNumber": "UM-2026-0042", "estimate": { "…": "sama kuju mis /estimate" } }

Staatused: 201 loodud; 400 valideerimisviga; 409 aeg pole vaba; 404 tundmatu asukoht; 429 rate limit.

GET /testimonials

Kliendikommentaarid, mida klient on pärast sündmust lubanud avalikult näidata (kuni 50 uusimat, uuemad ees). Iga kirje: eesnimi + perekonnanime algustäht (name), hinne 0–10 (score), kuu (month, YYYY-MM) ja kommentaar. E-posti, broneeringut ega muid isikuandmeid ei tagastata. Vahemälu 60 s; rate limit nagu catalog (120 minutis).

curl "https://APP/api/v1/uks-maja/testimonials"
{ "testimonials": [ { "name": "Anna T.", "score": 10, "month": "2026-10", "comment": "Suurepärane õhtu!" } ] }

POST /waitlist

Liitub ootenimekirjaga, kui valitud ruumid ja aeg on hõivatud (requests vastas 409). Keha on sama mis requestspluss kohustuslik waitlistConsent: true (kliendi nõusolek, et talle kirjutatakse, kui aeg vabaneb). Ei broneeri midagi. Kui aeg vabaneb (broneering tühistati, hoidmine aegus, pakkumisest loobuti), saab vanim ootaja e-kirja lingiga /w/{token}, mis kehtib venue seades waitlist_offer_hours (vaikimisi 24 h). Kinnitamisel tekib tavaline broneeringutaotlus; kui ta ei vasta, pakutakse aega järgmisele. Sama rate limit mis requests (10 päringut minutis).

curl -X POST "https://APP/api/v1/uks-maja/waitlist" \
  -H "Content-Type: application/json" \
  -d '{
    "bookingTypeId": "…",
    "date": "2026-11-20",
    "startTime": "18:00",
    "endTime": "22:00",
    "roomIds": ["…"],
    "guests": 40,
    "customer": { "name": "Mari Maasikas", "email": "mari@example.ee", "phone": "+3725551234" },
    "waitlistConsent": true
  }'
HTTP 201
{ "ok": true }

Staatused: 201 liitutud; 400 valideerimisviga (sh puuduv waitlistConsent); 404 tundmatu asukoht; 409 aeg on vaba (saada tavaline päring); 429 rate limit.

Connect anything (Zapier, Make, n8n)

Kaks viisi andmete liigutamiseks Zapieri, Make'i, n8n-i või oma skripti: webhookid (me saadame sündmuse, kui midagi juhtub) ja võtmega REST API (te loete andmeid ise). Zapieri ega Make'i jaoks pole avaldatud eraldi rakendust: kasutage nende üldist "Webhooks" / "HTTP" moodulit allolevalt juhendilt. API on saadaval Basic ja Pro paketis, webhookid Pro paketis.

1. API-võtme loomine

Admin → Integratsioonid → API-võtmed → anna võtmele nimi, vali õigused (bookings:read, customers:read) ja vajuta "Loo võti". Võti (vb_live_…, arenduskeskkonnas vb_test_…) näidatakse ainult üks kord; serveris hoitakse vaid selle räsi. Tühistatud võti lakkab kohe töötamast. Ühel asukohal võib olla kuni 20 aktiivset võtit.

2. REST lõpp-punktid

Baas-URL https://APP/api/v1/rest/ (võti tuvastab asukoha, slugi pole vaja). Autentimine: Authorization: Bearer <võti>. Kõik päringud on GET ja ainult lugemiseks; vastused on no-store.

Lõpp-punktÕigusKirjeldus
GET /memis tahes võtiAsukoht ja võtme õigused. Sobib Zapieri autentimise testiks.
GET /bookingsbookings:readBroneeringud, uuemad ees.
GET /bookings/{id}bookings:readÜks broneering.
GET /customerscustomers:readKliendid, uuemad ees.
GET /customers/{id}customers:readÜks klient.
curl "https://APP/api/v1/rest/me" -H "Authorization: Bearer vb_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
{
  "venue": { "id": "…", "name": "Üks Maja", "slug": "uks-maja", "timezone": "Europe/Tallinn", "currency": "EUR" },
  "key": { "id": "…", "name": "Zapier: Google Sheet", "scopes": ["bookings:read", "customers:read"] }
}

Broneeringute filtrid (kõik valikulised):

ParameeterKohustuslikKirjeldus
statuseiÜks või mitu komaga eraldatud staatust: new_request, quote_drafting, awaiting_staff, quote_sent, awaiting_customer, confirmed, cancelled, completed
updated_sinceeiISO 8601; ainult broneeringud, mida on muudetud sellest ajast (updated_at >= …)
from / toeiISO 8601 kuupäev või kellaaeg (UTC); algusaeg starts_at >= from ja < to
sorteicreated_at (vaikimisi) või updated_at; alati uuemad ees. Muudatuste jälgimiseks kasuta sort=updated_at
limitei1 kuni 100, vaikimisi 25
cursoreiEelmise vastuse next_cursor
curl "https://APP/api/v1/rest/bookings?status=confirmed&updated_since=2026-11-01T00:00:00Z&limit=25" \
  -H "Authorization: Bearer $VENUE_API_KEY"
{
  "data": [
    {
      "id": "6f1c…", "number": "B-2026-00042", "status": "confirmed",
      "starts_at": "2026-11-20T16:00:00.000Z", "ends_at": "2026-11-20T20:00:00.000Z",
      "guests": 40, "whole_house": false,
      "rooms": [ { "id": "…", "name": "Saal" } ],
      "customer_id": "9a2d…", "total_gross_cents": 29760, "currency": "EUR",
      "created_at": "2026-11-02T08:15:00.000Z", "updated_at": "2026-11-05T12:00:00.000Z"
    }
  ],
  "next_cursor": "WyJjcmVhdGVkX2F0IiwiMjAyNi0xMS0wMlQwODoxNTowMC4wMDAwMDBaIiwiNmYxYy4uLiJd"
}

Broneeringu väljad: id, number, status, starts_at, ends_at, guests, whole_house, rooms[], customer_id, total_gross_cents, currency, created_at, updated_at. total_gross_cents on aktsepteeritud pakkumise summa sentides koos käibemaksuga (null, kuni pakkumist pole aktsepteeritud).

Kliendid: id, name, email, phone, company, created_at, erased. Kliendil pole updated_at välja, seega kliente sorteeritakse loomise järgi ja updated_since neil puudub. GDPR-i alusel kustutatud kliendil on erased: true ja nimi, e-post, telefon ning ettevõte null.

Lehitsemine / Pagination

Vastus on { "data": [...], "next_cursor": "..." }. Kui next_cursor ei ole null, kutsu sama päring uuesti parameetriga cursor. Kursor on läbipaistmatu: ära tõlgenda ega muuda seda, ja kasuta sama sort väärtust. Lehitsemine on stabiilne: uued kirjed ei nihuta juba nähtud lehti.

curl "https://APP/api/v1/rest/bookings?limit=25&cursor=WyJjcmVhdGVkX2F0Ii4uLl0" -H "Authorization: Bearer $VENUE_API_KEY"

Rate limit ja vead

60 päringut minutis võtme kohta. Iga vastus sisaldab X-RateLimit-Limit, X-RateLimit-Remaining ja X-RateLimit-Reset (UNIX-sekundid, millal limiit on täielikult taastunud). Ületamisel tuleb 429 ja Retry-After (sekundites).

StaatusTähendus
401 unauthorizedVõti puudub, on vigane või tühistatud
403 insufficient_scopeVõtmel puudub vajalik õigus
403 api_not_availableAsukoha pakett ei sisalda API-t või on tasuta periood läbi
400 invalid_parameterVigane filter, limit või cursor; message ütleb, milline
404 not_foundKirjet pole selles asukohas
429 rate_limitedLiiga palju päringuid
HTTP/1.1 401 Unauthorized
{ "error": "unauthorized", "message": "Invalid or revoked API key." }
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1793000000
{ "error": "rate_limited", "message": "Rate limit exceeded: 60 requests per minute per key." }

3. Webhookid

Admin → Integratsioonid → Webhookid lisab aadressi ja sündmused ning näitab allkirjastamise saladuse üks kord (webhookid on Pro paketis). Sündmused: booking.confirmed, booking.cancelled, booking.changed, request.created. Sisu sisaldab ainult id-sid, numbrit, aega ja staatust, kliendi andmeid mitte (need saab vajadusel REST API-st, customer_id kaudu).

POST https://hooks.zapier.com/hooks/catch/123456/abcdef/
content-type: application/json
x-venue-event: booking.confirmed
x-venue-delivery: 3b1f6c0e-6a8e-4c0e-9d52-0d1f6c7a1b11
x-venue-signature: sha256=9f2c…

{
  "event": "booking.confirmed",
  "occurred_at": "2026-11-05T12:00:03.120Z",
  "data": {
    "bookingId": "6f1c…", "number": "B-2026-00042", "status": "confirmed",
    "startsAt": "2026-11-20T16:00:00.000Z", "endsAt": "2026-11-20T20:00:00.000Z", "guests": 40
  }
}
  • x-venue-signature: sha256=<hex> on keha toorbaitide HMAC-SHA256 saladusega. Kontrolli seda enne sisu usaldamist.
  • x-venue-delivery on saatmise unikaalne id; kordused kasutavad sama id-d ja sama keha, nii saad topeltsaatmised ära tunda.
  • x-venue-event on sündmuse nimi. Vasta 2xx staatusega 5 sekundi jooksul. Ümbersuunamisi (3xx) ei järgita ja need loetakse ebaõnnestunuks.
  • Kordused: ebaõnnestunud saatmist (mitte-2xx, ajalõpp, viga) korratakse 1 min, 5 min, 30 min, 2 h ja 12 h pärast. Pärast kuuendat katset märgitakse see "dead". Admin → Integratsioonid → Webhooki saatmislogi näitab viimast 100 saatmist (30 päeva), saadetud sisu ja nuppu "Proovi uuesti".
const crypto = require("node:crypto");

// rawBody: the request body exactly as received (a Buffer or string), NOT re-serialised JSON
function verify(rawBody, signatureHeader, secret) {
  const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Express: app.post("/hook", express.raw({ type: "application/json" }), (req, res) => {
//   if (!verify(req.body, req.get("x-venue-signature"), process.env.WEBHOOK_SECRET)) return res.sendStatus(401);
//   const delivery = req.get("x-venue-delivery"); // skip if you have already processed this id
//   res.sendStatus(200);
// });

Retsept: kinnitatud broneering → Google Sheeti rida (Zapier)

  1. Zapieris loo Zap, trigger: Webhooks by Zapier → Catch Hook. Kopeeri antud URL.
  2. Lisa URL Integratsioonide lehel webhookina, vali ainult booking.confirmed. Salvesta saladus.
  3. Tee Zapieris Test trigger: kinnita testbroneering, et Zapier saaks näidissisu.
  4. Action: Google Sheets → Create Spreadsheet Row; vii veergudesse data.number, data.startsAt, data.guests.
  5. Kliendi nime/e-posti vajadusel: lisa samm Webhooks by Zapier → Custom Request (GET /api/v1/rest/bookings/{data.bookingId} ja /customers/{customer_id}, päis Authorization: Bearer …).

Zapier ei kontrolli allkirja ise; kui see on oluline, lisa vahele Code by Zapier samm ülaltoodud kontrolliga.

Retsept: Make (Custom webhook)

  1. Make'is lisa moodul Webhooks → Custom webhook, loo uus webhook ja kopeeri URL.
  2. Lisa URL Integratsioonide lehel webhookina ja vali sündmused. Käivita Run once ja tekita sündmus, et Make õpiks struktuuri.
  3. Pärast seda saad kasutada välju data.number, data.status jne järgmistes moodulites.
  4. Andmete lugemiseks: HTTP → Make a request, URL https://APP/api/v1/rest/bookings, päis Authorization: Bearer <võti>, parse response = jah; tulemuse data[] massiivi saab itereerida. n8n-is sama: HTTP Request sõlm.

Veaformaat / Errors

HTTP 400  { "problems": [ { "code": "invalid", "message": "…", "field": "guests" } ] }
HTTP 400  { "error": "invalid", "problems": ["date: Invalid"] }      // availability
HTTP 404  { "error": "not_found" }
HTTP 409  { "problems": [ { "code": "not_available", "message": "Valitud aeg ei ole vaba", "field": "time" } ] }
HTTP 429  { "error": "rate_limited" }

Vead on alati JSON. Kontrolli esmalt HTTP staatust, seejärel error või problems[].

WordPress

WordPressile on pistikprogramm (lühikood [venue_booking] ja plokk). Vidin v2 näitab vormi, vabade kuupäevade kalendrit või ruumide nimekirja (data-mode="form|calendar|rooms|events"), laeb alles siis, kui külastaja selleni kerib, ja võtab värvid CSS-muutujatest --vb-primary, --vb-radius, --vb-bg, --vb-text, --vb-font:

<div data-venue-booking data-venue="uks-maja" data-mode="calendar" data-lang="et"
     style="--vb-primary:#0f766e;--vb-radius:12px"></div>
<script src="https://APP/widget.js" async></script>

Vidin (iframe)

Lisa lehele "Custom HTML" plokk ja kleebi sinna:

<script src="https://APP/widget.js"
        data-venue="uks-maja"
        data-lang="et"
        async></script>

Valikuline data-target="#element-id" paigutab vormi etteantud elementi. Vidin kohandab kõrgust automaatselt ja töötab ühel lehel mitu korda. Pärast taotluse saatmist tekib iframe elemendil sündmus venuebooking:submitted (detail.number on broneeringu number):

document.addEventListener("venuebooking:submitted", function (e) {
  console.log("Taotlus saadetud, number:", e.detail.number);
});

Google Tag Manageri jaoks lisa skripti sildile data-datalayer="1": siis lisatakse window.dataLayer massiivi sündmus { event: "venue_booking_submitted", booking_number }. Meie vidin ise jälgimisskripte ei laadi; nõusoleku ja edasise töötluse otsustab teie oma tag manager.

Vabade aegade päring PHP-s

<?php
$res = wp_remote_get( add_query_arg( array(
    'date'     => '2026-11-20',
    'duration' => 180,
    'rooms'    => 'ROOM_ID_1',
), 'https://APP/api/v1/uks-maja/availability' ), array( 'timeout' => 10 ) );

if ( ! is_wp_error( $res ) && 200 === wp_remote_retrieve_response_code( $res ) ) {
    $data   = json_decode( wp_remote_retrieve_body( $res ), true );
    $starts = $data['commonStarts']; // nt ["10:00", "18:00"]
}

Saabuv e-kiri / Inbound email

Taotlusi saab saata ka e-kirjaga. Kui kirja tekst järgib allolevat vormi, tõlgendatakse see otse (ilma tehisaruta). Rea võti on enne koolonit; suur- ja väiketähti ei eristata. Kohustuslikud on Kuupäev ja Algus. Kuupäev kujul AAAA-KK-PP, kellaaeg HH:MM, loetelud komaga eraldatud.

Tüüp: private
Sündmus: Sünnipäev
Kuupäev: 2026-11-20
Algus: 18:00
Lõpp: 22:00
Osalejaid: 40
Ruumid: Saal, Köök
Lisateenused: Heli
Ettevõte: Näide OÜ
Telefon: +372 5555 5555
Pealkiri: Mari 30
Sõnum: Soovime ka kohvilauda.

Puuduvate väljade korral märgitakse taotlus mittetäielikuks ja personal täiendab selle käsitsi.