openapi.yaml file-level

at sha256:3 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:9 feat(calendar): hosted bridge/gateway route parity and timeline noteRec… · aaronrene · Jun 19, 2026
1 openapi: 3.0.3
2 info:
3 title: Knowtation Hub API
4 description: REST API for the Knowtation Hub (vault read/write, proposals, capture). Same contract as CLI/MCP where applicable.
5 version: 1.0.0
6 links:
7 - description: API contract (human-readable)
8 url: ./HUB-API.md
9
10 servers:
11 - url: /api/v1
12 description: Relative to Hub base URL (e.g. https://hub.example.com)
13
14 tags:
15 - name: Health
16 - name: Auth
17 - name: Notes
18 - name: Search
19 - name: Proposals
20 - name: Capture
21
22 security:
23 - BearerAuth: []
24
25 paths:
26 /health:
27 get:
28 tags: [Health]
29 summary: Health check
30 security: []
31 responses:
32 '200':
33 description: Hub is up
34 content:
35 application/json:
36 schema: { type: object, properties: { ok: { type: boolean } }, required: [ok] }
37
38 /api/v1/auth/session:
39 get:
40 tags: [Auth]
41 summary: C7 Session introspection — current identity and scopes
42 description: |
43 Returns the verified identity and derived API scopes for the caller. Accepts only a
44 `Bearer` JWT in the `Authorization` header — no cookie required, making it safe to call
45 cross-origin from Scooling or any other consumer.
46
47 The response is derived entirely from the signed token — no database call is made.
48 Scopes are role-derived today (C4 will replace this with per-user explicit grants without
49 changing the response shape).
50 security:
51 - bearerAuth: []
52 responses:
53 '200':
54 description: Verified session
55 content:
56 application/json:
57 schema:
58 type: object
59 required: [sub, provider, id, name, role, iat, exp, scopes]
60 properties:
61 sub:
62 type: string
63 description: Canonical user ID (`provider:id`)
64 example: google:104164334692309763642
65 provider:
66 type: string
67 enum: [google, github]
68 id:
69 type: string
70 description: Provider-specific user ID
71 name:
72 type: string
73 description: Display name (empty for refresh-path tokens)
74 role:
75 type: string
76 enum: [admin, member]
77 iat:
78 type: integer
79 description: Token issued-at (Unix seconds)
80 exp:
81 type: integer
82 description: Token expires-at (Unix seconds)
83 scopes:
84 type: array
85 items: { type: string }
86 description: Derived API scopes. `admin` role → `[vault:read, vault:write, admin]`; `member` → `[vault:read, vault:write]`
87 example: [vault:read, vault:write]
88 '401':
89 description: Missing, expired, or tampered token
90 content:
91 application/json:
92 schema:
93 type: object
94 properties:
95 error: { type: string }
96 code: { type: string, enum: [UNAUTHORIZED] }
97
98 /auth/providers:
99 get:
100 tags: [Auth]
101 summary: OAuth providers configured
102 security: []
103 responses:
104 '200':
105 content:
106 application/json:
107 schema:
108 type: object
109 properties:
110 google: { type: boolean }
111 github: { type: boolean }
112
113 /notes/facets:
114 get:
115 tags: [Notes]
116 summary: Projects, tags, folders for filter dropdowns
117 responses:
118 '200':
119 content:
120 application/json:
121 schema:
122 type: object
123 properties:
124 projects: { type: array, items: { type: string } }
125 tags: { type: array, items: { type: string } }
126 folders: { type: array, items: { type: string } }
127
128 /notes:
129 get:
130 tags: [Notes]
131 summary: List notes
132 parameters:
133 - name: folder
134 in: query
135 schema: { type: string }
136 - name: project
137 in: query
138 schema: { type: string }
139 - name: tag
140 in: query
141 schema: { type: string }
142 - name: since
143 in: query
144 schema: { type: string }
145 - name: until
146 in: query
147 schema: { type: string }
148 - name: limit
149 in: query
150 schema: { type: integer, minimum: 0, maximum: 100 }
151 - name: offset
152 in: query
153 schema: { type: integer, minimum: 0 }
154 - name: order
155 in: query
156 schema: { type: string, enum: [date, date-asc] }
157 - name: fields
158 in: query
159 schema: { type: string, enum: [path, path+metadata, full] }
160 - name: count_only
161 in: query
162 schema: { type: boolean }
163 responses:
164 '200':
165 content:
166 application/json:
167 schema:
168 oneOf:
169 - type: object
170 properties:
171 notes: { type: array, items: { $ref: '#/components/schemas/NoteListItem' } }
172 total: { type: integer }
173 - type: object
174 properties:
175 total: { type: integer }
176 post:
177 tags: [Notes]
178 summary: Write or update a note
179 requestBody:
180 required: true
181 content:
182 application/json:
183 schema:
184 type: object
185 required: [path]
186 properties:
187 path: { type: string }
188 body: { type: string }
189 frontmatter: { type: object }
190 append: { type: boolean }
191 responses:
192 '200':
193 content:
194 application/json:
195 schema: { type: object, properties: { path: { type: string }, written: { type: boolean } } }
196 '400':
197 '500':
198 content:
199 application/json:
200 schema: { $ref: '#/components/schemas/Error' }
201
202 /notes/{path}:
203 get:
204 tags: [Notes]
205 summary: Get one note by path
206 parameters:
207 - name: path
208 in: path
209 required: true
210 schema: { type: string }
211 responses:
212 '200':
213 content:
214 application/json:
215 schema: { $ref: '#/components/schemas/NoteFull' }
216 '404':
217 content:
218 application/json:
219 schema: { $ref: '#/components/schemas/Error' }
220
221 /note-outline:
222 get:
223 tags: [Notes]
224 summary: Body-free NoteOutline headings for one note
225 description: >
226 Returns knowtation.note_outline/v1 metadata for one authorized vault-relative note.
227 The response excludes note body text, snippets, full frontmatter, absolute paths,
228 provider payloads, MCP resource URIs, summaries, vectors, OCR, PageIndex output,
229 persistence records, and write-back state.
230 parameters:
231 - name: path
232 in: query
233 required: true
234 schema: { type: string }
235 description: Vault-relative Markdown note path.
236 responses:
237 '200':
238 content:
239 application/json:
240 schema: { $ref: '#/components/schemas/NoteOutline' }
241 '400':
242 content:
243 application/json:
244 schema: { $ref: '#/components/schemas/Error' }
245 '401':
246 content:
247 application/json:
248 schema: { $ref: '#/components/schemas/Error' }
249 '403':
250 content:
251 application/json:
252 schema: { $ref: '#/components/schemas/Error' }
253 '404':
254 content:
255 application/json:
256 schema: { $ref: '#/components/schemas/Error' }
257 '502':
258 content:
259 application/json:
260 schema: { $ref: '#/components/schemas/Error' }
261
262 /document-tree:
263 get:
264 tags: [Notes]
265 summary: Body-free DocumentTree heading hierarchy for one note
266 description: >
267 Returns knowtation.document_tree/v0 metadata for one authorized vault-relative note.
268 The response excludes note body text, snippets, full frontmatter, absolute paths,
269 provider payloads, MCP resource URIs, summaries, vectors, OCR, PageIndex output,
270 persistence records, sidecars, LLM calls, and write-back state.
271 parameters:
272 - name: path
273 in: query
274 required: true
275 schema: { type: string }
276 description: Vault-relative Markdown note path.
277 responses:
278 '200':
279 content:
280 application/json:
281 schema: { $ref: '#/components/schemas/DocumentTree' }
282 '400':
283 content:
284 application/json:
285 schema: { $ref: '#/components/schemas/Error' }
286 '401':
287 content:
288 application/json:
289 schema: { $ref: '#/components/schemas/Error' }
290 '403':
291 content:
292 application/json:
293 schema: { $ref: '#/components/schemas/Error' }
294 '404':
295 content:
296 application/json:
297 schema: { $ref: '#/components/schemas/Error' }
298 '502':
299 content:
300 application/json:
301 schema: { $ref: '#/components/schemas/Error' }
302
303 /calendar/timeline:
304 get:
305 tags: [Calendar]
306 summary: Merged note-date and external-event timeline (self-hosted)
307 description: >
308 Returns knowtation.calendar_timeline/v0 items for an authorized vault.
309 Merges note-date buckets and stored calendar events. No OAuth tokens or connector secrets.
310 parameters:
311 - name: from
312 in: query
313 required: true
314 schema: { type: string }
315 description: Range start (YYYY-MM-DD or ISO8601).
316 - name: to
317 in: query
318 required: true
319 schema: { type: string }
320 description: Range end (YYYY-MM-DD or ISO8601).
321 - name: layers
322 in: query
323 required: false
324 schema: { type: string }
325 description: Comma-separated layers (`notes`, `events`). Default both.
326 - name: source_calendar_ids
327 in: query
328 required: false
329 schema: { type: string }
330 description: Comma-separated source calendar ids to include for the events layer.
331 responses:
332 '200':
333 content:
334 application/json:
335 schema: { $ref: '#/components/schemas/CalendarTimeline' }
336 '400':
337 content:
338 application/json:
339 schema: { $ref: '#/components/schemas/Error' }
340 '401':
341 content:
342 application/json:
343 schema: { $ref: '#/components/schemas/Error' }
344 '403':
345 content:
346 application/json:
347 schema: { $ref: '#/components/schemas/Error' }
348
349 /calendar/agent-context:
350 get:
351 tags: [Calendar]
352 summary: Tier-enforced calendar context for agents (self-hosted)
353 description: >
354 Returns knowtation.calendar_agent_context/v0 — redacted calendar events an agent
355 may see. Enforced server-side: calendars with enabled_for_agents=false contribute
356 nothing; per-calendar agent_context_tier_max and the org policy cap clamp the tier;
357 the v0 retrieval ceiling is tier 2. Agent visibility is independent of
358 enabled_for_display. Tier 1 omits the event summary; tier 0 returns no events.
359 Calendar text is untrusted prompt content.
360 parameters:
361 - name: from
362 in: query
363 required: true
364 schema: { type: string }
365 description: Range start (YYYY-MM-DD or ISO8601).
366 - name: to
367 in: query
368 required: true
369 schema: { type: string }
370 description: Range end (YYYY-MM-DD or ISO8601).
371 - name: agent_context_tier
372 in: query
373 required: true
374 schema: { type: integer, minimum: 0, maximum: 2 }
375 description: Requested agent tier (0 none, 1 busy blocks, 2 titles + label). Clamped by caps.
376 - name: source_calendar_ids
377 in: query
378 required: false
379 schema: { type: string }
380 description: Comma-separated source calendar ids to restrict the agent scope.
381 responses:
382 '200':
383 content:
384 application/json:
385 schema: { $ref: '#/components/schemas/CalendarAgentContext' }
386 '400':
387 content:
388 application/json:
389 schema: { $ref: '#/components/schemas/Error' }
390 '401':
391 content:
392 application/json:
393 schema: { $ref: '#/components/schemas/Error' }
394 '403':
395 content:
396 application/json:
397 schema: { $ref: '#/components/schemas/Error' }
398
399 /calendar/source-calendars:
400 get:
401 tags: [Calendar]
402 summary: List source calendars and display/agent toggles (self-hosted)
403 responses:
404 '200':
405 content:
406 application/json:
407 schema: { $ref: '#/components/schemas/SourceCalendarList' }
408 '401':
409 content:
410 application/json:
411 schema: { $ref: '#/components/schemas/Error' }
412 '403':
413 content:
414 application/json:
415 schema: { $ref: '#/components/schemas/Error' }
416
417 /calendar/source-calendars/{id}:
418 patch:
419 tags: [Calendar]
420 summary: Update source calendar display/agent toggles (self-hosted)
421 description: >
422 Partial update for enabled_for_display, enabled_for_agents, agent_context_tier_max (0–4),
423 and optional user_group. Org policy may cap agent_context_tier_max via
424 KNOWTATION_CALENDAR_AGENT_TIER_MAX_CAP or data/hub_calendar_policy.json.
425 parameters:
426 - name: id
427 in: path
428 required: true
429 schema: { type: string }
430 description: Source calendar id.
431 requestBody:
432 required: true
433 content:
434 application/json:
435 schema: { $ref: '#/components/schemas/SourceCalendarPatchRequest' }
436 responses:
437 '200':
438 content:
439 application/json:
440 schema: { $ref: '#/components/schemas/SourceCalendarPatchResult' }
441 '400':
442 content:
443 application/json:
444 schema: { $ref: '#/components/schemas/Error' }
445 '401':
446 content:
447 application/json:
448 schema: { $ref: '#/components/schemas/Error' }
449 '403':
450 content:
451 application/json:
452 schema: { $ref: '#/components/schemas/Error' }
453 '404':
454 content:
455 application/json:
456 schema: { $ref: '#/components/schemas/Error' }
457
458 /calendar/events/import:
459 post:
460 tags: [Calendar]
461 summary: Import ICS text into the local event store (read-only, self-hosted)
462 requestBody:
463 required: true
464 content:
465 application/json:
466 schema: { $ref: '#/components/schemas/CalendarIcsImportRequest' }
467 responses:
468 '200':
469 content:
470 application/json:
471 schema: { $ref: '#/components/schemas/CalendarIcsImportResult' }
472 '400':
473 content:
474 application/json:
475 schema: { $ref: '#/components/schemas/Error' }
476 '401':
477 content:
478 application/json:
479 schema: { $ref: '#/components/schemas/Error' }
480 '403':
481 content:
482 application/json:
483 schema: { $ref: '#/components/schemas/Error' }
484
485 /metadata-facets:
486 get:
487 tags: [Notes]
488 summary: Body-free MetadataFacets hints for one note
489 description: >
490 Returns knowtation.metadata_facets/v0 metadata for one authorized vault-relative note.
491 The response excludes note body text, snippets, full frontmatter, absolute paths,
492 provider payloads, MCP resource URIs, summaries, labels, vectors, OCR, PageIndex output,
493 media metadata, memory events, persistence records, sidecars, LLM calls, and write-back state.
494 parameters:
495 - name: path
496 in: query
497 required: true
498 schema: { type: string }
499 description: Vault-relative Markdown note path.
500 responses:
501 '200':
502 content:
503 application/json:
504 schema: { $ref: '#/components/schemas/MetadataFacets' }
505 '400':
506 content:
507 application/json:
508 schema: { $ref: '#/components/schemas/Error' }
509 '401':
510 content:
511 application/json:
512 schema: { $ref: '#/components/schemas/Error' }
513 '403':
514 content:
515 application/json:
516 schema: { $ref: '#/components/schemas/Error' }
517 '404':
518 content:
519 application/json:
520 schema: { $ref: '#/components/schemas/Error' }
521 '502':
522 content:
523 application/json:
524 schema: { $ref: '#/components/schemas/Error' }
525
526 /section-source:
527 get:
528 tags: [Notes]
529 summary: Body-free SectionSource metadata for one note
530 description: >
531 Returns knowtation.section_source/v0 metadata for one authorized vault-relative note.
532 The response excludes note body text, section body text, snippets, full frontmatter,
533 line ranges, byte offsets, section body lengths, absolute paths, raw canister payloads,
534 provider payloads, and MCP resource URIs.
535 parameters:
536 - name: path
537 in: query
538 required: true
539 schema: { type: string }
540 description: Vault-relative Markdown note path.
541 responses:
542 '200':
543 content:
544 application/json:
545 schema: { $ref: '#/components/schemas/SectionSource' }
546 '400':
547 content:
548 application/json:
549 schema: { $ref: '#/components/schemas/Error' }
550 '401':
551 content:
552 application/json:
553 schema: { $ref: '#/components/schemas/Error' }
554 '403':
555 content:
556 application/json:
557 schema: { $ref: '#/components/schemas/Error' }
558 '404':
559 content:
560 application/json:
561 schema: { $ref: '#/components/schemas/Error' }
562 '502':
563 content:
564 application/json:
565 schema: { $ref: '#/components/schemas/Error' }
566
567 /index:
568 post:
569 tags: [Notes]
570 summary: Re-run indexer (vault to vector store)
571 responses:
572 '200':
573 content:
574 application/json:
575 schema:
576 type: object
577 properties:
578 ok: { type: boolean }
579 notesProcessed: { type: integer }
580 chunksIndexed: { type: integer }
581 vectors_deleted: { type: integer, description: Rows removed for this vault before upsert (hosted sqlite-vec) }
582 '500':
583 content:
584 application/json:
585 schema: { $ref: '#/components/schemas/Error' }
586
587 /export:
588 post:
589 tags: [Notes]
590 summary: Export one note to content (returns body + filename for client download)
591 requestBody:
592 required: true
593 content:
594 application/json:
595 schema:
596 type: object
597 required: [path]
598 properties:
599 path: { type: string }
600 format: { type: string, enum: [md, html] }
601 responses:
602 '200':
603 content:
604 application/json:
605 schema:
606 type: object
607 properties:
608 content: { type: string }
609 filename: { type: string }
610 '400':
611 content:
612 application/json:
613 schema: { $ref: '#/components/schemas/Error' }
614 '404':
615 content:
616 application/json:
617 schema: { $ref: '#/components/schemas/Error' }
618
619 /import:
620 post:
621 tags: [Notes]
622 summary: Import from uploaded file or ZIP (multipart: source_type; file except for google-sheets; optional project, tags, spreadsheet_id for google-sheets)
623 requestBody:
624 required: true
625 content:
626 multipart/form-data:
627 schema:
628 type: object
629 required: [source_type]
630 properties:
631 source_type:
632 type: string
633 description: Importer id. For google-sheets, omit file and set spreadsheet_id; optional sheets_range (A1 notation). See lib/import-source-types.mjs.
634 file: { type: string, format: binary, description: Required for all importers except google-sheets. }
635 spreadsheet_id:
636 type: string
637 description: Required when source_type is google-sheets (id from the Google Sheets URL).
638 sheets_range:
639 type: string
640 description: Optional for google-sheets; A1 range. Omit to read the first sheet from A1.
641 project: { type: string }
642 output_dir: { type: string }
643 tags: { type: string }
644 responses:
645 '200':
646 content:
647 application/json:
648 schema:
649 type: object
650 properties:
651 imported: { type: array, items: { type: object } }
652 count: { type: integer }
653 '400':
654 content:
655 application/json:
656 schema: { $ref: '#/components/schemas/Error' }
657 '500':
658 content:
659 application/json:
660 schema: { $ref: '#/components/schemas/Error' }
661
662 /import-url:
663 post:
664 tags: [Notes]
665 summary: Import from a public https URL (JSON body; editor/admin)
666 requestBody:
667 required: true
668 content:
669 application/json:
670 schema:
671 type: object
672 required: [url]
673 properties:
674 url: { type: string, description: 'Full https URL' }
675 mode: { type: string, enum: [auto, bookmark, extract], description: 'Capture mode (default auto)' }
676 project: { type: string }
677 output_dir: { type: string }
678 tags: { oneOf: [{ type: string }, { type: array, items: { type: string } }] }
679 responses:
680 '200':
681 content:
682 application/json:
683 schema:
684 type: object
685 properties:
686 imported: { type: array, items: { type: object } }
687 count: { type: integer }
688 '400':
689 content:
690 application/json:
691 schema: { $ref: '#/components/schemas/Error' }
692 '500':
693 content:
694 application/json:
695 schema: { $ref: '#/components/schemas/Error' }
696
697 /settings:
698 get:
699 tags: [Notes]
700 summary: Config status for Settings UI (no secrets)
701 responses:
702 '200':
703 content:
704 application/json:
705 schema:
706 type: object
707 properties:
708 vault_path_display: { type: string }
709 vault_git:
710 type: object
711 properties:
712 enabled: { type: boolean }
713 has_remote: { type: boolean }
714 auto_commit: { type: boolean }
715 auto_push: { type: boolean }
716
717 /vault/sync:
718 post:
719 tags: [Notes]
720 summary: Manual vault backup (git add, commit, push)
721 description: Self-hosted runs local git. Hosted (bridge) pushes notes as Markdown plus `.knowtation/backup/v1/snapshot.json` with full proposals.
722 responses:
723 '200':
724 content:
725 application/json:
726 schema:
727 type: object
728 properties:
729 ok: { type: boolean }
730 message: { type: string }
731 notesCount: { type: integer, description: Hosted bridge only }
732 proposalsCount: { type: integer, description: Hosted bridge only }
733 '400':
734 content:
735 application/json:
736 schema: { $ref: '#/components/schemas/Error' }
737 '500':
738 content:
739 application/json:
740 schema: { $ref: '#/components/schemas/Error' }
741
742 /search:
743 post:
744 tags: [Search]
745 summary: Vault search (semantic or keyword)
746 requestBody:
747 required: true
748 content:
749 application/json:
750 schema:
751 type: object
752 required: [query]
753 properties:
754 query: { type: string }
755 mode: { type: string, enum: [semantic, keyword], description: Omitted or semantic = vector search; keyword = substring/token match on note text }
756 match: { type: string, enum: [phrase, all_terms], description: Keyword only; phrase = full query substring; all_terms = every token must appear }
757 folder: { type: string }
758 project: { type: string }
759 tag: { type: string }
760 since: { type: string }
761 until: { type: string }
762 chain: { type: string }
763 entity: { type: string }
764 episode: { type: string }
765 limit: { type: integer }
766 order: { type: string }
767 fields: { type: string }
768 content_scope: { type: string, enum: [notes, approval_logs], description: Narrow to normal notes vs approvals/ logs }
769 snippetChars: { type: integer }
770 count_only: { type: boolean }
771 countOnly: { type: boolean }
772 responses:
773 '200':
774 content:
775 application/json:
776 schema:
777 type: object
778 properties:
779 results: { type: array, items: { $ref: '#/components/schemas/SearchResult' } }
780 query: { type: string }
781 mode: { type: string, enum: [semantic, keyword] }
782 count: { type: integer, description: Present when count_only keyword search }
783 '400':
784 content:
785 application/json:
786 schema: { $ref: '#/components/schemas/Error' }
787
788 /proposals:
789 get:
790 tags: [Proposals]
791 summary: List proposals
792 parameters:
793 - name: status
794 in: query
795 schema: { type: string }
796 - name: limit
797 in: query
798 schema: { type: integer }
799 - name: offset
800 in: query
801 schema: { type: integer }
802 - name: label
803 in: query
804 description: Match if proposal labels include this string (case-insensitive)
805 schema: { type: string }
806 - name: source
807 in: query
808 schema: { type: string }
809 - name: path_prefix
810 in: query
811 schema: { type: string }
812 - name: evaluation_status
813 in: query
814 description: Filter by evaluation_status (none, pending, passed, failed, needs_changes)
815 schema: { type: string }
816 - name: review_queue
817 in: query
818 description: Exact match on proposal review_queue
819 schema: { type: string }
820 - name: review_severity
821 in: query
822 description: standard or elevated
823 schema: { type: string }
824 responses:
825 '200':
826 content:
827 application/json:
828 schema:
829 type: object
830 properties:
831 proposals: { type: array, items: { $ref: '#/components/schemas/Proposal' } }
832 total: { type: integer }
833 post:
834 tags: [Proposals]
835 summary: Create proposal
836 requestBody:
837 content:
838 application/json:
839 schema:
840 type: object
841 properties:
842 path: { type: string }
843 body: { type: string }
844 frontmatter: { type: object }
845 intent: { type: string }
846 base_state_id: { type: string }
847 external_ref: { type: string }
848 labels: { type: array, items: { type: string } }
849 source: { type: string }
850 responses:
851 '201':
852 content:
853 application/json:
854 schema:
855 type: object
856 properties:
857 proposal_id: { type: string }
858 path: { type: string }
859 status: { type: string, enum: [proposed] }
860 '400':
861
862 /proposals/{id}:
863 get:
864 tags: [Proposals]
865 summary: Get one proposal
866 parameters:
867 - name: id
868 in: path
869 required: true
870 schema: { type: string }
871 responses:
872 '200':
873 content:
874 application/json:
875 schema: { $ref: '#/components/schemas/ProposalDetail' }
876 '404':
877
878 /proposals/{id}/review-hints:
879 post:
880 tags: [Proposals]
881 summary: Store async LLM review hints (canister; not a merge gate)
882 parameters:
883 - name: id
884 in: path
885 required: true
886 schema: { type: string }
887 requestBody:
888 content:
889 application/json:
890 schema:
891 type: object
892 properties:
893 review_hints: { type: string }
894 review_hints_model: { type: string }
895 responses:
896 '200':
897 content:
898 application/json:
899 schema:
900 type: object
901 properties:
902 proposal_id: { type: string }
903 ok: { type: boolean }
904
905 /proposals/{id}/evaluation:
906 post:
907 tags: [Proposals]
908 summary: Submit human evaluation (admin or evaluator)
909 parameters:
910 - name: id
911 in: path
912 required: true
913 schema: { type: string }
914 requestBody:
915 content:
916 application/json:
917 schema:
918 type: object
919 required: [outcome]
920 properties:
921 outcome:
922 type: string
923 enum: [pass, fail, needs_changes]
924 checklist:
925 type: array
926 items:
927 type: object
928 properties:
929 id: { type: string }
930 passed: { type: boolean }
931 grade: { type: string }
932 comment: { type: string }
933 responses:
934 '200':
935 content:
936 application/json:
937 schema: { $ref: '#/components/schemas/ProposalDetail' }
938 '400':
939 '404':
940
941 /proposals/{id}/approve:
942 post:
943 tags: [Proposals]
944 summary: Apply proposal to vault
945 parameters:
946 - name: id
947 in: path
948 required: true
949 schema: { type: string }
950 requestBody:
951 content:
952 application/json:
953 schema:
954 type: object
955 properties:
956 base_state_id: { type: string }
957 waiver_reason:
958 type: string
959 description: Admin override when evaluation is not passed (min length 3 after trim)
960 external_ref:
961 type: string
962 description: Optional cross-system lineage id (e.g. Muse); server may resolve via MUSE_URL when omitted
963 responses:
964 '200':
965 content:
966 application/json:
967 schema:
968 type: object
969 properties:
970 proposal_id: { type: string }
971 status: { type: string, enum: [approved] }
972 external_ref: { type: string }
973 '403':
974 description: EVALUATION_REQUIRED — pass evaluation or provide waiver_reason
975 '409':
976 description: base_state_id mismatch (CONFLICT)
977
978 /proposals/{id}/enrich:
979 post:
980 tags: [Proposals]
981 summary: Optional LLM summary and suggested labels (KNOWTATION_HUB_PROPOSAL_ENRICH=1)
982 parameters:
983 - name: id
984 in: path
985 required: true
986 schema: { type: string }
987 responses:
988 '200':
989 content:
990 application/json:
991 schema: { $ref: '#/components/schemas/ProposalDetail' }
992 '400':
993 description: >-
994 ICP canister — suggested_labels_json or assistant_suggested_frontmatter_json is valid JSON
995 but exceeds max length (4000 / 14000 characters) after validation.
996 '404':
997
998 /proposals/{id}/discard:
999 post:
1000 tags: [Proposals]
1001 summary: Discard proposal
1002 parameters:
1003 - name: id
1004 in: path
1005 required: true
1006 schema: { type: string }
1007 responses:
1008 '200':
1009 content:
1010 application/json:
1011 schema:
1012 type: object
1013 properties:
1014 proposal_id: { type: string }
1015 status: { type: string, enum: [discarded] }
1016
1017 /capture:
1018 post:
1019 tags: [Capture]
1020 summary: Ingest message into vault inbox (webhook-style)
1021 description: Same contract as capture-webhook. If CAPTURE_WEBHOOK_SECRET is set, require X-Webhook-Secret header.
1022 security: []
1023 requestBody:
1024 content:
1025 application/json:
1026 schema:
1027 type: object
1028 required: [body]
1029 properties:
1030 body: { type: string }
1031 source_id: { type: string }
1032 source: { type: string }
1033 project: { type: string }
1034 tags: { type: array, items: { type: string } }
1035 responses:
1036 '200':
1037 content:
1038 application/json:
1039 schema: { type: object, properties: { ok: { type: boolean }, path: { type: string } } }
1040 '400':
1041
1042 components:
1043 securitySchemes:
1044 BearerAuth:
1045 type: http
1046 scheme: bearer
1047 bearerFormat: JWT
1048
1049 schemas:
1050 Error:
1051 type: object
1052 properties:
1053 error: { type: string }
1054 code: { type: string }
1055
1056 NoteListItem:
1057 type: object
1058 properties:
1059 path: { type: string }
1060 title: { type: string, nullable: true }
1061 project: { type: string, nullable: true }
1062 tags: { type: array, items: { type: string } }
1063 date: { type: string, nullable: true }
1064
1065 NoteFull:
1066 type: object
1067 properties:
1068 path: { type: string }
1069 frontmatter: { type: object }
1070 body: { type: string }
1071
1072 SearchResult:
1073 type: object
1074 properties:
1075 path: { type: string }
1076 snippet: { type: string }
1077 score: { type: number }
1078 project: { type: string }
1079 tags: { type: array, items: { type: string } }
1080
1081 NoteOutline:
1082 type: object
1083 required: [schema, path, headings, truncated]
1084 properties:
1085 schema:
1086 type: string
1087 enum: [knowtation.note_outline/v1]
1088 path: { type: string }
1089 title: { type: string, nullable: true }
1090 headings:
1091 type: array
1092 maxItems: 500
1093 items: { $ref: '#/components/schemas/NoteOutlineHeading' }
1094 truncated: { type: boolean }
1095
1096 NoteOutlineHeading:
1097 type: object
1098 required: [level, text, id]
1099 properties:
1100 level: { type: integer, minimum: 1, maximum: 6 }
1101 text: { type: string }
1102 id: { type: string }
1103
1104 DocumentTree:
1105 type: object
1106 required: [schema, path, root, truncated]
1107 properties:
1108 schema:
1109 type: string
1110 enum: [knowtation.document_tree/v0]
1111 path: { type: string }
1112 title: { type: string, nullable: true }
1113 root:
1114 type: object
1115 required: [children]
1116 properties:
1117 children:
1118 type: array
1119 maxItems: 500
1120 items: { $ref: '#/components/schemas/DocumentTreeNode' }
1121 truncated: { type: boolean }
1122
1123 DocumentTreeNode:
1124 type: object
1125 required: [id, level, text, children]
1126 properties:
1127 id: { type: string }
1128 level: { type: integer, minimum: 1, maximum: 6 }
1129 text: { type: string }
1130 children:
1131 type: array
1132 items: { $ref: '#/components/schemas/DocumentTreeNode' }
1133
1134 MetadataFacets:
1135 type: object
1136 required: [schema, path, facets, inferred, truncated]
1137 properties:
1138 schema:
1139 type: string
1140 enum: [knowtation.metadata_facets/v0]
1141 path: { type: string }
1142 facets:
1143 type: object
1144 required: [project, tags, date, updated, causal_chain_id, entity, episode_id]
1145 properties:
1146 project: { type: string, nullable: true }
1147 tags:
1148 type: array
1149 maxItems: 100
1150 items: { type: string }
1151 date: { type: string, nullable: true }
1152 updated: { type: string, nullable: true }
1153 causal_chain_id: { type: string, nullable: true }
1154 entity:
1155 type: array
1156 maxItems: 100
1157 items: { type: string }
1158 episode_id: { type: string, nullable: true }
1159 inferred:
1160 type: object
1161 required: [folder, source_type]
1162 properties:
1163 folder: { type: string, nullable: true }
1164 source_type: { nullable: true, enum: [null] }
1165 truncated: { type: boolean }
1166
1167 SectionSource:
1168 type: object
1169 required: [schema, path, sections, truncated]
1170 properties:
1171 schema:
1172 type: string
1173 enum: [knowtation.section_source/v0]
1174 path: { type: string }
1175 title: { type: string, nullable: true }
1176 sections:
1177 type: array
1178 items: { $ref: '#/components/schemas/SectionSourceSection' }
1179 truncated: { type: boolean }
1180
1181 SectionSourceSection:
1182 type: object
1183 required:
1184 - section_id
1185 - heading_id
1186 - level
1187 - heading_path
1188 - heading_text
1189 - child_section_ids
1190 - body_available
1191 - body_returned
1192 - snippet_returned
1193 properties:
1194 section_id: { type: string }
1195 heading_id: { type: string }
1196 level: { type: integer, minimum: 1, maximum: 6 }
1197 heading_path: { type: array, items: { type: string } }
1198 heading_text: { type: string }
1199 child_section_ids: { type: array, items: { type: string } }
1200 body_available: { type: boolean }
1201 body_returned: { type: boolean, enum: [false] }
1202 snippet_returned: { type: boolean, enum: [false] }
1203
1204 Proposal:
1205 type: object
1206 properties:
1207 proposal_id: { type: string }
1208 path: { type: string }
1209 status: { type: string }
1210 intent: { type: string }
1211 base_state_id: { type: string }
1212 external_ref: { type: string }
1213 vault_id: { type: string }
1214 proposed_by: { type: string }
1215 labels: { type: array, items: { type: string } }
1216 source: { type: string }
1217 suggested_labels: { type: array, items: { type: string } }
1218 assistant_notes: { type: string }
1219 assistant_model: { type: string }
1220 assistant_at: { type: string }
1221 created_at: { type: string }
1222 updated_at: { type: string }
1223 evaluation_status:
1224 type: string
1225 enum: [none, pending, passed, failed, needs_changes]
1226 evaluation_grade: { type: string }
1227 evaluation_comment: { type: string }
1228 evaluated_by: { type: string }
1229 evaluated_at: { type: string }
1230 evaluation_waiver:
1231 type: object
1232 nullable: true
1233 properties:
1234 by: { type: string }
1235 at: { type: string }
1236 reason: { type: string }
1237 review_queue: { type: string }
1238 review_severity: { type: string, enum: [standard, elevated] }
1239 auto_flag_reasons:
1240 type: array
1241 items: { type: string }
1242 auto_flag_reasons_json: { type: string, description: JSON array string on canister }
1243 review_hints: { type: string }
1244 review_hints_at: { type: string }
1245 review_hints_model: { type: string }
1246 assistant_suggested_frontmatter:
1247 type: object
1248 description: Normalized SPEC-aligned suggested note metadata from Enrich (object on GET); omitted or empty on older proposals
1249 additionalProperties: true
1250
1251 ProposalDetail:
1252 allOf:
1253 - { $ref: '#/components/schemas/Proposal' }
1254 - type: object
1255 properties:
1256 body: { type: string }
1257 frontmatter: { type: object }
1258 evaluation_checklist:
1259 type: array
1260 items:
1261 type: object
1262 properties:
1263 id: { type: string }
1264 label: { type: string }
1265 passed: { type: boolean }
1266
1267 CalendarTimeline:
1268 type: object
1269 required: [schema, vault_id, from, to, layers, items]
1270 properties:
1271 schema:
1272 type: string
1273 enum: [knowtation.calendar_timeline/v0]
1274 vault_id: { type: string }
1275 from: { type: string }
1276 to: { type: string }
1277 layers:
1278 type: array
1279 items:
1280 type: string
1281 enum: [notes, events]
1282 items:
1283 type: array
1284 items:
1285 oneOf:
1286 - { $ref: '#/components/schemas/CalendarTimelineNoteItem' }
1287 - { $ref: '#/components/schemas/CalendarTimelineEventItem' }
1288
1289 CalendarTimelineNoteItem:
1290 type: object
1291 required: [kind, date, path, title, project, tags, sort_at]
1292 properties:
1293 kind:
1294 type: string
1295 enum: [note]
1296 date: { type: string }
1297 path: { type: string }
1298 title: { type: string, nullable: true }
1299 project: { type: string, nullable: true }
1300 tags:
1301 type: array
1302 items: { type: string }
1303 sort_at: { type: string }
1304
1305 CalendarTimelineEventItem:
1306 type: object
1307 required: [kind, event_id, source_calendar_id, start, end, timezone, summary, busy, status, calendar_label, sort_at]
1308 properties:
1309 kind:
1310 type: string
1311 enum: [event]
1312 event_id: { type: string }
1313 source_calendar_id: { type: string }
1314 start: { type: string }
1315 end: { type: string }
1316 timezone: { type: string }
1317 summary: { type: string, nullable: true }
1318 busy: { type: boolean }
1319 status:
1320 type: string
1321 enum: [confirmed, cancelled, tentative]
1322 calendar_label: { type: string, nullable: true }
1323 sort_at: { type: string }
1324
1325 CalendarAgentContext:
1326 type: object
1327 required: [schema, vault_id, from, to, requested_tier, effective_tier, policy_agent_context_tier_max_cap, source_calendars, items]
1328 properties:
1329 schema:
1330 type: string
1331 enum: [knowtation.calendar_agent_context/v0]
1332 vault_id: { type: string }
1333 from: { type: string }
1334 to: { type: string }
1335 requested_tier:
1336 type: integer
1337 minimum: 0
1338 maximum: 2
1339 effective_tier:
1340 type: integer
1341 minimum: 0
1342 maximum: 2
1343 description: Requested tier after the org policy cap is applied.
1344 policy_agent_context_tier_max_cap:
1345 type: integer
1346 minimum: 0
1347 maximum: 4
1348 source_calendars:
1349 type: array
1350 items: { $ref: '#/components/schemas/AgentContextCalendarSummary' }
1351 items:
1352 type: array
1353 items: { $ref: '#/components/schemas/CalendarAgentContextEventItem' }
1354
1355 AgentContextCalendarSummary:
1356 type: object
1357 required: [source_calendar_id, display_name, user_group, enabled_for_agents, agent_context_tier_max, effective_tier, event_count]
1358 properties:
1359 source_calendar_id: { type: string }
1360 display_name: { type: string }
1361 user_group:
1362 type: string
1363 nullable: true
1364 enum: [personal, work, school, other, null]
1365 enabled_for_agents: { type: boolean }
1366 agent_context_tier_max:
1367 type: integer
1368 minimum: 0
1369 maximum: 4
1370 effective_tier:
1371 type: integer
1372 minimum: 0
1373 maximum: 2
1374 event_count: { type: integer }
1375
1376 CalendarAgentContextEventItem:
1377 type: object
1378 required: [event_id, source_calendar_id, external_uid, start, end, timezone, busy, status, agent_tier]
1379 description: >
1380 Redacted event. `summary` and `calendar_label` are present only at tier 2;
1381 tier 1 omits the event title entirely.
1382 properties:
1383 event_id: { type: string }
1384 source_calendar_id: { type: string }
1385 external_uid: { type: string }
1386 start: { type: string }
1387 end: { type: string }
1388 timezone: { type: string }
1389 busy: { type: boolean }
1390 status:
1391 type: string
1392 enum: [confirmed, cancelled, tentative]
1393 agent_tier:
1394 type: integer
1395 minimum: 1
1396 maximum: 2
1397 summary: { type: string, nullable: true }
1398 calendar_label: { type: string, nullable: true }
1399
1400 SourceCalendarList:
1401 type: object
1402 required: [schema, vault_id, source_calendars]
1403 properties:
1404 schema:
1405 type: string
1406 enum: [knowtation.source_calendars/v0]
1407 vault_id: { type: string }
1408 source_calendars:
1409 type: array
1410 items: { $ref: '#/components/schemas/SourceCalendar' }
1411
1412 SourceCalendar:
1413 type: object
1414 required: [source_calendar_id, connector_id, display_name, enabled_for_sync, enabled_for_display, enabled_for_agents, agent_context_tier_max]
1415 properties:
1416 source_calendar_id: { type: string }
1417 connector_id: { type: string }
1418 display_name: { type: string }
1419 color: { type: string, nullable: true }
1420 user_group:
1421 type: string
1422 nullable: true
1423 enum: [personal, work, school, other, null]
1424 enabled_for_sync: { type: boolean }
1425 enabled_for_display: { type: boolean }
1426 enabled_for_agents: { type: boolean }
1427 agent_context_tier_max:
1428 type: integer
1429 minimum: 0
1430 maximum: 4
1431 provider: { type: string }
1432
1433 CalendarIcsImportRequest:
1434 type: object
1435 required: [ics_text]
1436 properties:
1437 ics_text: { type: string }
1438 display_name: { type: string }
1439 source_calendar_id: { type: string }
1440 connector_id: { type: string }
1441 default_timezone: { type: string }
1442
1443 CalendarIcsImportResult:
1444 type: object
1445 required: [schema, vault_id, source_calendar_id, connector_id, imported, updated]
1446 properties:
1447 schema:
1448 type: string
1449 enum: [knowtation.calendar_import/v0]
1450 vault_id: { type: string }
1451 source_calendar_id: { type: string }
1452 connector_id: { type: string }
1453 imported: { type: integer }
1454 updated: { type: integer }
1455
1456 SourceCalendarPatchRequest:
1457 type: object
1458 minProperties: 1
1459 properties:
1460 enabled_for_display: { type: boolean }
1461 enabled_for_agents: { type: boolean }
1462 agent_context_tier_max:
1463 type: integer
1464 minimum: 0
1465 maximum: 4
1466 user_group:
1467 type: string
1468 nullable: true
1469 enum: [personal, work, school, other, null]
1470
1471 SourceCalendarPatchResult:
1472 type: object
1473 required: [schema, vault_id, policy_agent_context_tier_max_cap, source_calendar]
1474 properties:
1475 schema:
1476 type: string
1477 enum: [knowtation.source_calendar_patch/v0]
1478 vault_id: { type: string }
1479 policy_agent_context_tier_max_cap:
1480 type: integer
1481 minimum: 0
1482 maximum: 4
1483 source_calendar: { $ref: '#/components/schemas/SourceCalendar' }