REST API
Malé, záměrně úzké API na čtení a úpravy map z vlastního kódu. Úkol je uzel s řešitelem nebo termínem — žádný samostatný úkolový objekt neexistuje. Je to tatáž plocha, nad kterou stojí MCP server.
Hledáte hotový příklad?
Napojení na Google Sheets je celý recept krok za krokem — kód pro Apps Script i pro n8n, včetně ošetření konfliktu 409.
- Platí pro cloud i vlastní server — hostovaná instance (
vasefirma.killbottleneck.com) má stejné API jako instalace u vás; liší se jen adresa - Základní cesta:
/api/kb/v1— starší prefix/api/flowmap/v1míří na tytéž handlery - Autentizace: API klíč v hlavičce
Authorization. Nikdy ne přes session cookie ani JWT - Formát: JSON dovnitř, JSON ven
- Jazyk: záměrně jen anglicky
Autentizace
Klíč si vytvoříte v aplikaci pod uživatelským menu. Token v čitelné podobě se ukáže jednou — ukládá se jen jeho SHA-256 otisk, takže ztracený klíč nejde získat zpět, jen rotovat.
Authorization: Bearer kb_user_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXKlíče vydané před přejmenováním začínají fm_user_ a fungují dál.
Veřejné API je jen /api/kb/v1/*
Samotná aplikace mluví s řadou dalších rout /api/kb/* (/my-day, /portfolio, /export, /import-all, /share, …). Ty jsou interní, ověřují se přihlášením v prohlížeči a API klíč na nich dostane 401. Popsané jsou jako funkce, ne jako endpointy, a mezi verzemi se mohou bez ohlášení změnit.
Rozsahy
| Rozsah | Smí volat |
|---|---|
read | všechny GET endpointy |
read_write | všechno včetně POST endpointů |
Klíč s rozsahem read dostane na zápisovém endpointu 403. Výchozí rozsah nového klíče je read.
Co klíč nikdy nemůže
- Vlastník se bere z klíče, nikdy z těla požadavku. Klíč jedná za svého vlastníka: dosáhne přesně na mapy, které vlastník vidí v aplikaci — vlastní, týmové a sdílené jemu — a zapisuje podle úrovně sdílení (
owner/edit= plný zápis;workiread= jenstatusvlastních uzlů přesPOST …/nodes/{nodeId}, přesně jako odškrtnutí v aplikaci; cokoli jiného → 403).GET /v1/mapsaGET /v1/maps/{id}úroveň vrací jakoaccess. - Klíč nemůže eskalovat. Server pro něj nikdy nezakládá session a nečte roli účtu, takže přes API klíč nejdou dělat správcovské věci — klíč admina nevidí cizí soukromou mapu a klíč s rozsahem
readnezapíše nikdy, ať je úroveň sdílení jakákoli. - Mapa, kterou vlastník nevidí, vrátí 404, ne 403 — API nepotvrzuje, že nějaké ID existuje. Cizí veřejná mapa je také 404: veřejná vývěska není pracovní přístup.
- Přiřazení
ownerpřes API nasdílí mapu tomu člověku jako spolupracovníkovi (work), stejně jako to dělá aplikace (nikdy nesnižuje, externí kontakty se nesdílejí) — ale jen když vlastník klíče vůbec smí sdílet (vlastník mapy nebo jmenovaný editor; týmový editor přiřadí bez sdílení, jako v aplikaci). Odpověď vsharedříká, komu se nasdílelo. - Pravidla a jejich log běhů (
GET …/rules,…/rule-runs) vidí jen kdo má právo upravovat, jako v aplikaci; čtenář a spolupracovník dostanou 403.
Limity
| Limit | Hodnota |
|---|---|
Požadavků za minutu, read | 120 na klíč |
Požadavků za minutu, read_write | 30 na klíč |
| Tělo požadavku | 2 MB |
| Položek na jedno volání (uzly) | 200 |
| Klíčů na účet | 20 |
GET /v1/portfolio | 60 za minutu na uživatele, navíc k limitu na klíč |
Čtení a zápis se počítají zvlášť, takže hromadné čtení nevyhladoví vaše zápisy. Přes limit dostanete 429.
Optimistické zamykání
Každý endpoint, který mění existující mapu, vyžaduje base_updated — hodnotu updated, kterou jste dostali při posledním načtení mapy.
GET /api/kb/v1/maps/{id} → { "updated": "2026-07-30 08:12:44.031Z", … }
POST /api/kb/v1/maps/{id}/nodes ← { "base_updated": "2026-07-30 08:12:44.031Z", … }Když ji vynecháte, dostanete 400. Když pošlete starou, dostanete 409 — někdo mapu mezitím změnil. Načtěte mapu znovu, znovu aplikujte svou změnu a zkuste to znovu.
Je to záměr: znemožňuje to „zapsat bez přečtení“, což je přesně ta chyba, kterou dělá překotný AI asistent.
Endpointy
GET /api/kb/v1/maps
Vypíše mapy, které vlastník klíče vidí — vlastní, týmové i sdílené jemu. Každá položka nese access: owner, edit, work nebo read.
| Parametr | Význam |
|---|---|
archived=1 | místo aktivních vypíše archivované |
{
"maps": [
{ "id": "abc123", "title": "Nový web", "node_count": 14, "updated": "2026-07-30 08:12:44.031Z", "access": "owner" }
]
}GET /api/kb/v1/maps/{id}
Jedna mapa jako strom. Bez souřadnic na plátně — tvar, ne kresba.
{
"id": "abc123",
"title": "Nový web",
"description": "",
"archived": false,
"updated": "2026-07-30 08:12:44.031Z",
"access": "owner",
"tree": [ {
"id": "n_1", "type": "goal", "title": "Texty", "status": "todo", "description": "",
"deadline": "2026-08-15", "planned_on": "", "owner": "anna@example.com", "color": "",
"wait_for_children": false, "executor_kind": "human", "executor_name": "",
"automation_wanted": false, "automation_note": "", "children": []
} ],
"notes": []
}Hodnotu updated si nechte — potřebujete ji jako base_updated pro každý zápis.
Pole uzlu
Jeden tvar všude: co vrací GET …/maps/{id} u uzlu, to přijímají tree/items při zakládání a POST …/nodes/{nodeId} při úpravě. Cokoli jiného je 400 s výčtem povolených polí (a u běžných názvů z jiných nástrojů s ekvivalentem v killBottlenecku).
| Pole | Typ | Význam |
|---|---|---|
id, type | jen čtení | type je apex (kořenový cíl) nebo goal |
title | řetězec, ≤ 200 | při zakládání povinný |
status | todo / in_progress / done | výchozí todo; done může odblokovat čekající uzly a spustit pravidla |
description | řetězec | volný text |
deadline | YYYY-MM-DD nebo "" | dohoda s někým jiným — nic v aplikaci ho tiše neposouvá; "" maže |
planned_on | YYYY-MM-DD nebo "" | plán: kdy na tom vlastník klíče chce pracovat, dnes až 7 dní dopředu; "" ruší. Takhle killBottleneck vyjadřuje prioritu — pole priorita neexistuje |
owner | zodpovědná osoba: člen instance nebo externí kontakt, který vlastník klíče vidí (GET …/members); neznámý → 400 s nápovědou | |
color | #rrggbb nebo "" | barva uzlu |
wait_for_children | boolean | uzel čeká, dokud není hotový celý jeho podstrom |
executor_kind | human / automation | kdo krok vykonává (owner zůstává člověk v obou případech) |
executor_name | řetězec, ≤ 100 | která automatizace to obsluhuje — záznam, ne instrukce |
automation_wanted | boolean | přání, aby byl krok automatizovaný; upozorní správce AI agentů |
automation_note | řetězec, ≤ 1000 | kontext k přání |
children | pole | jen položky stromu (tree / items), rekurzivně vnořené; jejich pořadí = pořadí na plátně (zleva doprava) — to, co v editoru mění Uspořádat |
POST /api/kb/v1/maps
Založí mapu z osnovy. Vyžaduje read_write.
| Pole | Povinné | Význam |
|---|---|---|
title | ano | Název mapy, ořezaný na 200 znaků |
tree | ne | Pole položek (viz níže); celkem max 200 uzlů |
description | ne | Volný text |
apex_text | ne | Text kořenového cíle; výchozí je title |
Každá položka v tree může nést title, description, deadline, planned_on, owner, status, color, wait_for_children, executor_kind, executor_name, automation_wanted, automation_note a children. Rozmístění se dopočítá samo.
Vrací { id, title, updated, tree }.
Neznámá pole se odmítají, ne ignorují
Každá zápisová cesta odpoví 400 na pole, které nezná — nahoře i uvnitř položky — a hláška vyjmenuje povolená pole. Běžné názvy z jiných nástrojů dostanou nápovědu: priority → planned_on, due_date → deadline, assignee → owner, tags → struktura nebo color, reminder / remind_at / time / hour → endpoint připomínek (uzel nemá čas v rámci dne; pravidlo deadline_approaching zůstává cestou k upozornění bez času nebo pro celou mapu), event / meeting → endpoint událostí. Překlep tedy selže nahlas, místo aby vrátil 200 a nic nezměnil.
POST /api/kb/v1/maps/{id}/nodes
Přidá podstrom. Vyžaduje read_write.
| Pole | Povinné | Význam |
|---|---|---|
base_updated | ano | Verze, kterou jste načetli (viz optimistické zamykání) |
items | ano | Pole položek, aspoň jedna, max 200 |
parent_id | ne | Kam připojit; vynechané = pod vrchol |
Vrací { updated, added_ids, tree }.
Tohle přepočítá rozmístění celé mapy
Přidání uzlů posune pozice napříč mapou. Není to chirurgický vpich.
POST /api/kb/v1/maps/{id}/nodes/{nodeId}
Upraví jeden uzel. Vyžaduje read_write a base_updated.
Nastavitelná pole: title, status (todo / in_progress / done), description, deadline (YYYY-MM-DD, prázdný řetězec maže), planned_on (viz níže), owner (e-mail, prázdný řetězec maže), wait_for_children, color, executor_kind, executor_name, automation_wanted, automation_note. Cokoli jiného je 400 s výčtem povolených polí.
planned_on je plán, ne termín. Říká, kdy na uzlu chce vlastník klíče pracovat — dnes až 7 dní dopředu (YYYY-MM-DD, prázdný řetězec plán ruší; datum mimo tohle okno je 400). Takhle killBottleneck vyjadřuje prioritu: pole „priorita" záměrně neexistuje a termín je dohoda s někým jiným, kterou nikdo nesmí posunout jen proto, aby řekl „tohle je důležité". Uzel naplánovaný na dnes se objeví v Můj den přesně jako by si ho člověk naplánoval v aplikaci. Každé čtení (GET …/maps/{id}, tree v odpovědích zápisů) vrací i planned_on.
Vrací { updated, shared, node } — novou verzi mapy, komu se mapa kvůli novému owner nasdílela, a uložený uzel (id, title, status, deadline, planned_on, owner, executor_kind, executor_name, automation_wanted). Hodnotu si čtěte odsud: co ukazuje odpověď, to mapa opravdu drží.
Nastavení status na done má záměrně vedlejší účinky: může odblokovat čekající uzly, upozornit jejich řešitele a spustit automatizace, které na nich visí.
POST /api/kb/v1/maps/{id}/nodes/{nodeId}/delete
Smaže uzel včetně celého podstromu. Vyžaduje read_write a base_updated.
Vrchol smazat nejde a celé mapy přes API smazat nejdou vůbec. Není žádné zpět — nejdřív si mapu načtěte a zkontrolujte ID.
GET /api/kb/v1/maps/{id}/rules
Automatizační pravidla mapy („když X → udělej Y“) — id, název, enabled, spouštěč, podmínky, akce, last_fired (poslední časový běh — schedule/deadline_approaching; u událostních pravidel zůstává prázdné) a last_error (neprázdná chyba = pravidlo je rozbité a vlastník mapy o tom dostal zprávu).
POST /api/kb/v1/maps/{id}/rules
Založí pravidlo. Vyžaduje read_write. Limity jsou jen strukturální: 50 pravidel na mapu, 10 akcí a 20 podmínek na pravidlo — počet spuštění se nepočítá ani neúčtuje. Pravidlo platí jen do budoucna, nikdy zpětně.
| Pole | Povinné | Význam |
|---|---|---|
name | ano | název (max 120 znaků) |
trigger | ano | {"type": …} — node_status_changed (volitelně status), node_unblocked, deadline_approaching (when: before/overdue, days 0–365), node_created, file_uploaded, schedule (freq: daily/weekly, weekday 1–7, hour 0–23) |
actions | ano | pole 1–10 akcí, vykonají se popořadě: set_status, set_owner (e-mail člena nebo dynamický cíl deputy_of_node_owner), set_deadline (date, relative_days, nebo advance = daily/weekly/monthly — posune stávající termín cíle o interval a drží rytmus od původního termínu: každé pondělí zůstane pondělí, 31. zůstává 31. s clampem v kratších měsících; prošlé výskyty přeskočí na nejbližší budoucí) — tyto tři umí target: trigger_node (výchozí) / parent / id uzlu, move_node (to = id nového rodiče; přesune SPOUŠTĚCÍ uzel na konec jeho řady — kanban posun; vrchol, zmizelý cíl nebo cyklus = přiznaný skip v logu), create_subnodes (items = stejný strom jako add_nodes, max 50 uzlů), notify (to: node_owner/deputy_of_node_owner/map_owner/e-mail, message), run_agent (agent_name z registru) |
conditions | ne | AND řetěz {field, op, value} — field: status/owner/deadline/executor_kind/parent (id nadřazeného uzlu — „karta pod sloupcem", jen eq/ne); op: eq/ne/empty/not_empty/before/after (jen termín, YYYY-MM-DD) |
node_id | ne | pravidlo jen pro jeden uzel; povinné u schedule pravidel s akcemi na uzel |
enabled | ne | výchozí true |
Časové triggery běží v průběhu hodiny po nastavené hodině (lokální čas serveru) — žádný slib „přesně o půlnoci“; po výpadku se doženou týž den. deadline_approaching s when=overdue vystřelí, když je termín propadlý aspoň days dní — chytí i termíny propadlé dřív, než pravidlo vzniklo — a vystřelí jednou na daný termín (změněný termín smí vystřelit znovu); when=before platí přesně pro den (termín − days). Řetězení pravidel (akce spustí další pravidlo) je dovolené do hloubky 3, pak běh skončí přiznaným skipped záznamem v logu.
Dynamické cíle se rozřeší až za běhu pravidla, ne při uložení — výměna lidí ve firmě pravidlo nerozbije. deputy_of_node_owner = zástupce zodpovědné osoby trigger uzlu: přednost mají zástupci pozic z organizační struktury (víc pozic s různými zástupci → notify jde všem, set_owner se přiznaně přeskočí s radou zacílit konkrétní pozici), osobní zástupce ze Správy organizace je záloha. position:<nodeId> / deputy_of_position:<nodeId> cílí držitele/zástupce pozice org struktury (id vypíše GET /v1/org-structure). Nerozřešitelný cíl (bez zástupce, neobsazená či smazaná pozice) akci přeskočí a log běhu to přizná — pravidlo se NEoznačí za rozbité. Cíle odvozené od trigger uzlu potřebují u celomapového schedule pravidla node_id; cíle position: ne.
POST /api/kb/v1/maps/{id}/rules/{ruleId}
Upraví pravidlo. Jen {"enabled": true/false} = zapnout/vypnout; jinak pošlete celý nový tvar (částečné úpravy polí se neslévají). Úprava vynuluje chybový stav pravidla.
POST /api/kb/v1/maps/{id}/rules/{ruleId}/delete
Smaže pravidlo. Log jeho běhů zůstává (se snímkem názvu).
GET /api/kb/v1/maps/{id}/rule-runs
Log běhů pravidel mapy (nejnovější první, max 100). ?rule= omezí na jedno pravidlo. status: ok / failed / skipped (pojistka proti smyčce nebo strop na jedno uložení — detail říká který).
GET /api/kb/v1/rule-templates
Knihovna šablon pravidel celé instance (tvar pravidla bez mapy a bez scope uzlu). Šablona se „načítá“ tak, že její trigger/conditions/actions pošlete do POST …/rules cílové mapy — vznikne nezávislá kopie.
POST /api/kb/v1/rule-templates
Založí (nebo s id upraví — jen autor či admin) šablonu: name (unikátní), trigger, actions, volitelně conditions. Šablona nesmí mít node_id a create_subnodes smí mířit jen na trigger_node. Vyžaduje read_write.
POST /api/kb/v1/rule-templates/{id}/delete
Smaže šablonu (jen autor či admin). Pravidla z ní načtená v mapách zůstávají.
GET /api/kb/v1/portfolio
Limit: 60 za minutu na uživatele (navíc k limitu na klíč).
Pohled shora — stejný JSON jako stránka Organizace (counts, scope, sections.projects / overdue / stuck / people / changes, truncated), spočítaný nad týmovými a sdílenými mapami, které vlastník klíče smí číst; jeho soukromé mapy nejdou ani do součtů. Volitelně ?today=YYYY-MM-DD. Role se nečte: běžný člen dostane souhrn svých map (v aplikaci je stránka jen pro admina a manažera). MCP: get_portfolio.
GET /api/kb/v1/members
Komu jde práci přiřadit: členové instance a externí kontakty, které vlastník klíče vidí. Odpověď: {members: [{id, email, full_name, name, role}], external_contacts: [{id, name, owner_email}]} — bezpečná podmnožina, nikdy nastavení notifikací ani tajemství. Jako owner uzlu použijte email (u externího kontaktu pseudo-adresu ext-<id>@kontakt.invalid z owner_email); neznámý e-mail server odmítne s 400 a nápovědou, koho jste asi mysleli. MCP: list_people.
GET /api/kb/v1/org-structure
Organizační struktura (org mapa): pozice a funkce s id uzlů, držiteli a zástupci. Odpověď: {exists, map_id, positions: [{node_id, title, position_kind, holder, deputy}]}, kde position_kind je position (daná strukturou) nebo function (jmenovaná). node_id použijte jako dynamický cíl pravidla position:<nodeId> (držitel) nebo deputy_of_position:<nodeId> (zástupce té pozice) — obojí se rozřeší až za běhu, výměna lidí pravidla nerozbije. Archivovaná org mapa platí všude jako „struktura neexistuje" (exists: false, cíle na pozice se přiznaně přeskočí). Jen čtení: držitele a zástupce jmenuje admin v aplikaci (Správa organizace) — kontrakt API klíčů role nikdy nečte, zápisový endpoint tu proto není.
Události
Událost je osobní položka kalendáře s časem — schůzka, hovor, zubař. Není to úkol: nepatří do žádné mapy, nemá stav ani řešitele a v projektech nic nemění. V aplikaci žije v kalendáři (/tasks?view=calendar) pod filtrem Osobní. Časy jsou lokální čas instance (TZ), jako řetězce YYYY-MM-DD a HH:MM — nikdy ISO časové značky s posunem.
Vlastník klíče vidí své události a ty, na které ho někdo pozval; zakládá, mění a maže jen vlastní. MCP: create_event, list_events.
Objekt události (vrací se všude):
{
"id": "ev1", "title": "Zubař", "day": "2026-09-20", "time": "14:00", "note": "",
"owner_email": "ja@example.com", "participants": ["anna@example.com"],
"remind": true, "remind_before_min": 30, "mine": true,
"created": "2026-09-19 10:01:12.004Z", "updated": "2026-09-19 10:01:12.004Z"
}time je "" u celodenní události; participants jsou e-maily členů; mine říká, jestli událost založil vlastník klíče.
GET /api/kb/v1/events
Události v okně dní — vlastní i pozvané, seřazené podle dne a času.
| Query | Význam |
|---|---|
from, to | YYYY-MM-DD, včetně; výchozí dnes − 365 … dnes + 730 |
Vrací { events: [...], from, to }.
POST /api/kb/v1/events
Založí událost. Vyžaduje read_write.
| Pole | Povinné | Význam |
|---|---|---|
title | ano | ≤ 200 znaků |
day | ano | YYYY-MM-DD |
time | ne | HH:MM (24 h); vynechané nebo "" = celý den |
note | ne | ≤ 2000 znaků |
participants | ne | pole e-mailů členů (max 50). Jen členové instance — neznámá adresa nebo externí kontakt je 400 s nápovědou (GET …/members). Každý pozvaný dostane notifikaci pozvání na událost a vidí událost ve svém kalendáři |
remind | ne | boolean; připomínka pro vlastníka i všechny pozvané |
remind_before_min | ne | 0–10080 minut před začátkem (0 = v čas začátku); poslané pole znamená remind: true, samotné remind: true bez něj znamená 30. Celodenní událost: připomínka přijde ráno v KB_DEADLINE_HOUR |
Vrací { event }. Bez base_updated — událost není součástí mapy.
POST /api/kb/v1/events/{id}
Upraví událost; pošlete jen pole, která se mění (stejná jako při založení). Jen vlastník: pozvaný dostane 403, událost, kterou vlastník klíče nevidí, je 404. Změna dne, času nebo připomínky připomínku znovu nastaví; nově pozvaní dostanou notifikaci, stávající znovu ne.
POST /api/kb/v1/events/{id}/delete
Smaže událost. Jen vlastník (403 / 404 jako výše).
POST /api/kb/v1/events/{id}/leave
Vyřadí vlastníka klíče z účastníků události, na kterou byl pozván (událost mu zmizí z kalendáře). Zakladatel se odebrat nemůže (400) — událost maže; nepozvaný → 404.
Připomínky
Připomínka s časem k uzlu, relativní k jeho termínu: offset_days před termínem (0 = v den termínu, 1 = den před …) v čas time. Je soukromá pro vlastníka klíče (uzel zůstává pro ostatní beze změny), je jedna na osobu a uzel a termín nikdy nemění — když se termín posune, připomínka se posune s ním. Hotový nebo smazaný uzel, nebo uzel bez termínu, připomínku odloží. Mapu stačí vidět; bez base_updated, protože se v mapě nic nemění. MCP: create_reminder.
GET /api/kb/v1/maps/{id}/nodes/{nodeId}/reminders
Připomínka vlastníka klíče k uzlu — { reminders: [...] }, prázdné nebo jedna položka.
POST /api/kb/v1/maps/{id}/nodes/{nodeId}/reminders
Založí nebo nahradí připomínku (upsert). Vyžaduje read_write.
| Pole | Povinné | Význam |
|---|---|---|
offset_days | ne | celé číslo 0–30, výchozí 0 (den termínu) |
time | ano | HH:MM (24 h), lokální čas instance |
Vrací uloženou připomínku, název uzlu a jeho termín:
{
"reminder": { "id": "r1", "map_id": "abc123", "node_id": "n_1", "offset_days": 1, "time": "09:00",
"day": "2026-08-14", "fires_at": "2026-08-14 09:00", "fired": false },
"node_title": "Napsat texty", "deadline": "2026-08-15"
}Chyby: uzel nemá termín → 400 (nejdřív ho nastavte přes POST …/nodes/{nodeId}), spočítaný čas už minul → 400, neznámý uzel → 404.
POST /api/kb/v1/maps/{id}/nodes/{nodeId}/reminders/{rid}/delete
Smaže připomínku. Cizí připomínka je 404.
Doručení u obojího: server každou minutu zkontroluje, co je na řadě, a pošle notifikaci připomínka do zvonečku a e-mailem (e-mail je u tohoto typu výchoze zapnutý a chodí i v režimu denního souhrnu). Po výpadku se připomínky starší než KB_REMINDER_CATCHUP_H hodin (výchozí 48) jen zalogují. Viz Notifikace.
/api/kb/v1/tasks — odstraněno
Úkolové endpointy (GET/POST /v1/tasks, POST /v1/tasks/{id}) byly odstraněny a vrací 410 Gone. Úkol v killBottlenecku není samostatný záznam: úkol je uzel s řešitelem (owner) nebo termínem. Nová práce = nový uzel. Práce se zakládá a upravuje přes /v1/maps/{id}/nodes (MCP: add_nodes, update_node); úkol se odbavuje nastavením status na done.
Příklad od začátku do konce
KEY="kb_user_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
HOST="http://localhost:8090"
# 1) najít mapu
curl -s -H "Authorization: Bearer $KEY" "$HOST/api/kb/v1/maps"
# 2) načíst ji — a schovat si `updated`
MAP=abc123
UPDATED=$(curl -s -H "Authorization: Bearer $KEY" "$HOST/api/kb/v1/maps/$MAP" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["updated"])')
# 3) přidat dva cíle pod vrchol
curl -s -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d "{\"base_updated\":\"$UPDATED\",\"items\":[
{\"title\":\"Napsat texty\",\"deadline\":\"2026-08-15\"},
{\"title\":\"Nafotit fotky\"}]}" \
"$HOST/api/kb/v1/maps/$MAP/nodes"
# 4) „tohle je priorita“ = naplánovat na dnes (nejdřív znovu přečíst `updated` — mapa se právě změnila)
NODE=n_1
UPDATED=$(curl -s -H "Authorization: Bearer $KEY" "$HOST/api/kb/v1/maps/$MAP" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["updated"])')
curl -s -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d "{\"base_updated\":\"$UPDATED\",\"planned_on\":\"$(date +%F)\"}" \
"$HOST/api/kb/v1/maps/$MAP/nodes/$NODE"
# → 200, cíl je v Můj den v sekci „Dnes“; termín se nepohnul
# 5) pole, které API nezná, je chyba — nikdy tiché „nic se nestalo“
curl -s -X POST -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d "{\"base_updated\":\"$UPDATED\",\"priority\":\"high\"}" \
"$HOST/api/kb/v1/maps/$MAP/nodes/$NODE"
# → 400 {"error":"Neznámá pole: priority. Povolená pole: title, status, description, deadline,
# planned_on, owner, … Pole „priority“ v killBottlenecku není — prioritu vyjadřuje plán
# planned_on (KDY na tom chcete pracovat, dnes až +7 dní). …"}Chybové kódy
Celou tabulku najdete v Chybových kódech.

