{"components":{"schemas":{"ApiSummary":{"description":"Response from GET /api.","properties":{"async_memorize_queue":{"description":"Live snapshot of the async-memorize queue. Operators use `drain_safe` to safely time restarts: when true, no in-flight tasks would be killed by a systemctl restart.","properties":{"completed_24h":{"example":11,"type":"integer"},"drain_safe":{"description":"Shortcut for `pending == 0`. True when a restart will not lose work.","example":true,"type":"boolean"},"failed_24h":{"example":0,"type":"integer"},"lost_24h":{"description":"Count of (lost) rows over the last 24h — a measure of restart cost.","example":22,"type":"integer"},"oldest_pending_age_seconds":{"description":"Age (in seconds) of the oldest pending task. Null when pending = 0.","example":null,"nullable":true,"type":"integer"},"pending":{"description":"Currently in-flight: queued but no (async)/(async-failed)/(lost) completion yet.","example":0,"type":"integer"}},"type":"object"},"endpoints":{"properties":{"agent_contract":{"items":{"type":"string"},"type":"array"},"documentation":{"items":{"type":"string"},"type":"array"},"operator":{"items":{"type":"string"},"type":"array"}},"type":"object"},"ops_token_required":{"description":"True when DONTO_MEMORY_OPS_TOKEN is set on the runtime. /jobs and /explore require the bearer token in that case.","type":"boolean"},"service":{"example":"donto-memory","type":"string"},"substrate_contract_floor":{"example":"0.1.0-m10","type":"string"},"version":{"example":"0.1.0","type":"string"}},"type":"object"},"IngestInput":{"properties":{"holder":{"description":"Agent IRI (e.g. agent:ajax)","type":"string"},"key":{"description":"preference only","nullable":true,"type":"string"},"modality":{"default":"model_output","enum":["descriptive","prescriptive","reconstructed","inferred","elicited","corpus_observed","typological_summary","experimental_result","clinical_observation","legal_holding","archival_metadata","oral_history","community_protocol","model_output","other"],"type":"string"},"object_iri":{"description":"semantic-claim only","nullable":true,"type":"string"},"object_lit":{"description":"semantic-claim only","nullable":true,"type":"object"},"predicate":{"description":"semantic-claim only","nullable":true,"type":"string"},"session_id":{"nullable":true,"type":"string"},"subject":{"description":"semantic-claim only","nullable":true,"type":"string"},"text":{"default":"","type":"string"},"value":{"description":"preference only","nullable":true,"type":"string"}},"required":["holder"],"type":"object"},"JobDetail":{"allOf":[{"$ref":"#/components/schemas/JobRow"},{"properties":{"completion_tokens":{"nullable":true,"type":"integer"},"prompt_tokens":{"nullable":true,"type":"integer"},"request":{"description":"Full request body that was POSTed.","type":"object"},"response":{"description":"Full response body returned to the caller.","type":"object"}},"type":"object"}]},"JobRow":{"description":"One audit-log row as returned by /jobs/list.json.","properties":{"created_at":{"format":"date-time","type":"string"},"elapsed_ms":{"type":"integer"},"endpoint":{"example":"POST /memorize","type":"string"},"error":{"nullable":true,"type":"string"},"facts_extracted":{"nullable":true,"type":"integer"},"facts_ingested":{"nullable":true,"type":"integer"},"holder":{"nullable":true,"type":"string"},"job_id":{"format":"uuid","type":"string"},"model":{"nullable":true,"type":"string"},"rows_returned":{"nullable":true,"type":"integer"},"session_id":{"nullable":true,"type":"string"},"status_code":{"example":200,"type":"integer"},"total_tokens":{"nullable":true,"type":"integer"}},"type":"object"},"MemoryEvidenceBundle":{"properties":{"action":{"type":"string"},"as_of":{"format":"date-time","nullable":true,"type":"string"},"holder":{"type":"string"},"lens":{"nullable":true,"type":"string"},"modules_used":{"items":{"type":"string"},"type":"array"},"policy_report":{"type":"object"},"row_count":{"type":"integer"},"rows":{"items":{"$ref":"#/components/schemas/RecallRow"},"type":"array"}},"type":"object"},"RecallQuery":{"properties":{"action":{"default":"read_content","enum":["read_metadata","read_content","quote","view_anchor_location","derive_claims","derive_embeddings","translate","summarize","export_claims","export_sources","export_anchors","train_model","publish_release","share_with_third_party","federated_query","request_deletion"],"type":"string"},"as_of_tx":{"description":"Bitemporal time travel — what did we know on this date?","format":"date-time","nullable":true,"type":"string"},"holder":{"type":"string"},"lens_name":{"description":"Identity hypothesis name (e.g. 'strict_identity_v1')","nullable":true,"type":"string"},"limit":{"default":50,"minimum":1,"type":"integer"},"min_maturity":{"default":0,"maximum":4,"minimum":0,"type":"integer"},"module_iris":{"items":{"type":"string"},"nullable":true,"type":"array"},"object_iri":{"nullable":true,"type":"string"},"permitted_only":{"default":true,"type":"boolean"},"polarity":{"default":"asserted","type":"string"},"predicate":{"nullable":true,"type":"string"},"query":{"nullable":true,"type":"string"},"session_id":{"nullable":true,"type":"string"},"subject":{"nullable":true,"type":"string"}},"required":["holder"],"type":"object"},"RecallRow":{"properties":{"action_allowed":{"type":"boolean"},"context":{"type":"string"},"effective_actions":{"additionalProperties":{"type":"boolean"},"type":"object"},"maturity":{"type":"integer"},"module_iri":{"nullable":true,"type":"string"},"object_iri":{"nullable":true,"type":"string"},"object_lit":{"nullable":true,"type":"object"},"polarity":{"type":"string"},"predicate":{"type":"string"},"rank":{"nullable":true,"type":"integer"},"record_iri":{"nullable":true,"type":"string"},"resolved_object":{"nullable":true,"type":"string"},"resolved_subject":{"nullable":true,"type":"string"},"score":{"nullable":true,"type":"number"},"statement_id":{"format":"uuid","type":"string"},"subject":{"type":"string"},"tx_hi":{"format":"date-time","nullable":true,"type":"string"},"tx_lo":{"format":"date-time","type":"string"},"valid_hi":{"format":"date","nullable":true,"type":"string"},"valid_lo":{"format":"date","nullable":true,"type":"string"}},"type":"object"}},"securitySchemes":{"opsBearer":{"description":"Operator-only bearer token (set on the runtime via DONTO_MEMORY_OPS_TOKEN). When the env var is unset on the deployment, the gate is a pass-through and these routes are open. Pass via `Authorization: Bearer <token>` or the `?token=<token>` query parameter.","scheme":"bearer","type":"http"}}},"info":{"contact":{"url":"https://github.com/thomasdavis/donto-memory"},"description":"Agentic-memory runtime that runs on top of the donto evidence substrate.\n\nMemory content lives as evidence-anchored claims in donto. donto-memory adds:\n- a *module* abstraction (episodic / semantic-claim / preference);\n- a *hot path* that fuses module outputs into a Memory Evidence Bundle;\n- a *sleep path* that reconsolidates via append-only DontoDelta ops.\n\nSee README at https://github.com/thomasdavis/donto-memory.","license":{"name":"Apache-2.0 OR MIT"},"title":"donto-memory","version":"0.1.0"},"openapi":"3.1.0","paths":{"/":{"get":{"responses":{"200":{"description":"OK"}},"summary":"Service summary + endpoint list","tags":["system"]}},"/agent.md":{"get":{"description":"Comprehensive contract guide aimed at AI agents implementing memory storage and recall. Served as text/markdown.","responses":{"200":{"content":{"text/markdown":{}},"description":"markdown"}},"summary":"Agent-facing markdown guide","tags":["system"]}},"/api":{"get":{"description":"Lists every endpoint grouped as `agent_contract` (never gated), `documentation` (never gated), or `operator` (gated when `DONTO_MEMORY_OPS_TOKEN` is set). The `ops_token_required` flag lets monitoring detect whether the gate is active. Always open.","responses":{"200":{"content":{"application/json":{"example":{"endpoints":{"agent_contract":["GET /health","POST /memorize","POST /recall"],"documentation":["GET /","GET /agent.md","GET /openapi.json"],"operator":["GET /jobs","GET /explore"]},"ops_token_required":true,"service":"donto-memory","substrate_contract_floor":"0.1.0-m10","version":"0.1.0"},"schema":{"$ref":"#/components/schemas/ApiSummary"}}},"description":"Endpoint inventory"}},"summary":"Categorized endpoint summary + ops-token state","tags":["system"]}},"/docs":{"get":{"responses":{"200":{"description":"HTML"}},"summary":"Swagger UI rendering of /openapi.json","tags":["system"]}},"/explore":{"get":{"description":"Three-pane progressive drill-down: holders → sessions → records → facts. Backed by /explore/*.json endpoints below. Same ops-token gate.","responses":{"200":{"description":"HTML"}},"security":[{"opsBearer":[]}],"summary":"HTML memory-explorer page — OPERATOR","tags":["operator"]}},"/explore/facts.json":{"get":{"description":"Two query modes. `?record_iri=ctx:memory/claim/<uuid>` returns the single claim. `?record_iri=ctx:memory/episodic/<uuid>` returns all derived facts via mem:claim/derived_from edges. `?session=X&holder=Y` walks both session-scoped contexts isolated to one holder. `holder` is REQUIRED when `session` is set to prevent cross-holder leakage.","parameters":[{"in":"query","name":"record_iri","schema":{"type":"string"}},{"in":"query","name":"session","schema":{"type":"string"}},{"description":"Required when `session` is set.","in":"query","name":"holder","schema":{"type":"string"}},{"in":"query","name":"limit","schema":{"default":100,"maximum":2000,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{}},"description":"OK"},"400":{"description":"Either `record_iri` or (`session` AND `holder`) required."}},"security":[{"opsBearer":[]}],"summary":"Underlying substrate facts, scoped by record or session — OPERATOR","tags":["operator"]}},"/explore/holders.json":{"get":{"parameters":[{"description":"Substring filter on holder IRI.","in":"query","name":"q","schema":{"type":"string"}},{"in":"query","name":"limit","schema":{"default":100,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{}},"description":"OK"}},"security":[{"opsBearer":[]}],"summary":"Distinct holders with record + session counts — OPERATOR","tags":["operator"]}},"/explore/records.json":{"get":{"parameters":[{"in":"query","name":"holder","required":true,"schema":{"type":"string"}},{"in":"query","name":"session","schema":{"type":"string"}},{"in":"query","name":"module","schema":{"type":"string"}},{"in":"query","name":"limit","schema":{"default":100,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{}},"description":"OK"}},"security":[{"opsBearer":[]}],"summary":"Records owned by a holder, optionally filtered by session/module — OPERATOR","tags":["operator"]}},"/explore/sessions.json":{"get":{"parameters":[{"in":"query","name":"holder","required":true,"schema":{"type":"string"}},{"in":"query","name":"limit","schema":{"default":100,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{}},"description":"OK"}},"security":[{"opsBearer":[]}],"summary":"Sessions for a holder, grouped by module — OPERATOR","tags":["operator"]}},"/explore/stats.json":{"get":{"responses":{"200":{"content":{"application/json":{}},"description":"OK"}},"security":[{"opsBearer":[]}],"summary":"Aggregate overlay stats (records / holders / sessions / modules / recall_events) — OPERATOR","tags":["operator"]}},"/health":{"get":{"responses":{"200":{"description":"OK"}},"summary":"Liveness probe — returns `{status: ok}`.","tags":["system"]}},"/ingest/{module_iri}":{"post":{"description":"`module_iri` is one of the short names (`episodic`, `semantic-claim`, `preference`) or the full IRI (`mem:module/episodic` etc.). The request body's required fields depend on the module:\n\n* **episodic**: `holder`, `session_id`, `text`.\n* **semantic-claim**: `holder`, `subject`, `predicate`, and exactly one of `object_iri` / `object_lit`.\n* **preference**: `holder`, `key`, `value`.\n","parameters":[{"examples":{"episodic":{"value":"episodic"},"full_iri":{"value":"mem:module/episodic"},"preference":{"value":"preference"},"semantic":{"value":"semantic-claim"}},"in":"path","name":"module_iri","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"examples":{"episodic_basic":{"summary":"Episodic chunk","value":{"holder":"agent:ajax","modality":"model_output","session_id":"s-2026-05-28","text":"I met Annie Davis at the Cooktown Festival in 1979."}},"preference_basic":{"summary":"Preference (will supersede prior)","value":{"holder":"agent:ajax","key":"preferred_language","value":"en"}},"semantic_basic":{"summary":"Semantic claim with IRI object","value":{"holder":"agent:ajax","object_iri":"ex:cooktown-festival-1979","predicate":"ex:metAt","subject":"ex:annie-davis"}}},"schema":{"$ref":"#/components/schemas/IngestInput"}}},"required":true},"responses":{"200":{"description":"Record created"},"400":{"description":"Invalid input"},"404":{"description":"Unknown module"}},"summary":"Ingest into a module.","tags":["ingest"]}},"/integration-patterns.md":{"get":{"description":"Concrete TypeScript-flavored patterns for wiring an existing conversational backend (Discord/Slack/web bot) into donto-memory. Tiered by impact, ~2700 words. Read this after /agent.md when building a new integration.","responses":{"200":{"content":{"text/markdown":{}},"description":"markdown"}},"summary":"Markdown ship-recipe spec for agent backend devs","tags":["system"]}},"/jobs":{"get":{"description":"Browseable list of every /memorize, /recall, and /ingest call with full request/response bodies. Filter via the `endpoint` and `holder` query parameters.","parameters":[{"description":"Substring filter on the endpoint label.","in":"query","name":"endpoint","schema":{"type":"string"}},{"description":"Exact-match filter on the holder IRI.","in":"query","name":"holder","schema":{"type":"string"}},{"description":"Max rows to return (1..1000).","in":"query","name":"limit","schema":{"default":100,"type":"integer"}}],"responses":{"200":{"description":"HTML"}},"security":[{"opsBearer":[]}],"summary":"HTML observability page (job history) — OPERATOR","tags":["operator"]}},"/jobs/list.json":{"get":{"description":"Programmatic equivalent of GET /jobs. Same query params; returns `{count, jobs[]}`.","parameters":[{"in":"query","name":"endpoint","schema":{"type":"string"}},{"in":"query","name":"holder","schema":{"type":"string"}},{"in":"query","name":"limit","schema":{"default":100,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"count":{"type":"integer"},"jobs":{"items":{"$ref":"#/components/schemas/JobRow"},"type":"array"}},"type":"object"}}},"description":"OK"}},"security":[{"opsBearer":[]}],"summary":"JSON list view of recent jobs — OPERATOR","tags":["operator"]}},"/jobs/{id}":{"get":{"description":"Per-job page showing the full request/response. For /memorize jobs, also renders every extracted fact as a sortable table.","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"HTML"},"400":{"description":"Invalid UUID"},"404":{"description":"Job not found"}},"security":[{"opsBearer":[]}],"summary":"HTML detail for a single job — OPERATOR","tags":["operator"]}},"/jobs/{id}/raw":{"get":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobDetail"}}},"description":"OK"},"404":{"description":"Job not found"}},"security":[{"opsBearer":[]}],"summary":"Raw JSON for a single job — OPERATOR","tags":["operator"]}},"/llms.txt":{"get":{"description":"Follows the llms.txt convention for AI-agent site documentation. Identical body to /agent.md but served as text/plain.","responses":{"200":{"content":{"text/plain":{}},"description":"plain text"}},"summary":"Same content as /agent.md, plain text","tags":["system"]}},"/memorize":{"post":{"description":"The agent-facing 'save memory' entrypoint. Always writes the raw text as an episodic chunk. If an LLM is configured (env `DONTO_MEMORY_LLM_BASE_URL` + `DONTO_MEMORY_LLM_API_KEY`), also calls the LLM to extract ontological statements about the text — each becomes a semantic-claim record with `source_record_iri` pointing back at the episodic chunk. Without an LLM configured, episodic-only with a warning.","requestBody":{"content":{"application/json":{"examples":{"basic":{"summary":"Plain text memorize","value":{"holder":"agent:my-bot","session_id":"discord:user:12345","text":"I met Annie Davis at the Cooktown Festival in 1979."}},"data_url":{"summary":"Base64 inline image","value":{"holder":"agent:my-bot","images":["data:image/png;base64,iVBORw0K…"],"mode":"single","session_id":"uploads","text":"Screenshot from a chat"}},"image_url":{"summary":"Image-only with OCR + extraction","value":{"holder":"agent:my-bot","images":["https://picsum.photos/seed/example/512/512"],"mode":"single","session_id":"screenshots","text":""}}},"schema":{"properties":{"extract":{"default":true,"description":"Set false to skip LLM extraction and store episodic only.","type":"boolean"},"holder":{"description":"Agent IRI (e.g. agent:my-bot).","type":"string"},"images":{"description":"Optional images. Each entry is an http(s) URL the LLM provider can fetch OR a `data:image/...;base64,…` data URL. When non-empty: (1) the extractor switches to OpenAI multimodal message format and uses `DONTO_MEMORY_LLM_VISION_MODEL` (currently `openai/gpt-4o-mini` in production), (2) an OCR pre-pass transcribes any visible text and prepends it to the episodic chunk as `[OCR text from image #N]\\n<transcript>` blocks — visible labels in screenshots/signs/captions become searchable via /recall query=. Disable OCR via DONTO_MEMORY_OCR_ENABLED=false.","items":{"type":"string"},"type":"array"},"modality":{"default":"model_output","description":"How this chunk came to exist. Values: model_output | descriptive | oral_history | community_protocol | inferred | reconstructed | elicited | experimental_result | clinical_observation.","type":"string"},"mode":{"description":"Extraction mode. `single` = one LLM call (~30-100 facts, ~30-100s). `exhaustive` = five parallel apertures (~80-250 facts, ~60-180s, ~5× tokens). Defaults to DONTO_MEMORY_EXTRACT_MODE on the runtime.","enum":["single","exhaustive","multi","apertures"],"type":"string"},"session_id":{"description":"Optional scope. Conventional shapes: `discord:user:<id>`, `conversation:<id>`. Recall can filter on this.","nullable":true,"type":"string"},"text":{"description":"The memory to store. Required unless `images` is non-empty (in which case OCR populates text).","type":"string"}},"required":["holder"],"type":"object"}}},"required":true},"responses":{"200":{"description":"Episodic record + (optional) semantic claim records"},"400":{"description":"Invalid input"}},"summary":"Save memory — episodic chunk + LLM-extracted semantic claims","tags":["ingest"]}},"/memorize/batch":{"post":{"description":"Same flow as /memorize for each item in `items[]`. Per-item failures do not abort the rest; each result carries either the response or an error.","requestBody":{"content":{"application/json":{"schema":{"properties":{"items":{"items":{"$ref":"#/components/schemas/IngestInput"},"type":"array"}},"required":["items"],"type":"object"}}},"required":true},"responses":{"200":{"description":"Per-item results"}},"summary":"Batch memorize","tags":["ingest"]}},"/modules":{"get":{"responses":{"200":{"description":"OK"}},"summary":"List registered modules (runtime + DB).","tags":["modules"]}},"/openapi.json":{"get":{"responses":{"200":{"description":"OK"}},"summary":"This OpenAPI 3.1 document","tags":["system"]}},"/recall":{"post":{"description":"Composes a single-call response across every enabled module. Substrate /recall is consulted for the policy gate + identity-lens resolution; donto-memory adds RRF fusion across modules and bookkeeping (access events + reconsolidation enqueue).","requestBody":{"content":{"application/json":{"examples":{"as_of":{"summary":"Bitemporal time-travel","value":{"action":"read_metadata","as_of_tx":"2026-05-01T00:00:00Z","holder":"agent:ajax","limit":50}},"basic":{"summary":"Permission-gated recall","value":{"action":"read_content","holder":"agent:ajax","limit":20,"permitted_only":true,"query":"Annie Davis","session_id":"s-2026-05-28"}}},"schema":{"$ref":"#/components/schemas/RecallQuery"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryEvidenceBundle"}}},"description":"Memory Evidence Bundle"}},"summary":"Memory Evidence Bundle","tags":["recall"]}},"/reconsolidate/enqueue":{"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"priority":{"default":0.0,"type":"number"},"reason":{"default":"explicit","type":"string"},"record_id":{"format":"uuid","type":"string"}},"required":["record_id"],"type":"object"}}},"required":true},"responses":{"200":{"description":"Enqueued"},"404":{"description":"Record not found"}},"summary":"Manually enqueue a record for sleep-path reconsolidation","tags":["reconsolidate"]}},"/reconsolidate/queue":{"get":{"responses":{"200":{"description":"OK"}},"summary":"List head-of-queue items (unfinished work)","tags":["reconsolidate"]}},"/substrate":{"get":{"description":"Verifies donto-memory is bound to the substrate the operator expects. No state mutated.","responses":{"200":{"description":"OK"}},"summary":"Echo dontosrv /discovery/contract-version + /discovery/substrate-health","tags":["system"]}},"/version":{"get":{"responses":{"200":{"description":"OK"}},"summary":"Service version + substrate contract floor","tags":["system"]}}},"servers":[{"description":"production","url":"https://memories.apexpots.com"},{"description":"local","url":"http://localhost:7900"}],"tags":[{"description":"Health / version / substrate handshake.","name":"system"},{"description":"Memory module discovery.","name":"modules"},{"description":"Write a unit of memory into a module.","name":"ingest"},{"description":"Read a Memory Evidence Bundle.","name":"recall"},{"description":"Sleep-path queue management.","name":"reconsolidate"},{"description":"Observability surfaces (/jobs, /explore). Gated by the optional ops bearer token — see the `opsBearer` security scheme. Open when the runtime has no `DONTO_MEMORY_OPS_TOKEN` set.","name":"operator"}]}