Tehnička API specifikacija
Dobrodošli na portal za programere. Gost365 omogućava jednostavnu integraciju rezervacionog kalendara i formi na bilo kom sajtu (WordPress, Webflow, React, PHP ili statički HTML).
Autentifikacija
Svaki zahtev prema /api/v1/* mora sadržati dva zaglavlja: vaš javni API ključ i zaglavlje Origin sa domenom sa kog se poziv vrši.
Ključ vlasnik objekta generiše sam u Gost365 Dashboard-u pod Podešavanja → API, a može ga generisati i Super Admin iz Control Panela. Ima oblik gost365_live_ + 32 heksadecimalna karaktera (ukupno 45), npr. gost365_live_e83bc2a3a9f14c7b8d2e5a6f0b1c3d4e. Novi ključ važi tek nakon čuvanja forme, a čuvanjem stari prestaje da važi - integracija ne radi dok u njoj ne zamenite ključ. Poseban test ključ ne postoji: test režim se određuje statusom naloga, a ne prefiksom ključa.
Zaglavlja:
Za ključ koristite jedno od prva dva zaglavlja. Ako pošaljete oba, x-api-key ima prednost i Authorization se ignoriše. Šema se poredi doslovno kao Bearer - veliko početno slovo i tačno jedan razmak; bearer malim slovima ili dupli razmak daju 401 Invalid API key.
Origin je obavezan - i proverava se pre API ključa.
Browser ga kod poziva na drugi domen postavlja automatski. Ali pozivi sa servera (PHP cURL, WordPress, Node backend, Postman) ne šalju Origin sam od sebe - morate ga dodati ručno. Zahtev bez njega se odbija sa 403 i telom { "error": "Missing Origin header" }, bez obzira na to što je ključ ispravan. Vrednost mora odgovarati nekom od domena upisanih u allowed_domains.
Pozive uvek šaljite preko HTTPS-a - ključ putuje u zaglavlju i preko običnog HTTP-a bi bio čitljiv u mreži. Napomena: sam API ne proverava protokol i ne odbija HTTP zahteve; HTTPS obezbeđuje hosting sloj, a ne validacija zahteva.
Autentifikacija prolazi ako je status naloga active, test ili trial (probni period od 14 dana od otvaranja naloga, tokom kojeg nalog radi punom snagom i prima prave rezervacije). Kada probni period istekne, API vraća 403 sa porukom Probni period je istekao. Kontaktirajte Gost365 za aktivaciju naloga. Za svaki drugi status API vraća 403 Account suspended. Kod naloga u statusu test sve email notifikacije odlaze na adresu superadmina umesto vlasniku objekta - koristite ga dok razvijate integraciju.
CORS & Whitelist Zaštita
Kako bi se sprečilo da neko zloupotrebi vaš javni API ključ na drugom sajtu, Gost365 poredi domen iz Origin zaglavlja sa listom dozvoljenih domena naloga. Lista se uređuje u Dashboard-u pod Podešavanja → API (polje allowed_domains), a može je postaviti i Super Admin iz Control Panela. Više domena odvaja se zarezom.
Kako se domeni porede
Pre poređenja se i dolazni Origin i svaki unos iz liste normalizuju: uklanjaju se razmaci, http:// odnosno https://, prefiks www., broj porta i završna kosa crta, i sve se prevodi u mala slova.
- Jedan unos
mojapartman.compokriva iwww.mojapartman.com,https://mojapartman.com/iMOJAPARTMAN.COM. - Poddomeni se ne pokrivaju automatski -
shop.mojapartman.commora biti poseban unos. - Za lokalni razvoj upišite samo
localhost- port se zanemaruje, pa taj unos pokriva svaki port. - Unos
*propušta sve domene. Koristite ga samo privremeno - javni ključ tada nije ničim vezan za vaš sajt. - Ako je lista prazna, nijedan domen ne prolazi i svaki poziv vraća
403.
403 sa telom { "error": "CORS origin ne dozvoljava pristup sa domena: ..." }. Provera se radi u samoj ruti - OPTIONS preflight zahtev prolazi bez provere ključa i domena i uvek vraća 204, pa neuspeh vidite tek na stvarnom GET ili POST pozivu.Rate Limiting (Ograničenje)
U produkciji je aktivna zaštita od preopterećenja koja dozvoljava maksimalno 100 zahteva u minuti po kombinaciji IP adrese i API ključa. Kvota se vodi po tom paru, a ne po samoj IP adresi - različiti ključevi sa iste IP adrese imaju odvojene kvote. Ukoliko pređete limit, API vraća 429 sa telom { "error": "Too many requests" }.
Šta se zapravo broji
Limit se meri u kliznom prozoru od 60 sekundi, brojanjem zapisa u internom logu zahteva. Uspešni GET zahtevi se iz razloga performansi ne loguju, pa praktično ne troše kvotu. U kvotu ulaze uspešni POST zahtevi (rezervacije, upiti, forme) i svi odbijeni zahtevi (401 i 403), uključujući neuspele GET pozive. Sami 429 odgovori se ne broje, pa se blokada ne produžava sama od sebe.
U praksi: čitanje dostupnosti je praktično neograničeno, ali imate najviše 100 slanja rezervacija (ili 100 odbijenih zahteva) u minuti po paru IP + ključ.
IP adresa se očitava iz zaglavlja x-real-ip, a ako ga nema iz prvog elementa x-forwarded-for. Ako provera limita ne uspe zbog greške u bazi, zahtev se propušta (fail-open) umesto da se blokira.
API Reference: Jedinice
Dohvatite rezervacione jedinice klijenta i njihove zauzete termine. Jedini query parametar je opcioni include=amenities_detailed (opisan niže). Samo polje status se ne vraća u odgovoru.
Koje jedinice se NE vraćaju
- deaktivirane (status
INACTIVE), - jedinice čiji modul nije uključen na nalogu,
- jedinice koje je vlasnik sakrio sa sajta u podešavanjima jedinice.
Nova jedinica koju vlasnik sam doda u Dashboard-u je na početku sakrivena sa sajta, dok je ne popuni i ne uključi prikaz. Ako vam jedinica ne stiže, prvo proverite to.
Primer odgovora (200 OK) - odaberite tip jedinice
Primer prikazuje odgovor za GET /api/v1/units?include=amenities_detailed. Bez tog parametra polje amenities_detailed ne postoji, sve ostalo je isto.
Smeštaj
Vozila
Oprema
Ugostiteljstvo
{
"success": true,
"language": "sr",
"data": [
{
"id": "8f7e6d5c-4b3a-2a19-0e9d-8c7b6a5f4e3d",
"name": "Apartman Morski Konjic",
"type": "Apartman",
"billing_mode": "nightly",
"description": "Luksuzan studio sa pogledom na more.",
"address": "Bulevar Plaže 12, 85310, Budva, Crna Gora",
"base_price": 75,
"check_in": "14:00",
"check_out": "10:00",
"amenities": ["wifi", "ac", "pool", "fridge"],
"amenities_detailed": [
{
"id": "wifi",
"label": "WiFi",
"group": "Opšte",
"icon": "wifi"
},
{
"id": "ac",
"label": "Klima",
"group": "Opšte",
"icon": "air-vent"
},
{
"id": "pool",
"label": "Bazen",
"group": "Opšte",
"icon": "waves"
},
{
"id": "fridge",
"label": "Frižider",
"group": "Kuhinja",
"icon": "refrigerator"
}
],
"details": {
"location": {
"street": "Bulevar Plaže 12",
"city": "Budva",
"postalCode": "85310",
"country": "Crna Gora"
},
"max_guests": "4",
"bedrooms": "2",
"beds": "3",
"living_rooms": "1",
"bathrooms": "1",
"terraces": "1",
"balconies": "0",
"parking_spots": "1",
"kitchen_type": "open",
"area_size": "65",
"floor": "2. sprat",
"total_floors": "4",
"house_rules": "Tiho posle 22h. Ljubimci uz dogovor.",
"checkin_instructions": "Ključ se uzima u kafiću u dvorištu."
},
"images": ["https://<vaš-supabase-projekat>.supabase.co/storage/v1/object/public/units/slika1.jpg"],
"pricing_config": {
"base_price": 75,
"hourly_price": null,
"currency": "EUR",
"tiers": [],
"seasons": [],
"extra_guest_threshold": null,
"extra_guest_charge": null,
"cleaning_fee": null,
"addons": [
{ "id": "a1b2c3", "label": "Doručak", "price": 7, "charge": "per_guest_per_night", "required": false, "default_on": true }
]
},
"availability_blocks": [
{ "start_date": "2026-07-01T00:00:00+00:00", "end_date": "2026-07-08T00:00:00+00:00", "status": "confirmed" },
{ "start_date": "2026-07-15T00:00:00+00:00", "end_date": "2026-07-20T00:00:00+00:00", "status": "confirmed" }
]
}
]
}status: "confirmed"). Rezervacije na čekanju (pending) i otkazane (cancelled) se ne vraćaju. Termin poslat kroz POST /api/v1/bookings kreira se kao pending i postaje vidljiv ovde tek kada ga vlasnik potvrdi u Dashboard-u. Zato je odbijanje sa 409 moguće i za termin koji na vašem kalendaru izgleda slobodno - obavezno obradite taj status u UI-ju.start_date i end_date u blokovima uvek stižu kao ISO datetime sa vremenskom zonom (npr. "2026-07-01T10:00:00+00:00") - i kod dnevnih tipova, gde je vreme 00:00:00. Za satne tipove vreme je značajno i blokira tačan interval. Ako vam treba samo datum, uzmite String(v).split('T')[0].
Termin se smatra zauzetim po pravilu preklapanja: postojeći blok blokira novi zahtev samo ako se intervali stvarno seku. Zato check-out jednog gosta i check-in drugog mogu biti isti dan, a dva satna termina isti dan ne smetaju jedan drugom ako se ne preklapaju.
Polje address je ravan string koji sistem automatski sastavlja iz strukturirane adrese, redosledom ulica, poštanski broj, grad, država. Ako vam adresa treba po delovima, koristite objekat details.location sa poljima street, city, postalCode, country. Top-level polje location ne postoji u odgovoru.
Polja check_in i check_out su podrazumevana vremena dolaska i odlaska za jedinicu (npr. "14:00"), namenjena prikazu na sajtu. Ona ne ograničavaju vrednosti koje šaljete u POST /api/v1/bookings - API ih pri kreiranju rezervacije ne proverava.
Polje description je slobodan tekst koji vlasnik piše sam i može da sadrži prelome redova (\n) kojima deli pasuse. Prikažite ga sa CSS pravilom white-space: pre-line, inače pregledač sve prelome pretvori u razmake i opis se slije u jedan pasus. Stariji zapisi mogu imati i \r\n; pre-line pravilno obrađuje oba.
Cenu ne morate računati sami: pricing_config je informativan, a konačan iznos server obračunava pri kreiranju rezervacije.
Ako cenu prikazujete unapred, pricing_config se čita ovako. base_price je osnovna cena po noći (satu, danu). tiers su cene po broju gostiju (odrasli + deca, bez beba): pravilo sa guests_exact važi za tačno toliko osoba i ima prednost, zatim prvo pravilo po redu sa guests_up_to ili guests_from koje odgovara. seasons su periodi sa svojom cenom (date_from i date_to uključivo, i sopstvena pravila po broju gostiju); sezona se bira prema datumu dolaska i važi za ceo boravak. Na kraju se dodaju cleaning_fee (jednom) i dodatne usluge iz addons.
Opremljenost. Polje amenities je niz id-jeva, npr. ["wifi", "fridge"]. Može biti prazno ili izostati. Id-jevi su vezani za tip jedinice i ne smeju se spajati između tipova: heating kod smeštaja znači „Grejanje", a kod bazena „Grijanje". Zato nemojte praviti sopstvenu tabelu id → naziv.
Umesto toga dodajte ?include=amenities_detailed na zahtev. Odgovor tada uz svaku jedinicu nosi i razrešen oblik, spreman za prikaz:
"amenities_detailed": [
{ "id": "wifi", "label": "WiFi", "group": "Opšte", "icon": "wifi" },
{ "id": "fridge", "label": "Frižider", "group": "Kuhinja", "icon": "refrigerator" }
]labelje na jeziku klijenta. Koji je to jezik piše u poljulanguagena vrhu odgovora ("sr","sl"ili"bs"). Poljelanguagestiže uvek, i bezinclude.groupje naziv sekcije i uvek je na srpskom, bez obzira na jezik klijenta. Kod smeštaja su toOpšte,Kuhinja,Kupatilo,Spavaća soba,Dnevni boravak,Wellness,Spoljašnji prostor,Pogled,Porodica,Bezbednost,Pristupačnost,Usluge. Ako naslove sekcija prikazujete na drugom jeziku, prevedite ih sami. Može bitinullkod tipova koji još nisu podeljeni na sekcije - tada prikažite sve u jednom bloku.iconje stabilan naziv ikonice, npr."refrigerator". Vrednosti se ne menjaju i poklapaju se sa nazivima iz skupa lucide, pa ako ga već koristite, mapiranje radi samo od sebe. Može bitinull.- Grupišite po polju
group, ne po redosledu. Stavke su uglavnom poređane po sekcijama, ali dodatne stavke pojedinih tipova (npr. doručak kod Sobe, roštilj kod Kuće) stižu na kraju spiska. Ko iscrtava naslov sekcije pri svakoj promeni grupe dobiće istu sekciju dva puta. Primer ispod grupiše ispravno.
amenities_detailed se šalje samo kada ga zatražite kroz include. Sajtovi koji su već napravljeni dobijaju isti oblik jedinica kao do sada, uz jedino novo polje language na vrhu odgovora.Prikaz po sekcijama, kao na velikim sajtovima za smeštaj, tada staje u petnaestak linija:
const res = await fetch(
'https://gost365.app/api/v1/units?include=amenities_detailed',
{ headers: { 'x-api-key': API_KEY } }
)
const { data } = await res.json()
for (const jedinica of data) {
const sekcije = {}
for (const a of jedinica.amenities_detailed ?? []) {
const g = a.group ?? 'Ostalo'
;(sekcije[g] ??= []).push(a)
}
for (const [naziv, stavke] of Object.entries(sekcije)) {
console.log(naziv)
for (const a of stavke) {
console.log(' ', IKONICE[a.icon] ?? '•', a.label)
}
}
}Ako ne koristite biblioteku ikonica, prekopirajte ovu tabelu i gotovi ste:
const IKONICE = {
'wifi': '📶', 'air-vent': '❄️', 'flame': '🔥', 'arrow-up-down': '🛗',
'trees': '🌳', 'fence': '🏞️', 'circle-parking': '🅿️', 'warehouse': '🚘',
'waves': '🏊', 'dog': '🐕', 'cigarette': '🚬', 'croissant': '🥐',
'beef': '🍖', 'sprout': '🌱', 'chef-hat': '🍳', 'refrigerator': '🧊',
'cooking-pot': '🍲', 'microwave': '🍱', 'utensils-crossed': '💧', 'coffee': '☕',
'soup': '🥣', 'shower-head': '🚿', 'bath': '🛁', 'wind': '💨',
'washing-machine': '🧺', 'layers': '🧖', 'sparkles': '✨', 'door-open': '🚪',
'bed-double': '🛏️', 'shirt': '👕', 'bed-single': '🛌', 'baby': '👶',
'tv': '📺', 'sofa': '🛋️', 'utensils': '🍽️', 'laptop': '💻',
'fan': '🌀', 'volume-x': '🔇', 'door-closed': '🚪', 'plug-zap': '🔌',
'snowflake': '❄️', 'glass-water': '🫖', 'sandwich': '🍞', 'blender': '🥤',
'salad': '🧂', 'wine': '🍷', 'droplet': '💧', 'thermometer': '🌡️',
'footprints': '🩴', 'blinds': '🪟', 'bed': '🛏️', 'monitor-play': '🎬',
'satellite-dish': '📡', 'speaker': '🔊', 'book-open': '📚', 'dices': '🎲',
'gamepad-2': '🎮', 'armchair': '🪑', 'dumbbell': '🏋️', 'heater': '♨️',
'droplets': '🫧', 'sun': '☀️', 'umbrella': '⛱️', 'tree-palm': '🏖️',
'sailboat': '⛵', 'mountain-snow': '🏔️', 'building-2': '🏙️', 'flower-2': '🌷',
'toy-brick': '🧸', 'puzzle': '🛝', 'shield': '🛡️', 'alarm-smoke': '🚨',
'siren': '⚠️', 'fire-extinguisher': '🧯', 'briefcase-medical': '🩹', 'vault': '🔐',
'cctv': '📹', 'lock-keyhole': '🔒', 'person-standing': '🚶', 'accessibility': '♿',
'hand': '✋', 'key-round': '🔑', 'luggage': '🧳', 'plane': '✈️',
'spray-can': '🧹', 'calendar-days': '📅', 'bike': '🚲', 'concierge-bell': '🛎️',
}API Reference: Rezervacije
Pošaljite zahtev za rezervaciju sa vašeg sajta u Dashboard. Rezervacija se uvek kreira sa statusom pending i izvorom website, a vlasniku objekta automatski stiže email za potvrdu - API ne kreira odmah potvrđenu rezervaciju i ta polja se ne mogu poslati kroz telo zahteva.
Primer Request Body - odaberite tip jedinice
Smeštaj
Vozila
Oprema
Ugostiteljstvo
{
"unit_id": "8f7e6d5c-4b3a-2a19-0e9d-8c7b6a5f4e3d",
"start_date": "2026-07-01",
"end_date": "2026-07-08",
"guest_name": "Jovan Jovanović",
"guest_email": "jovan.jovanovic@email.com",
"guest_phone": "+381641234567",
"adults_count": 2,
"children_count": 1,
"infants_count": 0,
"notes": "Molimo za tihu sobu i krevetac za bebu."
}Opis Polja (JSON Body)
| Polje | Tip | Obavezno | Opis |
|---|---|---|---|
| unit_id | UUID | Da | Jedinstveni ID apartmana iz GET /units |
| start_date | String (YYYY-MM-DD ili YYYY-MM-DDTHH:mm:ss) | Da | Datum dolaska / početak termina. Za smeštaj i dnevne tipove dovoljan je datum (2026-07-01). Za tipove sa billing_mode: "hourly" šaljite datum i vreme sa vremenskom zonom. Neispravan datum vraća 400 Invalid date format. |
| end_date | String (YYYY-MM-DD ili YYYY-MM-DDTHH:mm:ss) | Da | Datum odlaska / kraj termina. Kod satnih tipova mora biti posle start_date, a kod ostalih ne sme biti pre njega - inače 400 end_date must be after start_date. Za satne termine obavezno pošaljite vremensku zonu (npr. 2026-07-01T19:00:00+02:00); vreme bez zone se čita kao UTC. |
| guest_name | String | Da | Ime i prezime gosta |
| guest_email | String | Da | Kontakt email adresa gosta |
| guest_phone | String | Ne | Kontakt telefon gosta |
| adults_count | Integer | Ne (podr. 1) | Broj odraslih osoba. Mora biti veći od nule. Ako pošaljete 0, a children_count i infants_count izostavite, ukupan broj gostiju je 0 i baza odbija upis - dobijate 500, ne 400. Kada broj nije poznat, polje izostavite. |
| guests_count | Integer | Ne (zastarelo) | Zadržano radi kompatibilnosti sa starijim integracijama. Koristi se samo ako adults_count nije poslat. Za nove integracije koristite razbijena polja. |
| children_count | Integer | Ne (podr. 0) | Broj dece (2-17 godina) |
| infants_count | Integer | Ne (podr. 0) | Broj beba (do 2 godine) |
| notes | String | Ne | Napomena gosta |
| service_type | String | Ne (praktično obavezno za jedinice sa details.service_options) | Naziv usluge iz details.service_options jedinice. Ako vrednost tačno odgovara nazivu jedne od opcija, cena se uzima iz te opcije: obračunava se kao 1 × cena usluge, trajanje termina se ignoriše i naknada za čišćenje se ne dodaje. |
| addons | Array<String> | Ne | Id-jevi dodatnih usluga iz pricing_config.addons koje je gost izabrao, npr. ["a1b2..."]. Šalju se samo id-jevi - cena se uvek uzima sa servera. Usluge sa required: true se naplaćuju i kada nisu poslate. Iznos se dodaje na cenu noćenja i ulazi u total_price. Ignoriše se kada je poslat važeći service_type.Način naplate je u polju charge svake usluge: per_guest_per_night (po osobi po noći), per_night (po noći), per_guest (po osobi, jednom), once (jednom za ceo boravak). Bebe se ne računaju kao osobe. Kod satnih i dnevnih tipova „noć" znači sat, odnosno dan. Usluge bez naziva ili sa cenom 0 se ne naplaćuju. |
Kada jedinica ima service_options
Ako jedinica u odgovoru GET /units ima details.service_options, uvek šaljite i service_type. Poređenje je doslovno - razlikuje velika i mala slova i ne uklanja razmake sa krajeva.
Ako se izostavi ili ne odgovara nijednoj opciji, cena se računa iz pricing_config. Kod jedinica bez base_price i bez odgovarajućeg tiera to daje 400 Pricing not configured for this unit, a kod jedinica koje imaju base_price dobićete rezervaciju sa pogrešno obračunatom cenom (po noći ili satu umesto fiksne cene usluge) - bez ikakve greške.
Poslata vrednost se uvek upisuje uz rezervaciju i pojavljuje se u email obaveštenju vlasniku, čak i kada ne odgovara nijednoj opciji. Zato proverite naziv.
Odgovor pri uspešnom kreiranju (201 Created)
{
"success": true,
"booking_id": "7a8b9c0d-e1f2-3a4b-5c6d-7e8f9a0b1c2d",
"message": "Booking created successfully"
}Greške i Statusni Kodovi
Svaki neuspešan odgovor ima polje { "error": "opis greške" } (kod Database transaction failed uz njega stiže i detail). Razlog uvek čitajte iz polja error, jer isti statusni kod pokriva više različitih uzroka.
- 400
Missing required fields- nedostaje neko od obaveznih polja.Pricing not configured for this unit- cenovnik jedinice nije konfigurisan za dati broj gostiju i datum.Too many guests for this unit (maximum N)- zbiradults_countichildren_countje veći oddetails.max_guestsjedinice; bebe se ne računaju.Invalid date format- datum se ne može pročitati.end_date must be after start_date- kraj je pre početka (kod satnih tipova i jednak početku). - 401
Missing API keyiliInvalid API key- ključ nije poslat ili ne odgovara nijednom nalogu. - 403 Četiri različita uzroka:
Missing Origin header- zahtev nemaOriginzaglavlje (pogađa server-to-server pozive, proverava se pre ključa).Probni period je istekao...- nalog je u statusutrialduže od 14 dana.Account suspended- status naloga nijeactive,testnitrial.CORS origin ne dozvoljava pristup sa domena: ...- domen nije na listi dozvoljenih. - 404
Unit not found or access denied- jedinica sa datimunit_idne postoji, ne pripada vašem nalogu, deaktivirana je, njen modul nije uključen, ili ju je vlasnik sakrio sa sajta. Najčešća greška pri integraciji. - 409 Konflikt preklapanja (
Booking overlap detected) - termin se preklapa sa postojećom rezervacijom te jedinice. Provera obuhvata sve rezervacije čiji status nijecancelled, dakle i one na čekanju kojeGET /unitsne vraća. Zato je 409 moguć i za termin koji na vašem kalendaru izgleda slobodno. - 429
Too many requests- pređen je limit od 100 zahteva u minuti po paru IP + ključ. - 500
Database transaction failed- upis u bazu je odbijen (npr. ukupan broj gostiju je 0).Internal Server Error- neočekivana greška, npr. neispravan JSON u telu zahteva.
Šta se dešava posle kreiranja
Rezervacija ostaje u statusu pending dok je vlasnik ne potvrdi ili otkaže u Dashboard-u. Do tada se ne pojavljuje u availability_blocks kroz GET /units, ali blokira preklapajuće termine i izaziva 409.
API ne nudi endpoint za čitanje, izmenu ni otkazivanje rezervacije. Sačuvajte booking_id iz odgovora za sopstvenu evidenciju, a dalji tok pratite kroz Dashboard.
Ostali endpointi
Pored jedinica i rezervacija, API izlaže i kontakt upite, prilagođene forme i iCal izvoz zauzetosti. Svi endpointi pod /api/v1/ traže ista zaglavlja kao i prethodni - API ključ i Origin.
Slanje kontakt upita sa sajta. Vlasniku objekta se šalje email obaveštenje.
{
"name": "Jovan Jovanović", // obavezno
"email": "jovan@email.com", // obavezno
"message": "Da li je slobodno u julu?", // obavezno
"phone": "+381641234567", // opciono
"type": "kontakt" // opciono, podrazumevano "kontakt"
}201 { "success": true, "message": "Upit je uspešno primljen." }
400 Polja name, email i message su obavezna. · 500 Greška pri čuvanju upita.
Dohvatanje definicije prilagođene forme koju je vlasnik napravio u Dashboard-u - koristite je da formu iscrtate na svom sajtu. Vraćaju se samo aktivne forme koje pripadaju vašem nalogu.
{
"form": {
"id": "3f2a1b0c-9d8e-7f6a-5b4c-3d2e1f0a9b8c",
"name": "Zahtev za ponudu",
"description": "Popunite za personalizovanu ponudu"
},
"fields": [
{
"label": "Broj osoba",
"field_name": "broj_osoba",
"field_type": "number",
"placeholder": "npr. 4",
"is_required": true,
"options": null
}
]
}Polja stižu sortirana redosledom koji je vlasnik zadao. Polje options je niz stringova za polja tipa izbora, inače null.
404 Forma nije pronađena.
Slanje popunjene forme. Pored form_id, sva ostala polja iz tela zahteva se čuvaju kao odgovori - koristite field_name vrednosti iz prethodnog endpointa kao ključeve.
{
"form_id": "3f2a1b0c-9d8e-7f6a-5b4c-3d2e1f0a9b8c", // obavezno
"broj_osoba": "4",
"napomena": "Dolazimo sa psom"
}201 { "success": true, "message": "Upit je uspešno primljen." }
400 form_id je obavezan. ili Podaci forme su prazni. · 404 Forma nije pronađena ili nije aktivna.
iCalendar (.ics) izvoz zauzetosti jedne jedinice, namenjen sinhronizaciji sa Booking.com, Airbnb i sličnim kanalima. Sadrži isključivo potvrđene rezervacije, sortirane po datumu početka, svaku kao celodnevni VEVENT.
/api/v1/ i ne traži ni API ključ ni Origin - jedina zaštita je nepogodljivost UUID-a jedinice. Tretirajte link kao poluprivatan i ne objavljujte ga javno.Odgovor je text/calendar; charset=utf-8 uz Content-Disposition: attachment i onemogućeno keširanje.
404 običan tekst Smeštajna jedinica nije pronađena. - vraća se i kada unitId nije validan UUID. 500 Greška na serveru. Greške ovog endpointa nisu u JSON formatu.
Schema po Tipu Jedinice
Polje details u odgovoru API-ja sadrži specifične podatke za svaki tip jedinice. Polje billing_mode govori programeru koji UI prikazati korisniku - ono se izvodi iz tipa jedinice i ne može se posebno podesiti.
"max_guests": "4", a ne 4. Pre računanja ili poređenja konvertujte sa parseInt() odnosno parseFloat().Apartman, Soba, Kuća
Date range picker - datum dolaska / odlaska
Auto, Šator, Ostalo
Date range picker - datum preuzimanja / vraćanja
Kvad, Motor, Bicikl, Plovilo, Bazen, Sto, Očni pregled
Jedan datum + vremenski period (hh:mm), uvek sa vremenskom zonom
Smeštaj
Polja u details
| Polje | Tip |
|---|---|
| max_guests | string (broj) |
| bedrooms | string (broj) |
| beds | string (broj) |
| living_rooms | string (broj) |
| bathrooms | string (broj) |
| terraces | string (broj) |
| balconies | string (broj) |
| parking_spots | string (broj) |
| kitchen_type | string |
| area_size | string (broj) |
| floor | string |
| total_floors | string (broj) |
| house_rules | string |
| checkin_instructions | string |
Amenities IDs (96)
Primer details objekta
{
"max_guests": "4",
"bedrooms": "2",
"beds": "3",
"living_rooms": "1",
"bathrooms": "1",
"terraces": "1",
"balconies": "0",
"parking_spots": "1",
"kitchen_type": "open",
"area_size": "65",
"floor": "2. sprat",
"total_floors": "4",
"house_rules": "Tiho posle 22h. Ljubimci uz dogovor.",
"checkin_instructions": "Ključ se uzima u kafiću u dvorištu."
}Polja u details
| Polje | Tip |
|---|---|
| max_guests | string (broj) |
| beds | string (broj) |
| bathrooms | string (broj) |
| terraces | string (broj) |
| balconies | string (broj) |
| parking_spots | string (broj) |
| kitchen_type | string |
| area_size | string (broj) |
| floor | string |
| total_floors | string (broj) |
| house_rules | string |
| checkin_instructions | string |
Amenities IDs (97)
Primer details objekta
{
"max_guests": "2",
"beds": "2",
"bathrooms": "1",
"terraces": "0",
"balconies": "1",
"parking_spots": "0",
"kitchen_type": "kitchenette",
"area_size": "22",
"floor": "3. sprat",
"total_floors": "4",
"house_rules": "Zabranjeno pušenje u sobi.",
"checkin_instructions": "Prijava na recepciji, ulaz iz dvorišta."
}Polja u details
| Polje | Tip |
|---|---|
| max_guests | string (broj) |
| bedrooms | string (broj) |
| beds | string (broj) |
| living_rooms | string (broj) |
| bathrooms | string (broj) |
| terraces | string (broj) |
| balconies | string (broj) |
| parking_spots | string (broj) |
| kitchen_type | string |
| area_size | string (broj) |
| floors | string (broj) |
| house_rules | string |
| checkin_instructions | string |
Amenities IDs (97)
Primer details objekta
{
"max_guests": "8",
"bedrooms": "3",
"beds": "5",
"living_rooms": "2",
"bathrooms": "2",
"terraces": "2",
"balconies": "0",
"parking_spots": "2",
"kitchen_type": "separate",
"area_size": "150",
"floors": "2",
"house_rules": "Roštilj samo na terasi. Depozit 100 EUR.",
"checkin_instructions": "Šifra sefa sa ključem dolazi dan pre prijave."
}Vozila
Polja u details
| Polje | Tip |
|---|---|
| brand_model | string |
| manufacture_year | string (broj) |
| registration_plates | string |
| seats | string (broj) |
| transmission | string |
| fuel_type | string |
| engine_power | string |
Amenities IDs (6)
Primer details objekta
{
"brand_model": "Yamaha Raptor 350",
"manufacture_year": "2022",
"registration_plates": "KV 123-AB",
"seats": "1",
"transmission": "manual",
"fuel_type": "petrol",
"engine_power": "350cc"
}Polja u details
| Polje | Tip |
|---|---|
| brand_model | string |
| manufacture_year | string (broj) |
| registration_plates | string |
| transmission | string |
| fuel_type | string |
| engine_power | string |
Amenities IDs (5)
Primer details objekta
{
"brand_model": "Honda CB500F",
"manufacture_year": "2021",
"registration_plates": "BG 456-CD",
"transmission": "manual",
"fuel_type": "petrol",
"engine_power": "500cc"
}Polja u details
| Polje | Tip |
|---|---|
| bike_type | string |
| frame_size | string |
| speeds | string (broj) |
| manufacture_year | string (broj) |
Amenities IDs (5)
Primer details objekta
{
"bike_type": "ebike",
"frame_size": "M / 48cm",
"speeds": "9",
"manufacture_year": "2023"
}Polja u details
| Polje | Tip |
|---|---|
| brand_model | string |
| manufacture_year | string (broj) |
| registration_plates | string |
| seats | string (broj) |
| engine_power | string |
| fuel_type | string |
Amenities IDs (5)
Primer details objekta
{
"brand_model": "Fiart Mare 28",
"manufacture_year": "2020",
"registration_plates": "TV-4521",
"seats": "6",
"engine_power": "150 KS",
"fuel_type": "petrol"
}Polja u details
| Polje | Tip |
|---|---|
| brand_model | string |
| manufacture_year | string (broj) |
| registration_plates | string |
| seats | string (broj) |
| transmission | string |
| fuel_type | string |
| engine_power | string |
Amenities IDs (6)
Primer details objekta
{
"seats": "5",
"brand_model": "VW Golf 8",
"manufacture_year": "2023",
"registration_plates": "BG 789-EF",
"transmission": "automatic",
"fuel_type": "petrol",
"engine_power": "130 KS"
}Oprema
Polja u details
| Polje | Tip |
|---|---|
| capacity | string (broj) |
| dimensions | string |
| water_temperature | string (broj) |
Amenities IDs (6)
Primer details objekta
{
"capacity": "20",
"dimensions": "25×10×1.8m",
"water_temperature": "28"
}Polja u details
| Polje | Tip |
|---|---|
| capacity | string (broj) |
| dimensions | string |
| equipment | string |
Amenities IDs (6)
Primer details objekta
{
"capacity": "100",
"dimensions": "10×20m",
"equipment": "Stolice, stolovi, LED rasveta, pozornica"
}Polja u details
| Polje | Tip |
|---|---|
| capacity | string (broj) |
| dimensions | string |
| equipment | string |
Amenities IDs (5)
Primer details objekta
{
"capacity": "50",
"dimensions": "200m²",
"equipment": "Projektor, platno, PA sistem"
}Ugostiteljstvo
Polja u details
| Polje | Tip |
|---|---|
| capacity | string (broj) |
| min_guests | string (broj) |
| zone | string |
| notes | string |
Amenities IDs (7)
Primer details objekta
{
"capacity": "4",
"min_guests": "1",
"zone": "basta",
"notes": "Sto je pogodan za proslave."
}Zajednički ključevi u details (svi tipovi)
| Ključ | Tip | Opis |
|---|---|---|
| location | object | Strukturirana adresa: street, city, postalCode, country - sva podpolja su stringovi. Postoji samo ako je vlasnik popunio adresu. Isti podatak u spljoštenom obliku stiže i kao top-level address. |
| service_options | array | Lista usluga sa fiksnom cenom: [{ "name": "...", "price": 25 }]. Ovde je price pravi broj. Ako je prisutno, uz rezervaciju šaljite service_type. |
| notification_sound | string | null | Interno podešavanje Dashboard-a. Zanemarite ga u integraciji. |
Polja u details su opciona i details je slobodan JSON objekat - ne pretpostavljajte fiksan skup ključeva. Ključ koji vlasnik nikad nije popunio uopšte ne postoji u objektu (dobijate undefined, ne null), a polje koje je popunjeno pa obrisano, kao i izbor vraćen na praznu opciju, čuva se kao prazan string. Zato proveravajte i nepostojanje ključa i prazan string, npr. if (details.transmission) { ... }.
Implementacija u Kodu
Kopirajte gotove kodne primere za vašu platformu i zamenite placeholder API ključ svojim.
// 1. Dobijanje rezervacionih jedinica i zauzetih termina
// Origin zaglavlje browser dodaje sam kod poziva na drugi domen.
const fetchUnits = async () => {
const response = await fetch('https://gost365.app/api/v1/units', {
method: 'GET',
headers: {
'x-api-key': 'gost365_live_vaš_api_ključ_ovde',
'Content-Type': 'application/json'
}
});
if (!response.ok) {
// Razlog greške je uvek u polju "error" u telu odgovora, ne u statusText.
const errorData = await response.json();
throw new Error(errorData.error || 'Greška pri učitavanju jedinica');
}
const result = await response.json();
console.log(result.data); // Niz jedinica
// Zauzeti termini za prvu jedinicu (samo potvrđene rezervacije):
const zauzeti = result.data[0].availability_blocks.map(b => ({
od: String(b.start_date).split('T')[0],
do: String(b.end_date).split('T')[0]
}));
console.log(zauzeti);
};// 2. Slanje novog upita za rezervaciju sa sajta
const createBooking = async (bookingData) => {
const response = await fetch('https://gost365.app/api/v1/bookings', {
method: 'POST',
headers: {
'x-api-key': 'gost365_live_vaš_api_ključ_ovde',
'Content-Type': 'application/json'
},
body: JSON.stringify({
unit_id: "8f7e6d5c-4b3a-2a19-0e9d-8c7b6a5f4e3d", // UUID jedinice
start_date: "2026-07-01",
end_date: "2026-07-08",
guest_name: "Jovan Jovanović",
guest_email: "jovan.jovanovic@email.com",
guest_phone: "+381641234567", // opciono
adults_count: 2, // opciono, podrazumevano 1
children_count: 1, // opciono, podrazumevano 0
infants_count: 0, // opciono, podrazumevano 0
notes: "Molimo za tihu sobu i krevetac za bebu." // opciono
})
});
if (response.status === 409) {
// Pažnja: 409 je moguć i za termin koji na vašem kalendaru izgleda slobodno,
// jer se provera radi i protiv rezervacija na čekanju koje GET /units ne vraća.
alert('Izabrani termin je već zauzet. Molimo izaberite drugi.');
return;
}
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Neuspešno slanje rezervacije');
}
const result = await response.json();
// Rezervacija je kreirana sa statusom "pending" - vlasniku je poslat email za potvrdu.
console.log('Zahtev poslat! ID rezervacije:', result.booking_id);
};