Knowledge API
Free-text note ingest with AI classification, plus structured entries, connections, and comments. Mounted at /api/knowledge. The vocabulary module (/api/vocabulary) writes the same underlying table with a narrower surface.
Note ingest (the interesting endpoint)
POST /api/knowledge/notes with scope knowledge:write. Three accepted body shapes (strict: unknown keys are 400):
{"text": "today I learned gracias means thank you in Spanish"}
{"text": "...", "source": "Nibbler"}
{"notes": [{"text": "...", "source": {"book": "War and Peace", "location": "ch. 3"}}]}
texttrimmed 1..5000.sourceis a string (becomes{app}) or an object with app/book/author/location/rawContext.- Single: 201
{entries:[entry]}. Batch (1..50): 201 all-ok, 207{entries, failures:[{index, text, error}]}on partial failure, 502 when every note failed or the LLM chain is exhausted (retryable). - Caller-supplied source fields beat classifier-extracted ones. No cross-request dedup by design; within one batch, duplicate (word, language) pairs are silently dropped.
- Rate: 30/min single, 5/min batch (separate buckets).
Entries
| Method | Path | Auth | Notes |
|---|---|---|---|
| GET | /entries | public | ?language=&label=&search=&category=&app=&limit=1..500&offset=; returns {entries, total, limit, offset} (offset pagination, unlike most modules) |
| GET | /entries/:id | public | {entry, connections, connectedEntries} |
| POST | /entries | knowledge:write | word + language required; 409 {error, existingEntry} on (word, language, category) duplicate; auto-enriches vocabulary entries via LLM unless autoEnrich:false (enrich failure never blocks) |
| PUT | /entries/:id | auth only | owner-only (legacy ownerless rows editable by anyone authed); memorizationLocations feeds the long-term-memorized derivation |
| DELETE | /entries/:id | auth only | same ownership rule |
| POST | /entries/bulk-delete | auth only | {ids: [1..100]}; always 200 with per-id {deleted, denied, notFound} |
| POST | /entries/:id/enrich | knowledge:write | re-run AI enrichment; 502 on LLM exhaustion |
Connections (synonym, antonym, related, translation, root): GET/POST /connections, DELETE /connections/:id. The knowledge-module versions do NOT check word ownership; the vocabulary-module twins DO (403 unless you own both words). Pick the path matching the permission behavior you want.
Comments: same shape as journal comments (GET/POST /entries/:id/comments, PATCH/DELETE /comments/:id, scope comments:write, snake_case parent_comment_id, one reply level).
Vocabulary module differences
GET /api/vocabulary/wordssearch matches the word column only; list key is{words}not{entries}.- Duplicate check is (word, language) with no category dimension.
- All writes use scope
knowledge:write(there is no vocabulary:write), bucketvocabulary-writes30/min, enrich 10/min.