---
title: "JSON API — Tickets for Laravel | Roundly"
description: "Voliteľné JSON API — routy, telá požiadaviek, hromadné akcie, resources podľa roly, filtre a radenie zoznamu, limity požiadaviek a chybové kódy."
url: https://roundly-consulting.com/sk/open-source/navody/tickets-for-laravel/json-api
language: sk
---

[Všetky balíky](https://roundly-consulting.com/sk/open-source.md)

[Tickets for Laravel](https://roundly-consulting.com/sk/open-source/navody/tickets-for-laravel.md)

# JSON API

Routy, form requesty, kontroléry, resources podľa roly, filtre zoznamu cez query-builder, policy, limity požiadaviek a endpointy pre hostí — vypnuté, kým ich nezapnete:

```bash
TICKETS_ROUTES=on
```

- Prefix tickets.routes.prefix (api), názvy rout tickets.\*, middleware tickets.routes.middleware (\['api', 'auth', 'throttle:tickets-api'\]).
- {ticket} je číslo tiketu (\[A-Z\]\[A-Z0-9\]{0,9}-\[0-9\]+). Kontroléry hľadajú tikety cez miesto pre modely, takže API funguje bez SubstituteBindings aj s vymeneným modelom tiketu. Soft-deleted tiket je 404, okrem obnovenia.
- Ak chcete zmeniť routu, publikujte routes/tickets.php (--tag=tickets-routes); publikovaná kópia natrvalo nahradí routy balíka.

## So Sanctum

```php
// config/tickets.php
'routes' => [
    'enabled' => env('TICKETS_ROUTES') ?: 'off',
    'middleware' => ['api', 'auth:sanctum', 'throttle:tickets-api'],
    // …
],
```

Rovnako funguje akýkoľvek guard (auth:api, …). Bez middleware auth by každý endpoint odpovedal anonymným volajúcim — tickets:doctor upozorní.

## Limity požiadaviek

Definujú sa len pri zapnutých routách a platia, len kým je ich middleware throttle: v zozname: tickets-api povolí tickets.routes.rate\_limit (120) požiadaviek za minútu na prihláseného používateľa (inak na IP); tickets-guest povolí tickets.routes.guest.rate\_limit (30) za minútu na IP a tiket.

## Routy

| Metóda a cesta | Názov | Oprávnenie | Poznámky |
| --- | --- | --- | --- |
| `GET /tickets` | `index` | `viewAny` | Obmedzené cez visibleTo; filtre, radenie a stránkovanie nižšie. |
| `POST /tickets` | `store` | `create` | 201 |
| `POST /tickets/bulk` | `bulk` | pre každý tiket | Hromadné akcie, nižšie. |
| `GET /tickets/{ticket}` | `show` | `view` | Detailné: plus hodnoty polí a prílohy. |
| `PATCH /tickets/{ticket}` | `update` | view + update (s dotknutými atribútmi) | — |
| `DELETE /tickets/{ticket}` | `destroy` | `view + delete` | Soft; 204. |
| `POST /tickets/{ticket}/restore` | `restore` | `view + restore` | — |
| `GET /tickets/{ticket}/transitions` | `transitions.index` | `view` | Prechody, ktoré tento používateľ smie práve spustiť: name, label, to, to\_label, requires\_reason, payload. |
| `POST /tickets/{ticket}/transitions/{transition}` | `transitions.store` | `view + transition` | {reason?, payload?} |
| `PUT /tickets/{ticket}/assignee` | `assignee.update` | `view + assign` | {assignee: {type, id}} \| {claim: true} |
| `DELETE /tickets/{ticket}/assignee` | `assignee.destroy` | `view + unassign` | — |
| `GET /tickets/{ticket}/comments` | `comments.index` | view (+ viewInternal pri ?internal=1) | — |
| `POST /tickets/{ticket}/replies` | `replies.store` | reply (+ transition) | {body, channel?, attachments\[\]?, transition?, reason?, payload?}; 201 — aj keď odpoveď zadrží moderácia hostiteľa; vtedy sa žiadny prechod neuplatní. |
| `POST /tickets/{ticket}/notes` | `notes.store` | `view + note` | {body, channel?, attachments\[\]?}; 201 |
| `PATCH /tickets/{ticket}/comments/{comment}` | `comments.update` | `view + updateComment` | {body}; komentár musí patriť tomuto tiketu; zaznamená sa ako comment\_edited. |
| `DELETE /tickets/{ticket}/comments/{comment}` | `comments.destroy` | `view + deleteComment` | 204; zaznamená sa ako comment\_deleted. |
| `GET /tickets/{ticket}/fields` | `fields.index` | `view` | Filtrované podľa roly. |
| `PUT /tickets/{ticket}/fields` | `fields.update` | view + update (fields) | {fields: {key: value}}; null hodnotu zruší. |
| `GET /tickets/{ticket}/watchers` | `watchers.index` | `view + manageWatchers` | Len agenti. |
| `POST /tickets/{ticket}/watchers` | `watchers.store` | `view + manageWatchers` | {watcher: {type, id}}; 201 |
| `DELETE /tickets/{ticket}/watchers/{alias:key}` | `watchers.destroy` | `view + manageWatchers` | 204 |
| `POST / DELETE /tickets/{ticket}/watch` | `watch / unwatch` | view + manageWatchers (pre seba) | 204 |
| `PUT /tickets/{ticket}/tags` | `tags.sync` | `view + tag` | {tags: \[names\]} |
| `GET /tickets/{ticket}/links` | `links.index` | `view + link` | — |
| `POST /tickets/{ticket}/links` | `links.store` | view + link na oboch tiketoch | {ticket: reference, type}; 201 |
| `DELETE /tickets/{ticket}/links/{link}` | `links.destroy` | `view + link` | 204; odstráni presne tento riadok, z ktoréhokoľvek konca. |
| `POST /tickets/{ticket}/merge` | `merge` | view + merge na zdroji aj cieli | {target: reference, reason?} |
| `GET /tickets/{ticket}/attachments` | `attachments.index` | `view` | Krátkodobé podpísané URL. |
| `POST /tickets/{ticket}/attachments` | `attachments.store` | view + update (attachments) — agenti | Multipart file; 201. |
| `GET /tickets/{ticket}/attachments/{media}` | `attachments.show` | `view` | — |
| `DELETE /tickets/{ticket}/attachments/{media}` | `attachments.destroy` | view + update (attachments) | 204 |
| `GET /tickets/{ticket}/activity` | `activity` | `view` | Filtrované podľa roly. |
| `GET / POST /ticket-categories` | `admin.categories.index / .store` | `viewAny / manageCategories` | Strom; ?project=alias:key; neaktívne kategórie (aj s podstromami) len pre agentov. |
| `GET / PATCH / DELETE /ticket-categories/{id}` | `admin.categories.show / .update / .destroy` | `viewAny / manageCategories` | Neaktívna kategória je pre neagentov 404 category\_not\_found; mazanie berie {reassign\_to?}. |
| `POST /ticket-categories/{id}/move` | `admin.categories.move` | `manageCategories` | {parent\_id, position?} |
| `GET / POST /ticket-fields` | `admin.fields.index / .store` | `viewAny / manageFields` | ?project=, ?category=; filtrované podľa roly. |
| `GET / PATCH / DELETE /ticket-fields/{id}` | `admin.fields.show / .update / .destroy` | `viewAny / manageFields` | — |
| `GET / POST /ticket-tags` | `admin.tags.index / .store` | viewTags (agenti) / manageTags | — |
| `PATCH / DELETE /ticket-tags/{id}` | `admin.tags.update / .destroy` | `manageTags` | — |
| `GET /tickets/guest/{ticket}` | `guest.show` | podpísaný odkaz | Pozrite Hostia a odkazy pre hostí. |
| `POST /tickets/guest/{ticket}/replies` | `guest.reply` | podpísaný odkaz | — |
| `POST /tickets/guest/{ticket}/transitions/{transition}` | `guest.transition` | podpísaný odkaz | Len prechody žiadateľa. |
| `GET /tickets/guest/{ticket}/attachments/{media}` | `guest.attachment` | podpísaný odkaz | Súbory tiketu a jeho verejného vlákna. |

Endpoint na obnovenie kategórie neexistuje — použite Tickets::categories()->restore().

## Založenie tiketu

```json
POST /api/tickets
{
  "subject": "Printer on floor 3 is jammed",
  "description": "Paper jam error E-102.",
  "category_id": 4,
  "priority": "high",
  "channel": "web",
  "fields": {"asset_tag": "PRN-0042"},
  "project": {"type": "project", "id": 1}
}
```

Samoobsluha zakladá tiket pre prihláseného používateľa. subject (≤ 255), description, category\_id, priority, channel, project a fields (pre neagenta len polia, ktoré smie upravovať) sú otvorené každému, kto smie zakladať. requester, guest (polia namiesto žiadateľa), assignee, due\_at (po teraz), tags a watchers sú vstupy len pre agentov; riešiteľ iný ako vy vyžaduje oprávnenie assign. Polymorfné vstupy sú {type: alias, id} s aliasom z povolených zoznamov — názov triedy, neznámy alias alebo kľúč bez záznamu vráti 422. Prílohy sú multipart attachments\[\].

## Hromadné akcie

```json
POST /api/tickets/bulk
{"action": "transition", "tickets": ["TCK-1", "TCK-2"], "transition": "resolve", "payload": {"resolution": "Fixed"}}
```

action je assign (assignee alebo claim: true), unassign, transition (transition, reason?, payload?), priority (priority) alebo tags (tags.attach / tags.detach, každé najviac 50 názvov) — najviac tickets.routes.bulk.max (100) rôznych čísel. Každý tiket ide vlastnou kontrolou policy aj transakciou a zlyhanie jedného nikdy nezastaví ostatné:

```json
{"data": [
  {"ticket": "TCK-1", "ok": true, "status": 200},
  {"ticket": "TCK-2", "ok": false, "status": 422, "code": "ticket_transition_denied", "message": "…"}
]}
```

Zlyhanie validácie pri jednom tikete hlási code validation\_failed; každé iné zlyhanie nesie kód z tabuľky chýb (ticket\_not\_found pre neznáme číslo alebo tiket, ktorý používateľ vidieť nesmie). assign bez assignee a bez claim: true je 422 pre celú požiadavku.

## Telá požiadaviek

| Endpoint | Telo |
| --- | --- |
| `PATCH /tickets/{ticket}` | Ktorékoľvek z subject (≤ 255), description (≤ 65535, nullable), priority, category\_id (nullable), due\_at (po teraz, nullable), requester ({type, id}) — zmení sa len to, čo pošlete; meta hostiteľa sa cez API zapisovať nedá. |
| `POST …/transitions/{transition}` | {reason?: string ≤ 5000, payload?: object}; {transition} zodpovedá \[a-z\]\[a-z0-9\_\]{0,63}. |
| `POST …/replies` | {body: string ≤ 65535, channel?, attachments\[\]?, transition?, reason?, payload?} |
| `POST …/notes, POST /tickets/guest/{ticket}/replies` | {body, channel?, attachments\[\]?} |
| `PATCH …/comments/{comment}` | {body} |
| `GET …/comments` | ?internal=1 pre interné vlákno (agenti). |
| `PUT …/fields` | {fields: {key: value, …}} (present; null hodnotu zruší). |
| `POST …/watchers` | {watcher: {type, id}}; DELETE …/watchers/{alias}:{key}. |
| `PUT …/tags` | {tags: \[name, …\]} (present, ≤ 50, každý ≤ 64) — nahradí celú sadu. |
| `POST …/links` | {ticket: reference, type: relates\_to \| duplicates \| blocks \| parent\_of} |
| `POST …/merge` | {target: reference, reason?: string ≤ 5000} |
| `POST …/attachments` | Multipart file. |
| `GET /tickets` | per\_page (1 – max\_per\_page), page, filter\[…\], sort |

Uploady (attachments\[\], file) sa cez media-library riadia mime typmi a limitom veľkosti svojej skupiny — tickets.attachments.\* pre súbory tiketu, comments.media.\* z balíka comments pre súbory správ — najviac 10 súborov na požiadavku.

## Admin endpointy

Preložiteľný text (name, description, label, help, popis možnosti) je reťazec v aktuálnom jazyku alebo mapa jazykov (objekt s kľúčmi en, sk, …). Polymorfné vstupy sú {type: alias, id}; filtre zoznamov berú alias:key.

| Endpoint | Telo / query |
| --- | --- |
| `GET /ticket-categories` | ?project=alias:key — globálny strom plus kategórie projektu, najprv korene, potomkovia vnorení. |
| `POST /ticket-categories` | {name, description?, parent\_id?, project?: {type, id}, position?, is\_active?, default\_priority?, default\_assignee?: {type, id}, default\_due\_in?: ISO-8601 (PT4H)} |
| `PATCH /ticket-categories/{id}` | Ktorékoľvek z name, description, position, is\_active, default\_priority, default\_assignee, default\_due\_in (null zruší popis alebo predvolenú hodnotu); rodič sa mení cez /move, projekt sa nemení nikdy. |
| `POST /ticket-categories/{id}/move` | {parent\_id: id \| null, position?} (parent\_id musí byť prítomné; null = do koreňa). |
| `DELETE /ticket-categories/{id}` | {reassign\_to?: id} — potrebné, kým kategóriu používajú živé tikety. |
| `GET /ticket-fields` | ?project=alias:key, ?category=id (sada, ktorú by dostal tiket v tejto kategórii). Neagenti vidia len aktívne polia viditeľné pre žiadateľa. |
| `POST /ticket-fields` | {key, type, label, help?, options?: {value: label}, rules?: \[string\], required?, encrypted?, default?, requester\_visible?, requester\_editable?, position?, is\_active?, project?: {type, id}, category\_id?} — najviac 200 možností a 20 pravidiel. |
| `PATCH /ticket-fields/{id}` | Ktorékoľvek z type, label, help, options, rules, required, encrypted, default, requester\_visible, requester\_editable, position, is\_active (key, projekt a kategória sú pevné). |
| `GET /ticket-tags` | ?project=alias:key — globálne štítky plus štítky projektu, podľa názvu (agenti). |
| `POST /ticket-tags` | {name: string ≤ 64, color?: #rrggbb, project?: {type, id}} — opätovné vytvorenie zmazaného názvu ho obnoví, bez tiketov. |
| `PATCH /ticket-tags/{id}` | Ktorékoľvek z name, color (null ju zruší). |

## Resources

Podľa roly: divák je agent (oprávnenie agenta a rozsah agentov mu tiket povoľujú), žiadateľ, účastník (sledujúci) alebo hosť (cez podpísaný odkaz).

| Resource | Každý, kto smie vidieť | Agenti navyše |
| --- | --- | --- |
| `TicketResource` | reference, number, subject, description, status, priority, category ({id, name, slug}), project ({type, id}), requester, assignee, channel, due\_at, resolved\_at, closed\_at, merged\_into (číslo tiketu), messages\_count (verejné správy), created\_at, updated\_at; detailné odpovede pridajú fields (viditeľné pre žiadateľa) a attachments. | assigned\_at, first\_responded\_at, last\_activity\_at, tags ({name, slug, color}), úplný žiadateľ, všetky správy v počte, všetky polia. |
| `TicketCommentResource` | id, body, role, author, channel, attachments, created\_at, updated\_at | internal, status (stav moderácie z comments). |
| `TicketActivityResource` | id, type, label, system, actor, properties (typy morph ako aliasy), occurred\_at — len verejné záznamy. | visibility, interné záznamy, surové properties. |
| `TicketLinkResource` | id, type, label, direction (outgoing / incoming), ticket {reference, subject, status} — len ticket {reference}, ak divák druhý tiket vidieť nesmie. | — |
| `TicketAttachmentResource` | id (uuid), name, mime\_type, size, url, created\_at | — |
| `TicketGuestResource` | {type: 'guest', name} — meno len pre samotného hosťa. | {type: 'guest', id, name, fields} — všetky polia hosťa; osobné údaje zostávajú agentom. |
| `TicketCategoryResource` | id, name, description, slug, parent\_id, project, position, is\_active, children | translations {name, description} (mapy jazykov), defaults {priority, assignee, due\_in}. |
| `TicketFieldResource` | id, key, type, label, help, options (\[{value, label}\], aktuálny jazyk), required, category\_id, project, position. | translations {label, help}, rules, default, encrypted, requester\_visible, requester\_editable, is\_active |
| `TicketTagResource` | id, name, slug, color, project (dostanú sa k nemu len agenti). | — |

Osoby (žiadateľ, riešiteľ, autor, aktér, sledujúci) sa zobrazujú svojím aliasom z povolených zoznamov, nikdy názvom triedy: agenti dostanú {type, id, name}; ostatní {type, name}, pričom name je null pre hosťa, ktorý nie je samotným divákom. Žiadatelia nikdy neuvidia interné poznámky, zadržané odpovede, interné aktivity, last\_activity\_at, štítky ani zoznamy sledujúcich a meta hostiteľa sa neobjaví v žiadnom resource. Hodnoty enumov prichádzajú s preloženým popisom a farbou, takže klient vykreslí odznak bez vlastných tabuliek:

```json
"status":   {"value": "pending", "label": "Waiting on requester", "category": "waiting", "color": "amber"},
"priority": {"value": "high", "label": "High", "weight": 30, "color": "orange"},
"channel":  {"value": "web", "label": "Web", "color": "blue"}
```

## Telá odpovedí

| Endpoint | Telo |
| --- | --- |
| `GET /tickets` | Stránkovaná kolekcia TicketResource (data, links, meta). |
| `POST /tickets (201), GET / PATCH /tickets/{ticket}, POST …/restore, POST …/transitions/{transition}, PUT / DELETE …/assignee` | Detailný TicketResource. |
| `GET …/transitions` | {data: \[{name, label, to, to\_label, requires\_reason, payload}\]} |
| `GET …/comments` | Kolekcia TicketCommentResource, od najstaršej. |
| `POST …/replies, POST …/notes (201), PATCH …/comments/{comment}` | TicketCommentResource. |
| `GET / PUT …/fields` | {data: \[{key, label, type, value}\]} — platné polia v poradí (pre neagentov len viditeľné pre žiadateľa); value je null, ak nie je nastavená. |
| `GET / POST …/watchers (201)` | {data: \[person, …\]} — všetci sledujúci od najstaršieho; neagent, ktorý pridá seba, dostane len vlastný záznam. |
| `PUT …/tags` | Štítky tiketu, kolekcia TicketTagResource. |
| `GET / POST …/links (201)` | {data: \[TicketLinkResource, …\]} — všetky prepojenia z pohľadu tohto tiketu. |
| `POST …/merge` | {source: TicketResource, target: TicketResource, moved: {comments, attachments}} |
| `GET …/attachments, GET …/attachments/{media}, POST …/attachments (201)` | TicketAttachmentResource (kolekcia). |
| `GET …/activity` | Kolekcia TicketActivityResource od najstaršieho (\[\] pri vypnutom tickets.activity.enabled). |
| `POST /tickets/bulk` | {data: \[{ticket, ok, status, code?, message?}\]} |
| admin endpointy | TicketCategoryResource (strom pre index), TicketFieldResource, TicketTagResource; store vráti 201. |
| každé iné DELETE, POST / DELETE …/watch | 204, bez tela. |

## Zoznam tiketov — filtre, radenie, stránkovanie

GET /tickets je query-builder-for-laravel nad visibleTo($user); každý filter je jeden zo scopes modelu Ticket:

| Filter | Hodnota | Scope |
| --- | --- | --- |
| `filter[status]` | zoznam hodnôt stavov | `withStatus` |
| `filter[status_category]` | zoznam: open, active, waiting, resolved, closed | `withStatusCategory` |
| `filter[priority]` | zoznam | `withPriority` |
| `filter[category]` | zoznam id; filter\[category\_descendants\]=1 pridá podkategórie | `inCategory` |
| `filter[assignee]` | zoznam alias:key, me, none | `assignedTo / unassigned` |
| `filter[requester]` | zoznam alias:key (agenti; ktokoľvek iný nájde len seba), me | `requestedBy` |
| `filter[project]` | alias:key, none | `inProject` |
| `filter[tag]` | zoznam slugov — len agenti (pre ostatných 400) | `taggedWith` |
| `filter[watching]` | me | `watchedBy` |
| `filter[overdue]` | 1 (0 nepridá nič) | `overdue` |
| `filter[due_from], filter[due_to], filter[created_from], filter[created_to]` | dátum alebo dátum a čas (inkluzívne; \*\_to len s dátumom, napríklad 2026-10-17, platí do konca dňa) | `dueFrom / dueTo / createdFrom / createdTo` |
| `filter[channel]` | zoznam | `viaChannel` |
| `filter[merged]` | boolean | `merged` |
| `filter[search]` | text — presne číslo tiketu, alebo predmet / popis (% a \_ doslova) | `search` |
| `filter[fields.<key>]` | zoznam | `whereField` |
| `filter[fields.<key>.from], filter[fields.<key>.to]` | polia number / decimal / date / datetime (inkluzívne) | `whereFieldBetween` |
| `filter[trashed]` | with, only — len držitelia oprávnenia manage | — |

Filtre len pre agentov — tag, requester=alias:key a pole, ktoré nie je viditeľné pre žiadateľa — nájdu len tikety, na ktorých diváka nechá pracovať rozsah agentov. Neznámy filter alebo radenie vráti 400 pri query-builder.mode.unknown\_filter = reject (predvolené); ignore ich zahodí.

Radenie: created\_at, updated\_at, due\_at, number, priority (váhy enumu) a pre agentov last\_activity\_at. Predvolene -last\_activity\_at pre agentov a -created\_at pre ostatných. Stránkovanie: per\_page (predvolene 20, najviac tickets.routes.max\_per\_page) a page; zoznam načíta kategóriu, riešiteľa, žiadateľa, štítky a počty správ vopred, v pevnom počte dopytov.

## Chyby

Mapujú sa v kontroléroch balíka — nikdy vo vašom exception handleri — na {message, code}. Chyby konfigurácie zostávajú 500: opraviť ich musí hostiteľ, nie klient.

| Stav | code | Kedy |
| --- | --- | --- |
| 400 | (query-builder) | Neznámy filter alebo radenie. |
| 403 | `forbidden` | Zamietnutie policy; UnauthorizedCommentActionException z comments (pri zapnutom comments.authorization). |
| 403 | `access_denied` | Nie je účastníkom tiketu. |
| 403 | `guest_link_invalid` | Akýkoľvek nefunkčný odkaz pre hosťa — jedno telo pre všetky. |
| 404 | `ticket_not_found` | Neznáme číslo, alebo tiket, ktorý používateľ vidieť nesmie (skrytý = neexistujúci); na každom endpointe tiketu sa najprv overí view. |
| 404 | `comment_not_found, attachment_not_found, link_not_found, category_not_found, field_not_found, tag_not_found` | Záznam, ktorý nepatrí tomuto tiketu, alebo neznámy. |
| 409 | `ticket_closed` | Správa na uzavretom tikete (alebo zámok z comments). |
| 409 | `ticket_trashed` | Zmena zmazaného tiketu. |
| 409 | `field_type_locked, duplicate_reference` | Uzamknutý typ poľa; duplicitný prefix projektu. |
| 422 / 429 | `ticket_transition_denied` | Lifecycle prechod odmietol (s denials); 429 s Retry-After, keď zamietnutie uvádza, kedy to skúsiť znova. |
| 422 | `unknown_transition, unknown_field, field_not_editable, unknown_*_type` | — |
| 422 | `invalid_assignee, invalid_link, invalid_merge, invalid_category, invalid_field, invalid_tag, invalid_watcher, invalid_project` | — |
| 422 | `comment_rejected, invalid_comment_body` | comments text odmietol. |
| 422 | validačné telo Laravelu | Validácia form requestu alebo hodnôt polí — vstupy pomenované v jazyku požiadavky; polia hosťa pod guest.<name>. |

## 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 kryptomien](https://roundly-consulting.com/sk/podporte-nas.md)

Odoslaním daru súhlasíte s našimi [podmienkami prijímania darov](https://roundly-consulting.com/sk/podmienky-darov.md).

[Podporte našu open-source prácu (otvorí sa v novej karte)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Pridajte sa k nám na Patreone (otvorí sa v novej karte)](https://www.patreon.com/cw/roundly)

## 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.

[Ponuka do 48 hodín](https://roundly-consulting.com/sk/kontakt.md) [Prezrieť všetky balíky](https://roundly-consulting.com/sk/open-source/navody/tickets-for-laravel.md)
