Files
csbot-prototype/documentation/04-api.md
T
JiriUhlirandClaude Opus 5 a771834e57 Realne sluzby, OpenAI, odesilani e-mailu a helpdesk
Katalog srovnany s tim, co opravdu bezi na services.csbot.cz/apps:
trinact sluzeb dostalo pristupove udaje a levne cteci overeni, opravena
appId, ktera nikam nevedla (ppl, microsoft365, transcription), a GA4,
Search Console, Google Ads i Sklik ted stoji na aplikaci analytics,
kazda s vlastnimi udaji. Nove sluzby SAP Business One, Google Workspace
a Meta Ads. K tomu 23 skriptu, ktere s nimi opravdu neco delaji.

OpenAI jako prvni sluzba, ktera nebezi u nas: Service.baseUrl s absolutni
adresou, prepis pres <SLUZBA>_BASE_URL nebo adresu u konektoru, predpona
hlavicky u pole udaju (uzivatel vlepi holy klic, Bearer dopise runtime).
Dotaz na model, nahrani souboru, otazka nad souborem, prepis zvuku.
Skript umi odeslat soubor pres ctx.http.postForm (multipart, obsah Base64).

Sluzba E-mail pres SMTP. Neni to skript, ale vnitrni krok - SMTP neni HTTP.
Konektor nese schranku firmy, krok ma HTML telo, ve kterem se dosazene
hodnoty escapuji (znacky autora sablony jsou zamer, ostre zavorky od
zakaznika ne). Overeni konektoru se prihlasi na server a nic neodesle.

Helpdesk: Ticket.helpdeskSourceId drzi firmu, ktera pozadavek poslala,
vlastnikem zustava ta, ktera ho resi - jinak by ho resitel nemel ve sve
fronte. Komu pozadavek pripadne, urcuje Tenant.helpdeskProviderId.
Zadavatel vidi jen svoje pozadavky a smi k nim pripsat komentar.

Opravy v portalu:
- hlasky o ulozisti a odchozi IP vidi jen spravce platformy
- typ ticketu se v automatizaci vybira ze seznamu firmy, nebo dosadi z dat
- stav ticketu je otevreny naseptavac, ne ciselnik
- ticket jde zalozit rucne, zakaznik u nej neni povinny
- kanal se prejmenoval a parametry u webhooku jsou oznacene jako nepovinne
- srovnane markdown tabulky v cele dokumentaci

Co z teto davky jeste neni: prepinac firmy je porad jen stav uvnitr stranky
Prehled, takze se prepnuti neprojevi v Lidech ani jinde.

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

16 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
POST /api/dashboard/tickets/:id/claim
GET /api/dashboard/tickets/:id/actions
POST /api/dashboard/tickets/:id/actions/:actionId
GET /api/dashboard/invites
POST /api/dashboard/invites
DELETE /api/dashboard/invites/:id
GET /api/invites/:kod
POST /api/invites/:kod/accept
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.

POST /api/dashboard/tickets/:id/claim je prevzeti prace, ne prehozeni: volajici si bere ticket sam a telo je prazdne. Smi to u ticketu bez resitele a u ticketu ve skupine, ve ktere je. Kdyz uz ticket nekdo resi, vraci 409, resp. 403 u cizi skupiny - vzit nekomu rozdelanou praci je jine rozhodnuti a chce to pravo ticket.assign.others. Kdo neni vedeny jako resitel, dostane 400.

Pozvanky do firmy

/api/invites/:kod je verejne, protoze kdo prijde za odkazem, jeste ucet mit nemusi. Autorizuje kod v adrese, proto je nahodny a dlouhy - stejne jako u webhooku. Odpoved je zamerne uzka: nazev firmy, jestli pozvanka plati, a kdyz uz adresu zname, tak ji, at ji clovek nemusi psat. Nic o tom, kdo ve firme je.

POST /api/invites/:kod/accept s telem {"name", "email", "password"}:

Situace Co se stane
ucet neexistuje zalozi se a pripoji k firme
ucet existuje, heslo sedi jen se pripoji k firme
ucet existuje, heslo nesedi 401
pozvanka je na jinou adresu 403
pozvanka uz byla pouzita nebo vyprsela 409 s konkretnim duvodem

Overeni hesla u existujiciho uctu neni formalita: bez nej by kdokoliv s odkazem pripojil cizi adresu ke sve firme a videl by jeji data.

Sprava pozvanek (/api/dashboard/invites) chce pravo user.manage. Seznam vraci u kazde pozvanky celou adresu vcetne prefixu proxy, aby slo rovnou kopirovat - relativni cesta se do zpravy vlepit neda.

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.