Pe scurt
| Metodă | Adresă | La ce folosește |
|---|---|---|
GET | /locations | Locațiile de preluare și returnare, cu numele exacte. |
GET | /vehicles | Mașinile, cu prețul „de la X / zi” pentru paginile de listă. |
GET | /prices | Prețul exact pentru o perioadă și o locație. |
POST | /booking-requests | Trimite în RentOS rezervarea făcută de client. |
Toate adresele încep cu https://rentoshub.app/api/v1/<companie>, iar <companie> e identificatorul companiei, afișat în RentOS la Setări → Legătura cu site-ul. Răspunsurile sunt JSON, iar textele pentru oameni sunt în română.
Înainte de a începe
- Cheia de acces. O creează administratorul companiei în Setări → Legătura cu site-ul. Se vede o singură dată, la creare, și se trimite la fiecare apel în antetul
Authorization: Bearer rk_live_…. Aceeași cheie merge pentru toate cele patru adrese. - Doar de pe server. Apelurile se fac de pe serverul site-ului, niciodată din browserul vizitatorului: cheia nu are voie să ajungă în pagină.
- Ora locală. Datele și orele sunt în ora locală a companiei (
AAAA-LL-ZZșiHH:MM), fără fus orar. - Moneda. Prețurile vin în moneda companiei (câmpul
currency). Trimiteți rezervarea în aceeași monedă. - RentOS e sursa prețurilor. Nu copiați prețurile în baza site-ului. Citiți-le din RentOS și țineți-le cel mult cât spune antetul
Cache-Controlal fiecărui răspuns.
Rețeta pas cu pas
1. Legați mașinile de pe site de RentOS
Fiecare mașină din RentOS (Oferte → Vehicule) are un cod RentOS, afișat pe mașină, cu buton de copiere. Pe site, adăugați la fiecare mașină un câmp „Cod RentOS” și lipiți acolo codul mașinii corespunzătoare. În toate răspunsurile, mașina apare cu id egal cu acest cod.
- O mașină nouă în RentOS: copiați codul ei la mașina de pe site.
- O mașină redenumită în RentOS rămâne legată: codul nu se schimbă.
- O mașină ștearsă și creată din nou în RentOS primește alt cod: îl schimbați și pe site.
- Dacă un cod salvat pe site nu mai apare în răspuns, mașina nu mai are prețuri în RentOS: nu-i afișați un preț vechi.
2. Luați locațiile
GET /locations dă locațiile companiei, cu numele exact. Pe acest nume îl trimiteți la prețuri și la rezervări. Folosiți-l în lista de locații din formularul de căutare al site-ului.
Locația de preluare alege și prețurile. O companie poate avea mai multe flote (de exemplu București și Cluj), fiecare cu prețurile ei. Site-ul nu are nevoie de identificatorul flotei: fiecare locație aparține unei singure flote, iar RentOS alege prețurile după locația trimisă în pickup_location.
- Codul RentOS al mașinii e același în toate flotele. Aceeași mașină poate avea alt preț în alt oraș.
- Fiecare locație de la
/locationsîși spune flota, în câmpulfleet(idșiname). Locațiile cu acelașifleet.idau aceleași prețuri: pentru prețurile unui oraș, cereți/vehiclessau/pricescu oricare dintre ele. - Grupați după
fleet.id, nu după nume: numele flotei se poate schimba în RentOS,id-ul nu. - Locația de returnare nu schimbă prețul.
- Două locații cu același nume, chiar în flote diferite, nu apar în
/locationsși nu primesc prețuri. Compania trebuie să le dea nume diferite.
3. Pagina cu lista de mașini: prețul „de la”
Pe paginile fără perioadă aleasă, GET /vehicles dă fiecare mașină cu prețul cel mai mic pe zi pe care îl poate avea (from_per_day), pe orice durată. Cu pickup_location, prețul e al flotei acelei locații, pe toate perioadele anului ale flotei. Fără el, vine doar din prețurile de bază ale companiei, fără perioadele flotelor: dacă firma are prețuri diferite pe orașe, trimiteți mereu o locație.
4. Căutarea clientului: prețul exact
Când clientul alege perioada și locația, GET /prices dă pentru fiecare mașină prețul pe zi și totalul chiriei, calculate exact ca în ofertele din RentOS: aceeași durată (cu ora de grație a companiei), aceeași perioadă a anului, aceleași ajustări.
5. Alegeți ce variantă afișați
Fiecare preț vine în două variante:
with_insurance: cu asigurare, fără garanție;with_deposit: cu garanție, iar suma garanției e îndeposit.
Un site poate afișa doar una dintre ele sau pe amândouă. Folosiți doar câmpurile variantei pe care o afișați și ignorați-le pe celelalte. Un preț null înseamnă că mașina nu are preț în acea variantă pentru perioada cerută: nu afișați mașina în varianta respectivă.
6. Trimiteți rezervarea
Când clientul rezervă, trimiteți POST /booking-requests cu ce a văzut el pe site. Corespondența cu răspunsul de la /prices e directă:
| În rezervare | Luați din |
|---|---|
vehicle | Numele mașinii exact cum l-a văzut clientul pe site. |
price.kind | "rent": trimiteți chiria fără taxe. |
price.amount | total.with_insurance sau total.with_deposit, după varianta aleasă. |
price.variant | "insurance" sau "deposit", după varianta aleasă. |
price.deposit | deposit, dacă varianta e "deposit". |
fees[] | Taxele alese de client pe site, cu numele și suma lor. |
pickup.location, dropoff.location | Numele de la /locations. |
Taxele de pe site ajung în rezervare ca taxe separate, cu numele și suma trimise, deci nimic nu se pierde din total.
7. Tratați răspunsul
Un 201 sau un 200 înseamnă că rezervarea e în RentOS. Erorile 4xx înseamnă că cererea trebuie corectată și nu se reîncearcă. Erorile 429 și 5xx sunt trecătoare și se reîncearcă după pauza din antetul Retry-After. Trimiteți mereu external_ref (numărul rezervării de pe site): așa o reîncercare nu face niciodată o rezervare dublă.
Referință
Autentificare
Fiecare apel poartă antetul Authorization: Bearer rk_live_…. Fără cheie sau cu o cheie greșită, răspunsul e 401 invalid_key. O cheie revocată primește 403 key_revoked. Toate erorile au forma:
{ "ok": false, "error": "invalid_key", "message": "Cheia nu e valabilă." }GET /locations
GET https://rentoshub.app/api/v1/<companie>/locations
Authorization: Bearer rk_live_…{
"ok": true,
"locations": [
{
"id": "…",
"name": "Aeroport Otopeni (OTP)",
"city": "Otopeni",
"address": "…",
"fleet": { "id": "3c1e…", "name": "București" }
},
{
"id": "…",
"name": "Craiova Centru",
"city": "Craiova",
"address": "…",
"fleet": { "id": "8a40…", "name": "Craiova" }
}
]
}fleete flota locației: toate locațiile cu acelașifleet.idau aceleași prețuri.fleet.nameenulldoar dacă numele n-a putut fi citit în acel moment.
Lista se schimbă rar și poate fi ținută o oră.
GET /vehicles
GET https://rentoshub.app/api/v1/<companie>/vehicles?pickup_location=Aeroport%20Otopeni%20(OTP)
Authorization: Bearer rk_live_…| Parametru | Reguli |
|---|---|
pickup_location | Opțional. Numele exact de la /locations. Fără el, prețurile sunt cele de bază ale companiei, fără perioadele flotelor. |
{
"ok": true,
"currency": "EUR",
"pickup_location": { "id": "…", "name": "Aeroport Otopeni (OTP)", "fleet": { "id": "3c1e…", "name": "București" } },
"vehicles": [
{
"id": "9f12257b-1475-4036-adab-f084445aab34",
"name": "Dacia Logan MANUAL",
"category": "Economică",
"offer_active": true,
"photo_url": "https://…",
"from_per_day": { "with_insurance": 22, "with_deposit": 18 }
}
]
}ide codul RentOS al mașinii.offer_activespune dacă mașina e activă în ofertele din RentOS. Lista cuprinde și mașinile ascunse din ofertare; site-ul hotărăște ce afișează.from_per_daye cel mai mic preț pe zi, pe orice durată și în orice perioadă.nullînseamnă fără preț în acea variantă.photo_urle valabil o oră; nu-l păstrați mai mult.
Răspunsul poate fi ținut cinci minute.
GET /prices
GET https://rentoshub.app/api/v1/<companie>/prices?pickup_date=2026-10-01&pickup_time=10:00&dropoff_date=2026-10-05&dropoff_time=10:00&pickup_location=Aeroport%20Otopeni%20(OTP)
Authorization: Bearer rk_live_…| Parametru | Reguli |
|---|---|
pickup_date, dropoff_date | AAAA-LL-ZZ, ora locală a companiei. |
pickup_time, dropoff_time | HH:MM. Returnarea trebuie să fie după preluare. |
pickup_location | Numele exact de la /locations. Prețurile sunt ale flotei din care face parte locația; locația de returnare nu le schimbă. |
{
"ok": true,
"currency": "EUR",
"period": {
"pickup": "2026-10-01T10:00",
"dropoff": "2026-10-05T10:00",
"hours": 96,
"days": 4,
"interval": { "id": "…", "label": "4-5 zile", "min_days": 4, "max_days": 5 },
"season": null
},
"pickup_location": { "id": "…", "name": "Aeroport Otopeni (OTP)", "fleet": { "id": "3c1e…", "name": "București" } },
"vehicles": [
{
"id": "9f12257b-1475-4036-adab-f084445aab34",
"name": "Dacia Logan MANUAL",
"category": "Economică",
"offer_active": true,
"photo_url": "https://…",
"per_day": { "with_insurance": 27, "with_deposit": 23 },
"total": { "with_insurance": 108, "with_deposit": 92 },
"deposit": 500
}
]
}daysse socotesc cu ora de grație a companiei, exact ca în ofertă.totale chiria pentru toată perioada, rotunjită la unitate ca pe ofertă.per_daye prețul pe zi rotunjit, pentru afișare.deposite garanția pentru varianta cu garanție; 0 înseamnă fără garanție.seasonapare ca{ "id", "name" }când data preluării cade într-o perioadă cu prețuri proprii; altfel enull. Perioadele sunt ale fiecărei flote, deci diferă de la un oraș la altul.intervalenullcând compania nu are durate definite; atunci toate prețurile suntnull.- Prețurile nu includ taxele. Taxele se trimit cu rezervarea.
Răspunsul poate fi ținut un minut.
POST /booking-requests
{
"external_ref": "WEB-10231",
"customer": { "name": "Ion Popescu", "phone": "+40722123456", "email": "ion@example.com" },
"pickup": { "date": "2026-10-01", "time": "10:00", "location": "Aeroport Otopeni (OTP)" },
"dropoff": { "date": "2026-10-05", "time": "10:00", "location": "Aeroport Otopeni (OTP)" },
"vehicle": "Dacia Logan sau similar",
"price": { "kind": "rent", "amount": 92, "currency": "EUR", "variant": "deposit", "deposit": 500 },
"fees": [ { "name": "Scaun copil", "amount": 20 } ],
"notes": "Zbor RO123, ajung la 22:10"
}| Câmp | Obligatoriu | Reguli |
|---|---|---|
external_ref | recomandat | Numărul rezervării de pe site, cel mult 80 de caractere. Un identificator tehnic, fără date personale. Pe el se recunosc retrimiterile. |
customer.name | da | 2..120 de caractere. |
customer.phone | telefonul sau emailul | Cel mult 40 de caractere, de preferat în formă internațională (+40…). |
customer.email | telefonul sau emailul | Adresă validă, cel mult 254 de caractere. |
pickup, dropoff | da | date (AAAA-LL-ZZ), time (HH:MM) și location (numele de la /locations). Preluarea: cel mult un an în urmă, cel mult doi ani înainte. Returnarea după preluare. |
vehicle | da | Mașina exact cum a văzut-o clientul pe site, cel mult 200 de caractere. Rămâne așa în RentOS; mașina reală o alocă operatorul. |
price.kind | da | "rent": amount e chiria fără taxe. "total": amount include și taxele. |
price.amount | da | Între 0 și 9.999.999. |
price.currency | da | EUR, RON, GBP sau USD, aceeași cu currency de la prețuri. RentOS nu convertește. |
price.variant | nu | "insurance" sau "deposit": varianta aleasă de client. Apare operatorului în cerere. |
price.deposit | nu | Garanția arătată clientului, pentru varianta "deposit". Se precompletează în rezervare. |
fees[] | nu | Cel mult 20 de taxe, fiecare cu name (1..120) și amount (peste 0). Intră în rezervare cu numele și suma trimise. |
notes | nu | Cel mult 2000 de caractere; apar în observațiile rezervării. |
test | nu | true marchează o cerere de probă: apare ca „Probă”, nu creează client și nu devine rezervare. |
Cum se socotește prețul: cu "rent", totalul e chiria plus taxele; cu "total", chiria e suma trimisă minus taxele, iar dacă taxele depășesc suma, cererea e refuzată (fees_exceed_total).
Răspuns 201 pentru o cerere nouă sau 200 pentru una deja primită:
{
"ok": true,
"id": "6c2f…",
"duplicate": false,
"updated": false,
"price": { "rent": 92, "fees_total": 20, "total": 112, "currency": "EUR" }
}Răspunsuri și erori
| Cod | error | Ce înseamnă | Reîncercare |
|---|---|---|---|
| 400 | validation | Câmpuri lipsă sau greșite. fields spune care, de exemplu "pickup.date": "…". | nu, corectați cererea |
| 400 | invalid_json | Corpul nu e JSON valid. | nu |
| 400 | fees_exceed_total | Taxele depășesc totalul trimis. | nu |
| 400 | unknown_location | Locația nu există în RentOS sau are același nume cu altă locație. known_locations dă numele acceptate. | nu |
| 401 | invalid_key | Cheia lipsește, are altă formă sau nu e valabilă. | nu |
| 403 | key_revoked | Cheia a fost revocată. | nu, cereți o cheie nouă |
| 403 | missing_scope | Cheia nu are dreptul pentru adresa cerută. | nu |
| 403 | license_blocked | Contul RentOS al companiei e blocat. | nu |
| 409 | already_converted | Rezervarea a fost retrimisă cu alt conținut după ce a devenit rezervare în RentOS. | nu, clientul sună compania |
| 413 | payload_too_large | Corpul depășește 32 KB. | nu |
| 415 | unsupported_media_type | Lipsește Content-Type: application/json. | nu |
| 429 | rate_limited | Prea multe cereri într-un timp scurt. | da, după Retry-After |
| 500 | save_failed | Cererea nu s-a putut salva. | da, cu pauză |
| 503 | temporarily_unavailable | RentOS nu a putut răspunde acum. | da, după Retry-After |
Retrimiteri și dubluri
Dublurile se recunosc pe aceeași cheie și același `external_ref`:
- același conținut (o reîncercare după o pauză de rețea):
200cuduplicate: true, fără nicio schimbare; - alt conținut, iar cererea e încă nelucrată în RentOS: cererea se aduce la zi,
200cuupdated: true, iar operatorul vede „Actualizată de site”; - alt conținut, dar cererea a devenit deja rezervare:
409 already_converted, fără nicio schimbare.
Fără external_ref, fiecare trimitere e o cerere nouă. După înlocuirea cheii, nu retrimiteți rezervările deja trimise cu cheia veche.
Limite și cache
| Adresă | Pe adresa apelantului | Pe cheie | Pe companie | Cache |
|---|---|---|---|---|
/locations | 120 / minut | 60 / minut, 5.000 / zi | 300 / minut | o oră |
/vehicles | 120 / minut | 120 / minut, 10.000 / zi | 300 / minut | cinci minute |
/prices | 300 / minut | 300 / minut, 20.000 / zi | 900 / minut | un minut |
/booking-requests | 120 / minut | 60 / minut, 1.500 / zi | 300 / minut | fără |
Peste limită, răspunsul e 429 cu antetul Retry-After. Țineți răspunsurile de citire cât spune Cache-Control: site-ul rămâne rapid, iar prețurile rămân la zi.
Ce vede compania în RentOS
Rezervarea apare în Oferte → De pe site, cu clientul, perioada, locațiile, mașina cerută, varianta aleasă, chiria, taxele și totalul. Pentru un client nou se creează fișa lui; pentru unul cunoscut, operatorul confirmă legătura. Cu „Creează rezervarea”, operatorul primește formularul precompletat și alocă mașina reală. RentOS nu trimite emailuri clientului pentru aceste cereri, fiindcă site-ul i-a trimis deja confirmarea.
Exemplu complet
BASE="https://rentoshub.app/api/v1/<companie>"
KEY="rk_live_…"
# 1. Locațiile
curl -s "$BASE/locations" -H "Authorization: Bearer $KEY"
# 2. Prețurile pentru o perioadă
curl -s "$BASE/prices?pickup_date=2026-10-01&pickup_time=10:00&dropoff_date=2026-10-05&dropoff_time=10:00&pickup_location=Aeroport%20Otopeni%20(OTP)" \
-H "Authorization: Bearer $KEY"
# 3. Rezervarea clientului
curl -s -X POST "$BASE/booking-requests" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"external_ref":"WEB-10231","customer":{"name":"Ion Popescu","phone":"+40722123456"},"pickup":{"date":"2026-10-01","time":"10:00","location":"Aeroport Otopeni (OTP)"},"dropoff":{"date":"2026-10-05","time":"10:00","location":"Aeroport Otopeni (OTP)"},"vehicle":"Dacia Logan MANUAL","price":{"kind":"rent","amount":92,"currency":"EUR","variant":"deposit","deposit":500},"fees":[{"name":"Scaun copil","amount":20}]}'