Partnerski API
https://api.sporty.plus/api/partner/v1
Ovo je jedino mjesto na kojem OAuth pristupni token radi. Običan JSON — ne JSON:API — s namjerno malenom površinom: svako polje je ono koje je netko odlučio izložiti.
curl https://api.sporty.plus/api/partner/v1/courts \
-H "Authorization: Bearer $ACCESS_TOKEN"
Krajnje točke
| Metoda | Putanja | Opseg |
|---|---|---|
GET | /me | me.read |
GET | /me/reservations | me.reservations.read |
POST | /me/reservations | me.reservations.write |
DELETE | /me/reservations/{id} | me.reservations.write |
GET | /me/results | me.results.read |
GET | /me/memberships | me.memberships.read |
GET | /clubs/me | club.read |
GET | /courts | club.courts.read |
GET | /availability | club.courts.read |
GET | /reservations | club.reservations.read |
GET | /reservations/{id} | club.reservations.read |
GET | /reservation-categories | club.reservations.read |
POST | /reservations | club.reservations.write |
PATCH | /reservations/{id} | club.reservations.write |
DELETE | /reservations/{id} | club.reservations.write |
GET PUT DELETE | /webhook | club.reservations.read |
GET | /members | club.members.read |
POST | /members | club.members.write |
GET /me
Član za kojeg je token izdan: id, first_name, last_name, name, picture i locale.
Adresa e-pošte nije dio toga; zatražite opseg email i pročitajte je s /oauth2/userinfo.
Ova krajnja točka i /me/reservations traže token izdan za člana (authorization code), a
riječ je o vlastitim podacima člana, ne kluba, pa nijedan klub vašoj aplikaciji ne mora ništa
odobriti. Token dobiven putem client credentials dobiva 401. Ako je član vašu aplikaciju
autorizirao samo za jedan klub, vidite onaj dio njegovih podataka koji se odnosi na taj klub.
GET /me/reservations
Rezervacije u kojima član igra ili trenira, samo nadolazeće, osim ako ne zatražite drugačije.
from i to (ISO 8601) određuju vremenski raspon; per_page ide do 100.
{
"data": [
{
"id": 918301,
"club": { "id": 57, "name": "TK Test" },
"court_id": 412,
"court_name": "Teren 3",
"from": "2026-10-07T16:00:00+00:00",
"to": "2026-10-07T17:00:00+00:00",
"approved": true
}
]
}
Ostali igrači nisu uključeni: s kim član igra, odluka je ostalih igrača hoće li to podijeliti.
POST /me/reservations
Rezervira teren kao član. Ovdje vrijede sva pravila koja klub na njega primjenjuje u aplikaciji Sporty:
- radno vrijeme i cijena njegova članstva;
- koliko unaprijed smije rezervirati i koliko blizu početka;
- koliko rezervacija smije imati;
- mora li najprije platiti.
{
"court_id": 412,
"from": "2026-10-07T18:00:00+02:00",
"to": "2026-10-07T19:30:00+02:00",
"external_id": "agent-7f3a"
}
fromitomoraju se poklapati s terminima terena, onako kako ih navodiGET /availability. Rezervacija mora trajati cijeli broj termina duljinereservation_duration, i svaki od njih mora biti otvoren za ovog člana.external_idje neobavezan: vaš id zahtjeva, do 191 znaka. Ponovno slanje odgovara200s već napravljenom rezervacijom, pa ponovni pokušaj nikad ne rezervira dvaput.
Odgovor je 201:
{
"data": {
"id": 918402,
"club": { "id": 57, "name": "TK Test" },
"court_id": 412,
"court_name": "Teren 3",
"from": "2026-10-07T16:00:00+00:00",
"to": "2026-10-07T17:30:00+00:00",
"approved": false,
"price": 20,
"needs_payment": true,
"pay_by": "2026-10-01T10:20:00+00:00"
}
}
Plaćanje se nikad ne naplaćuje putem API-ja. Kad je needs_payment true, član mora platiti
u aplikaciji Sporty prije pay_by, inače klub termin vraća. Recite mu to. Klub koji ne traži
plaćanje unaprijed odgovara s needs_payment: false i pay_by: null. pay_by se navodi samo u
odgovoru kojim je rezervacija napravljena, a pri ponavljanju je null.
Član dobiva obavijest kao i za svaku rezervaciju koju napravi sam, a klub je u svojem rasporedu vidi kao njegovu.
Kad član ne može rezervirati, odgovor je 409 s kodom te porukom samog pravila, prevedenom za
člana i namijenjenom da mu se prikaže:
error | Značenje |
|---|---|
slot_taken | Netko drugi ima dio tog vremena. |
outside_opening_hours | Teren nije otvoren za ovog člana tijekom cijelog tog vremena. |
court_closed | Teren je tog dana zatvoren. |
in_past | To je vrijeme prošlo. |
too_soon | Preblizu početka za njegovo članstvo. |
too_far_ahead | Dalje unaprijed nego što njegovo članstvo dopušta. |
leaves_a_gap | Ostala bi praznina od pola sata koju klub ne dopušta. |
duration_not_offered | Klub u to vrijeme ne prodaje takvo trajanje. |
limit_reached | Već ima onoliko rezervacija koliko mu članstvo dopušta. |
not_a_member | Klub ga još nije prihvatio. |
not_bookable | Bilo koje drugo pravilo; poruka kaže koje. |
invalid_times (422) znači da vremena nisu cijeli termini ili prelaze ponoć. 403 access_denied znači da je član vašu aplikaciju autorizirao za neki drugi klub.
DELETE /me/reservations/{id}
Otkazuje jednu od članovih rezervacija kao član, pa vrijedi njegov rok za otkazivanje. Sve
što je platio vraća se u njegov klupski novčanik, a ostali igrači dobivaju obavijest. Odgovor je
204. Odgovor je 409 not_cancelable, s razlogom, kad je prekasno ili rezervaciju ne može
otkazati on (rezervirao ju je netko drugi). Odgovor je 404 za rezervaciju u kojoj nije.
GET /me/results
Rezultati članovih mečeva, od najnovijih. from i to (ISO 8601) sužavaju popis; per_page
ide do 100.
{
"data": [
{
"id": 22875,
"club": { "id": 57, "name": "TK Test" },
"played_at": "2026-09-27T18:00:00+00:00",
"sport": "tennis",
"kind": "league",
"sets": [
{ "games": [2, 6], "tie_break": null },
{ "games": [6, 3], "tie_break": null },
{ "games": [7, 6], "tie_break": [10, 8] }
],
"won": true,
"official": true,
"verified": true
}
]
}
- Rezultati su iz perspektive člana. Svaki par je
[članov, protivnički], pa nikad ne morate znati na kojoj je strani meča bio. - Setovi: navode se samo odigrani setovi.
won:nullkad nije bilo pobjednika.kind:league,tournamentilimatch.verified: protivnici su potvrdili rezultat.- Protivnici nisu uključeni: s kim je član igrao, odluka je protivnika hoće li to podijeliti.
GET /me/memberships
Klubovi kojima član pripada i s kojim planom: svako članstvo koje nije završilo.
{
"data": [
{
"club": { "id": 57, "name": "TK Test" },
"membership": { "id": 12, "name": "Seniori", "basic": false },
"start": "2026-01-01T00:00:00+00:00",
"end": "2026-12-31T00:00:00+00:00",
"renews": false
}
]
}
end je null za članstvo bez datuma završetka. renews kaže obnavlja li se samo. basic
označava plan koji svaki član kluba ima prije nego što kupi neki drugi.
GET /clubs/me
Za koji klub djeluje vaš token. Isplati se pozvati prvo, osobito ako imate odobrenja za više klubova.
GET /courts
per_page do 100. Svaki teren ima id, name, active, indoor, lights, working_from,
working_to, reservation_duration (minute po terminu) i sports, ono što se na njemu igra:
"sports": [ { "id": 2, "slug": "padel", "name": "Padel" } ]
Većina terena ima jedan sport; nekoliko ih ima dva, a teren kojem ga nitko nije postavio nema nijedan.
GET /availability
Koji su termini slobodni. date (YYYY-MM-DD, lokalni dan kluba) je obavezan; days obuhvaća
do 7 dana od tog datuma; court_id sužava upit na jedan teren.
{
"data": [
{
"court_id": 412,
"date": "2026-10-07",
"timezone": "Europe/Zagreb",
"closed": null,
"slots": [
{ "from": "2026-10-07T06:00:00+00:00", "to": "2026-10-07T07:00:00+00:00", "available": false },
{ "from": "2026-10-07T07:00:00+00:00", "to": "2026-10-07T08:00:00+00:00", "available": true }
]
}
]
}
- Jedan unos po terenu po danu. Termini prate klupski raspored za taj dan, svaki traje
reservation_duration. Vremena su u UTC-u;timezoneje vremenska zona kluba. - Zauzeto ili slobodno. Termin je zauzet (
"available": false) kad se s njim preklapa bilo koja rezervacija, makar i djelomično. - Zatvoreni tereni.
closedjeinactiveilicourt_breakkad se teren tog dana uopće ne može rezervirati, a njegov jeslotstada prazan. - Čiji raspored. Ovo je standardni raspored kluba, onaj koji vidi posjetitelj koji nije prijavljen. Članstvo pojedinog člana može otvoriti ili zatvoriti neke sate i odrediti cijenu. Ništa od toga ovdje se ne prikazuje.
GET /reservations
from i to (ISO 8601, UTC) sužavaju vremenski raspon; per_page do 100. Rezervacije se
vraćaju bez osoba koje u njima sudjeluju. Za njih je potreban club.members.read i krajnja točka
za članove, pa klub te dvije odluke donosi zasebno.
updated_since umjesto toga vraća svaku rezervaciju promijenjenu u tom trenutku ili nakon njega,
od najstarije promjene, uključujući otkazane. Namijenjen je za
održavanje sinkronizirane kopije.
Svaka rezervacija ima:
| Polje | |
|---|---|
id, court_id, court_name | |
from, to | UTC |
category_id | Kategorija rezervacije kluba, ako postoji |
approved | Je li rezervacija potvrđena |
cancelled | true samo uz updated_since ili na GET /reservations/{id} |
source | partner za rezervaciju koju je poslala vaša aplikacija, sporty za svaku drugu |
external_id, name | Vaš id i naziv za rezervaciju koju ste poslali; null za svaku drugu |
updated_at | Kad je rezervacija posljednji put promijenjena |
Upisivanje rezervacija i webhookovi
POST, PATCH i DELETE /reservations te /webhook namijenjeni su sustavu koji vlastiti
raspored usklađuje s klupskim. Opisani su, s primjerima, u
Sinkronizaciji rezervacija.
GET /members
search pretražuje ime ili adresu e-pošte. per_page do 100.
POST /members
{
"email": "new@example.com",
"firstName": "New",
"lastName": "Member",
"language": "hr",
"countryCode": "HR"
}
language i countryCode su neobavezni, a zadane su im vrijednosti one kluba.
Idempotentno prema adresi e-pošte. Član kojeg već poznajemo pridružuje se klubu i vraća s
200; novi se stvara i vraća s 201. Ponovno slanje iste osobe je sigurno, što je važno kad
dva sustava sinkroniziraju osobe.
Što ovdje ne postoji
Naplata. Članska rezervacija napravljena putem API-ja plaća se u aplikaciji Sporty, nikad putem API-ja.
Pogreške
| Status | Tijelo | Značenje |
|---|---|---|
401 | invalid_token | Nedostaje, istekao je, opozvan je ili nije naš. |
403 | insufficient_scope | Token je valjan, opseg nije odobren. WWW-Authenticate navodi što je potrebno. |
403 | access_denied | Nijedan klub nije odobrio vašu aplikaciju, ili to nije učinio klub koji ste naveli. |
400 | club_required | Djelujete za više klubova; navedite jedan s X-Club. |
422 | Laravel pogreške validacije | Neispravno tijelo zahtjeva. |
429 | — | 120 zahtjeva u minuti, po klijentu. |
Verzioniranje
v1 je u putanji i neće vam promijeniti oblik ispod ruku. Mogu se dodavati nova polja i nove
krajnje točke; postojeća polja neće se uklanjati niti mijenjati značenje. Promjena koja narušava
kompatibilnost znači v2, i javit ćemo vam prije nego što stigne.