Files
csbot-prototype/documentation/04-api.md
T
JiriUhlirandClaude Opus 5 a57eca123e Fronta a worker: webhook odpovi hned, praci udelaji workeri
Webhook uz nic nevykonava v requestu. Zapise udalost do fronty a odpovi 202
do jednotek milisekund; strom vykona worker na pozadi. Za konektory nerucime,
takze cekat na cizi sluzbu v requestu znamena ztracet udalosti pri timeoutu.

Fronta ma opakovani s rostouci prodlevou (30 s, 2 min, 10 min, hodina),
spravedlive poradi po firmach (jedna firma s tisicem udalosti nezablokuje
ostatni), navrat zaseknutych behu po restartu a uklid hotovych. Marna chyba
se neopakuje - chybejici skript za minutu existovat nezacne.

Tri druhy spoustecu: push (webhook), vnitrni udalost (vznik a zmena ticketu)
a pull, tedy pravidelne dotazovani u sluzeb bez webhooku (posta, zpravy).
Planovac jen rekne "je cas", samotny dotaz je prvni krok stromu, takze ma
zaznam v logu a opakuje se pri chybe jako cokoliv jineho.

Kontrakt tela webhooku: kazdy parametr ma cestu (data.order.id,
errors.0.message), takze jde napojit i odesilatel s vnorenym modelem.
U adresy je metoda, ukazka tela a kopiruje se cela adresa vcetne domeny.

Vnitrni kroky, ktere sahaji do naseho uloziste: ticket/upsert (zaloz nebo
dopln podle externiho ID), assign-least-busy, assign-by-external, set-type,
set-stage, add-tags, set-status, incident/create, flow/pause a flow/log.

Faze ticketu jako treti osa vedle stavu a stitku. Stav je zivotni cyklus
a pocitaji se z nej statistiky, faze je workflow daneho typu a muze byt jen
jedna, takze se na ni da spolehnout v podmince.

ID z cizich aplikaci u resitele: voicebot posle voicebotId a ticket skonci
u toho, komu patri. Vazba je na jednom miste, ne v kazde automatizaci.

Kazda chyba zaklada incident se dvema urovnemi: impact cte klient a je
srozumitelny, detail cte admin a je v nem cely beh, ktery krok selhal, cele
hlaseni a data na vstupu. Detail vidi jen spravce platformy.

Ochrana proti smycce: automatizace navazana na zmenu ticketu ticket meni,
cimz se spousti znovu - pri vyvoji to server polozilo. Resi to oznaceni behu
pres AsyncLocalStorage a strop peti behu na jeden ticket za minutu.

Upozorneni pri prideleni prace vcetne cisla u zalozky Tickety. Zivy dashboard:
dlazdice nad nasimi daty na udalost, data z konektoru podle ttlSec s moznosti
vynutit nacteni znovu.

Opraveno: path a intervalSec u spoustece se pri ulozeni zahazovaly; nad
seznamem neslo pouzit contains, takze na stitky neslo postavit podminku;
novejsi vystup kroku ted prekryje starsi misto hlaseni konfliktu.

Overeno dvema scenari proti bezicimu serveru, 34 kontrol: firma se skladem,
expedici a IT, a hovory z voicebota (callSid do externiho ID, status do faze,
prirazeni podle voicebotId, tri zpravy = jeden ticket se tremi udalostmi).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 16:41:02 +02:00

14 KiB

04 - API

Interaktivni dokumentace je na /apps/<app-id>/docs. Tenhle soubor popisuje to, co ze Swaggeru neni videt.

Endpointy

Verejne:

Metoda Cesta Popis
GET /health liveness, nezavisi na databazi
GET /health/ready readiness, 503 pri nedostupne databazi
GET /docs Swagger UI
GET /openapi.json OpenAPI definice
POST /api/auth/login prihlaseni, vraci JWT
POST /api/contact poptavka z webu
POST /webhook/:token prijem dat do automatizace
POST /webhook/ticket/:token prijem udalosti do ticketu
GET /webhook/ticket/:token napoveda k prijmu

Vyzaduji Authorization: Bearer <token>:

Metoda Cesta
GET /api/auth/me
POST /api/auth/logout
GET /api/dashboard/access
GET /api/dashboard/widgets
GET /api/dashboard/layout
PUT /api/dashboard/layout
DELETE /api/dashboard/layout
GET /api/dashboard/summary
GET /api/dashboard/people
GET /api/dashboard/people/:id
GET /api/dashboard/intake
POST /api/dashboard/intake/regenerate
GET /api/dashboard/settings/actions/:id/scope
GET /api/dashboard/notifications
POST /api/dashboard/notifications/read
GET /api/dashboard/runs
GET /api/dashboard/tickets
GET /api/dashboard/tickets/workload
GET /api/dashboard/tickets/:id
POST /api/dashboard/tickets/:id/assign
POST /api/dashboard/tickets/:id/status
POST /api/dashboard/tickets/:id/comment
POST /api/dashboard/tickets/:id/type
POST /api/dashboard/tickets/:id/tags
POST /api/dashboard/tickets/:id/group
GET /api/dashboard/tickets/:id/actions
POST /api/dashboard/tickets/:id/actions/:actionId
GET /api/dashboard/incidents
GET /api/dashboard/storage
GET /api/dashboard/services
GET /api/dashboard/connectors/services
GET /api/dashboard/connectors
POST /api/dashboard/connectors
GET /api/dashboard/connectors/:id
PATCH /api/dashboard/connectors/:id
DELETE /api/dashboard/connectors/:id
POST /api/dashboard/connectors/:id/test
GET /api/dashboard/scripts
GET /api/dashboard/scripts/:id
PUT /api/dashboard/scripts/:id
POST /api/dashboard/scripts/:id/test
POST /api/dashboard/scripts/reload
GET /api/dashboard/stream
GET /api/dashboard/automations
POST /api/dashboard/automations
GET /api/dashboard/automations/:id
PUT /api/dashboard/automations/:id
DELETE /api/dashboard/automations/:id
POST /api/dashboard/automations/:id/webhook/regenerate
POST /api/dashboard/widget-data
GET /api/dashboard/widget-data/options
GET /api/dashboard/settings/catalog
GET /api/dashboard/settings/features
GET /api/dashboard/settings/people-overview
GET /api/dashboard/settings/users-overview
PATCH /api/dashboard/settings/users/:id/password
POST /api/admin/impersonate
POST /api/admin/impersonate/stop
GET /api/admin/impersonate/candidates
GET /api/admin/audit

Sprava zaznamu ma u kazde entity stejnou petici (seznam, detail, vytvoreni, uprava, mazani) na /api/dashboard/settings/<entita>, protoze ji dela jedna fabrika (src/routes/crud.ts):

tenants, users, roles, people, groups, ticket-types, actions, widgets, features.

Format chyb

Jednotny pro cele API:

{ "error": "validation_error", "message": "Zadejte platny e-mail." }
HTTP error Kdy
400 validation_error vstup neprosel schematem
401 unauthorized chybi nebo neplatny token
401 invalid_credentials spatny e-mail nebo heslo
403 forbidden nedostatecna role
404 not_found zaznam nebo endpoint neexistuje
409 ruzne operace nedava v danem stavu smysl
500 internal_error neodchycena chyba, detail jen mimo produkci

message je vzdy cesky a je urcena k zobrazeni uzivateli.

Autentizace

Hesla se hashuji bcryptem, plaintext se nikde neuklada. Login vraci JWT podepsany JWT_SECRET s platnosti JWT_EXPIRES_IN.

Spatne heslo i neexistujici e-mail vraci stejnou odpoved, aby se neprozradilo, ktere ucty existuji. Pokus se loguje bez hesla.

Token si drzi klient v localStorage. Pro produkci je cilovy stav httpOnly cookie se Secure a SameSite plus CSRF token.

Zivy stream

GET /api/dashboard/stream je Server-Sent Events. Po pripojeni posle potvrzeni a poslednich par udalosti, pak uz jen nove. Kazdych 25 sekund jde komentarovy radek, aby spojeni neuspalo proxy.

Typy udalosti: ticket.created, ticket.updated, ticket.assigned, ticket.resolved, incident.started, incident.updated, incident.resolved, automation.created, automation.updated, automation.deleted, automation.run, webhook.received.

Klient se pripojuje pres fetch s hlavickou Authorization, ne pres EventSource. Duvod je v 03-architektura-a-mapa-kodu.md.

Webhook

Verejny endpoint bez prihlaseni. Autorizuje neuhodnutelny token v adrese, 32 znaku z randomBytes(24) v base64url.

Token generuje vyhradne server. Hodnota webhookToken poslana klientem se ignoruje, jinak by si sel nastavit predvidatelnou adresu.

Situace Odpoved
vse v poradku 202
neznamy token 404
automatizace je pozastavena 409
chybi povinny parametr, spatny typ 400

Parametry navic se neodmitaji, jen loguji. Odesilatele bezne posilaji i vlastni data a odmitat je by rozbijelo integrace.

curl -X POST https://services.csbot.cz/apps/<app-id>/webhook/<token> \
  -H "Content-Type: application/json" \
  -d '{"customer":"Nordis","score":18}'

Prototyp pozadavek prijme, zvaliduje a zapocita do metrik, ale strom akci nevykona - runtime neexistuje.

Firmy a pohledy

Prava popisuje 07-firmy-a-prava.md, tady jen API.

Endpointy dashboardu berou scope (all, tenant, mine) a tenantId. GET /api/dashboard/access rekne, co uzivatel smi, aby to klient nedovozoval.

Pozadavek na pohled nebo firmu bez opravneni vraci 403 nebo 404, nikdy tise zuzeny vysledek. Uzivatel nesmi koukat na cizi cisla v domneni, ze jsou spravna.

Tickety

Popis modelu je v 06-tickety.md, tady jen to, co se tyka API.

GET /api/dashboard/tickets bere navic filtry assignee, status, channel. U assignee je zvlastni hodnota unassigned pro frontu bez resitele. U pohledu mine se assignee ignoruje, pohled je silnejsi. Neznama hodnota filtru se zaloguje a ignoruje - je lepsi ukazat vic ticketu nez prazdny seznam bez vysvetleni.

Odpoved nese vedle items jeste meId. Klient podle nej pozna, ktere tickety jsou jeho, a jestli ma vubec smysl nabizet filtr "moje".

Filtrovani dela server, ne klient. Seznam a prehled vytizeni tak nikdy neukazuji jina cisla. Vyhledavaci pole v portalu je jina vec - to jen dohledava v uz nactenem seznamu.

GET /api/dashboard/tickets/:id vraci navic trace, tedy log prubehu vcetne toho, co ktera volana sluzba vratila.

POST /api/dashboard/tickets/:id/assign s telem {"assigneeId": null} vrati ticket do fronty. Neznamy resitel vraci 404, ne tiche odpojeni.

Akce na ticketu

Popis modelu je v 09-navrh-rozsireni.md.

GET /api/dashboard/tickets/:id/actions vraci jen akce, ktere v teto situaci opravdu jdou spustit: sedi typ nebo tag, projdou podminky a volajici na ne ma pravo. Klient si nefiltruje nic - jinak by se to pocitalo na dvou mistech a jednou se to rozejde.

POST /api/dashboard/tickets/:id/actions/:actionId vraci 200 i kdyz akce selhala. Selhani akce neni chyba API. V odpovedi je ok, summary, detail s celym chybovym hlasenim a durationMs. Cely prubeh se zapise do logu ticketu.

Vestavene akce (type, tags, group, assign, status, comment) jsou zvlast: meni ticket sam, ne cizi sluzbu, a kazda ma vlastni pravo.

Prijem udalosti do ticketu

Popis modelu je v 18-ticketovaci-system.md.

POST /webhook/ticket/:token je verejny, autorizuje token firmy v adrese. Vraci 201 kdyz ticket vznikl, 200 kdyz se udalost navesila na existujici:

{ "ok": true, "created": false, "ticketId": "TK-4822", "externalId": "3", "eventId": "tev_2" }

externalId je unikatni v ramci firmy. Token urcuje firmu, takze dve firmy mohou obe poslat objednavku cislo 3 a nedojde ke smichani. Cislo i retezec jsou tentyz klic.

Neznamy typeId se zahodi a zaloguje, ticket vznikne bez typu. Odmitnout celou udalost kvuli jednomu poli by znamenalo ztratu dat.

Fronta behu

Popis je v 20-fronta-a-runtime.md.

POST /webhook/:token vraci 202, ne 200: data jsme prevzali a strom se vykona na pozadi. Vysledek se hleda v GET /api/dashboard/runs nebo v logu ticketu. Cekat na cizi sluzbu v requestu nejde - za jeji rychlost nerucime a odesilateli by vyprsel timeout.

GET /webhook/:token vraci kontrakt: co se v tele ceka, na jakych cestach a ukazku. Bez toho by musel ten, kdo webhook zapojuje, hadat.

GET /api/dashboard/runs ma u kazdeho behu cele chybove hlaseni, pocet pokusu a kdy se to zkusi znovu.

Prava a navigace

GET /api/dashboard/access vraci permissions (efektivni prava po slouceni roli), nav (zalozky, ktere ma volajici videt) a platformAdmin. Klient podle toho kresli, ale nic si nedovozuje - kdo co smi, rozhoduje server u kazdeho requestu znovu.

GET /api/dashboard/settings/catalog vraci katalog prav a modulu, aby formular role nemel seznam prav napsany v kodu klienta.

Prepnuti na jiny ucet

POST /api/admin/impersonate vraci novy token s narokem act (kdo se za koho vydava) a writes. Bez writes middleware odmitne cokoliv jineho nez GET s 403. Kazde prepnuti i ukonceni je v auditu vcetne toho, kdo to byl doopravdy.

Svuj puvodni token si klient odklada do sessionStorage, server o nem nic nevi.

Sluzby a konektory

Popis modelu je v 12-sluzby-a-konektory.md, tady jen API.

Sluzba je to, co umime. Konektor je napojeni jedne firmy vcetne jejich pristupovych udaju.

Hodnoty pristupovych udaju se nikdy nevraci, jen filled a missing. V PATCH staci poslat jen to, co se meni: prazdny retezec hodnotu smaze, chybejici klic ji nechava.

Sluzba, kterou uzivatel nevidi, se nevraci vubec, ne se stavem 403.

POST /connectors/:id/test vraci 200 i pri neuspechu. checked rika, co se vlastne overilo - u sluzby bez verifyPath jen dostupnost, ne udaje.

Skripty konektoru

Popis modelu je v 11-skripty-konektoru.md, tady jen API.

Cteni smi kazdy prihlaseny, protoze builder potrebuje vedet, co skript umi. Uprava, zkusebni spusteni a vynucene nacteni smi jen spravce platformy - uprava skriptu meni chovani vseho, co ho pouziva.

GET /api/dashboard/scripts vraci vedle manifestu i problems s rozbitymi skripty a connections se stavem napojeni. Hodnoty pristupovych udaju se nevraci nikdy, jen jmena chybejicich environment variables.

PUT /api/dashboard/scripts/:id kod nejdriv nacte a overi a az pak prepise soubor. Rozbita uprava vraci 400 s issues a puvodni skript dal funguje.

POST /api/dashboard/scripts/:id/test vola opravdovou sluzbu. Chyba skriptu neni chyba API, vraci se 200 a popis v error vcetne toho, jestli ma smysl zkusit to znovu.

Pri pridani endpointu

Soucasne aktualizovat src/openapi.ts a tenhle soubor. Swagger musi odpovidat skutecnemu chovani aplikace, jinak je horsi nez zadny.