Skip to Content
API Reference

API Reference

The Khwan REST API. The Python and TypeScript clients are thin wrappers over these endpoints — you can call them directly from any language.

  • Base URL: https://api.khwan.ai
  • Content type: application/json for all request and response bodies.
  • Errors: any non-2xx response is an error. The body carries a message; common codes are listed under Errors.

Authentication

Every request carries these headers:

HeaderRequiredPurpose
X-API-KeyYesYour Khwan API key (kwk_live_...). Authenticates the account.
X-Khwan-UserRecommendedThe end-user id. Identifies the user on the request; does not select an isolated brain.
X-Khwan-CoreOptionalCore slug. Selects an isolated brain within the account. Omit for the account’s default core.
POST /prepare HTTP/1.1 Host: api.khwan.ai X-API-Key: kwk_live_xxx X-Khwan-User: alice X-Khwan-Core: client1 Content-Type: application/json

X-API-Key is your Khwan key, not your model provider’s key. In the BYOM flow Khwan never receives your Anthropic / OpenAI key.

Isolation is by core (and by account), not by X-Khwan-User. See Cores.

POST /prepare

Builds the context for one turn (memory + constitution + coherence). Does not call an LLM.

Request

{ "input": "remember I prefer short answers in Thai", "model": "claude-sonnet-4-6", "constitution": "support-agent-v2" }
FieldTypeRequiredNotes
inputstringYesThe end user’s message.
modelstringNoSession model hint; may be overridden by dashboard settings.
constitutionstringNoNamed constitution profile to apply.

Response

{ "messages": [ { "role": "system", "content": "<constitution + memory + lessons + coherence>" }, { "role": "user", "content": "remember I prefer short answers in Thai" } ], "coherence": 0.92, "sources": [{ "id": "mem_123", "text": "prefers Thai", "score": 0.81 }], "allowed": true, "reason": null, "turn_token": "tt_9f2c..." }

Feed messages to your own model, then send turn_token back to /record. If allowed is false, skip generation and surface reason.

POST /record

Persists your model’s answer and lets Khwan learn from the turn.

Request

{ "turn_token": "tt_9f2c...", "answer": "Got it. I'll keep my answers short." }
FieldTypeRequiredNotes
turn_tokenstringYesThe token from the matching /prepare.
answerstringYesThe text your model produced.

Response200 OK, a small acknowledgement object (may be empty).

{ "ok": true }

POST /sync

Triggers a learning / consolidation pass for the selected core (distilling lessons from recent turns).

Request — no body required.

Response

{ "ok": true, "learned": 3 }

GET /memory

Returns recent memory entries for the selected core.

Query parameters

ParamTypeDefaultNotes
limitinteger20Max entries to return.
GET /memory?limit=20 X-API-Key: kwk_live_xxx X-Khwan-Core: client1

Response

{ "items": [ { "id": "mem_123", "text": "prefers short answers in Thai", "created_at": "2026-07-16T10:00:00Z" } ] }

GET /metrics

Returns coherence / learning / usage metrics for the selected core.

GET /metrics X-API-Key: kwk_live_xxx X-Khwan-Core: client1

Response

{ "turns": 128, "avg_coherence": 0.9, "memory_size": 42, "lessons": 7 }

Cores

A core is a fully isolated brain within an account — its own memory, identity, and learning. Select one on any request with the X-Khwan-Core header; omit it to target the account’s default core. Quota is pooled at the account level.

Slugs match ^[a-z0-9][a-z0-9-]{0,38}$. The default core is virtual — always available, and cannot be created or deleted.

POST /cores

Create a core. Admin key required.

Request

{ "slug": "client1", "name": "Client One", "context": "Acme support desk" }
FieldTypeRequiredNotes
slugstringYesMatches ^[a-z0-9][a-z0-9-]{0,38}$. Immutable once created.
namestringYesHuman-readable label.
contextstringNoSeed context / description for the core.

Response — the created core.

{ "slug": "client1", "name": "Client One", "context": "Acme support desk" }

GET /cores

List the account’s cores (includes the virtual default).

{ "items": [ { "slug": "default", "name": "Default" }, { "slug": "client1", "name": "Client One" } ] }

GET /cores/{slug}

Fetch a single core.

{ "slug": "client1", "name": "Client One", "context": "Acme support desk" }

PATCH /cores/{slug}

Update a core’s name or context (the slug is immutable). Admin key required.

Request

{ "name": "Client One — EU", "context": "Acme support desk, EU region" }

DELETE /cores/{slug}

Delete a core and its memory. Admin key required. The default core cannot be deleted.

Response200 OK.

{ "ok": true }

Errors

Any non-2xx status is an error; the body contains a message. Frequently seen:

StatusMeaning
401Unauthorized — bad or missing API key.
402Payment required — add a payment method / upgrade your plan.
429Quota exceeded — over your plan’s limit.

The clients raise a typed error on these (for example KhwanError in Python, with a .status code).

Last updated on