JSON API
Kým ho nezapnete, je vypnuté:
EVENTS_ROUTES=on- Prefix events.routes.prefix (api), názvy rout events.*, middleware events.routes.middleware (['api', 'throttle:events-api']); zapisovacie routy pridajú events.routes.auth_middleware (['auth']).
- Nenastavený prepínač zostáva vypnutý — aj v publikovanej konfigurácii, ktorej pole routes kľúč nemá, pretože zlučovanie je plytké.
- Ak chcete upraviť súbor rout, publikujte --tag=events-routes: keď routes/events.php vo vašej aplikácii existuje, načíta sa namiesto súboru balíka a prepínač ho naďalej riadi.
- Zápisy vždy vyžadujú prihláseného používateľa. Po vašom auth_middleware spustí každá zapisovacia routa vlastnú kontrolu balíka: chýbajúci prihlásený používateľ alebo používateľ guardu, ktorý nie je Eloquent model, je 401 — aj pri auth_middleware = []. Beží pred validáciou, takže hosť sa nikdy nedozvie tvar požiadavky. Čítacie routy a routy odkazu z pozvánky hostí naďalej pustia.
Konvencie
- {event} je uuid — obmedzené tak, aby sa naň /events/feed.ics nikdy nedostal — a podujatie, ktoré volajúci vidieť nesmie, je 404 presne ako neexistujúce, na každom endpointe. {rsvp} a {tier} sú obmedzené na podujatie (typ vstupenky podľa id alebo kľúča); {venue} je číselné.
- Na vaše modely sa API odvoláva cez alias, nikdy cez názov triedy: {type: user, id: 42} v telách a user:42 v cestách a filtroch (events.organizers, events.attendees). V tele je každé zlyhanie — neznámy alias, chybný kľúč, chýbajúci riadok — rovnaké 422 unknown_alias.
- Časy v požiadavkách sú miestny čas v pásme podujatia alebo okamih ISO-8601 s posunom; odpovede používajú ISO-8601 s vlastným posunom podujatia.
- Textové polia prijímajú reťazec (aktuálny jazyk) alebo mapu jazykov. Ceny sú desatinné reťazce s voliteľnou menou; odpovede vykresľujú MoneyResource z money-for-laravel — {minor, decimal, currency, exponent, formatted}, pričom minor je reťazec a formatted je len na zobrazenie. Bezplatný typ vstupenky má price: null.
- Zoznamy sú length-aware paginátory z Laravelu (data, links, current_page, per_page, total, …) s per_page obmedzeným na events.routes.max_per_page.
Routy
Všetkých 49 rout, zoskupených, s cestami relatívne k prefixu. Riadky s poznámkou „Aj hostia“ odpovedajú aj neprihláseným; každá iná routa vyžaduje prihláseného používateľa:
| Metóda · cesta | Názov | Oprávnenie | Poznámky |
|---|---|---|---|
GET /events | events.index | viewAny | Aj hostia. Filtre objavovania nižšie → stránkovaný EventResource. |
GET /events/feed.ics | events.feed | — | Aj hostia. ?category=&label=&organizer=alias:key&venue= → text/calendar (inline); throttle events-feeds. |
GET /events/{event} | events.show | view | Aj hostia. {data: EventResource} s hosts a viewer.can. |
GET /events/{event}/calendar.ics | events.calendar | view | Aj hostia. text/calendar (príloha); odkaz na pripojenie len pre diváka, ktorý ho smie vidieť. |
GET /events/{event}/tiers | events.tiers.index | view | Aj hostia. Skryté len pre správcov. |
GET /events/{event}/attendees | events.attendees.index | viewAttendees | Aj hostia. ?status=&checked_in=&tier=&per_page= (predvolene 25) — filtre len pre správcov. |
GET /events/{event}/hosts | events.hosts.index | view | Aj hostia. {data: [referencia spoluorganizátora]}. |
GET /event-venues | events.venues.index | — | Aj hostia. ?mine=1&near=lat,lng&radius=&per_page= (predvolene 25). |
GET /event-venues/{venue} | events.venues.show | — | Aj hostia. |
POST /event-invitations/accept · decline | events.invitations.accept · decline | token | Aj hostia. {token, response?} (predvolene going, interested) → RsvpResource (pohľad vlastníka) / {status: declined}; throttle events-rsvp. |
POST /events | events.store | create | 201 {data: EventResource}. |
PATCH /events/{event} · DELETE /events/{event} | events.update · destroy | update · delete | Zmenia sa len poslané polia; ?notify=0 zapíše potichu · 204. |
POST /events/{event}/restore | events.restore | restore | Zmazané podujatia, len správcovia (ostatní 404). |
POST /events/{event}/publish · unpublish · complete | events.publish · … | publish · … | {data: EventResource} |
POST /events/{event}/postpone · cancel | events.postpone · cancel | postpone · cancel | {reason?} (≤ 500). |
POST /events/{event}/reschedule | events.reschedule | reschedule | {starts_at, ends_at?, reason?} |
POST /events/{event}/move-online | events.move-online | moveOnline | {url, keep_venue?} |
PUT · DELETE /events/{event}/poster | events.poster.update · destroy | update | Multipart poster alebo token draftu · odstráni ho. |
POST /events/{event}/tiers · tiers/reorder | events.tiers.store · reorder | manageTiers | 201 AdmissionTierResource · {order: [id typu, …]} → 204. |
PATCH · DELETE /events/{event}/tiers/{tier} | events.tiers.update · destroy | manageTiers | Typ podľa id alebo kľúča; len poslané polia · 204. |
GET /events/{event}/rsvp · rsvp/qr.svg | events.rsvp.show · qr | — | Vaše RSVP (pohľad vlastníka) alebo null · váš kód ako image/svg+xml, Cache-Control: no-store, private. |
PUT · DELETE /events/{event}/rsvp | events.rsvp.update · destroy | rsvp | {response, guests?, tier?, note?} · odpovie not_going; throttle events-rsvp. |
POST /events/{event}/attendees | events.attendees.store | manageAttendees | {attendee: {type, id}, tier?, guests?, over_capacity?} → 201 |
PATCH · DELETE /events/{event}/attendees/{rsvp} | events.attendees.update · destroy | manageAttendees | {tier?, guests?} — uplatní sa oboje alebo nič · 204. |
POST · DELETE /events/{event}/attendees/{rsvp}/check-in | events.attendees.check-in · undo-check-in | checkIn | Ručný check-in {guests?} (bez kódu) · zrušenie. |
POST /events/{event}/check-ins | events.check-ins.store | checkIn | Skenovanie pri vstupe {code, guests?} → CheckInResultResource, 201 prvý raz, 200 opakovane; throttle events-check-in. |
GET /events/{event}/waitlist · POST …/waitlist/promote | events.waitlist.index · promote | viewWaitlist · manageAttendees | V poradí posúvania · {limit?} (1–1000). |
POST · DELETE /events/{event}/hosts… | events.hosts.store · destroy | manageHosts | {person: {type, id}} (Organizer) → 201 · {host} = alias:key → 204. |
GET · POST · DELETE /events/{event}/invitations… | events.invitations.index · store · destroy | invite | Tokeny sa nikdy nevykreslia · {person: {type, id}, message?} (≤ 1000) → 201 · {invitee} = alias:key → 204. |
POST · PATCH · DELETE /event-venues… | events.venues.store · update · destroy | VenuePolicy | Telo miesta konania nižšie → 201 · len poslané polia · 204. |
POST /event-venues/{venue}/geocode | events.venues.geocode | VenuePolicy geocode | Vždy geokóduje znova (force). |
Routa na potvrdenie platby neexistuje: potvrďte ju z webhooku poskytovateľa v PHP cez Events::rsvps()->confirmPayment($reference).
Vytvorenie podujatia
{
"title": { "en": "Jazz Night", "sk": "Jazzový večer" },
"venue_id": 7,
"starts_at": "2026-11-14 20:00",
"duration": "PT3H",
"capacity": 120,
"max_guests": 1,
"category": "jazz",
"labels": ["Live", "Late night"],
"tiers": [
{ "name": "Free entry", "key": "free" },
{ "name": "Supporter", "price": "15.00", "currency": "EUR", "capacity": 30 }
],
"publish": true
}| Pole | Pravidlá |
|---|---|
title | Povinné; reťazec alebo mapa jazykov, každý ≤ 200 znakov. |
description | ≤ 20 000. |
organizer | {type, id} z events.organizers — predvolene volajúci; musí volajúceho povoliť. |
venue_id · venue | Existujúce miesto konania (akýkoľvek organizátor môže usporiadať podujatie na akomkoľvek mieste) — alebo nové miesto priamo v tele, vlastnené volajúcim: name (povinné), street, city, region, postal_code, country, coordinates, timezone. |
online_url · attendance_mode · visibility · guest_list · timezone | ≤ 2048 · hodnoty enumov · názov IANA. |
starts_at · ends_at · duration · doors_open_at · all_day_from · all_day_through | starts_at povinné, ak nie je all_day_from; celodenné dátumy Y-m-d. |
capacity · max_guests | 0–10,000,000 · 0–1000 |
waitlist · waitlist_auto_promote · show_counts · publish | Boolean hodnoty; publish: true ho ohlási v tej istej transakcii. |
rsvp_opens_at · rsvp_closes_at · reminders | Reťazce; ≤ 5 pripomienok. |
category · labels | Kategória stromu · ≤ 10 reťazcov (v katalógu organizátora pri events.tags.scope = organizer). |
tiers | ≤ 50 položiek {name, key?, price?, currency?, capacity?, hidden?}; price v tvare ^\d{1,12}(\.\d{1,4})?$. |
poster_draft | Token draftu z media-library. |
PATCH /events/{event} zmení len poslané polia a null vymaže pole, ktoré môže byť prázdne; category (null vymaže) a labels (presne tieto) sa uplatnia v tej istej transakcii. Nový začiatok ohláseného podujatia sa odmietne — použite POST /reschedule. ?notify=0, alebo notify: false v tele, zapíše potichu.
Typy vstupeniek a miesta konania
Telá typov vstupeniek prijímajú name (pri vytvorení povinné, ≤ 200), description (≤ 2000), key (≤ 64; bez neho sa odvodí z názvu), price + currency, capacity, sale_starts_at, sale_ends_at, min_party (1–1000), max_party, hidden a position. Telá miest konania prijímajú name (pri vytvorení povinné), street, city, region, postal_code, country (2 písmená), formatted_address, coordinates (lat,lng, [lat, lng] alebo {latitude, longitude}), timezone, capacity a geocode a pri vytvorení owner ({type, id}, ktorý musí volajúceho povoliť) alebo shared (len administrátori a abilities.shared_venues). Bez owner a shared patrí nové miesto volajúcemu. Pri PATCH sa každé pole adresy mení samostatne a vlastníctvo sa nikdy nemení.
Vaše RSVP
PUT /api/events/{event}/rsvp s telom:
{ "response": "going", "tier": "free", "guests": 1, "note": "Wheelchair access" }vráti vaše vlastné RSVP v pohľade vlastníka, s kódom pre check-in a URL QR kódu:
{
"data": {
"id": 42,
"status": "going",
"guests": 1,
"party_size": 2,
"tier": { "id": 1, "key": "free", "name": "Free entry" },
"note": "Wheelchair access",
"waitlist_position": null,
"held_until": null,
"amount": null,
"checked_in_at": null,
"checked_in_guests": null,
"responded_at": "2026-11-02T18:12:40+01:00",
"payment_url": null,
"check_in_code": "EV1-…",
"qr_url": "https://example.com/api/events/0195f2c4-7a1e-7c3b-9d2e-5b8a1f4c6e21/rsvp/qr.svg"
}
}Zoznam hostí
GET /events/{event}/attendees vypíše správcom akýkoľvek stav (?status=, predvolene going) so záznamom o check-ine — nikdy s kódmi — a filtrom podľa checked_in a tier. Ktokoľvek iný, kto zoznam smie vidieť, uvidí len účastníkov so stavom going ako {attendee, guests}: status=interested je preňho 403 a status, checked_in a tier sa ignorujú — vždy celý zoznam, nikdy zúžený na to, kto je na mieste alebo kto má skrytý typ vstupenky.
Objavovanie: GET /events
Postavené na scopes modelu, takže PHP aj HTTP filtrujú rovnako; query-builder rozdelí každú hodnotu podľa čiarok. Základom je browse($viewer) — viditeľné, verejne uvedené, ohlásené:
| Parameter | Scope |
|---|---|
filter[when]=upcoming|ongoing|past | upcoming() … |
filter[from], filter[to] | Prekrývajúce sa s rozsahom, miestny čas v ?tz= (predvolene app.timezone). |
filter[date]=2026-11-14 | onLocalDate() — skutočný dátum (2027-02-30 je 422). |
filter[near]=48.14,17.11,25 | near() — polomer voliteľný, ≤ geo.max_radius_km. |
filter[category]=jazz | inCategory() vrátane podkategórií. |
filter[labels]=live,outdoor | Všetky štítky. |
filter[organizer]=user:42 | organizedBy() — riadok sa nikdy nevyhľadáva; neexistujúci nezodpovedá ničomu. |
filter[venue]=7,8 · filter[mode]=online,mixed | atVenue() · withAttendanceMode() |
filter[free]=true · filter[available]=true | free() · withSeatsAvailable() |
filter[search]=… | search() — vyhľadávanie podreťazca v názve a popise so zneškodnenými znakmi (čiarky zostanú). |
filter[status]=postponed,rescheduled | inPhase() |
filter[mine]=organizing|attending|interested | Vaše podujatia, každá viditeľnosť (organizing vrátane konceptov); hostia nedostanú nič. |
sort=starts_at (default), -starts_at, created_at, -created_at, popularity, distance | distance vyžaduje filter[near] (inak 400); popularity počíta podujatie s vypnutým show_counts ako nula účastníkov — okrem filter[mine]=organizing. |
per_page | Predvolene 15, ≤ events.routes.max_per_page. |
Neplatná hodnota filtra je 422 invalid_query. Stránka načíta miesta konania a ich vlastníkov, organizátorov, štítky, plagáty, typy vstupeniek, vlastné RSVP diváka, cesty kategórií a to, ktoré podujatia divák spravuje, v pevnom počte dotazov — počet nerastie s veľkosťou stránky.
Resources
Bezplatné podujatie tak, ako ho vidí hosť v zozname:
{
"id": "0195f2c4-7a1e-7c3b-9d2e-5b8a1f4c6e21",
"slug": "jazz-night",
"title": "Jazz Night",
"description": "A live quartet, then an open jam session.",
"phase": "scheduled",
"schema_status": "EventScheduled",
"attendance_mode": "offline",
"starts_at": "2026-11-14T20:00:00+01:00",
"ends_at": "2026-11-14T23:00:00+01:00",
"doors_open_at": null,
"previous_starts_at": null,
"all_day": false,
"timezone": "Europe/Bratislava",
"starts_on": "2026-11-14",
"ends_on": "2026-11-14",
"venue": {
"id": 7,
"name": "Blue Note",
"slug": "blue-note",
"address": {
"street": "Hlavná 1", "city": "Bratislava", "region": null, "postal_code": "811 01",
"country": "SK", "formatted": "Hlavná 1, 811 01 Bratislava, SK"
},
"coordinates": { "latitude": 48.1439, "longitude": 17.1097 },
"timezone": "Europe/Bratislava",
"capacity": null,
"owner": null,
"distance_km": null,
"deleted": false
},
"online_url": null,
"organizer": { "id": 1, "type": "User" },
"category": { "id": 3, "slug": "jazz", "name": "Jazz", "color": null, "icon": null, "path": ["Music"] },
"labels": [{ "id": 4, "slug": "live", "name": "Live", "color": null, "icon": null }],
"poster": null,
"tiers": [
{
"id": 1, "key": "free", "name": "Free entry", "description": null, "price": null, "free": true,
"capacity": null, "seats_left": null, "on_sale": true, "sale_starts_at": null, "sale_ends_at": null,
"min_party": 1, "max_party": null, "position": 1
}
],
"capacity": 120,
"seats_left": 85,
"counts": { "going": 35, "waitlisted": 0 },
"viewer": { "rsvp": null },
"links": {
"self": "https://example.com/api/events/0195f2c4-7a1e-7c3b-9d2e-5b8a1f4c6e21",
"ics": "https://example.com/api/events/0195f2c4-7a1e-7c3b-9d2e-5b8a1f4c6e21/calendar.ics",
"url": "https://example.com/en/events/jazz-night"
}
}| Resource | Tvar |
|---|---|
EventResource | Pozrite príklad vyššie. status a visibility len pre správcov; online_url pre správcov a účastníkov so stavom going; seats_left a counts pre správcov, alebo pre všetkých pri show_counts (counts.interested len pre správcov); hosts a viewer.can len v detaile. |
VenueResource | id, name, slug, address {street, city, region, postal_code, country, formatted}, coordinates {latitude, longitude} | null, timezone, capacity, owner, distance_km (3 decimals, when selected), deleted — never meta |
AdmissionTierResource | id, key, name, description, price (MoneyResource alebo null), free, capacity, seats_left, on_sale, sale_starts_at, sale_ends_at, min_party, max_party, position a hidden pre správcov. |
RsvpResource | id, status, guests, party_size, tier {id, key, name}, note, waitlist_position, held_until, amount, checked_in_at, checked_in_guests, responded_at; pohľad vlastníka pridá payment_url (kým je rezervované), check_in_code a qr_url; pohľad správcov pridá attendee, check_in_method a checked_in_by — nikdy kód. |
InvitationResource | id, status, invitee, invited_by, message, expires_at (null, kým je odložené), responded_at, sent_at — nikdy token. |
CheckInResultResource | first_check_in, guests, checked_in_at, method, attendee, rsvp (RsvpResource) |
Referenčné resources
Každá osoba či tím, ktorý odpoveď spomína, sa vykreslí cez vlastný resource — predvolene {id, type}, kde type je krátky názov triedy modelu (User), nikdy názov PHP triedy ani morph typ, aj bez morph mapy. Chýbajúci model sa vykreslí ako null:
| Konfiguračný kľúč | Predvolené | Vykresľuje |
|---|---|---|
events.resources.organizer | EventOrganizerResource | organizer |
events.resources.host | EventHostResource | spoluorganizátori |
events.resources.attendee | EventAttendeeResource | účastníci |
events.resources.invitee | EventInviteeResource | pozvaní |
events.resources.actor | EventActorResource | invited_by, checked_in_by |
events.resources.venue_owner | VenueOwnerResource | vlastník miesta konania |
Ak chcete pridať meno či avatar, nastavte kľúč na vlastný JsonResource — dostane model. Prázdna hodnota znamená predvolený; čokoľvek, čo nie je inštanciovateľná podtrieda JsonResource, vyhodí výnimku s názvom kľúča. Zoznamy súvisiace modely načítajú vopred, takže resource, ktorý číta len ich atribúty, nepridá žiadne dotazy:
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
final class PersonResource extends JsonResource
{
public function toArray(Request $request): array
{
return ['id' => $this->resource->getKey(), 'name' => $this->resource->name, 'avatar' => $this->resource->avatar_url];
}
}
// config/events.php
'resources' => [
'organizer' => App\Http\Resources\PersonResource::class,
'host' => EventHostResource::class,
'attendee' => App\Http\Resources\PersonResource::class,
'invitee' => EventInviteeResource::class,
'actor' => EventActorResource::class,
'venue_owner' => VenueOwnerResource::class,
],Chyby
{message, code} so stavom balíka — plus reason pri odmietnutí check-inu, denials pri zamietnutom prechode a errors pri validačnom 422. Správy sú preložené a nikdy neuvádzajú triedu a váš exception handler sa nikdy nemení:
{ "message": "This code is for another event.", "code": "check_in_refused", "reason": "wrong_event" }| Stav | code | Kedy |
|---|---|---|
| 401 | (váš handler) | Zápis bez prihláseného Eloquent používateľa. |
| 403 | forbidden · invitation_required | Policy zamietla · súkromné podujatie bez pozvánky. |
| 404 | event_not_found, venue_not_found, tier_not_found, rsvp_not_found, invalid_code, invalid_invitation | Neexistuje alebo je skryté. |
| 409 | event_trashed, rsvp_closed, event_full, tier_sold_out, tier_not_on_sale, hold_expired, invalid_rsvp_transition, check_in_refused | Konflikt stavu (check_in_refused pridá reason). |
| 422 | validation_failed, invalid_event, invalid_schedule, invalid_venue, venue_in_use, invalid_tier, tier_key_taken, tier_in_use, tier_required, invalid_guest_count, invalid_organizer, unknown_alias, unknown_transition, transition_denied, invalid_query, tag_not_found, tag_limit_exceeded, invalid_tag, invalid_poster | Chybný vstup (validation_failed pridá errors, transition_denied pridá denials). |
| 400 | invalid_query | sort=distance bez filter[near]. |
| 429 | transition_denied (+ Retry-After) alebo throttle | Prekročený limit požiadaviek. |
Štítok iného rozsahu odpovie tag_not_found, rovnako ako chýbajúci. Plagát, ktorý media-library odmietne, alebo neznámy či vypršaný draft je invalid_poster.
Limity požiadaviek
Definujú sa, len keď sú routy zapnuté, za minútu:
| Limiter | Predvolené | Kľúč | Routy |
|---|---|---|---|
events-api | routes.rate_limits.api = 120 | používateľ, inak IP | Každá routa (v routes.middleware). |
events-rsvp | rate_limits.rsvp = 20 | používateľ (alebo IP) a podujatie | PUT / DELETE /rsvp, prijatie / odmietnutie pozvánky. |
events-check-in | rate_limits.check_in = 300 | používateľ | POST /check-ins — skenery pri vstupe pracujú v nárazoch. |
events-feeds | rate_limits.feeds = 60 | IP | feed.ics, calendar.ics |
Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.