API Reference
Every endpoint on the Nocturnus.AI HTTP server. Run the smoke test below to verify your setup, then explore context management and the simplified API. Base URL: http://localhost:9300
Common Headers
Content-Type: application/json— required for all POST requestsX-Database: default— selects the logical database (optional, defaults to "default")X-Tenant-ID: <name>— selects the tenant within that database (required on most endpoints; MCP routes default to "default")Authorization: Bearer <key>— required when auth is enabled
Error Responses
All endpoints return errors as JSON with a consistent shape:
{
"code": "VALIDATION_ERROR",
"message": "Missing required header: X-Tenant-ID",
"details": null
} Common error codes: VALIDATION_ERROR, NOT_FOUND, CONFLICT, UNAUTHORIZED, RATE_LIMITED.
API Smoke Test (Copy/Paste)
Use this first. It validates /tell, /teach, and /ask directly from the API reference.
export NOCTURNUS_BASE_URL=http://localhost:9300
export NOCTURNUS_DB=default
export NOCTURNUS_TENANT=default
curl -sS "$NOCTURNUS_BASE_URL/health"
## Tell two parent facts (need a chain for grandparent to work)
curl -sS -X POST "$NOCTURNUS_BASE_URL/tell" \
-H "Content-Type: application/json" \
-H "X-Database: $NOCTURNUS_DB" \
-H "X-Tenant-ID: $NOCTURNUS_TENANT" \
-d '{"predicate":"parent","args":["alice","bob"]}'
curl -sS -X POST "$NOCTURNUS_BASE_URL/tell" \
-H "Content-Type: application/json" \
-H "X-Database: $NOCTURNUS_DB" \
-H "X-Tenant-ID: $NOCTURNUS_TENANT" \
-d '{"predicate":"parent","args":["bob","charlie"]}'
## Teach a rule: grandparent(?x,?z) :- parent(?x,?y), parent(?y,?z)
curl -sS -X POST "$NOCTURNUS_BASE_URL/teach" \
-H "Content-Type: application/json" \
-H "X-Database: $NOCTURNUS_DB" \
-H "X-Tenant-ID: $NOCTURNUS_TENANT" \
-d '{
"head": {"predicate":"grandparent","args":["?x","?z"]},
"body": [
{"predicate":"parent","args":["?x","?y"]},
{"predicate":"parent","args":["?y","?z"]}
]
}'
## Ask: should return grandparent(alice, charlie)
curl -sS -X POST "$NOCTURNUS_BASE_URL/ask" \
-H "Content-Type: application/json" \
-H "X-Database: $NOCTURNUS_DB" \
-H "X-Tenant-ID: $NOCTURNUS_TENANT" \
-d '{"predicate":"grandparent","args":["?who","charlie"]}' Context Management (cost optimization)
| Endpoint | Description |
|---|---|
| POST /context | Simplest API — turns in, facts out. Array of strings → optimized facts. Requires LLM provider. |
| POST /context/optimize | Deprecated (sunset 2026-07-01) — use POST /memory/context with goals instead |
| POST /context/diff | Incremental changes since last context window |
| POST /context/summary | Knowledge base summary (predicate counts, contradictions) |
| POST /context/session/clear | Clear diff session state |
| POST /context/ingest | Extract + assert + optimize in one call (requires LLM) |
/tell, /teach, and /ask which work immediately with no LLM config. For the full turn-reduction workflow with POST /context, you'll need an LLM provider configured. See the context workflow guide → Simplified API
Developer-friendly aliases for the most common operations. These are the endpoints shown in the quickstart.
| Endpoint | Full Form | Description |
|---|---|---|
| POST /tell | POST /assert/fact | Store a fact |
| POST /ask | POST /infer | Query with inference (backward chaining) |
| POST /teach | POST /assert/rule | Define a logical rule |
| POST /forget | POST /retract | Remove a fact (cascading) |
POST /assert/fact (or /tell)
Assert a fact into the knowledge base.
cURL
curl -X POST http://localhost:9300/assert/fact \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{"predicate":"parent","args":["alice","bob"]}' Request
{
"predicate": "parent",
"args": ["alice", "bob"],
"truthVal": true,
"scope": null,
"ttl": null,
"validUntil": null,
"metadata": {}
} Only predicate and args are required. All other fields are optional.
Response
Returns plain text (not JSON):
Fact Asserted: parent(alice, bob) POST /assert/rule (or /teach)
Define a logical rule for inference.
cURL
curl -X POST http://localhost:9300/assert/rule \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{
"head": {"predicate":"grandparent","args":["?x","?z"]},
"body": [
{"predicate":"parent","args":["?x","?y"]},
{"predicate":"parent","args":["?y","?z"]}
]
}' Request
{
"head": { "predicate": "grandparent", "args": ["?x", "?z"] },
"body": [
{ "predicate": "parent", "args": ["?x", "?y"] },
{ "predicate": "parent", "args": ["?y", "?z"] }
],
"scope": null
} POST /infer (or /ask)
Run backward-chaining inference. Use ?-prefixed variables for unknowns.
cURL
curl -X POST http://localhost:9300/infer \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{"predicate":"grandparent","args":["?who","charlie"]}' Request
{
"predicate": "grandparent",
"args": ["?who", "charlie"],
"scope": null
} To get the full derivation chain showing how each result was derived, use POST /ask (the simplified alias) which accepts withProof in the JSON body:
{
"predicate": "grandparent",
"args": ["?who", "charlie"],
"withProof": true
} Response
[
{
"predicate": "grandparent",
"args": ["alice", "charlie"],
"negated": false,
"scope": null,
"metadata": {},
"createdAt": null,
"validFrom": null,
"validUntil": null,
"ttl": null,
"confidence": null
}
] POST /query
Direct pattern matching without inference (no rule application). Reads the Hexastore directly — faster than /infer when you only need stored facts and don't need derived conclusions.
cURL
curl -X POST http://localhost:9300/query \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{"predicate":"parent","args":["?who","bob"]}' Request
{
"predicate": "parent",
"args": ["?who", "bob"],
"scope": null
} Response
[
{ "predicate": "parent", "args": ["alice", "bob"], "truthVal": true }
] POST /retract (or /forget)
Remove a fact. The TMS automatically cascade-retracts any derived conclusions that depended on it.
cURL
curl -X POST http://localhost:9300/retract \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{"predicate":"parent","args":["alice","bob"]}' Request
{
"predicate": "parent",
"args": ["alice", "bob"]
} POST /aggregate
Run aggregation operations over matched facts. Supports COUNT, SUM, MIN, MAX, and AVG.
cURL
curl -X POST http://localhost:9300/aggregate \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{"predicate":"salary","op":"AVG","argIndex":1}' Request
{
"predicate": "salary",
"op": "AVG",
"argIndex": 1,
"scope": null
} op is one of: COUNT, SUM, MIN, MAX, AVG. argIndex selects which argument to aggregate over (required for numeric ops, not needed for COUNT). scope is optional.
Response
{ "result": 75000.0 } POST /assert/facts (Bulk Assert)
Assert multiple facts in a single request.
cURL
curl -X POST http://localhost:9300/assert/facts \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{"facts":[
{"predicate":"role","args":["alice","engineer"]},
{"predicate":"role","args":["bob","manager"]}
]}' Request
{
"facts": [
{ "predicate": "role", "args": ["alice", "engineer"] },
{ "predicate": "role", "args": ["bob", "manager"] }
]
} POST /retract/pattern
Retract all facts matching a predicate pattern. Use null in the args array as a wildcard.
cURL
curl -X POST http://localhost:9300/retract/pattern \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{"predicate":"role","args":[null,"engineer"]}' Request
{
"predicate": "role",
"args": [null, "engineer"],
"scope": null
} This retracts all facts with predicate role where the second argument is "engineer", regardless of the first argument.
ACID Transactions
Group multiple operations into an atomic unit. If any operation fails, the entire transaction rolls back — ensuring consistency when multiple agents write simultaneously.
# Common headers used below
export NOCTURNUS_TENANT=default
export NOCTURNUS_DB=default
# 1. Begin transaction (/tx/begin returns plain text transaction id)
TX_ID=$(curl -s -X POST http://localhost:9300/tx/begin \
-H "X-Database: $NOCTURNUS_DB" \
-H "X-Tenant-ID: $NOCTURNUS_TENANT")
# 2. Assert facts within the transaction
curl -X POST http://localhost:9300/assert/fact \
-H "Content-Type: application/json" \
-H "X-Database: $NOCTURNUS_DB" \
-H "X-Tenant-ID: $NOCTURNUS_TENANT" \
-H "X-Transaction-ID: $TX_ID" \
-d '{"predicate":"balance","args":["alice","500"]}'
curl -X POST http://localhost:9300/assert/fact \
-H "Content-Type: application/json" \
-H "X-Database: $NOCTURNUS_DB" \
-H "X-Tenant-ID: $NOCTURNUS_TENANT" \
-H "X-Transaction-ID: $TX_ID" \
-d '{"predicate":"balance","args":["bob","300"]}'
# 3. Commit (or rollback)
curl -X POST "http://localhost:9300/tx/commit/$TX_ID" \
-H "X-Database: $NOCTURNUS_DB" \
-H "X-Tenant-ID: $NOCTURNUS_TENANT"
# curl -X POST "http://localhost:9300/tx/rollback/$TX_ID" \
# -H "X-Database: $NOCTURNUS_DB" \
# -H "X-Tenant-ID: $NOCTURNUS_TENANT" POST /extract
LLM-powered fact extraction from natural language text. See LLM Integration for details.
cURL
curl -X POST http://localhost:9300/extract \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{"text":"Acme Corp is on the enterprise plan.","assert":true}' Request
{
"text": "Acme Corp is on the enterprise plan.",
"assert": true,
"rules": false,
"scope": null,
"context": null
} POST /synthesize
LLM-powered natural language question answering grounded in facts. See LLM Integration.
cURL
curl -X POST http://localhost:9300/synthesize \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{"question":"What plan is Acme Corp on?"}' Request
{
"question": "What plan is Acme Corp on?",
"scope": null
} Response
{
"answer": "Acme Corp is on the enterprise plan.",
"derivation": [{ "fact": "subscription_tier(acme_corp, enterprise)", "type": "fact_match" }],
"confidence": 0.95,
"queriesExecuted": ["subscription_tier(acme_corp, ?tier)"],
"provider": "anthropic",
"model": "claude-sonnet-4-20250514"
} Memory Endpoints
POST /memory/context (unified context endpoint)
The single entry point for all context window operations. Simple requests (just maxFacts/minSalience) use a fast salience-ranked path. Adding goals, sessionId, or relevanceBuckets automatically triggers the full optimization engine.
Simple usage (fast path)
curl -X POST http://localhost:9300/memory/context \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{
"maxFacts": 100,
"minSalience": 0.0
}' Advanced usage (goal-driven optimization)
curl -X POST http://localhost:9300/memory/context \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{
"maxFacts": 25,
"goals": [{"predicate":"enforceable","args":["contract_001"]}],
"sessionId": "session-42",
"format": "natural",
"includeRules": true,
"autoResolveContradictions": true,
"relevanceBuckets": [
{"name":"legal","predicates":["enforceable","statute_of_frauds"],"weight":3.0}
]
}' Request fields
| Field | Type | Default | Description |
|---|---|---|---|
| maxFacts | int? | 100 | Maximum facts in the context window |
| minSalience | double? | 0.0 | Minimum salience score filter |
| predicates | string[]? | null | Filter to specific predicate names |
| scope | string? | null | Restrict to facts in this scope |
| format | string? | "structured" | "natural" returns pre-formatted formattedText for LLM prompts; "structured" returns entries array |
| includeRules | boolean? | false | Include relevant rules in the response |
| goals | GoalSpec[]? | null | Goal atoms for backward chaining (triggers optimization engine) |
| sessionId | string? | null | Session ID for incremental diffs (triggers optimization engine) |
| autoResolveContradictions | boolean? | true | Auto-resolve contradictions (keeps higher-salience fact) |
| maxFactsPerPredicate | int? | null | Diversity cap — max facts per predicate type |
| relevanceBuckets | Bucket[]? | null | Per-domain weighting: name, predicates, weight (triggers optimization engine) |
When goals, sessionId, or relevanceBuckets are present, the response includes additional fields: formattedText, windowId, rules, contradictionsFound, contradictionsResolved, contradictions, deduplicationSavings, bucketStats, goalDriven, knowledgeGeneration.
POST /memory/consolidate
Compress repetitive episodic patterns into semantic summaries.
CLI-friendly alias: POST /memory/compress.
POST /memory/decay
Evict expired and low-salience facts.
CLI-friendly alias: POST /memory/cleanup.
{ "threshold": 0.05 } Context Management Engine
The context optimization API — the core of NocturnusAI's cost reduction story. These endpoints deliver goal-driven, salience-ranked, contradiction-checked context windows that cut token costs by 82–90% (measured on live Claude Opus 4 and Gemini 2.0 Flash).
/context for fast turn ingestion, /memory/context with goals and a stable sessionId for assembly, /context/diff for follow-up turns, and /context/session/clear when the conversation ends.
| Method + Endpoint | When to Use It |
|---|---|
| POST /memory/context | Unified context endpoint — simple salience window OR goal-driven optimization (progressive disclosure) |
| POST /context | Turns in, facts out (simple ingestion + selection) |
| POST /context/optimize | Deprecated (sunset 2026-07-01) — use POST /memory/context with goals |
| POST /context/diff | Delta updates since last optimized window |
| POST /context/summary | Context/KB telemetry (counts, salience, contradictions) |
| POST /context/session/clear | Delete diff snapshot state at end of conversation |
| POST /context/ingest | One-shot extract + assert + optimize workflow |
POST /context
The simplest way to use NocturnusAI. Pass an array of conversation turns (strings), get back relevant facts. NocturnusAI extracts, stores, deduplicates, and ranks automatically.
cURL
curl -X POST http://localhost:9300/context \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{
"turns": [
"Acme Corp is on the enterprise plan and based in Austin.",
"They have a $2M contract with 24/7 SLA support."
],
"maxFacts": 50
}' Request
{
"turns": [
"Acme Corp is on the enterprise plan and based in Austin.",
"They have a $2M contract with 24/7 SLA support."
],
"maxFacts": 50,
"scope": null
} Only turns is required. maxFacts defaults to 50. scope is optional for data isolation.
Response
{
"facts": [
{ "predicate": "customer_tier", "args": ["acme_corp", "enterprise"], "salience": 0.95, "provenance": null },
{ "predicate": "location", "args": ["acme_corp", "austin"], "salience": 0.88, "provenance": null },
{ "predicate": "contract_value", "args": ["acme_corp", "2000000"], "salience": 0.92, "provenance": null },
{ "predicate": "sla_tier", "args": ["acme_corp", "24_7"], "salience": 0.90, "provenance": null }
],
"totalFactsInKB": 127,
"factsReturned": 4,
"contradictions": 0,
"newFactsExtracted": 4
} Feed the facts array directly into your LLM's system prompt. See the Context Optimization guide for full integration examples with OpenAI, Claude, LangChain, and MCP.
POST /context/optimize (deprecated — sunset 2026-07-01)
POST /memory/context with the same parameters instead. This endpoint returns a Deprecation: true response header. It will be removed after 2026-07-01.
Build an optimized context window. Optionally goal-driven via backward chaining.
cURL
curl -X POST http://localhost:9300/context/optimize \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: default" \
-d '{
"maxFacts": 25,
"goals": [
{"predicate":"enforceable","args":["contract_001"]}
],
"sessionId": "session-42"
}' Request
{
"maxFacts": 25,
"goals": [
{ "predicate": "enforceable", "args": ["contract_001"] },
{ "predicate": "exception_applies", "args": ["contract_001", "?e"] }
],
"relevanceBuckets": [
{ "name": "contract", "predicates": ["contract_type", "contract_value"], "weight": 1.5 },
{ "name": "legal", "predicates": ["enforceable", "statute_of_frauds"], "weight": 3.0 }
],
"scope": null,
"predicates": null,
"sessionId": "session-42",
"autoResolveContradictions": true,
"maxFactsPerPredicate": null
} | Field | Type | Default | Description |
|---|---|---|---|
| maxFacts | int? | 100 | Maximum facts in the context window |
| goals | GoalSpec[]? | null | Goal atoms for backward chaining. Each has predicate, args, optional negated |
| relevanceBuckets | Bucket[]? | null | Per-domain weighting: name, predicates, weight |
| scope | string? | null | Restrict to facts in this scope |
| predicates | string[]? | null | Filter to specific predicate names |
| sessionId | string? | null | Session ID for incremental diffs (stores snapshot) |
| autoResolveContradictions | boolean | true | Auto-resolve contradictions (keeps higher-salience fact) |
| maxFactsPerPredicate | int? | null | Diversity cap — max facts per predicate type |
Response
{
"windowId": "ctx-a7f3b2e1",
"entries": [
{
"predicate": "enforceable",
"args": ["contract_001"],
"negated": false,
"scope": null,
"salience": 0.98,
"category": "goal_relevant",
"charCount": 28,
"provenance": {
"rule": "enforceable(?c) :- signed(?c), consideration(?c)",
"premises": ["signed(contract_001)", "consideration(contract_001)"]
},
"createdAt": 1712180400000,
"validFrom": null,
"validUntil": null,
"metadata": {}
}
],
"relevantRules": ["enforceable(?c) :- signed(?c), consideration(?c)"],
"totalFactsAvailable": 512,
"totalFactsIncluded": 15,
"deduplicationSavings": 3,
"contradictionsFound": 1,
"contradictionsResolved": 1,
"contradictions": [
{
"predicate": "valid_until",
"args": ["contract_001", "2024-12"],
"positiveSalience": 0.7,
"negativeSalience": 0.3
}
],
"bucketStats": {
"contract": { "factsIncluded": 6, "maxAllocation": 8, "minSalience": 0.45, "maxSalience": 0.98 },
"legal": { "factsIncluded": 9, "maxAllocation": 10, "minSalience": 0.52, "maxSalience": 0.98 }
},
"totalCharCount": 820,
"goalDriven": true,
"knowledgeGeneration": 47,
"generatedAt": 1712180450000
} totalCharCount is useful for prompt budgeting. A quick estimate is tokens ≈ totalCharCount / 4.
POST /context/diff
Get incremental changes since a previous context window. Requires a sessionId that was used in a prior /memory/context call (with goals or sessionId). Only sends what changed — further reducing token spend on multi-turn conversations.
Request
{
"sessionId": "session-42",
"maxFacts": 25,
"scope": null,
"goals": [{ "predicate": "enforceable", "args": ["contract_001"] }]
} Response
{
"previousWindowId": "ctx-a7f3b2e1",
"currentWindowId": "ctx-b8g4c3f2",
"added": [
{ "predicate": "amendment", "args": ["contract_001", "clause_7"], "salience": 0.91, "category": "goal_relevant", "charCount": 38 }
],
"removed": [
{ "key": "valid_until/contract_001/2024-12", "predicate": "valid_until", "args": ["contract_001", "2024-12"], "negated": false, "scope": null }
],
"unchanged": 14,
"fullRefreshRecommended": false,
"reason": null
} POST /context/summary
Get a compact summary of the knowledge base — predicate counts, expiring facts, contradictions, and top salient facts. Useful for dashboard views and monitoring.
Request
{ "scope": null } Response
{
"totalFacts": 512,
"predicateCount": 23,
"topPredicates": [
{ "predicate": "contract_type", "count": 87 },
{ "predicate": "customer_tier", "count": 64 }
],
"factsWithTtl": 45,
"factsExpiringWithin1h": 12,
"contradictions": 3,
"topSalientFacts": [],
"totalCharCount": 24500,
"knowledgeGeneration": 47,
"generatedAt": 1712180450000
} POST /context/session/clear
Clear session state used for context diffing. Call this when a conversation ends or to reset diff tracking.
Request
{ "sessionId": "session-42" } Response
Session 'session-42' cleared POST /context/ingest
One-shot pipeline: extract facts from text (via LLM or predicate syntax), assert them, and return an optimized context window — all in a single request. Requires LLM extraction to be enabled for natural language input.
Request
{
"text": "Acme Corp signed contract #001 for $2M. The contract includes an arbitration clause.",
"goals": [{ "predicate": "enforceable", "args": ["contract_001"] }],
"maxFacts": 25,
"scope": null,
"sessionId": "session-42",
"autoResolveContradictions": true,
"contextHint": "Legal contract analysis"
} Response
{
"extracted": [
{ "predicate": "contract_signed", "args": ["acme_corp", "contract_001"], "confidence": 0.95 },
{ "predicate": "contract_value", "args": ["contract_001", "2000000"], "confidence": 0.92 },
{ "predicate": "has_clause", "args": ["contract_001", "arbitration"], "confidence": 0.88 }
],
"extractionProvider": "anthropic",
"context": {
"windowId": "ctx-c9h5d4g3",
"entries": [...],
"totalFactsAvailable": 515,
"totalFactsIncluded": 18,
"totalCharCount": 940,
"goalDriven": true
}
} Admin Endpoints
| Endpoint | Description |
|---|---|
| GET /health | Health check (Kubernetes liveness) |
| GET /health/ready | Readiness check |
| GET /predicates | Discover knowledge base schema |
| GET /admin/databases | List all databases |
| POST /admin/databases | Create a new database |
| GET /metrics | Prometheus metrics |
| POST /tx/begin | Begin ACID transaction |
| POST /tx/commit/:id | Commit transaction |
| POST /tx/rollback/:id | Rollback transaction |
| GET /admin/databases/:name/tenants | List tenants in a database |
| POST /admin/databases/:name/tenants | Create a tenant |
| DELETE /admin/databases/:name/tenants/:id | Delete a tenant and all its data |
| POST /admin/databases/:name/nuke | Delete all data in a database |
| POST /admin/databases/:name/tenants/:id/nuke | Delete all data in a tenant |
Scope Management
Scopes are logical partitions within a tenant for hypothetical reasoning, versioning, and A/B testing. Use Git-like fork/diff/merge operations to branch knowledge.
| Endpoint | Description |
|---|---|
| POST /scope/fork | Fork a scope — copies all facts from source to a new scope |
| POST /scope/diff | Diff two scopes — shows added/removed/modified facts |
| POST /scope/merge | Merge scopes with strategy: SOURCE_WINS, TARGET_WINS, KEEP_BOTH, REJECT |
| DELETE /scope/:name | Delete a scope and all its facts |
| GET /scopes | List all scopes in the current tenant |
Aggregation & Bulk Operations
| Endpoint | Description |
|---|---|
| POST /aggregate | Aggregation queries: COUNT, SUM, MIN, MAX, AVG on fact arguments |
| POST /assert/facts | Bulk assert — submit an array of facts in a single request |
| POST /retract/pattern | Pattern-based retraction — remove all facts matching a pattern |