--- name: jotty-notes-api description: Operate Jotty Notes self-hosted instances via REST API. Three instances — Rob (10.0.0.235), Zoe (10.0.0.212), Jennifer (10.0.0.213). Use for checklists, notes, tasks (Kanban), categories, user info, exports, or audit logs. version: 1.1.0 trigger: - jotty - jotty notes - jotty checklist - jotty task - my checklist - my note - my task - add to my list - check off - 10.0.0.213 - 213:3000 - 10.0.0.212 - 212:3000 - 10.0.0.235 - 235:3000 - ck_d8a1062be432e58b66c453a8f4c39fb4 - ck_ca2d142ec7d2adc2574e4aa73ecef3b0 - ck_53e7f7af7ece20557458d84eb6f01782 - jennifer to do - zoe to do - to-do list - todo list - jotty api profile: openj --- # jotty-notes-api — Jotty Notes REST API client Covers all three self-hosted Jotty instances. All run Jotty v1.25.1. The API is identical across instances — only the base URL, API key, and owner name differ. ## Instances | Owner | Base URL | API Key | Admin? | Profile | |----------|---------------------------|----------------------------------------|------------|----------| | Rob | `http://10.0.0.235:3000` | `ck_53e7f7af7ece20557458d84eb6f01782` | yes | general | | Zoe | `http://10.0.0.212:3000` | `ck_ca2d142ec7d2adc2574e4aa73ecef3b0` | no | openz | | Jennifer | `http://10.0.0.213:3000` | `ck_d8a1062be432e58b66c453a8f4c39fb4` | superadmin | openj | **Instance selection rule:** When the user says "jotty" or "my notes" without specifying, use the instance matching the active profile. When the user names a person ("Zoe's jotty", "Jennifer's checklist"), use that person's instance. ## Configuration ``` # Rob JOTTY_BASE_URL=http://10.0.0.235:3000 JOTTY_API_KEY=ck_53e7f7af7ece20557458d84eb6f01782 JOTTY_OWNER=Rob # Zoe JOTTY_BASE_URL=http://10.0.0.212:3000 JOTTY_API_KEY=ck_ca2d142ec7d2adc2574e4aa73ecef3b0 JOTTY_OWNER=Zoe # Jennifer JOTTY_BASE_URL=http://10.0.0.213:3000 JOTTY_API_KEY=ck_d8a1062be432e58b66c453a8f4c39fb4 JOTTY_OWNER=Jennifer ``` Rob and Jennifer have admin/superadmin access, so admin-only endpoints (logs stats, rebuild-index, export-all, querying other users' summary) are available on their instances. Zoe's key is user-tier — admin endpoints will 403. ## Authentication Every authenticated request uses a single header: ``` x-api-key: ck_d8a1062be432e58b66c453a8f4c39fb4 ``` API keys are permanent and do not expire. The key provides full access to the owner's account (and, because Jennifer is super-admin, to all users). ## Verified live response shapes These are real shapes captured by hitting the live server, not theory from the docs. GET `/api/health` → ```json {"status":"healthy","version":"1.25.1","timestamp":"2026-07-04T15:28:52.527Z"} ``` GET `/api/user` → ```json {"user":{"username":"Jennifer","isAdmin":true,"isSuperAdmin":true}} ``` GET `/api/summary` → ```json {"summary":{"username":"Jennifer","notes":{"total":1,"categories":{"Uncategorized":1}}, "checklists":{"total":1,"categories":{"Uncategorized":1},"types":{"simple":1}}, "items":{"total":3,"completed":0,"pending":3,"completionRate":0}, "tasks":{"total":0,"completed":0,"inProgress":0,"todo":0,"completionRate":0}}} ``` GET `/api/categories` → ```json {"categories":{"notes":[{"name":"Uncategorized","path":"Uncategorized","count":1,"level":0}], "checklists":[{"name":"Uncategorized","path":"Uncategorized","count":1,"level":0}]}} ``` Checklist item shape (real, from `GET /api/checklists`): ```json {"id":"Jennifer-to-do-list-1783178716552","index":0,"text":"Give Rob loveing.", "completed":false,"createdBy":"Jennifer","createdAt":"2026-07-04T15:25:16.552Z", "lastModifiedBy":"Jennifer","lastModifiedAt":"2026-07-04T15:25:16.552Z"} ``` Note that real items include `id`, `createdBy`, `lastModifiedBy`, `lastModifiedAt` beyond what the docs show — when presenting data to Jennifer, use these extra fields. ## Conventions to follow when talking to the user - 0-based item indices everywhere (first item is `0`). - Item references in URLs use dot-paths for nesting: `0`, `0.1`, `2.0.1`. - Timestamps are ISO 8601 UTC (`createdAt`, `updatedAt`, etc.). - `createdAt` for some seed data is the epoch (`1970-01-01T00:00:00.000Z`) — that's an upstream quirk, not an error. Prefer `updatedAt` for "last touched" answers. - Default category for new items is `Uncategorized` if not specified. - Default checklist `type` is `simple`; use `task` for Kanban with statuses + time tracking. - "Checked off" = `PUT /api/checklists/{listId}/items/{idx}/check` (which sets `completed: true`). - Errors return `{"error":"..."}` with HTTP 400/401/403/404/500. ## Endpoint quick reference All paths are prefixed with `/api`. Auth required unless marked public. ### Public - `GET /api/health` — health + version, no auth ### Identity & overview - `GET /api/user` — current user (username, isAdmin, isSuperAdmin) - `GET /api/user/{username}` — full profile if self/admin, public fields otherwise - `GET /api/summary[?username=...]` — counts: notes, checklists, items, tasks. Admin can pass `username`. - `GET /api/categories` — categories used by notes and checklists (excludes archived) ### Checklists (simple = checkbox to-do lists) - `GET /api/checklists[?category=...&type=simple|task&q=...]` — list all - `POST /api/checklists` — `{title, category?, type?}` → returns `{success, data:{id,...}}` - `PUT /api/checklists/{listId}` — `{title?, category?}` - `DELETE /api/checklists/{listId}` - `POST /api/checklists/{listId}/items` — `{text}` or for task type: `{text, status?, time?}`; use `parentIndex: "0.1"` for nested - `PATCH /api/checklists/{listId}/items/{itemIndex}` — partial update of `text`, `description`, `priority` (critical/high/medium/low/none), `score`, `startDate`, `targetDate`, `estimatedTime` - `PUT /api/checklists/{listId}/items/{idx}/check` - `PUT /api/checklists/{listId}/items/{idx}/uncheck` - `DELETE /api/checklists/{listId}/items/{idx}` ### Notes - `GET /api/notes[?category=...&q=...]` - `POST /api/notes` — `{title, content?, category?}` - `PUT /api/notes/{noteId}` — `{title, content?, category?, originalCategory?}` (`originalCategory` is used to locate the existing note) - `DELETE /api/notes/{noteId}` ### Tasks (Kanban — task-type checklists) - `GET /api/tasks[?category=...&status=...&q=...]` - `POST /api/tasks` — `{title, category?, statuses?}` (statuses default to todo/in_progress/completed; can supply custom columns with `id`, `label`, `color`, `order`) - `GET /api/tasks/{taskId}` - `PUT /api/tasks/{taskId}` — `{title?, category?}` - `DELETE /api/tasks/{taskId}` - `GET /api/tasks/{taskId}/statuses` - `POST /api/tasks/{taskId}/statuses` — `{id, label, color?, order?}` - `PUT /api/tasks/{taskId}/statuses/{statusId}` — `{label?, color?, order?}` - `DELETE /api/tasks/{taskId}/statuses/{statusId}` — items in that column auto-move to first available status - `POST /api/tasks/{taskId}/items` — `{text, status?, parentIndex?}` - `GET /api/tasks/{taskId}/items/{itemIndex}` — returns item + nested `children` - `PUT /api/tasks/{taskId}/items/{idx}/status` — `{status}` (moves between Kanban columns) - `DELETE /api/tasks/{taskId}/items/{idx}` ### Admin (Jennifer has access) - `POST /api/admin/rebuild-index` — `{username}` — rebuilds internal link index ### Exports - `POST /api/exports` — `{type, username?}` where `type` is `all_checklists_notes` | `user_checklists_notes` | `all_users_data` | `whole_data_folder`; returns `{success, downloadUrl}` - `GET /api/exports` — current export progress `{progress, message}` ### Audit logs (admin can see all; non-admin see own) - `GET /api/logs` — `?username&action&category&level&startDate&endDate&success&limit&offset` (categories: auth, user, checklist, note, sharing, settings, encryption, api, system, file, upload) - `POST /api/logs/export` — `{format:"json"|"csv", filters?}` returns downloadable file - `GET /api/logs/stats` — admin only — aggregated counts by level, category, top actions, top users, recent activity - `POST /api/logs/cleanup` — admin only — deletes logs past retention; returns `{success, deletedFiles}` ## Common action patterns These are the patterns the agent will most often need. They were all verified against the live server. ### Quick health check ```bash curl -s -H "x-api-key: $JOTTY_API_KEY" "$JOTTY_BASE_URL/api/health" ``` ### List everything Jennifer owns ```bash curl -s -H "x-api-key: $JOTTY_API_KEY" "$JOTTY_BASE_URL/api/checklists" \ | python3 -c "import json,sys;d=json.load(sys.stdin);[print(c['title'],c['id'],len(c['items']),'items') for c in d['checklists']]" curl -s -H "x-api-key: $JOTTY_API_KEY" "$JOTTY_BASE_URL/api/notes" \ | python3 -c "import json,sys;d=json.load(sys.stdin);[print(n['title'],n['id']) for n in d['notes']]" ``` ### Add an item to a checklist ```bash curl -X POST -H "x-api-key: $JOTTY_API_KEY" -H "Content-Type: application/json" \ -d '{"text":"Buy milk"}' \ "$JOTTY_BASE_URL/api/checklists//items" ``` ### Check off an item (mark complete) ```bash curl -X PUT -H "x-api-key: $JOTTY_API_KEY" \ "$JOTTY_BASE_URL/api/checklists//items/0/check" ``` ### Uncheck an item ```bash curl -X PUT -H "x-api-key: $JOTTY_API_KEY" \ "$JOTTY_BASE_URL/api/checklists//items/0/uncheck" ``` ### Create a new simple checklist ```bash curl -X POST -H "x-api-key: $JOTTY_API_KEY" -H "Content-Type: application/json" \ -d '{"title":"Groceries","category":"Shopping","type":"simple"}' \ "$JOTTY_BASE_URL/api/checklists" ``` ### Create a new task board (Kanban) ```bash curl -X POST -H "x-api-key: $JOTTY_API_KEY" -H "Content-Type: application/json" \ -d '{"title":"House Projects","category":"Home","statuses":[ {"id":"todo","label":"To Do","order":0}, {"id":"doing","label":"Doing","order":1,"color":"#f59e0b"}, {"id":"done","label":"Done","order":2} ]}' \ "$JOTTY_BASE_URL/api/tasks" ``` ### Add a task item to a specific column ```bash curl -X POST -H "x-api-key: $JOTTY_API_KEY" -H "Content-Type: application/json" \ -d '{"text":"Stain deck","status":"todo"}' \ "$JOTTY_BASE_URL/api/tasks//items" ``` ### Move a task item to another column ```bash curl -X PUT -H "x-api-key: $JOTTY_API_KEY" -H "Content-Type: application/json" \ -d '{"status":"doing"}' \ "$JOTTY_BASE_URL/api/tasks//items/0/status" ``` ### Create a note ```bash curl -X POST -H "x-api-key: $JOTTY_API_KEY" -H "Content-Type: application/json" \ -d '{"title":"Recipes","content":"# Family recipes\n- ..."}' \ "$JOTTY_BASE_URL/api/notes" ``` ### Update a note ```bash curl -X PUT -H "x-api-key: $JOTTY_API_KEY" -H "Content-Type: application/json" \ -d '{"title":"Recipes v2","content":"# Family recipes\n- ...","category":"Kitchen","originalCategory":"Uncategorized"}' \ "$JOTTY_BASE_URL/api/notes/" ``` ### Search ```bash curl -s -H "x-api-key: $JOTTY_API_KEY" \ "$JOTTY_BASE_URL/api/checklists?q=meeting" curl -s -H "x-api-key: $JOTTY_API_KEY" \ "$JOTTY_BASE_URL/api/notes?q=recipe" ``` ## Decision rules for the agent When Jennifer asks to: - "add X to my list" / "put X on my to-do" → find a matching checklist (or the default `Jennifer-to-do-list`) and POST to `/api/checklists/{listId}/items`. If no obvious list exists, ask which list or create a new one. - "check off X" / "mark X done" → `GET /api/checklists` to find the list, then `PUT .../items/{idx}/check`. Use search (`?q=`) if needed. - "show my notes" / "what notes do I have" → `GET /api/notes` and pretty-print titles. - "create a new board for X" / "kanban for X" → `POST /api/tasks` with custom statuses. - "move X to doing" → `PUT /api/tasks/{taskId}/items/{idx}/status` with `{"status":"doing"}`. - "what's on my plate" / "summary" → `GET /api/summary` and summarize. - "delete X" → confirm with Jennifer first; then DELETE the appropriate resource. ## Pitfalls - Item indices shift after delete. Always re-fetch the list before acting on an index if anything might have changed. - `PUT /api/notes/{noteId}` requires `originalCategory` to locate the note when categories differ from the requested `category` — supply it whenever the note is not in `Uncategorized`. - `createdAt` is the epoch (`1970-01-01T00:00:00.000Z`) for many existing items; never claim an item is "from 1970" — use `updatedAt` or `lastModifiedAt`. - Status IDs on task boards are user-defined (default: `todo`/`in_progress`/`completed`). When in doubt, `GET /api/tasks/{id}/statuses` first to learn the actual column IDs. - Custom statuses are only available on the `POST /api/tasks` creation call; you can't change the column set of a `simple` checklist, you have to make a `task`-type board. - `DELETE /api/tasks/{taskId}/statuses/{statusId}` auto-reassigns items in that column to the first available status — safe but irreversible. - Jennifer is `isSuperAdmin: true`, so admin endpoints work, but still scope queries to her own data when possible (`/api/summary` without `?username=`) to avoid leaking other users' totals in chat. - The web UI (`/api`, `/docs`) at `10.0.0.213:3000` returns the login HTML — those paths are for the browser, not API clients. Stick to `/api/*` paths. - "Audit log" queries can be heavy. Default `limit=50`; cap to ≤100 unless explicitly asked for more. ## Verification Run these in order to confirm the skill is wired up: ```bash # 1. Health curl -s -H "x-api-key: ck_d8a1062be432e58b66c453a8f4c39fb4" \ http://10.0.0.213:3000/api/health # expect: {"status":"healthy","version":"1.25.1",...} # 2. Identity curl -s -H "x-api-key: ck_d8a1062be432e58b66c453a8f4c39fb4" \ http://10.0.0.213:3000/api/user # expect: {"user":{"username":"Jennifer","isAdmin":true,"isSuperAdmin":true}} ``` If either fails with HTTP 307 → `/auth/login`, the key is wrong or the server is misconfigured. Re-check `JOTTY_API_KEY`. ## Source API behavior verified against Jotty v1.25.1 on 2026-07-04 from this profile. Docs mirrored from https://github.com/fccview/jotty/blob/main/howto/API.md (last commit `3307091`, Jun 14 2026). If the server upgrades past v1.25.1, re-run the two verification curls and update the `version` field.