318 lines
14 KiB
Markdown
318 lines
14 KiB
Markdown
---
|
|
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/<listId>/items"
|
|
```
|
|
|
|
### Check off an item (mark complete)
|
|
```bash
|
|
curl -X PUT -H "x-api-key: $JOTTY_API_KEY" \
|
|
"$JOTTY_BASE_URL/api/checklists/<listId>/items/0/check"
|
|
```
|
|
|
|
### Uncheck an item
|
|
```bash
|
|
curl -X PUT -H "x-api-key: $JOTTY_API_KEY" \
|
|
"$JOTTY_BASE_URL/api/checklists/<listId>/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/<taskId>/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/<taskId>/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/<noteId>"
|
|
```
|
|
|
|
### 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.
|