donto API reference
donto is a contradiction-preserving CLAIM/DISCOVERY substrate: a bitemporal, paraconsistent, evidence-first knowledge base that holds incompatible claims forever as legal state, anchors each to its source, links them with typed argument edges, and re-ranks by reality over time instead of deleting on conflict. This document is the developer-facing reference for donto's programmatic surfaces — every HTTP API you can use to read, write, and query the substrate, plus its MCP (Model Context Protocol) tools. It covers five services (a memory API, a distributed embedding coordinator, a genealogy extraction/ingest API, the core substrate sidecar, and a read-only admin console), the authentication model, the shared bitemporal data model, and end-to-end quickstart recipes.
Overview
| Service | Public base URL | Purpose | Auth |
|---|---|---|---|
| donto-memory | https://memories.apexpots.com |
Agentic episodic + semantic memory: memorize, recall, substrate-wide search, module ingest, reconsolidation, operator observability. | Public for the agent contract; operator routes (/jobs/*, /explore/*) gated by an optional bearer token. |
| donto-embed-coordinator | https://donto.org/embed (also https://memories.apexpots.com/embed) |
Distributed embedding work queue: enqueue items, lease batches, submit computed vectors. Lets spare machines/GPUs embed at billion scale. | Bearer token on every endpoint except /embed/health. |
| donto-api (genealogy) | https://genes.apexpots.com |
LLM extraction + ingest into the knowledge graph, plus rich read/search/graph/alignment endpoints over the substrate. | Public (no auth). |
| dontosrv (core substrate) | https://genes.apexpots.com (internal localhost:7879) |
The core bitemporal claim substrate HTTP sidecar: ~70 endpoints for the claim write-path, read/search, evidence, trust kernel, arguments, alignment, shapes/rules, and schema discovery. | Public reads; writes ungated at the HTTP layer (policy gates applied at the SQL layer per context/action). |
| donto-admin | https://admin.donto.org |
Read-only observability console: substrate metrics, contexts, fabric, processes, ingestion jobs, per-job charts/nebula/panels, relation introspection, BEAM progress. | Read-only Postgres role (donto_ro); no per-request credentials. |
donto-memory-mcp is not a separate HTTP service — it is the MCP front-end over donto-memory's
/recall,/search, and/memorizeendpoints. See MCP access.
Authentication
donto's programmatic surfaces use three distinct auth postures. There is no per-request API key for the public substrate — the substrate is openly readable and (for the genealogy/substrate write-path) openly writable by design.
The token model
Public endpoints (no auth). Most of the agent contract is public: donto-memory's
/health,/version,/modules,/memorize,/recall,/search,/search/resources,/ingest,/reconsolidate/*; all of donto-api; all of dontosrv reads/writes. Just send the request.Bearer token (embed coordinator + optional memory ops).
- donto-embed-coordinator requires
Authorization: Bearer <TOKEN>on every endpoint except/embed/health. The token is read from theDONTO_EMBED_TOKENenvironment variable, which is sourced from the secret file/etc/donto/embed-coordinator.token(server-side; never expose it). A missing or invalid token returns401 Unauthorized. If the env var is unset, the service runs open (dev mode only). - donto-memory operator routes (
/jobs,/jobs/list.json,/jobs/:id,/jobs/:id/raw,/explore,/explore/*.json) requireAuthorization: Bearer <TOKEN>only ifDONTO_MEMORY_OPS_TOKENis set on the server. If it is unset, these routes are a pass-through (local dev). The/apiendpoint'sops_token_requiredfield tells you which mode is live.
- donto-embed-coordinator requires
Read-only Postgres role (admin console). donto-admin requires no per-request credentials; access is enforced at the database level. It connects with the
donto_rorole (via the server-sideDONTO_RO_DSNenv var), which is locked toSELECT-only withdefault_transaction_read_only=on.
Where keys live (by reference, never inline)
| Secret | Server-side location | Used by |
|---|---|---|
| Embed coordinator bearer token | /etc/donto/embed-coordinator.token (env DONTO_EMBED_TOKEN, from /etc/donto/embed-coordinator.env) |
donto-embed-coordinator |
| Memory ops token | env DONTO_MEMORY_OPS_TOKEN (set on the donto-memory server if gating is enabled) |
donto-memory operator routes |
| Read-only DSN | env DONTO_RO_DSN (defaults to a [email protected]:5432/donto connection string) |
donto-admin |
Never paste the literal secret values into requests, configs, or logs. Pass tokens only as Authorization: Bearer <TOKEN>, substituting the real value from the secret store at runtime.
Passing tokens
Authorization: Bearer <TOKEN>
The Cloudflare User-Agent gotcha
All public domains are proxied through Cloudflare. Cloudflare blocks some default client User-Agents (e.g. plain python-urllib) with a 403 / Cloudflare error 1010. Set a non-default User-Agent such as curl/8 — curl, reqwest, and aiohttp pass by default, but if you build raw requests, add a User-Agent header:
curl -A 'curl/8' https://donto.org/embed/health
This is required against donto.org / memories.apexpots.com / genes.apexpots.com whenever your client library would otherwise send a blocked UA.
Conventions
Base URLs
Use the public base URLs from the Overview table. Internal ports (:7900, :7930, :7879, :8000, :3103, :8233) are reachable only on the host and are noted for operators; external developers use the HTTPS domains.
Content type
All request bodies and responses are JSON unless noted. Send Content-Type: application/json on POSTs with a body. A handful of endpoints return other media types (text/plain for /metrics, /health on dontosrv, the DontoQL grammar; text/markdown for /agent.md; HTML for /jobs, /explore, /docs; Server-Sent Events for /firehose/stream) — these are called out per endpoint.
The bitemporal / context data model
A caller must understand a few substrate concepts that recur in every payload:
- Statement (claim). The atomic unit:
subject predicate objectin acontext. Objects are either an IRI (object_iri) or a literal (object_lit, an object{v: value, dt: xsd-type, lang?: language}— e.g.{"v":"1860-01-15","dt":"xsd:date"}). Subject and object exactly one of IRI/literal. - IRIs. Subjects are minted
ex:kebab-case(e.g.ex:mary-watson). Predicates arecamelCase(e.g.bornIn,marriedTo). Contexts arectx:namespace/topic/source(e.g.ctx:genes/mary-watson/obituary) ordonto:.../mem:...namespaced IRIs. Memory holders are IRIs likeagent:my-botoruser:alice; sessions likediscord:user:12345. Predicates are freely minted by extractors (abundance) — variants are reconciled at query time by the alignment engine, not by a fixed schema. - Contexts. Named scopes (sources, extraction runs, research questions). Facts are scoped per context, and contexts can carry policies. Examples:
ctx:genes/<topic>/<source>,ctx:extract/<file>/<model>,mem:module/episodic. - Bitemporality. Every statement carries two time axes:
- transaction time (
tx_time, exposed astx_lo/tx_hi): when the system recorded / retracted the fact. An open interval (tx_hi/upper(tx_time)is null) means the fact is currently believed; a closed interval means it was retracted (soft-deleted, never destroyed). - valid time (
valid_from/valid_to, exposed asvalid_lo/valid_hi): when the fact became / ceased being true in the world. Distinct from when it was recorded. - Time-travel: read endpoints accept
as_of_tx(RFC3339) to ask "what did we believe on this past date?" andas_of_valid(date) for valid-time slicing.
- transaction time (
- Polarity. Statements are
asserted|negated|absent|unknown. (In rawflags, the low 2 bits encode polarity: 0=asserted, 1=negated, 2=absent, 3=unknown.) - Maturity (the E0–E4 / 0–4 ladder).
0=L0 raw,1=L1 registered,2=L2 evidenced,3=L3 validated,4=L4 certified. Higher maturity is prioritized in queries. (Admin charts may show buckets0–7.) - Evidence anchoring. Facts link to sources via
donto_evidence_link → donto_span → donto_document_revision → blob. A span carriessurface_text(the anchored snippet) plus character offsets. Extraction and anchoring are separate stages; anchorless facts are held honestly rather than given bogus spans. - Identity-as-hypothesis. Subjects may be the "same" entity under an identity lens (
lens_name); resolution happens at query time, not by destructive merge.
Error shape
Errors vary slightly by service:
- dontosrv / donto-api / donto-embed-coordinator: a JSON object with an
errorfield, e.g.{ "error": "..." }, possibly withdetail/reason. - donto-admin: a uniform envelope
{ "ok": false, "error": string, "at": ISO8601 }, HTTP 500 unless noted. Successful admin responses are{ "ok": true, "data": T, "at": ISO8601 }, never cached (cache-control: no-store). - HTTP statuses:
400bad input (empty query, missing/invalid fields, unknown module),401missing/invalid token (embed coordinator, gated memory ops),404not found (record, job, revision, unknown module/panel),500substrate/DB/extraction failure,202async deferred (extraction queued). Search endpoints degrade gracefully: on a 9s timeout they returnpartial: truewith partial results instead of failing.
donto-memory
https://memories.apexpots.com — an agentic episodic + semantic memory substrate. It stores raw text (episodic) alongside LLM-extracted structured claims (semantic) into the bitemporal substrate, scoped by holder (agent IRI, for recall isolation) and optionally session. Memory is composed over mem:module/* contexts and stored as donto_statement rows.
Extraction modes: single (one LLM call, ~20–100 facts), exhaustive (five parallel apertures, ~80–250 facts, ~5× token cost), deep (N sequential passes, default 3, max 10, dedup at end). The default mode is the server's DONTO_MEMORY_EXTRACT_MODE (defaults to exhaustive). Slow modes (deep, exhaustive) return 202 Accepted and run on a Temporal durable queue (with a tokio fallback); fast modes return 200.
OpenAPI spec: https://memories.apexpots.com/openapi.json. Swagger UI: https://memories.apexpots.com/docs.
Caveats (live-verified 2026-06-07):
POST /searchandPOST /search/resourcesare served and return real data, but are absent from the publishedopenapi.json— don't rely on the spec alone to discover them (they're documented below). Operator routes (/jobs/*,/explore/*) are gated only whenDONTO_MEMORY_OPS_TOKENis set; the currently deployed instance reportsops_token_required: false(ungated), so those routes answer without a token right now — checkGET /apifor the live mode rather than assuming a 401.
GET /health
Health check. Auth: none.
- Request: no parameters.
- Response:
{status: string}—{"status":"ok"}if running.
curl https://memories.apexpots.com/health
GET /version
Service version and configuration. Auth: none.
- Request: no parameters.
- Response:
{service: string, version: string, substrate_url: string, substrate_contract_floor: string}.
curl https://memories.apexpots.com/version
GET /substrate
Substrate health + contract details. Auth: none.
- Request: no parameters.
- Response:
{contract: {actions: [...], ...}, health: {...}, substrate_contract_floor: string}. Runscontract_version()andsubstrate_health()in parallel with an 8s timeout;healthdegrades gracefully if substrate counts are slow.
curl https://memories.apexpots.com/substrate
GET /api
API summary and endpoint listing. Auth: none.
- Request: no parameters.
- Response:
{service, version, substrate_contract_floor, ops_token_required: boolean, async_memorize_queue?: {pending, completed_24h, failed_24h, lost_24h, drain_safe}, endpoints: {agent_contract: string[], documentation: string[], operator: string[]}}.ops_token_requiredtells you whether operator routes are gated.
curl https://memories.apexpots.com/api
GET /modules
List memory modules. Auth: none.
- Request: no parameters.
- Response:
{modules: [{module_iri, label, description, form, function, version, source, enabled_in_db?: boolean}]}. Standard modules:mem:module/episodic,mem:module/semantic-claim,mem:module/preference.
curl https://memories.apexpots.com/modules
POST /memorize
Save an episodic chunk + LLM-extracted semantic claims. Auth: none.
- Request body:
holder(string, required) — agent IRI scope, e.g.agent:my-bot.session_id(string, optional) — e.g.discord:user:12345.text(string) — the chunk (required unlessimagesprovided).modality(string, optional, defaultmodel_output).extract(boolean, optional, defaulttrue) — extract semantic claims via LLM.mode(string, optional) —single|exhaustive|deep.passes(u32, optional) — formode=deep(1–10, default 3).async(boolean, optional) — force deferred execution.images(string[], optional) —http(s)URLs ordata:image/...;base64; OCR transcription is prepended to episodic text when enabled.facts(ExtractedFact[], optional) — pre-extracted facts from upstream.valid_from(string, optional) —YYYY-MM-DDor RFC3339; when the fact became true in the world (distinct fromtx_time).
- Response (200 sync, or 202 async):
{holder, session_id, episodic_record_id: uuid, episodic_record_iri, extracted: boolean, extract_mode, facts_extracted, facts_ingested, dedup_collisions, semantic_record_ids: uuid[], model?, usage?: {completion_tokens, input_tokens, output_tokens}, aperture_yields: [], facts: ExtractedFact[], elapsed_ms, warnings: string[], queue_id?: uuid}. On 202:{status:"queued", queue_id, durable: boolean, holder, session_id, extract_mode, passes, note}. - Behavior: async (
202) whenmodeisdeep/exhaustive/opencodeorasync=true; else sync (200). Poll/jobsor/recallfor async results.
curl -X POST https://memories.apexpots.com/memorize \
-H 'Content-Type: application/json' \
-d '{"holder":"agent:my-bot","session_id":"discord:user:12345","text":"I met Annie Davis in 1979."}'
POST /memorize/batch
Bulk memorize. Auth: none.
- Request body:
{items: MemorizeReq[]}— an array of/memorizerequest bodies. Processed sequentially. - Response:
{results: object[]}— each is aMemorizeResp(same fields as/memorize) or{error, holder, text_preview}.
curl -X POST https://memories.apexpots.com/memorize/batch \
-H 'Content-Type: application/json' \
-d '{"items":[{"holder":"agent:my-bot","text":"First memory"},{"holder":"agent:my-bot","text":"Second memory"}]}'
POST /recall
Holder-scoped episodic + semantic recall bundle (hybrid lexical + semantic, bitemporal). Auth: none.
- Request body:
holder(string, required) — scope isolation.query(string, optional) — hybrid search term.limit(number, optional, default 50; clamped 1–configured max).session_id(string, optional).subject/predicate/object_iri(string, optional) — exact IRI filters.action(string, optional, defaultread_content) — policy action.polarity(string, optional, defaultasserted).as_of_tx(RFC3339, optional) — bitemporal time-travel.as_of_valid(date, optional) — valid-time slice.lens_name(string, optional) — identity hypothesis.min_maturity(0–4, optional).module_iris(string[], optional).permitted_only(boolean, optional, defaulttrue).
- Response:
{holder, action, modules_used: string[], row_count, rows: [{statement_id, subject, predicate, object_iri, object_lit: {v, dt, lang?}, context, flags, maturity, valid_from, valid_to, as_of, provenance}], policy_report: {default_action, permitted_only}, lens?: string, as_of?: string}. Composed overmem:module/*contexts; the policy report is always included.
curl -X POST https://memories.apexpots.com/recall \
-H 'Content-Type: application/json' \
-d '{"holder":"agent:my-bot","limit":10}'
POST /search
Substrate-wide ranked full-text search over names. Auth: none (intentionally public).
- Request body:
query(string, required, non-empty) — lexemes are ANDed.context_prefix(string, optional) — scope filter,LIKE '%prefix%'(e.g.ctx:genes).limit(number, optional, default 50; clamped 1–500).
- Response:
{query, context_prefix, row_count, rows: [{statement_id, subject, predicate, object_iri, object_lit, context, score}], partial: boolean, elapsed_ms, note?}. Searches IRI path segments,rdfs:labelliterals, and short literal prefixes across the entire substrate (≈39M rows), backed by thedonto_statement_fts_nameGIN index. Candidate set capped at 2000 rows;partial=trueif the 9s timeout is exceeded;scoreists_rank.
curl -X POST https://memories.apexpots.com/search \
-H 'Content-Type: application/json' \
-d '{"query":"love","limit":3}'
POST /search/resources
Full-text search over resource source bodies. Auth: none.
- Request body:
{query: string (required, non-empty), limit?: number (default 20, clamped 1–200)}. - Response:
{query, row_count, rows: [{document_id, revision_id, score, snippet, blob_uri, body_uri}], partial: boolean, elapsed_ms, note?}. Searchesdonto_document_revision.body(raw source text); returns the latest revision per document, a highlighted snippet (max ~300 chars,ts_headlineMaxFragments=1/MaxWords=30),blob_uri(gs://apex-494316-donto/sha256/<hash>for stored blobs) andbody_uri(file://legacy). Candidate cap 300 rows.
curl -X POST https://memories.apexpots.com/search/resources \
-H 'Content-Type: application/json' \
-d '{"query":"love","limit":3}'
POST /ingest/:module_iri
Direct module ingest (bypass LLM extraction). Auth: none.
- Path param:
module_iri—mem:module/episodic(text),mem:module/semantic-claim(structured claims),mem:module/preference(key-value). Short names are canonicalized (episodic→mem:module/episodic). - Request body:
IngestInput{holder?, session_id?, ...module-specific fields}. - Response:
{record_id: uuid, record_iri, module_iri, anchored_to: {statement_id: uuid, frame_id: uuid, context_iri}}.404if module not registered,400for invalid input.
curl -X POST 'https://memories.apexpots.com/ingest/mem:module/episodic' \
-H 'Content-Type: application/json' \
-d '{"holder":"agent:my-bot","session_id":"test:1","text":"Raw episodic chunk"}'
POST /reconsolidate/enqueue
Enqueue a record for reconsolidation. Auth: none.
- Request body:
{record_id: uuid (required), reason?: string (defaultexplicit), priority?: f64}. Higher priority is processed first (default 0.0). - Response:
{queue_id: uuid, record_id, reason, priority, available_at: RFC3339}.404if record not found. Adds todonto_x_memory_reconsolidation_queuewith a coalesce window.
curl -X POST https://memories.apexpots.com/reconsolidate/enqueue \
-H 'Content-Type: application/json' \
-d '{"record_id":"<uuid>","reason":"explicit","priority":1.0}'
GET /reconsolidate/queue
List the reconsolidation queue. Auth: none.
- Query params:
limit?(default 100, clamped 1–1000). - Response:
{count, items: [{queue_id, record_id, reason, priority, available_at, claimed_at?, claimed_by?, completed_at?}]}. Shows pending entries (completed_at IS NULL), sorted by priority DESC,available_atASC.
curl 'https://memories.apexpots.com/reconsolidate/queue?limit=5'
GET /jobs
Jobs list (HTML view). Auth: bearer token if ops_token_required=true.
- Query params:
endpoint?(ilike filter),holder?(exact),limit?(default 100, clamped 1–1000). - Response:
200 OKHTML with a filterable audit table (fromdonto_x_memory_job_log).
curl -H 'Authorization: Bearer <TOKEN>' https://memories.apexpots.com/jobs
GET /jobs/list.json
Jobs list (JSON). Auth: bearer token if ops_token_required=true.
- Query params:
endpoint?,holder?,limit?(default 100, clamped 1–1000). - Response:
{count, jobs: [{job_id, created_at, endpoint, holder, session_id, status_code, elapsed_ms, facts_extracted?, facts_ingested?, rows_returned?, model?, total_tokens?, error?}]}.
curl -H 'Authorization: Bearer <TOKEN>' https://memories.apexpots.com/jobs/list.json
GET /jobs/:id
Job detail (HTML). Auth: bearer token if ops_token_required=true.
- Path param:
id(uuid). - Response:
200 OKHTML with the full job detail (request body, response body, metrics, elapsed time), or404.
curl -H 'Authorization: Bearer <TOKEN>' \
https://memories.apexpots.com/jobs/550e8400-e29b-41d4-a716-446655440000
GET /jobs/:id/raw
Job detail (JSON). Auth: bearer token if ops_token_required=true.
- Path param:
id(uuid). - Response:
JobDetail {job_id, created_at, endpoint, holder, session_id, status_code, elapsed_ms, request, response, facts_extracted?, facts_ingested?, rows_returned?, model?, prompt_tokens?, completion_tokens?, total_tokens?, error?}, or404.
curl -H 'Authorization: Bearer <TOKEN>' \
https://memories.apexpots.com/jobs/550e8400-e29b-41d4-a716-446655440000/raw
GET /explore
Interactive memory explorer (HTML). Auth: bearer token if ops_token_required=true.
- Request: no parameters.
- Response:
200 OKself-contained HTML/JS explorer that drills holder → sessions → records → facts via the/explore/*.jsonendpoints below.
curl -H 'Authorization: Bearer <TOKEN>' https://memories.apexpots.com/explore
GET /explore/stats.json
Memory system statistics. Auth: bearer token if ops_token_required=true.
- Response:
{holders, sessions, records, modules, recall_events}.
curl -H 'Authorization: Bearer <TOKEN>' https://memories.apexpots.com/explore/stats.json
GET /explore/holders.json
List memory holders. Auth: bearer token if ops_token_required=true.
- Query params:
q?(ilike match),limit?(default 100, clamped 1–1000). - Response:
{count, holders: [{holder, records, sessions, first_seen, last_seen}]}, ordered bylast_seenDESC.
curl -H 'Authorization: Bearer <TOKEN>' \
'https://memories.apexpots.com/explore/holders.json?limit=10'
GET /explore/sessions.json
List sessions under a holder. Auth: bearer token if ops_token_required=true.
- Query params:
holder(required),limit?(default 100, clamped 1–1000). - Response:
{holder, count, sessions: [{session, module_iri, records, first_seen, last_seen}]}. Session(no session)for null session IRIs.
curl -H 'Authorization: Bearer <TOKEN>' \
'https://memories.apexpots.com/explore/sessions.json?holder=agent%3Amy-bot'
GET /explore/records.json
List records under a holder/session. Auth: bearer token if ops_token_required=true.
- Query params:
holder(required),session?,module?,limit?(default 100, clamped 1–1000). - Response:
{holder, count, records: [{record_id, record_iri, module_iri, session_iri?, holder_iri?, root_statement?, root_context?, created_at, metadata}]}, ordered bycreated_atDESC.
curl -H 'Authorization: Bearer <TOKEN>' \
'https://memories.apexpots.com/explore/records.json?holder=agent%3Amy-bot&session=discord%3Auser%3A12345'
GET /explore/facts.json
List facts (statements) derived from a record or session. Auth: bearer token if ops_token_required=true.
- Query params:
record_iri?(ctx:memory/claim/<uuid>orctx:memory/episodic/<uuid>),session?,holder?(required whensessionis set),limit?(default 100, clamped 1–2000). - Response:
{count, facts: [{statement_id, subject, predicate, object_iri, object_lit, context, flags, polarity, tx_lo}]}. Eitherrecord_iriorsessionis required;holderis required whensessionis set (cross-holder isolation).flagslow 2 bits encode polarity.
curl -H 'Authorization: Bearer <TOKEN>' \
'https://memories.apexpots.com/explore/facts.json?record_iri=ctx%3Amemory%2Fepisodic%2F550e8400-e29b-41d4-a716-446655440000'
GET /openapi.json
OpenAPI 3.0 specification. Auth: none. Returns the full JSON spec (cached at startup).
curl https://memories.apexpots.com/openapi.json | jq .
Documentation surfaces (auth: none)
GET /docs— Swagger UI (HTML), loads/openapi.json.GET /agent.md— integration guide for AI agents (text/markdown).GET /llms.txt— same content at the canonicalllms.txtpath (text/plain).GET /integration-patterns.md— integration patterns and recipes (text/markdown).GET /— homepage (HTML, with ETag / Cache-Control).
curl https://memories.apexpots.com/agent.md
donto-embed-coordinator
https://donto.org/embed (also https://memories.apexpots.com/embed) — a distributed embedding work queue that offloads CPU-bound bge-small embedding to spare machines and GPUs. A worker is dumb: lease → embed (BAAI/bge-small-en-v1.5, 384 dims) → submit; it never touches the database directly. The queue uses FOR UPDATE SKIP LOCKED for concurrent-safe distribution, with ~15-minute stale-lease reclaim. Three providers ship: memory_chunk, predicate, entity — all 384-dim.
Every endpoint requires Authorization: Bearer <TOKEN> except /embed/health. The model is pinned; do not change it without re-embedding the entire fabric. No OpenAPI spec is exposed. Remember the Cloudflare UA gotcha — add -A 'curl/8'.
GET /embed/health
Health check with provider list. Auth: none.
- Request: no body.
- Response:
{ok: boolean, targets: string[]}— always{"ok": true, "targets": ["memory_chunk", "predicate", "entity"]}.
curl -A 'curl/8' https://donto.org/embed/health
GET /embed/stats
Embedding queue statistics and per-provider source/embedded counts. Auth: Bearer token.
- Request: no body / query params.
- Response:
{ "<provider>": {source: integer|null, embedded: integer|null, queue: {pending, leased, done}} }for each ofmemory_chunk,predicate,entity. Source/embedded counts are cached (TTL 300s, configurable viaEMBED_COUNT_TTL_S); queue counts are live.401if token missing/invalid.
curl -A 'curl/8' -H 'Authorization: Bearer <TOKEN>' https://donto.org/embed/stats
POST /embed/enqueue
Enqueue pending items from a source into the embedding work queue. Auth: Bearer token.
- Request body:
{target: string (required: memory_chunk|predicate|entity), limit?: integer (default 50000)}. - Response:
{target, enqueued: integer}. Scans the source table for items missing embeddings and inserts up tolimitintodonto_embed_queuewithstatus='pending'. Idempotent (ON CONFLICT (target,item_id) DO NOTHING).401if token missing/invalid;400if target unknown.
curl -A 'curl/8' -X POST -H 'Authorization: Bearer <TOKEN>' -H 'Content-Type: application/json' \
-d '{"target":"memory_chunk","limit":10000}' \
https://donto.org/embed/enqueue
POST /embed/lease
Lease embedding items for a worker to compute. Auth: Bearer token.
- Request body:
{worker_id?: string (defaultanon), n?: integer (default 128, capped 1024), target?: string (filter to one provider)}. - Response:
{batch: [{target, item_id, text, model, dim}]}.modelisBAAI/bge-small-en-v1.5,dimis 384. Atomically leases up tonpending items (or stale leases older thanEMBED_LEASE_TTL_MIN=15min), settingstatus='leased',leased_by,leased_at. UsesFOR UPDATE SKIP LOCKEDto avoid double-booking; leases auto-reclaim if not submitted within the TTL.
curl -A 'curl/8' -X POST -H 'Authorization: Bearer <TOKEN>' -H 'Content-Type: application/json' \
-d '{"worker_id":"worker-1","n":100,"target":"memory_chunk"}' \
https://donto.org/embed/lease
POST /embed/submit
Submit computed embeddings and mark the queue items done. Auth: Bearer token.
- Request body:
{items: [{target: string (memory_chunk|predicate|entity), item_id: string, vector: number[]}]}. Vector length must match the provider's dim (384 for all current providers). - Response:
{upserted: integer}. Upserts vectors into the target table (donto_x_memory_chunk_embedding,donto_predicate_embedding,donto_entity_embedding), setsstatus='done', records the textsig_hash. Wrong-dim vectors are logged and skipped. Transactional per target.
curl -A 'curl/8' -X POST -H 'Authorization: Bearer <TOKEN>' -H 'Content-Type: application/json' \
-d '{"items":[{"target":"memory_chunk","item_id":"abc-123","vector":[0.1,0.2]}]}' \
https://donto.org/embed/submit
donto-api (genealogy extraction & ingest)
https://genes.apexpots.com — a FastAPI service that extracts knowledge from text via an LLM and ingests it into the knowledge graph, plus rich read/search/graph/alignment endpoints over the substrate (it talks to dontosrv internally). The graph is bitemporal and paraconsistent: facts carry valid-time and transaction-time, support historical as-of queries, and are soft-deleted via bitemporal closure. Predicates are open-world minted by the LLM; alignment solves vocabulary variance. Maturity levels 0–4 track quality. No auth (the operator may add bearer-token enforcement later). OpenAPI: https://genes.apexpots.com/openapi.json (authoritative for signatures).
POST /extract-and-ingest
Extract knowledge from text and ingest into the graph (primary agent endpoint). Auth: none.
- Request body:
text(string, required) — source document;context(string, required) — context IRI, e.g.ctx:genes/mary-watson/obituary;model(string, optional, defaultglm) — LLM shortcut (glm=GLM-5,grok,mistral) or full OpenRouter ID;mode(string, optional, defaultopencode) —opencode(GLM-5.1 agent) |exhaustive|fast;budget_usd(number, optional) — per-call USD cap. - Response:
{model, context, mode, facts_extracted, statements_ingested, anchors_attached, yield (facts/char), elapsed_ms, timing: {...}, usage: {...}}. - Notes: synchronous; 10-minute timeout. ~60–150 facts per 500-word article. Idempotent (content-hash dedup).
curl -X POST https://genes.apexpots.com/extract-and-ingest \
-H 'Content-Type: application/json' \
-d '{"text": "Mary Watson was born in Cornwall in 1860. She married Robert Watson in Cooktown, Queensland in 1879.", "context": "ctx:genes/mary-watson/obituary"}'
POST /extract
Extract knowledge with optional dry-run preview. Auth: none.
- Request body:
text(required),context(required),model?(defaultglm),mode?,dry_run?(boolean, defaultfalse). - Response: if
dry_run=false:{model, context, facts_extracted, statements_ingested, anchors_attached, yield, elapsed_ms, document}. Ifdry_run=true:{model, facts_extracted, yield, dry_run: true, facts: [{subject (ex:kebab-case), predicate (camelCase), object (iri or {v, dt}), tier (1-8), confidence (0.0-1.0), notes}], elapsed_ms}. Whendry_run=false, registers the source asdonto_document+ revision and writesdonto_span+donto_evidence_linkrows.
curl -X POST https://genes.apexpots.com/extract \
-H 'Content-Type: application/json' \
-d '{"text": "...", "context": "ctx:genes/test", "dry_run": true}'
POST /jobs/extract
Submit an extraction job (async, returns immediately). Auth: none.
- Request body:
text(required),context(required),model?(defaultglm),mode?(defaultopencode). - Response:
{job_id: uuid}. Durable via Temporal (survives restarts); duplicate submissions for the same context are rejected. PollGET /jobs/{job_id}.
curl -X POST https://genes.apexpots.com/jobs/extract \
-H 'Content-Type: application/json' \
-d '{"text": "...", "context": "ctx:genes/mary-watson/async"}' | jq '.job_id'
GET /jobs/{job_id}
Get job status and result. Auth: none.
- Path param:
job_id(uuid). - Response:
{job_id, status (queued|extracting|ingesting|completed|failed), result?: {facts_extracted, statements_ingested, anchors_attached, model, context, elapsed_ms}, error?}.
curl https://genes.apexpots.com/jobs/{job_id} | jq '.status'
GET /jobs/{job_id}/facts
Get extracted facts from a completed job. Auth: none.
- Path param:
job_id(uuid). Query:limit?(default 200). - Response: array of
{statement_id, subject, predicate, object_iri|object_lit, context, polarity, maturity, valid_lo, valid_hi, tx_lo}. Usestatement_idwith/claim/{id}for evidence.
curl 'https://genes.apexpots.com/jobs/{job_id}/facts?limit=50'
POST /assert
Assert a single statement into the graph. Auth: none.
- Request body:
subject(required,ex:kebab-case),predicate(required,camelCase),object_iri?(mutually exclusive withobject_lit),object_lit?({v, dt, lang?}, mutually exclusive withobject_iri),context?(defaultdonto:anonymous),polarity?(defaultasserted:asserted|negated|absent|unknown),maturity?(default 0; 0–4). - Response:
{statement_id: uuid, existing: boolean}. Idempotent (content-hash dedup).
curl -X POST https://genes.apexpots.com/assert \
-H 'Content-Type: application/json' \
-d '{"subject": "ex:mary-watson", "predicate": "bornIn", "object_iri": "ex:cornwall-england", "context": "ctx:genes/mary-watson/manual", "maturity": 3}'
POST /assert/batch
Assert multiple statements atomically. Auth: none.
- Request body: an array of
/assertstatement objects. - Response:
{inserted, skipped, total}. Single transaction (all-or-none); per-statement dedup.
curl -X POST https://genes.apexpots.com/assert/batch \
-H 'Content-Type: application/json' \
-d '[{"subject": "ex:mary-watson", "predicate": "marriedTo", "object_iri": "ex:robert-watson"}, {"subject": "ex:mary-watson", "predicate": "bornOn", "object_lit": {"v": "1860-01-15", "dt": "xsd:date"}}]'
GET /search
Full-text search by name/label (trigram-indexed, ~5ms). Auth: none.
- Query params:
q(required) — trigram-matched, case-insensitive, partial;limit?(default 25; 1–100). - Response:
{matches: [{subject (iri), label, count (fact count)}], q}. Ordered by fact count (most-connected first). The fastest way to find entities.
curl 'https://genes.apexpots.com/search?q=mary+watson&limit=10'
GET /history/{subject}
Get all facts about an entity (complete statement history). Auth: none.
- Path param:
subject(entity IRI). Query:limit?. - Response:
{count, rows: [{statement_id, predicate, object_iri|object_lit, context, polarity, maturity, valid_lo, valid_hi, tx_lo, tx_hi}]}. Includes retracted statements (tx_hiset).
curl 'https://genes.apexpots.com/history/ex:mary-watson?limit=50'
GET /statement/{id}
Get a single statement by UUID. Auth: none.
- Path param:
id(uuid). - Response:
{statement_id, subject, predicate, object_iri|object_lit, context, polarity, maturity, valid_lo, valid_hi, tx_lo, tx_hi}.
curl 'https://genes.apexpots.com/statement/{statement_uuid}'
POST /retract/{statement_id}
Retract a statement (bitemporal soft-delete). Auth: none.
- Path param:
statement_id(uuid). - Response:
{retracted: boolean, statement_id}. Setstx_hi; data not destroyed (as-of queries before retraction still return it). Idempotent.
curl -X POST 'https://genes.apexpots.com/retract/{statement_uuid}'
GET /subjects
List top subjects by fact count. Auth: none.
- Request: none.
- Response:
{subjects: [{subject (iri), count}]}— top 50 by count descending.
curl 'https://genes.apexpots.com/subjects'
GET /contexts
List all contexts (scopes/namespaces). Auth: none.
- Request: none.
- Response:
{contexts: [{context (iri), kind, mode, count}]}. Formats:ctx:genes/<topic>/<source>,ctx:extract/<file>/<model>,ctx:genealogy/research-db.
curl 'https://genes.apexpots.com/contexts'
GET /predicates
List all predicates with usage counts. Auth: none.
⚠️ Shadowed on the public host. At
https://genes.apexpots.com/predicatesthis path is intercepted by the dontopedia web app and returns HTML, not API JSON (verified 2026-06-07). To get the JSON, call it on the internal port (http://localhost:7879/predicatesvia dontosrv) or use/viz/predicates, or query the substrate directly. Sibling paths (/subjects,/contexts,/search,/version) are not shadowed and correctly return JSON ongenes.apexpots.com.
- Request: none.
- Response:
{predicates: [{predicate, count}]}by count descending. (Top:rdf:type~3.9M,donto:status~1.6M,donto:textSpan~1.2M,ex:knownAs~1M,rdfs:label~589K.)
curl 'http://localhost:7879/predicates' # genes.apexpots.com/predicates returns the web app's HTML
POST /query
Run a DontoQL or SPARQL query. Auth: none.
- Request body:
{query: string}— DontoQL (MATCH ...) or SPARQL (SELECT/PREFIX ...). E.g.MATCH ?s ?p ?o LIMIT 20. - Response: array of result rows, each a JSON object of bound variables. Cost 1–10s depending on complexity.
curl -X POST 'https://genes.apexpots.com/query' \
-H 'Content-Type: application/json' \
-d '{"query": "MATCH ?s bornIn ?place LIMIT 10"}'
GET /connections/{entity}
Get all bidirectional connections (incoming + outgoing edges). Auth: none.
- Path param:
entity(iri). Query:limit?(default 500),context?,min_maturity?(default 0; 0–4). - Response:
{outgoing: [{predicate, object_iri|object_lit, context, maturity}], incoming: [{predicate, subject, context, maturity}], entity}.
curl 'https://genes.apexpots.com/connections/ex:mary-watson?limit=100&min_maturity=2'
POST /graph/neighborhood
Get a neighborhood subgraph for visualization. Auth: none.
- Request body:
subject(required, center IRI),depth?(default 1, max 3),predicates?(string[]),context?,min_maturity?(default 0; 0–4),limit?(default 500, max 5000). - Response:
{nodes: [{id (iri), label, type, degree}], edges: [{source, target, predicate, context, maturity}], center, depth}(D3-ready).
curl -X POST 'https://genes.apexpots.com/graph/neighborhood' \
-H 'Content-Type: application/json' \
-d '{"subject": "ex:mary-watson", "depth": 2, "predicates": ["marriedTo", "childOf", "parentOf"]}'
POST /graph/path
Find the shortest path between two entities. Auth: none.
- Request body:
source(required IRI),target(required IRI),max_depth?(default 6),predicates?(string[]). - Response:
{paths: [[{subject, predicate, object}, ...]], found: boolean, depth}.
curl -X POST 'https://genes.apexpots.com/graph/path' \
-H 'Content-Type: application/json' \
-d '{"source": "ex:mary-watson", "target": "ex:cooktown-municipality"}'
POST /graph/subgraph
Get all edges matching given predicates (themed visualization). Auth: none.
- Request body:
predicates(required string[]),context?. E.g. family=["childOf","parentOf","marriedTo","siblingOf"]. - Response:
{nodes: [{id, label, type, degree}], edges: [{source, target, predicate, context, maturity}], node_count, edge_count}(D3-ready).
curl -X POST 'https://genes.apexpots.com/graph/subgraph' \
-H 'Content-Type: application/json' \
-d '{"predicates": ["bornIn", "diedIn", "marriedTo", "childOf"]}'
POST /align/register
Register a predicate alignment. Auth: none.
- Request body:
source(required predicate),target(required predicate),relation(required:exact_equivalent|inverse_equivalent|sub_property_of|close_match|not_equivalent),confidence?(default 1.0; 0.0–1.0). - Response:
{alignment_id: uuid}. Must callPOST /align/rebuildafterward for it to take effect.
curl -X POST 'https://genes.apexpots.com/align/register' \
-H 'Content-Type: application/json' \
-d '{"source": "bornInPlace", "target": "bornIn", "relation": "exact_equivalent", "confidence": 0.95}'
POST /align/rebuild
Rebuild the predicate closure index. Auth: none.
- Request: none.
- Response:
{rows: integer}(closure table size, ~12K). Call after any/align/register. Cost 1–5s.
curl -X POST 'https://genes.apexpots.com/align/rebuild'
GET /align/suggest/{predicate}
Find similar predicates (trigram similarity). Auth: none.
- Path param:
predicate. Query:threshold?(default 0.3; 0.0–1.0),limit?(default 20). - Response:
[{source, target, similarity, label}]by similarity descending.
curl 'https://genes.apexpots.com/align/suggest/bornInPlace?threshold=0.6&limit=10'
GET /evidence/{statement_id}
Get evidence spans linked to a statement. Auth: none.
- Path param:
statement_id(uuid). - Response:
{evidence: [{link_type (produced_by|extracted_from|mentioned_in|supports|refutes|contextualizes), document (iri), span, surface_text, confidence (0-1)}]}. Empty if inserted via/assert.
curl 'https://genes.apexpots.com/evidence/{statement_uuid}'
GET /claim/{statement_id}
Full claim card (statement + evidence + arguments + obligations). Auth: none.
- Path param:
statement_id(uuid). - Response:
{subject, predicate, object_iri|object_lit, context, polarity, maturity, asserted_at, evidence: [...], arguments: [{type (supports|refutes|undercuts|qualifies), statement}], obligations: [...], blockers: [...]}.
curl 'https://genes.apexpots.com/claim/{statement_uuid}'
GET /health
Health check (verifies API + dontosrv). Auth: none.
- Response:
{status (ok|degraded|down), dontosrv (ok|error), agent_instructions?}.
curl 'https://genes.apexpots.com/health'
GET /version
Version info. Auth: none.
- Response:
{dir, service (dontosrv), version, agent_instructions?}.
curl 'https://genes.apexpots.com/version'
GET /firehose/stream
Real-time SSE stream of database activity. Auth: none.
- Request: none (Server-Sent Events).
- Response: SSE stream of
{audit_id, at, actor, action (insert|retract|...), statement_id, detail}. Backed by PostgresLISTEN/NOTIFYondonto_firehose/donto_audit.
curl 'https://genes.apexpots.com/firehose/stream' 2>/dev/null | head -20
GET /firehose/recent
Recent audit events (last N). Auth: none.
- Query:
limit?(default 100). - Response:
[{audit_id, at, actor, action, statement_id, detail}].
curl 'https://genes.apexpots.com/firehose/recent?limit=20'
GET /jobs
List all extraction jobs with status. Auth: none.
- Query:
status?(queued|extracting|ingesting|completed|failed). - Response:
{jobs: [{job_id, status, text_preview, context, model, created_at, updated_at, result?}]}.
curl 'https://genes.apexpots.com/jobs?status=completed'
dontosrv (core substrate)
The core bitemporal claim substrate HTTP sidecar — ~70 endpoints covering the claim write-path, rich read/search, evidence linking, the Trust Kernel, obligations/reactions, predicate alignment, shape validation & rule derivation, and schema discovery. It is reachable internally at localhost:7879; the public genealogy surface (https://genes.apexpots.com) proxies a curated subset. All endpoints return JSON unless noted. Reads are open; writes are not gated at the HTTP layer (policy gates apply at the SQL layer per context/action). Examples below use the internal http://localhost:7879 base; substitute your deployment's reachable host.
Claims are bitemporal (
tx_time/valid_time) with polarity (asserted/negated/absent/unknown) and maturity (E0–E4). WhenDONTO_ENFORCE_CLAIM_INVARIANT=true, anchorless/unflagged claims land at maturity E1 (never rejected, just un-promoted).
Health & metrics
GET /health
Service health check. Auth: none. Returns text/plain: ok.
curl http://localhost:7879/health
GET /version
Service version. Auth: none. Returns {service, version, dir}.
curl http://localhost:7879/version
GET /metrics
Prometheus-format substrate health metrics. Auth: none. Returns text/plain gauges (donto_up, donto_currently_believed_statements, donto_distinct_subjects, donto_distinct_predicates, donto_distinct_contexts, donto_evidence_links, donto_contested_subject_predicate_pairs, donto_total_contradiction_pressure, donto_consumer_namespaces, donto_registered_overlays, donto_identity_hypotheses, donto_argument_edges, donto_anchor_coverage_pct, donto_statements_estimated, …). Same numeric fields as the donto_v_substrate_health view.
curl http://localhost:7879/metrics | grep donto_currently_believed_statements
Claim write-path
POST /assert
Assert one bitemporal claim. Auth: none.
- Request body:
{subject, predicate, object_iri?, object_lit?: {v, dt, lang?}, context?='donto:anonymous', polarity?='asserted', maturity?=0, valid_from?: date, valid_to?: date, anchor?: {revision_id: uuid, start: int32, end: int32, surface_text: string, link_type?='extracted_from'}, hypothesis_only?=false}. - Response:
{statement_id: uuid}or{error}. Whenanchoris provided, materializesdonto_span+donto_evidence_link.
curl -X POST http://localhost:7879/assert -H 'Content-Type: application/json' \
-d '{"subject":"ex:person1","predicate":"ex:motherOf","object_iri":"ex:person2","context":"donto:evidence","maturity":2}'
POST /assert/batch
Assert many claims in one call. Auth: none.
- Request body:
{statements: [StatementInput]}(each like/assert). - Response:
{inserted: int64, anchors_attached: int64}or{error}.
curl -X POST http://localhost:7879/assert/batch -H 'Content-Type: application/json' \
-d '{"statements":[{"subject":"ex:a","predicate":"ex:p","object_iri":"ex:b"},{"subject":"ex:c","predicate":"ex:q","object_iri":"ex:d"}]}'
POST /retract
Retract a claim (close its tx_time interval). Auth: none.
- Request body:
{statement_id: uuid}. - Response:
{statement_id, retracted: boolean}. Retracted rows stay indonto_statementwith closed intervals.
curl -X POST http://localhost:7879/retract -H 'Content-Type: application/json' \
-d '{"statement_id":"49bbf329-e9d2-4c83-a56f-5c6c0aaf99ae"}'
POST /contexts/ensure
Idempotently create/ensure a context. Auth: none.
- Request body:
{iri, kind?='source', mode?='permissive', parent?}.kind∈source|hypothesis|derived|snapshot|user|…;mode∈permissive|strict|…. - Response:
{iri, ok: boolean}or{error}.
curl -X POST http://localhost:7879/contexts/ensure -H 'Content-Type: application/json' \
-d '{"iri":"donto:genes-evidence","kind":"source","mode":"permissive"}'
Read & search
GET /search
Label/IRI/literal substring search; distinct subjects ranked by live-row count. Auth: none.
- Query:
q(required),limit?(default 25, max 100). - Response:
{q, matches: [{subject, label|null, count}]}.
curl 'http://localhost:7879/search?q=person&limit=10'
GET /history/{subject}
All statements about a subject (open + closed, all contexts/polarities). Auth: none.
- Path:
subject(IRI). Query:limit?(default 2000, max 20000),context?,predicate?,from?(date),to?(date),include_retracted?(default true). - Response:
{subject, total, limit, statements: [{statement_id, subject, predicate, object_iri, object_lit, context, polarity, maturity, valid_lo, valid_hi, tx_lo, tx_hi, lineage: [uuid]}]}.
curl 'http://localhost:7879/history/ex:person1?limit=100&context=donto:evidence'
GET /statement/{id}
Full statement detail: row, lineage, audit log, certificate, siblings. Auth: none.
- Path:
id(uuid). - Response:
{statement: StatementRow, lineage: {sources: [...], derived: [...]}, audit: [...], certificate: {}|null, siblings: [...]}.
curl 'http://localhost:7879/statement/49bbf329-e9d2-4c83-a56f-5c6c0aaf99ae'
GET /claim/{id}
Claim card (donto_claim_card): the claim + evidence + contradiction context. Auth: none.
- Path:
id(uuid). - Response: free-form JSON projected by the
donto_claim_card(uuid)SQL function (shape varies by claim topology).
curl 'http://localhost:7879/claim/49bbf329-e9d2-4c83-a56f-5c6c0aaf99ae'
POST /recall
Policy-gated recall projection (donto_recall_projection): identity-resolved, bitemporally-sliced rows with per-row effective actions. Auth: none.
- Request body:
{holder, action ('read_metadata'|'read_content'|'quote'|'derive_claims'|…), subject?, predicate?, object_iri?, scope?: {root, depth?}, polarity?='asserted', min_maturity?=0, as_of_tx?: datetime, as_of_valid?: date, lens_name?, limit?=200, permitted_only?=false}. - Response:
{holder, action, lens|null, as_of|null, row_count, rows: [StatementRow + {resolved_subject|null, resolved_object|null, effective_actions: {}, action_allowed: boolean}]}.
curl -X POST http://localhost:7879/recall -H 'Content-Type: application/json' \
-d '{"holder":"[email protected]","action":"read_content","limit":50}'
GET /subjects
List subjects with live statement count (limit 5000). Auth: none. Returns {subjects: [{subject, count}]} (live counts, descending).
curl 'http://localhost:7879/subjects'
GET /subjects/all
List all subjects including retracted-only ones. Auth: none. Returns {subjects: [{subject, count}]}.
curl 'http://localhost:7879/subjects/all'
POST /subjects/refresh
Refresh the subject-count materialized view (donto_subject_count). Auth: none. Returns {ok: boolean}.
curl -X POST http://localhost:7879/subjects/refresh
GET /inbound/{subject}
Statements where the subject is the object (inverse view). Auth: none. Returns {subject, inbound: [StatementRow]}.
curl 'http://localhost:7879/inbound/ex:person1'
GET /cluster/{subject}
Subject's identity cluster (identity-resolved neighbours). Auth: none. Returns {subject, cluster: [{resolved_id, confidence}]}.
curl 'http://localhost:7879/cluster/ex:person1'
GET /context-facts
Distinct (subject, predicate, object) triple count per context. Auth: none. Returns {contexts: [{context, fact_count}]}.
curl 'http://localhost:7879/context-facts'
GET /context-spans
Context-scoped evidence span counts. Auth: none. Returns {contexts: [{context, span_count}]}.
curl 'http://localhost:7879/context-spans'
Identity & predicates
POST /subjects/merge
Merge two subjects (identity binding). Auth: none. Body {source, target} → {source, target, ok}.
curl -X POST http://localhost:7879/subjects/merge -H 'Content-Type: application/json' \
-d '{"source":"ex:person-a","target":"ex:person-b"}'
GET /predicates/align-suggest
Suggest predicate alignments (cross-vocabulary candidates). Auth: none. Query: limit? (default 100). Returns {suggestions: [{source, target, confidence}]}.
curl 'http://localhost:7879/predicates/align-suggest?limit=50'
POST /predicates/merge
Merge two predicates (canonical registration). Auth: none. Body {source, target} → {source, target, ok}.
curl -X POST http://localhost:7879/predicates/merge -H 'Content-Type: application/json' \
-d '{"source":"ex:parentOf","target":"ex:parent_of"}'
GET /predicates/fragmentation
Predicate fragmentation report (variant clustering). Auth: none. Returns {predicates: [{canonical, variant_count, variants: [string]}]}.
curl 'http://localhost:7879/predicates/fragmentation'
GET /predicates/suggest
Suggest predicates by substring/similarity. Auth: none. Query: q, limit? (default 25). Returns {q, predicates: [{iri, label|null, count}]}.
curl 'http://localhost:7879/predicates/suggest?q=mother&limit=10'
GET /entities/suggest
Suggest entities (subjects) by substring/similarity. Auth: none. Query: q, limit? (default 25). Returns {q, entities: [{subject, label|null, count}]}.
curl 'http://localhost:7879/entities/suggest?q=john&limit=10'
POST /predicates/refresh
Refresh the predicate-count view (donto_predicate_count). Auth: none. Returns {ok}.
curl -X POST http://localhost:7879/predicates/refresh
GET /predicates
List predicates by usage descending (limit 500). Auth: none. Returns {predicates: [{predicate, count}]}.
curl 'http://localhost:7879/predicates'
GET /contexts
List contexts (limit 500). Auth: none. Returns {contexts: [{context, kind, mode, count}]}.
curl 'http://localhost:7879/contexts'
POST /contexts/lookup
Targeted metadata lookup for known context IRIs (faster than /contexts on hot paths). Auth: none. Body {iris: [string]} → {contexts: [{context, kind, mode, count}]}.
curl -X POST http://localhost:7879/contexts/lookup -H 'Content-Type: application/json' \
-d '{"iris":["donto:evidence","donto:hypothesis"]}'
Documents & evidence
POST /sources/lookup
Metadata for registered document sources. Auth: none. Body {iris: [string]} → {sources: [{document_id, document_iri, label|null, source_url|null, source_kind|null, media_type, revision_id|null, has_body}]}.
curl -X POST http://localhost:7879/sources/lookup -H 'Content-Type: application/json' \
-d '{"iris":["donto:genes-paper-1"]}'
POST /statements/evidence
Evidence (spans + links) for statements. Auth: none. Body {statement_ids: [uuid]} → {evidence: {<statement_id>: [{link_type, target_document_id|null, target_span_id|null, confidence|null}]}}.
curl -X POST http://localhost:7879/statements/evidence -H 'Content-Type: application/json' \
-d '{"statement_ids":["49bbf329-e9d2-4c83-a56f-5c6c0aaf99ae"]}'
POST /documents/register
Register a document source (legacy; no required policy). Auth: none. Body {iri, media_type?='text/plain', label?, source_url?, language?, policy_iri?} → {document_id, iri, policy_iri|null, warning?}. Deprecated in favour of /sources/register.
curl -X POST http://localhost:7879/documents/register -H 'Content-Type: application/json' \
-d '{"iri":"donto:genes-paper-2026","media_type":"text/html"}'
POST /sources/register
Register a document source with a required policy. Auth: none. Body {iri, source_kind, policy_iri, media_type?, label?, source_url?, language?} → {document_id, iri, policy_iri}. No source without policy.
curl -X POST http://localhost:7879/sources/register -H 'Content-Type: application/json' \
-d '{"iri":"donto:genes-paper-2026","source_kind":"academic-paper","policy_iri":"donto:policy-public-read"}'
POST /documents/revision
Add a revision (version) of a document body. Auth: none. Body {document_id, body?, parser_version?} → {revision_id}.
curl -X POST http://localhost:7879/documents/revision -H 'Content-Type: application/json' \
-d '{"document_id":"...","body":"The full document text here..."}'
GET /documents/revision/{revision_id}/body
Retrieve the full body text of a revision. Auth: none. Returns {revision_id, body, byte_size} or 404 if the revision has no text body.
curl 'http://localhost:7879/documents/revision/49bbf329-e9d2-4c83-a56f-5c6c0aaf99ae/body'
POST /evidence/link/span
Link a statement to an evidence span. Auth: none. Body {statement_id, span_id, link_type?='extracted_from', confidence?, context?} → {link_id}.
curl -X POST http://localhost:7879/evidence/link/span -H 'Content-Type: application/json' \
-d '{"statement_id":"49bbf329-e9d2-4c83-a56f-5c6c0aaf99ae","span_id":"..."}'
GET /evidence/{stmt}
All evidence for a statement (donto_evidence_for). Auth: none. Path: stmt (uuid). Returns {evidence: [{link_id, link_type, target_document_id|null, target_revision_id|null, target_span_id|null, target_annotation_id|null, target_run_id|null, target_statement_id|null, confidence|null}]}.
curl 'http://localhost:7879/evidence/49bbf329-e9d2-4c83-a56f-5c6c0aaf99ae'
Arguments & reactions
POST /arguments/assert
Assert a typed argument edge between two statements. Auth: none. Body {source: uuid, target: uuid, relation ('supports'|'rebuts'|'undercuts'|'endorses'|'supersedes'|'qualifies'|'potentially_same'|'same_referent'|'same_event'), context?='donto:anonymous', strength?, agent_id?, evidence?} → {argument_id}.
curl -X POST http://localhost:7879/arguments/assert -H 'Content-Type: application/json' \
-d '{"source":"...","target":"...","relation":"supports","strength":0.8}'
GET /arguments/{stmt}
All argument edges touching a statement (donto_arguments_for). Auth: none. Returns {arguments: [{argument_id, source, target, relation, strength|null, context}]}.
curl 'http://localhost:7879/arguments/49bbf329-e9d2-4c83-a56f-5c6c0aaf99ae'
GET /arguments/frontier
Contradiction frontier: statements ranked by net attack/support pressure. Auth: none. Returns {frontier: [{statement_id, attack_count, support_count, net_pressure}]} (net_pressure = support_count - attack_count).
curl 'http://localhost:7879/arguments/frontier'
POST /react
Attach a reaction to a statement (endorse/reject/cite/supersede). Auth: none. Body {source: uuid, kind ('endorses'|'rejects'|'cites'|'supersedes'), object_iri?, context?='donto:anonymous', actor?} → {reaction_id}.
curl -X POST http://localhost:7879/react -H 'Content-Type: application/json' \
-d '{"source":"49bbf329-e9d2-4c83-a56f-5c6c0aaf99ae","kind":"endorses"}'
GET /reactions/{id}
Enumerate current reactions to a statement. Auth: none. Returns {reactions: [{reaction_id, kind, object_iri|null, context, polarity}], counts: {endorses, rejects, cites, supersedes}}.
curl 'http://localhost:7879/reactions/49bbf329-e9d2-4c83-a56f-5c6c0aaf99ae'
Trust kernel (policy, authorisation, attestations, agents)
POST /policy/assign
Assign a policy to a target. Auth: none. Body {target_kind ('context'|'document'|'overlay'|…), target_id, policy_iri, assigned_by?='system'} → {assignment_id}.
curl -X POST http://localhost:7879/policy/assign -H 'Content-Type: application/json' \
-d '{"target_kind":"context","target_id":"donto:evidence","policy_iri":"donto:policy-public-read"}'
GET /policy/effective/{target_kind}/{target_id}
Effective actions the caller may take on a target. Auth: none (caller from headers if present). Returns {target_kind, target_id, caller, actions: {<action>: boolean}}.
curl 'http://localhost:7879/policy/effective/context/donto:evidence'
POST /authorise
Authorisation probe (check without executing). Auth: none. Body {target_kind, target_id, action} → {caller, target_kind, target_id, action, allowed: boolean}.
curl -X POST http://localhost:7879/authorise -H 'Content-Type: application/json' \
-d '{"target_kind":"context","target_id":"donto:evidence","action":"read_content"}'
POST /protected/read
Policy-gated read: execute a SQL select only if the caller's action is allowed. Auth: none. Body {target_kind, target_id, action, query} → {rows: [...]} or {error, reason}. (Input not sanitised — SQL-injection risk; operator-internal.)
curl -X POST http://localhost:7879/protected/read -H 'Content-Type: application/json' \
-d '{"target_kind":"context","target_id":"donto:evidence","action":"read_content","query":"SELECT * FROM ..."}'
POST /attestations
Issue an attestation (Trust Kernel): holder may perform actions under a policy within time/scope. Auth: none. Body {holder, issuer, policy_iri, actions: [string], purpose, rationale, expires_at?} → {attestation_id}.
curl -X POST http://localhost:7879/attestations -H 'Content-Type: application/json' \
-d '{"holder":"alice@genes","issuer":"admin","policy_iri":"donto:policy-genes-read","actions":["read_content"],"purpose":"research","rationale":"approved"}'
POST /attestations/{id}/revoke
Revoke an attestation. Auth: none. Path: id (uuid). Body {revoked_by?='system', reason?} → {revoked: boolean, attestation_id}.
curl -X POST http://localhost:7879/attestations/49bbf329-e9d2-4c83-a56f-5c6c0aaf99ae/revoke \
-H 'Content-Type: application/json' -d '{"revoked_by":"admin","reason":"policy change"}'
POST /agents/register
Register an agent (LLM, person, bot, system). Auth: none. Body {iri, agent_type?='custom', label?, model_id?} → {agent_id, iri}.
curl -X POST http://localhost:7879/agents/register -H 'Content-Type: application/json' \
-d '{"iri":"donto:claude-opus","agent_type":"llm","model_id":"claude-opus-4"}'
POST /agents/bind
Bind an agent to a context (ownership/stewardship). Auth: none. Body {agent_id, context, role?='owner'} → {ok}.
curl -X POST http://localhost:7879/agents/bind -H 'Content-Type: application/json' \
-d '{"agent_id":"...","context":"donto:genes-evidence","role":"owner"}'
Obligations
POST /obligations/emit
Emit an obligation (task, reminder, review request). Auth: none. Body {statement_id, obligation_type, context?='donto:anonymous', priority?=0, detail?, assigned_agent?} → {obligation_id}.
curl -X POST http://localhost:7879/obligations/emit -H 'Content-Type: application/json' \
-d '{"statement_id":"...","obligation_type":"review-needed","priority":1}'
POST /obligations/resolve
Resolve an obligation. Auth: none. Body {obligation_id, resolved_by?, status?='resolved'} → {resolved: boolean}.
curl -X POST http://localhost:7879/obligations/resolve -H 'Content-Type: application/json' \
-d '{"obligation_id":"..."}'
POST /obligations/open
List open obligations (filterable). Auth: none. Body {obligation_type?, context?, limit?=100} → {obligations: [{obligation_id, statement_id|null, obligation_type, priority, context, assigned_agent|null}]}.
curl -X POST http://localhost:7879/obligations/open -H 'Content-Type: application/json' \
-d '{"obligation_type":"review-needed","limit":50}'
GET /obligations/summary
Obligation summary (counts by type + status). Auth: none. Returns {summary: [{obligation_type, status, count}]}.
curl 'http://localhost:7879/obligations/summary'
Shapes, rules, certificates
POST /shapes/validate
Validate claims against a shape (constraint) schema. Auth: none. Body {shape_iri, scope: {} (ContextScope)} → {shape_iri, focus_count, violations: [{focus, reason, evidence: [uuid]}], source ('builtin'|'cached'|'lean')}. Built-in shapes: builtin:functional/<pred>, builtin:datatype/<pred>/<dt>, builtin:range/*.
curl -X POST http://localhost:7879/shapes/validate -H 'Content-Type: application/json' \
-d '{"shape_iri":"builtin:functional/ex:motherOf","scope":{"root":"donto:evidence"}}'
POST /rules/derive
Run a derivation rule and emit derived statements. Auth: none. Body {rule_iri, scope: {} (ContextScope), into: string} → {rule_iri, into, emitted, source}. Built-in rules: builtin:transitive/<pred>, builtin:inverse/<pred>/<inv>, builtin:symmetric/<pred>. Idempotent.
curl -X POST http://localhost:7879/rules/derive -H 'Content-Type: application/json' \
-d '{"rule_iri":"builtin:transitive/ex:ancestorOf","scope":{"root":"donto:evidence"},"into":"donto:derived"}'
POST /certificates/attach
Attach a certificate to a statement (proof of derivation/justification). Auth: none. Body {statement_id, kind ('direct_assertion'|'substitution'|'transitive_closure'|'confidence_justification'|'shape_entailment'|'hypothesis_scoped'|'replay'), rule_iri?, inputs?: [uuid], body: {}, signature?} → {statement_id, kind}.
curl -X POST http://localhost:7879/certificates/attach -H 'Content-Type: application/json' \
-d '{"statement_id":"...","kind":"transitive_closure","body":{"predicate":"ex:ancestorOf"}}'
POST /certificates/verify/{stmt}
Re-run certificate verification for a statement. Auth: none. Path: stmt (uuid). Returns {statement_id, kind, ok: boolean, reason?}.
curl -X POST http://localhost:7879/certificates/verify/49bbf329-e9d2-4c83-a56f-5c6c0aaf99ae
Alignment & descriptors
POST /alignment/register
Register a predicate alignment. Auth: none. Body {source, target, relation ('exact_equivalent'|'inverse_equivalent'|'sub_property_of'|'close_match'|'decomposition'|'not_equivalent'), confidence, valid_lo?, valid_hi?, run_id?, provenance?, actor?} → {alignment_id}.
curl -X POST http://localhost:7879/alignment/register -H 'Content-Type: application/json' \
-d '{"source":"ex:parentOf","target":"rdfs:parent_of","relation":"exact_equivalent","confidence":0.95}'
POST /alignment/retract
Retract a predicate alignment. Auth: none. Body {alignment_id} → {alignment_id, retracted: boolean}.
curl -X POST http://localhost:7879/alignment/retract -H 'Content-Type: application/json' \
-d '{"alignment_id":"..."}'
POST /alignment/rebuild-closure
Rebuild the predicate closure (donto_rebuild_predicate_closure()). Auth: none. Returns {rows: int64}.
curl -X POST http://localhost:7879/alignment/rebuild-closure
POST /alignment/runs/start
Start an alignment run (batch matching/disambiguation). Auth: none. Body {run_type, model_id?, config?, source_predicates?} → {run_id}.
curl -X POST http://localhost:7879/alignment/runs/start -H 'Content-Type: application/json' \
-d '{"run_type":"embedding-similarity","model_id":"sentence-transformers/all-MiniLM-L6-v2"}'
POST /alignment/runs/complete
Complete an alignment run. Auth: none. Body {run_id, status, proposed?, accepted?, rejected?} → {run_id, ok: boolean}.
curl -X POST http://localhost:7879/alignment/runs/complete -H 'Content-Type: application/json' \
-d '{"run_id":"...","status":"success","proposed":150,"accepted":140,"rejected":10}'
POST /descriptors/upsert
Register/update a predicate descriptor. Auth: none. Body {predicate_iri, gloss?, subject_type?, object_type?, domain?, example_subject?, example_object?} → {predicate_iri, ok: boolean}.
curl -X POST http://localhost:7879/descriptors/upsert -H 'Content-Type: application/json' \
-d '{"predicate_iri":"ex:motherOf","gloss":"X is the mother of Y","subject_type":"Person","object_type":"Person"}'
POST /descriptors/nearest
Find nearest predicates to a descriptor (similarity search). Auth: none. Body {gloss, limit?=10} → {candidates: [{predicate, gloss, similarity}]}.
curl -X POST http://localhost:7879/descriptors/nearest -H 'Content-Type: application/json' \
-d '{"gloss":"X is parent of Y","limit":5}'
Overlays (shadows)
POST /shadow/materialize
Materialize an overlay shadow (consumer-owned projection). Auth: none. Body {overlay_iri, scope?, include_history?} → {overlay_iri, rows_materialized}.
curl -X POST http://localhost:7879/shadow/materialize -H 'Content-Type: application/json' \
-d '{"overlay_iri":"donto:genes-overlay","include_history":false}'
POST /shadow/rebuild
Rebuild all overlay shadows. Auth: none. Returns {overlays_rebuilt: int64}.
curl -X POST http://localhost:7879/shadow/rebuild
Query languages
POST /sparql
SPARQL query execution. Auth: none. Body {query, scope_preset?} → {rows: [...]} or {error}. Subset: PREFIX, SELECT, WHERE, GRAPH, FILTER, LIMIT, OFFSET.
curl -X POST http://localhost:7879/sparql -H 'Content-Type: application/json' \
-d '{"query":"SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10"}'
POST /dontoql
DontoQL query execution (donto's native query language; richer than SPARQL — bitemporal scoping, policy gating, identity lensing, rules). Auth: none. Body {query, scope_preset?} → {rows: [...]} or {error}.
curl -X POST http://localhost:7879/dontoql -H 'Content-Type: application/json' \
-d '{"query":"MATCH ?s ex:parentOf ?o FILTER ?s != ?o"}'
POST /dir
Directory service: resolve IRIs to structured metadata. Auth: none. Body {iris: [string]} → {<iri>: {label|null, description|null, ...}}.
curl -X POST http://localhost:7879/dir -H 'Content-Type: application/json' \
-d '{"iris":["ex:parentOf","ex:person1"]}'
Schema discovery (/discovery/*)
All GET, auth: none. These describe the substrate's schema, vocabulary, policies, and contract — ideal for SDK generators and curation tooling.
GET /discovery/contract-version
Substrate API contract version + supported clauses + action list. Returns {contract_version, dontosrv_version, dir_version, supported_dontoql_clauses: [string], supported_sparql_subset: [string], actions: [string]}. Supported actions: read_metadata, read_content, quote, derive_claims, derive_embeddings, translate, summarize, export_*, train_model, publish_release, share_with_third_party, federated_query, request_deletion.
curl 'http://localhost:7879/discovery/contract-version'
GET /discovery/contexts
List contexts (cap 5000). Returns {contexts: [{iri, kind, parent|null, label|null, mode, closed_at|null}], limit}.
curl 'http://localhost:7879/discovery/contexts'
GET /discovery/contexts/{iri}
Context detail with assigned policies and effective actions. Returns {iri, kind, parent|null, label|null, mode, closed_at|null, policies: [string], effective_actions: {}}.
curl 'http://localhost:7879/discovery/contexts/donto:evidence'
GET /discovery/predicates
List predicates by usage descending (cap 5000). Returns {predicates: [{iri, label|null, status, minting_status, statement_count}], limit}.
curl 'http://localhost:7879/discovery/predicates'
GET /discovery/predicates/{iri}
Predicate detail with descriptor + alignments (top 50). Returns {iri, label|null, description|null, status, minting_status, canonical_of|null, inverse_of|null, characteristics: {symmetric, transitive, functional, inverse_functional}, descriptor: {gloss|null, subject_type|null, object_type|null, domain|null, example_subject|null, example_object|null}, statement_count, alignments: [{source_iri, target_iri, relation, confidence}]}.
curl 'http://localhost:7879/discovery/predicates/ex:parentOf'
GET /discovery/predicate-inventory
Ranked predicate listing under an optional ?prefix=<curie>, capped by ?limit (default 100, clamped 1–5000). Returns {predicates: [{predicate, namespace, n}], prefix|null, limit}.
curl 'http://localhost:7879/discovery/predicate-inventory?prefix=ex&limit=50'
GET /discovery/predicate-namespaces
Namespace-level rollup: distinct predicates + total statements per CURIE prefix. Returns {namespaces: [{namespace, distinct_predicates, total_statements}]}.
curl 'http://localhost:7879/discovery/predicate-namespaces'
GET /discovery/modalities
Allowed modality values. Returns {modalities: [{name, description}]}.
curl 'http://localhost:7879/discovery/modalities'
GET /discovery/extraction-levels
Allowed extraction-level values (claim provenance classification). Returns {extraction_levels: [string], observed_in_corpus: [string]}. Standard list: quoted, table_read, example_observed, source_generalization, cross_source_inference, model_hypothesis, human_hypothesis, manual_entry, registry_import, adapter_import.
curl 'http://localhost:7879/discovery/extraction-levels'
GET /discovery/policies
Policy capsules (cap 1000). Returns {policies: [{policy_iri, policy_kind, revocation_status, expiry|null, summary|null, allowed_actions: {}, inheritance_rule}]}.
curl 'http://localhost:7879/discovery/policies'
GET /discovery/policies/{iri}
Policy capsule detail + assignment count. Returns {policy_iri, policy_kind, authority_refs: [string], allowed_actions: {}, inheritance_rule, revocation_status, expiry|null, summary|null, labels: {}, created_at, created_by, assignments_count}.
curl 'http://localhost:7879/discovery/policies/donto:policy-public-read'
GET /discovery/frame-types
Registered frame types (structural schemas). Returns {frame_types: [{kind, label|null, description|null}]}.
curl 'http://localhost:7879/discovery/frame-types'
GET /discovery/alignment-relations
The alignment-edge relation types. Returns {alignment_relations: [{name, description}]}. Relations: exact_equivalent, inverse_equivalent, sub_property_of, close_match, decomposition, not_equivalent.
curl 'http://localhost:7879/discovery/alignment-relations'
GET /discovery/identity-hypotheses
Registered identity hypotheses + clustered symbol counts. Returns {identity_hypotheses: [{hypothesis_id, name, description|null, threshold_same, threshold_distinct, method, authority|null, status, clustered_symbols}]}.
curl 'http://localhost:7879/discovery/identity-hypotheses'
GET /discovery/overlays
Registered consumer overlays. Returns {overlays: [{overlay_iri, consumer_iri, table_name, owns_key, policy_inherits, fixed_policy_iri|null, bitemporal, description|null, registered_by, registered_at}]}.
curl 'http://localhost:7879/discovery/overlays'
GET /discovery/dontoql-grammar
DontoQL grammar BNF (text/plain). Covers SCOPE, PRESET, MATCH, FILTER, POLARITY, MATURITY, IDENTITY, IDENTITY_LENS, PREDICATES, MODALITY, EXTRACTION_LEVEL, TRANSACTION_TIME, POLICY ALLOWS, SCHEMA_LENS, EXPANDS_FROM, ORDER BY, WITH evidence, PROJECT, LIMIT, OFFSET.
curl 'http://localhost:7879/discovery/dontoql-grammar' | head -30
GET /discovery/substrate-health
Substrate-level health dashboard (donto_v_substrate_health view; same numeric fields as GET /metrics). Returns {currently_believed_statements, retracted_statements, distinct_subjects, distinct_predicates, distinct_contexts, evidence_links, contested_subject_predicate_pairs, total_contradiction_pressure, consumer_namespaces, registered_overlays, identity_hypotheses, argument_edges, anchor_coverage_pct, statements_estimated}.
curl 'http://localhost:7879/discovery/substrate-health'
GET /discovery/openapi
The hand-written OpenAPI 3.1 specification (contract for SDK generators). Returns the JSON spec.
curl 'http://localhost:7879/discovery/openapi' | jq '.info'
donto-admin
https://admin.donto.org (internal localhost:3103, Next.js 16) — a read-only observability console over the substrate. Every route returns the uniform envelope {ok: boolean, data: T, at: ISO8601} (errors: {ok: false, error, at}, HTTP 500), never cached. Access is via the donto_ro Postgres role (SELECT-only, default_transaction_read_only=on); there are no per-request credentials. limit params are clamped to [1, max]; offset defaults to 0 (≥0). Cheap endpoints use reltuples estimates (no statement-table scans). No OpenAPI spec.
GET /api/health
Liveness probe for all observability surfaces (DB + HTTP services).
- Response:
{ok, data: {db: {ok, error?, target (redacted DSN), serverVersion?, currentUser?, readOnly?}, services: [{name, url, ok, detail|null}], temporalUiUrl}, at}. Probes donto-api (:8000), dontosrv (:7879), donto-memory (:7900), and the read-only Postgres connection; degrades gracefully if any service is down.
curl -s https://admin.donto.org/api/health | jq
GET /api/overview
At-a-glance health dashboard: substrate metrics + statement counts + daemon + extraction jobs.
- Response:
{ok, data: {health: SubstrateHealth|null, daemon: DaemonStatus, statementEstimate: number|null, jobsRunning, jobsCompleted, jobsFailed, jobsTotal}, at}.SubstrateHealthcarries the same fields asdonto_v_substrate_health. Each section degrades to null/0 on a fresh donto.
curl -s https://admin.donto.org/api/overview | jq .data.health
GET /api/contexts
Top contexts by statement count (from the donto_mv_context_stats matview) + totals.
- Query:
limit?(1–500, default 50). - Response:
{ok, data: {rows: [{iri, kind|null, mode|null, label|null, statementCount, openCount|null, refreshedAt|null}], totalContexts|null, totalStatements|null}, at}.
curl -s 'https://admin.donto.org/api/contexts?limit=10' | jq .data.rows
GET /api/fabric
Substrate fabric metrics: embedding coverage, closure breakdown, identity machinery, evidence, top predicates, GLM cap state.
- Response:
{ok, data: {coverage: {predicateEmbeddings, predicateTotal, entityEmbeddings, evidenceLinks, believedStatements, anchorPct}, closure: {total|null, byRelation: [{relation, n}]}, identity: {edges, clusters, proposals, hypotheses, memberships}, glmCap: {capUntil|null, capReason|null, glmCallsThisWindow, glmWindowStarted|null}|null, topPredicates: [{predicate, namespace, n}]}, at}.
curl -s https://admin.donto.org/api/fabric | jq .data.topPredicates
GET /api/processes
Live instance activity: extraction jobs, memory jobs, alignment/extraction runs, daemon heartbeat, service health, Temporal UI link.
- Response:
{ok, data: {jobs: {reachable, jobs: [ExtractionJob], running, completed, failed}, memoryJobs: {reachable, jobs: [MemoryJob], ok, failed, memorize, recall}, extractionRuns: [...], alignmentRuns: [...], daemon: DaemonStatus, daemonTicks: [DaemonTick], services: [ServiceHealth], temporalUiUrl}, at}. Fetches extraction jobs from donto-api/jobs, memory jobs from donto-memory/jobs/list.json, daemon status from DB introspection; each source degrades gracefully.
curl -s https://admin.donto.org/api/processes | jq '.data | {jobs: .jobs, services: .services}'
GET /api/jobs
Paginated list of ingested documents (generic ingestion-job explorer).
- Query:
q?(search iri/label),adapter?(parser_version filter),limit?(1–200, default 50),offset?(default 0). - Response:
{ok, data: {jobs: [{id (document_id), iri, label|null, mediaType|null, sourceKind|null, adapter|null, registeredBy|null, parserVersion|null, status|null, byteSize|null, createdAt|null, nStatements}], total, limit, offset, adapters: [{adapter, n}]}, at}.
curl -s 'https://admin.donto.org/api/jobs?limit=10&offset=0' | jq .data.jobs
GET /api/jobs/[id]
One ingested document: source + extracted statements + LLM transcript.
- Path:
id(document_id UUID). - Response:
{ok, data: {id, iri, label, mediaType, sourceKind, adapter, registeredBy, parserVersion, status, byteSize, createdAt, nStatements, source: string|null, sourceChars, context, statements: [{subject, predicate, object, isIri, maturity (0-7), confidence|null}], transcript: string|null, transcriptFile: string|null}, at}.404if not found.
curl -s 'https://admin.donto.org/api/jobs/550e8400-e29b-41d4-a716-446655440000' | jq .data
GET /api/jobs/[id]/charts
Complete chart datasets for one job: totals, maturity/polarity/evidence distributions, predicates, reach, entity shape.
- Path:
id(document_id UUID). - Response:
{ok, data: {totals: {statements, anchored, hypothesis, unanchored, aligned, argued, distinctPredicates, distinctSubjects, withValidTime}, maturity: [Pair] (0-7), polarity: [Pair], datatype: [Pair], evidence: [Pair], validTime: [Pair], objectKind: [Pair], predicateFreq: [{predicate, count}], hapax, contested: {subject, predicate, objects: [{object, weight, e, n}]}|null, entityShape: [{subject, predicates}], predicateReach: [{predicate, inJob, substrate, folds}], entityReach: [{entity, inJob, everywhere}]}, at}wherePair = {label, count}. Bounded to 5000 claims.
curl -s 'https://admin.donto.org/api/jobs/550e8400-e29b-41d4-a716-446655440000/charts' | jq '.data.totals'
GET /api/jobs/[id]/nebula
Belief Nebula data + layout for one job: claims with embeddings, contention systems, argument filaments, gas density.
- Path:
id(document_id UUID). - Response:
{ok, data: {claims: [{id, subject, predicate, object, isIri, maturityE (0-7), hasEvidence, evidenceCount, confidence, polarity (flags & 3), contextDomain, validFrom, validTo, validUnbounded, txFrom, txRetracted, predFamily, hasEntityEmbedding, hasPredicateEmbedding, placedByEmbedding, x (PCA), y}], systems: [{subject, predicate, objects: [{object, weight, evidenceLevel, polarity}], barycenter: [x, y]}], filaments: [{source, target, relation, strength}], gas: [{domain, density}]}, at}. Bounded ≤5000 claims; server-side PCA of entity⊕predicate embeddings (deterministic layout).
curl -s 'https://admin.donto.org/api/jobs/550e8400-e29b-41d4-a716-446655440000/nebula' | jq '.data.claims | length'
GET /api/jobs/[id]/panel/[key]
One lazy-loaded donto-capability lens for a job.
- Path:
id(document_id UUID),key— one ofevidence-anchors,contradictions,predicate-alignment,embedding-neighbors,bitemporal,identity,cross-source-lens,provenance-maturity,entity-reach,outgoing-graph. - Response:
{ok, data: PanelData[], at}(shape varies by key; e.g.evidence-anchors:[{subject, predicate, object, anchor (surface_text), start_offset, end_offset, confidence, e_level}];entity-reach:[{subject, in_job, believed_everywhere, beyond_this_job, pct_from_this_job}]).404ifkeyunknown; each panel bounded to 50–200 rows.
curl -s 'https://admin.donto.org/api/jobs/550e8400-e29b-41d4-a716-446655440000/panel/evidence-anchors' | jq '.data | length'
GET /api/relations
Introspected list of every public relation (tables, matviews, views, partitioned parents) with sizes and statistics.
- Response:
{ok, data: {count, relations: [{name, kind (r/m/v/p), kindLabel, estRows, totalBytes, tableBytes, indexBytes, totalSize, tableSize, indexSize, lastAnalyze|null, lastVacuum|null, comment|null}]}, at}. Purepg_catalogquery (no hardcoded table list), sorted by total relation size DESC.
curl -s https://admin.donto.org/api/relations | jq '.data.relations | length'
GET /api/table/[rel]
Introspected table/matview/view details: columns, indexes, recent rows, count estimate, value distributions.
- Path:
rel(relation name, validated against the live introspected allow-set). Query:view?(all|columns|rows|count|distributions, defaultall),limit?(1–200, default 50). - Response:
{ok, data: {relation: Relation, columns?: [{name, type, notNull, hasDefault, isPk, position, comment|null}], indexes?: [{name, def, isPrimary, isUnique}], count?, recent?: {rows: [...], totalOffset?}, distributions?: [{column, value, count}]}, at}. Injection-guarded (rel validated,%Iidentifier quoting). Big relations (>50k rows) return areltuplesestimate instead of an exact count.
curl -s 'https://admin.donto.org/api/table/donto_statement?view=columns' | jq '.data.columns'
GET /api/beam
BEAM-10M extraction-firehose progress.
- Response:
{ok, data: {beam: {available: boolean, total (chunks enqueued), pending, leased (in-flight), done, failed, claims (total claims emitted)}}, at}. Reads entirely fromdonto_extract_queue; degrades gracefully (available: false) on a donto without an extraction run.
curl -s https://admin.donto.org/api/beam | jq .data.beam
MCP access
The donto-memory MCP server exposes the three core memory operations as Model Context Protocol tools, so MCP-aware agents (Claude Code, Codex, etc.) can read and write the substrate without hand-rolling HTTP calls. It is a thin front-end over donto-memory's /recall, /search, and /memorize endpoints; it talks to https://memories.apexpots.com by default (override with DONTO_MEMORY_URL). No auth is required (the substrate is public by design). There are two interchangeable implementations exposing the identical 3 tools: a Node ESM npm package (primary) and a stdlib-only Python single-file server.
Tools
donto_recall(holder, query, limit=10)— recall a holder's own memories. Hybrid lexical + semantic, holder-scoped, bitemporal (prefers the latestvalid_fromfor multi-valued attributes). WrapsPOST /recall.donto_search(query, context_prefix=null, limit=10)— substrate-wide full-text search across all contexts (genealogy, memory, research-db, …). Returns ranked results. WrapsPOST /search.donto_memorize(holder, text, valid_from=null, extract=true)— store a memory as an episodic chunk, optionally extracting semantic claims via LLM. Fast modes return immediately; slow modes are queued. WrapsPOST /memorize.
Install
NPM (hosted), runs the package directly:
npx -y donto-memory-mcp
Register with an MCP client:
codex mcp add donto-memory -- npx -y donto-memory-mcp
claude mcp add donto-memory -- npx -y donto-memory-mcp
Config block with a custom endpoint:
{
"mcpServers": {
"donto-memory": {
"command": "npx",
"args": ["-y", "donto-memory-mcp"],
"env": { "DONTO_MEMORY_URL": "http://127.0.0.1:7900" }
}
}
}
Python (local, stdlib-only, no dependencies):
python3 /mnt/donto-data/workspace/donto-memory/mcp/donto_mcp.py
MCP docs are published at https://mcp.donto.org (index + /agents.md + /llms.txt + /mcp.json).
Quickstart recipes
1. Search the substrate, then read a claim
Find entities/claims by name, then pull the full evidence-backed claim card for one of them.
# Substrate-wide name search (via donto-memory)
curl -X POST https://memories.apexpots.com/search \
-H 'Content-Type: application/json' \
-d '{"query":"caroline brown","context_prefix":"ctx:genes","limit":10}'
# Take a statement_id from the rows, then fetch its claim card (via donto-api / dontosrv)
curl 'https://genes.apexpots.com/claim/<statement_uuid>'
2. Memorize, then recall (agentic memory)
Store an episodic memory with a real-world valid date, then recall it holder-scoped.
# Memorize (sync for fast modes; episodic always stored, semantic claims extracted if configured)
curl -X POST https://memories.apexpots.com/memorize \
-H 'Content-Type: application/json' \
-d '{"holder":"agent:my-bot","session_id":"discord:user:12345","text":"I met Annie Davis at Cooktown Festival in 1979.","valid_from":"1979-01-01"}'
# Recall what this holder knows (hybrid lexical + semantic)
curl -X POST https://memories.apexpots.com/recall \
-H 'Content-Type: application/json' \
-d '{"holder":"agent:my-bot","query":"who did I meet","limit":10}'
3. Run an embedding worker (lease → embed → submit)
A spare machine pulls work, computes bge-small (384-dim) vectors, and submits them. Remember the bearer token and the curl/8 UA.
# 1. (Operator) top up the queue from the source table
curl -A 'curl/8' -X POST -H 'Authorization: Bearer <TOKEN>' -H 'Content-Type: application/json' \
-d '{"target":"memory_chunk","limit":10000}' \
https://donto.org/embed/enqueue
# 2. Lease a batch to embed
curl -A 'curl/8' -X POST -H 'Authorization: Bearer <TOKEN>' -H 'Content-Type: application/json' \
-d '{"worker_id":"worker-1","n":100,"target":"memory_chunk"}' \
https://donto.org/embed/lease
# -> {"batch":[{"target":"memory_chunk","item_id":"abc-123","text":"...","model":"BAAI/bge-small-en-v1.5","dim":384}, ...]}
# 3. Compute vectors locally (bge-small, 384 dims), then submit
curl -A 'curl/8' -X POST -H 'Authorization: Bearer <TOKEN>' -H 'Content-Type: application/json' \
-d '{"items":[{"target":"memory_chunk","item_id":"abc-123","vector":[0.1,0.2]}]}' \
https://donto.org/embed/submit
# -> {"upserted":1}
# Monitor progress
curl -A 'curl/8' -H 'Authorization: Bearer <TOKEN>' https://donto.org/embed/stats
4. Extract and ingest a document (then verify in the graph)
Preview facts with a dry run, commit them, and confirm they landed in the substrate.
# Preview (no writes)
curl -X POST https://genes.apexpots.com/extract \
-H 'Content-Type: application/json' \
-d '{"text":"Mary Watson was born in Cornwall in 1860. She married Robert Watson in Cooktown, Queensland in 1879.","context":"ctx:genes/mary-watson/obituary","dry_run":true}'
# Commit (extract + ingest, anchors attached to the source)
curl -X POST https://genes.apexpots.com/extract-and-ingest \
-H 'Content-Type: application/json' \
-d '{"text":"Mary Watson was born in Cornwall in 1860. She married Robert Watson in Cooktown, Queensland in 1879.","context":"ctx:genes/mary-watson/obituary"}'
# Verify: find the entity, then read its full history
curl 'https://genes.apexpots.com/search?q=mary+watson&limit=5'
curl 'https://genes.apexpots.com/history/ex:mary-watson?limit=50'
For batch/durable extraction, submit a job instead and poll:
JOB=$(curl -sX POST https://genes.apexpots.com/jobs/extract \
-H 'Content-Type: application/json' \
-d '{"text":"...","context":"ctx:genes/mary-watson/async"}' | jq -r '.job_id')
curl "https://genes.apexpots.com/jobs/$JOB" | jq '.status'
curl "https://genes.apexpots.com/jobs/$JOB/facts?limit=50"
Verified 2026-06-07. Live-probed endpoints: donto-memory — GET /health, GET /version, GET /substrate, GET /api, GET /modules, POST /recall, POST /search, POST /search/resources, GET /reconsolidate/queue, GET /explore/stats.json, GET /openapi.json. donto-memory-mcp — POST /recall, POST /search, POST /memorize. donto-embed-coordinator — GET /embed/health (shape + Caddy routing). donto-api — GET /search, GET /subjects, GET /contexts, GET /health, GET /version (OpenAPI fetched live; note GET /predicates is shadowed by the web app on the public host and returns HTML). dontosrv — GET /health, GET /version, GET /metrics, POST /assert, GET /discovery/contract-version. donto-admin — GET /api/health, GET /api/overview. All other endpoints documented from source code.