Javni API v1

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:

x-api-key: gost365_live_vaš_ključ
Authorization: Bearer gost365_live_vaš_ključ
Origin: https://vasdomen.com

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.com pokriva i www.mojapartman.com, https://mojapartman.com/ i MOJAPARTMAN.COM.
  • Poddomeni se ne pokrivaju automatski - shop.mojapartman.com mora 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.
Ako domen nije na listi, API vraća 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.

GEThttps://gost365.app/api/v1/units

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" }
      ]
    }
  ]
}
availability_blocks sadrži isključivo potvrđene rezervacije (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" }
]
  • label je na jeziku klijenta. Koji je to jezik piše u polju language na vrhu odgovora ("sr", "sl" ili "bs"). Polje language stiže uvek, i bez include.
  • group je naziv sekcije i uvek je na srpskom, bez obzira na jezik klijenta. Kod smeštaja su to Opš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 biti null kod tipova koji još nisu podeljeni na sekcije - tada prikažite sve u jednom bloku.
  • icon je 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 biti null.
  • 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.
Zašto je opciono. Polje 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.

POSThttps://gost365.app/api/v1/bookings

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)

PoljeTipObaveznoOpis
unit_idUUIDDaJedinstveni ID apartmana iz GET /units
start_dateString (YYYY-MM-DD ili YYYY-MM-DDTHH:mm:ss)DaDatum 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_dateString (YYYY-MM-DD ili YYYY-MM-DDTHH:mm:ss)DaDatum 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_nameStringDaIme i prezime gosta
guest_emailStringDaKontakt email adresa gosta
guest_phoneStringNeKontakt telefon gosta
adults_countIntegerNe (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_countIntegerNe (zastarelo)Zadržano radi kompatibilnosti sa starijim integracijama. Koristi se samo ako adults_count nije poslat. Za nove integracije koristite razbijena polja.
children_countIntegerNe (podr. 0)Broj dece (2-17 godina)
infants_countIntegerNe (podr. 0)Broj beba (do 2 godine)
notesStringNeNapomena gosta
service_typeStringNe (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.
addonsArray<String>NeId-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) - zbir adults_count i children_count je veći od details.max_guests jedinice; 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 key ili Invalid API key - ključ nije poslat ili ne odgovara nijednom nalogu.
  • 403 Četiri različita uzroka: Missing Origin header - zahtev nema Origin zaglavlje (pogađa server-to-server pozive, proverava se pre ključa). Probni period je istekao... - nalog je u statusu trial duže od 14 dana. Account suspended - status naloga nije active, test ni trial. CORS origin ne dozvoljava pristup sa domena: ... - domen nije na listi dozvoljenih.
  • 404 Unit not found or access denied - jedinica sa datim unit_id ne 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 nije cancelled, dakle i one na čekanju koje GET /units ne 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.

POSThttps://gost365.app/api/v1/inquiries

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.

GEThttps://gost365.app/api/v1/forms/{formId}

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.

POSThttps://gost365.app/api/v1/submissions

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.

GEThttps://gost365.app/api/ical/{unitId}

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.

Ovaj endpoint je izvan /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.

Sve vrednosti u details su stringovi - i one koje predstavljaju brojeve, jer se upisuju iz obrasca u Dashboard-u. Očekujte "max_guests": "4", a ne 4. Pre računanja ili poređenja konvertujte sa parseInt() odnosno parseFloat().
"nightly"Po noćenju

Apartman, Soba, Kuća

Date range picker - datum dolaska / odlaska

"daily"Po danu

Auto, Šator, Ostalo

Date range picker - datum preuzimanja / vraćanja

"hourly"Po satu

Kvad, Motor, Bicikl, Plovilo, Bazen, Sto, Očni pregled

Jedan datum + vremenski period (hh:mm), uvek sa vremenskom zonom

Smeštaj

Apartmanbilling_mode: "nightly"

Polja u details

PoljeTip
max_guestsstring (broj)
bedroomsstring (broj)
bedsstring (broj)
living_roomsstring (broj)
bathroomsstring (broj)
terracesstring (broj)
balconiesstring (broj)
parking_spotsstring (broj)
kitchen_typestring
area_sizestring (broj)
floorstring
total_floorsstring (broj)
house_rulesstring
checkin_instructionsstring

Amenities IDs (96)

wifiacheatingelevatorgarageoutdoor_parkingpoolpetssmokingsmoking_areafanfireplacesoundproofprivate_entranceev_chargerfridgestovemicrowavedishwashercoffeedishesovenfreezerkettletoasterblendercooking_basicswine_glassesshowerbathtubhairdryerwashertowelstoiletriesbidethot_waterslippersdryerbed_linenwardrobeextra_bedbaby_cotironblackoutextra_pillowstvsofadiningdeskstreamingcable_tvsound_systembooksboard_gamesgame_consolearmchairgymsaunahot_tuboutdoor_furnitureoutdoor_diningsun_loungersparasolfire_pitoutdoor_showerbeach_accesssea_viewlake_viewriver_viewmountain_viewcity_viewgarden_viewhigh_chairkids_toysplaygroundbaby_bathchild_safetysmoke_detectorco_detectorfire_extinguisherfirst_aidsafecctvsmart_lockstep_freewheelchairgrab_railswide_doorsself_checkinluggage_storageairport_shuttlecleaninglong_staysbike_rentalhost_greetsbreakfast

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."
}
Sobabilling_mode: "nightly"

Polja u details

PoljeTip
max_guestsstring (broj)
bedsstring (broj)
bathroomsstring (broj)
terracesstring (broj)
balconiesstring (broj)
parking_spotsstring (broj)
kitchen_typestring
area_sizestring (broj)
floorstring
total_floorsstring (broj)
house_rulesstring
checkin_instructionsstring

Amenities IDs (97)

wifiacheatingelevatorgarageoutdoor_parkingpoolpetssmokingsmoking_areafanfireplacesoundproofprivate_entranceev_chargerfridgestovemicrowavedishwashercoffeedishesovenfreezerkettletoasterblendercooking_basicswine_glassesshowerbathtubhairdryerwashertowelstoiletriesbidethot_waterslippersdryerbed_linenwardrobeextra_bedbaby_cotironblackoutextra_pillowstvsofadiningdeskstreamingcable_tvsound_systembooksboard_gamesgame_consolearmchairgymsaunahot_tuboutdoor_furnitureoutdoor_diningsun_loungersparasolfire_pitoutdoor_showerbeach_accesssea_viewlake_viewriver_viewmountain_viewcity_viewgarden_viewhigh_chairkids_toysplaygroundbaby_bathchild_safetysmoke_detectorco_detectorfire_extinguisherfirst_aidsafecctvsmart_lockstep_freewheelchairgrab_railswide_doorsself_checkinluggage_storageairport_shuttlecleaninglong_staysbike_rentalhost_greetsbreakfastensuite

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."
}
Kućabilling_mode: "nightly"

Polja u details

PoljeTip
max_guestsstring (broj)
bedroomsstring (broj)
bedsstring (broj)
living_roomsstring (broj)
bathroomsstring (broj)
terracesstring (broj)
balconiesstring (broj)
parking_spotsstring (broj)
kitchen_typestring
area_sizestring (broj)
floorsstring (broj)
house_rulesstring
checkin_instructionsstring

Amenities IDs (97)

wifiacheatingelevatorgarageoutdoor_parkingpoolpetssmokingsmoking_areafanfireplacesoundproofprivate_entranceev_chargerfridgestovemicrowavedishwashercoffeedishesovenfreezerkettletoasterblendercooking_basicswine_glassesshowerbathtubhairdryerwashertowelstoiletriesbidethot_waterslippersdryerbed_linenwardrobeextra_bedbaby_cotironblackoutextra_pillowstvsofadiningdeskstreamingcable_tvsound_systembooksboard_gamesgame_consolearmchairgymsaunahot_tuboutdoor_furnitureoutdoor_diningsun_loungersparasolfire_pitoutdoor_showerbeach_accesssea_viewlake_viewriver_viewmountain_viewcity_viewgarden_viewhigh_chairkids_toysplaygroundbaby_bathchild_safetysmoke_detectorco_detectorfire_extinguisherfirst_aidsafecctvsmart_lockstep_freewheelchairgrab_railswide_doorsself_checkinluggage_storageairport_shuttlecleaninglong_staysbike_rentalhost_greetsbbqgarden

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

Kvadbilling_mode: "hourly"

Polja u details

PoljeTip
brand_modelstring
manufacture_yearstring (broj)
registration_platesstring
seatsstring (broj)
transmissionstring
fuel_typestring
engine_powerstring

Amenities IDs (6)

automaticmanual4x4helmetgpsinsurance

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"
}
Motorbilling_mode: "hourly"

Polja u details

PoljeTip
brand_modelstring
manufacture_yearstring (broj)
registration_platesstring
transmissionstring
fuel_typestring
engine_powerstring

Amenities IDs (5)

automaticmanualhelmetgpsinsurance

Primer details objekta

{
  "brand_model": "Honda CB500F",
  "manufacture_year": "2021",
  "registration_plates": "BG 456-CD",
  "transmission": "manual",
  "fuel_type": "petrol",
  "engine_power": "500cc"
}
Biciklbilling_mode: "hourly"

Polja u details

PoljeTip
bike_typestring
frame_sizestring
speedsstring (broj)
manufacture_yearstring (broj)

Amenities IDs (5)

helmetlockbasketchild_seatelectric

Primer details objekta

{
  "bike_type": "ebike",
  "frame_size": "M / 48cm",
  "speeds": "9",
  "manufacture_year": "2023"
}
Plovilobilling_mode: "hourly"

Polja u details

PoljeTip
brand_modelstring
manufacture_yearstring (broj)
registration_platesstring
seatsstring (broj)
engine_powerstring
fuel_typestring

Amenities IDs (5)

life_jacketmotor_incgpsinsurancefishing

Primer details objekta

{
  "brand_model": "Fiart Mare 28",
  "manufacture_year": "2020",
  "registration_plates": "TV-4521",
  "seats": "6",
  "engine_power": "150 KS",
  "fuel_type": "petrol"
}
Autobilling_mode: "daily"

Polja u details

PoljeTip
brand_modelstring
manufacture_yearstring (broj)
registration_platesstring
seatsstring (broj)
transmissionstring
fuel_typestring
engine_powerstring

Amenities IDs (6)

automaticmanual4x4gpsinsurancechild_seat

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

Bazenbilling_mode: "hourly"

Polja u details

PoljeTip
capacitystring (broj)
dimensionsstring
water_temperaturestring (broj)

Amenities IDs (6)

heatingoutdoorindoorsaunachanging_roomtowels

Primer details objekta

{
  "capacity": "20",
  "dimensions": "25×10×1.8m",
  "water_temperature": "28"
}
Šatorbilling_mode: "daily"

Polja u details

PoljeTip
capacitystring (broj)
dimensionsstring
equipmentstring

Amenities IDs (6)

installationoutdoorindoorpowersafety_nettables_chairs

Primer details objekta

{
  "capacity": "100",
  "dimensions": "10×20m",
  "equipment": "Stolice, stolovi, LED rasveta, pozornica"
}
Ostalobilling_mode: "daily"

Polja u details

PoljeTip
capacitystring (broj)
dimensionsstring
equipmentstring

Amenities IDs (5)

installationoutdoorindoorpowersafety_net

Primer details objekta

{
  "capacity": "50",
  "dimensions": "200m²",
  "equipment": "Projektor, platno, PA sistem"
}

Ugostiteljstvo

Stobilling_mode: "hourly"

Polja u details

PoljeTip
capacitystring (broj)
min_guestsstring (broj)
zonestring
notesstring

Amenities IDs (7)

terasavipindoorparkingdisabledmusicbirthday

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čTipOpis
locationobjectStrukturirana 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_optionsarrayLista usluga sa fiksnom cenom: [{ "name": "...", "price": 25 }]. Ovde je price pravi broj. Ako je prisutno, uz rezervaciju šaljite service_type.
notification_soundstring | nullInterno 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);
};