Titentiten.devꦠꦶꦠꦺꦤ꧀
v0.10.0 · stableGitHub
DocsReferenceHTTP API

HTTP API

Every route the service declares, with the scope its credential must hold and the two response envelopes.

96 routesBearer titen_sk_snake_case1 MiB body capMCP at /mcp

The route table below is the router’s own contents. Anything absent from it does not exist yet, and scripts/verify-live.ts fails if the router declares a route the verification script never called.

Authentication

Three routes accept no bearer credential: GET /healthz, GET /readyz, and the username/password exchange at POST /v1/dashboard-sessions. Every other route authenticates first; routes without a named scope still require a valid credential.

Send Authorization: Bearer titen_sk_…. A token that does not start with titen_sk_ is rejected without a database lookup.

Verification is one indexed lookup on the SHA-256 of the key. Nothing about the principal is cached, so a revoked key stops working on the next request.

Organization comes from the credential. No route accepts an org_id field.

A key may hold * in place of a scope list. A key can never mint another key with scopes or a trust ceiling it does not itself hold.

Scope failures are distinguishable from identity failures, but resource ownership is not: see Errors for why a foreign id returns 404 and never 403. The full scope list and trust ceilings are on Keys & scopes.

Envelopes

Success is data plus meta. Failure is error plus meta. Both always carry meta.request_id, repeated in an x-request-id response header. Envelope responses are content-type: application/json; charset=utf-8, and every response is cache-control: no-store.

{ "data": { "": "" },
  "meta": { "request_id": "req_930dd110186f42ad959858d8745fc0b6" } }
{ "error": { "code": "VALIDATION_ERROR", "message": "Field \"kind\" must be one of: …" },
  "meta": { "request_id": "req_71b6d2e2eba54b0298b41de8544dedc6" } }

Request ids are req_ + 32 lowercase hex. Record ids follow the same shape with their own prefix: org_, key_, obs_, claim_, ctx_, fb_.

Three responses sit outside the JSON envelope. GET /v1/export and GET /v1/audit/export stream application/x-ndjson; POST /mcp returns a bare JSON-RPC body, the shape an MCP client expects.

Request limits

REQUEST RULES

MAX_BODY_BYTES 1,048,576. Checked against the declared content-length first, then against the body that actually arrived. Over the limit is 413 PAYLOAD_TOO_LARGE.

content-type JSON requests are parsed as JSON; an empty body or unparseable body is a 400 VALIDATION_ERROR, not a 500.

Idempotency-Key Optional, trimmed to 200 characters. Honoured on the three endpoints below.

Idempotency and meta.replayed

POST /v1/observations, POST /v1/consolidations and POST /v1/context/:id/feedback accept an Idempotency-Key header. The replay record is inserted inside the same atomic batch as the canonical rows, so a duplicate key makes the whole write fail and roll back.

First call: the normal 201, with meta.replayed: false.

Same key, same body: the stored response, with meta.replayed: true and status 200 rather than 201.

Same key, different body: 409 CONFLICT, "Idempotency-Key was reused with a different request body." A key identifies one request, not one endpoint.

POST /v1/context/:id/feedback additionally accepts a client_mutation_id field in the body, deduplicated per organization by a unique index.

MUTATIONS WITHOUT A KEY

Every other mutating route follows its own transition or uniqueness rule. Retrying without an idempotency key can repeat a write, or return the route’s normal conflict.

Health

Method Path Scope What it does
GET /healthz Liveness, plus the runtime label and build revision. No sensitive detail.
GET /readyz Storage check, applied-vs-expected migration counts, capability flags. Returns 503 NOT_READY with the same body when not ready.

The memory loop

Method Path Scope What it does
POST /v1/projects/resolve projects:resolve Normalize a project reference to an opaque project id. create: true additionally requires projects:create.
POST /v1/observations observations:write Append content-hashed evidence. 201. trust may not exceed the credential’s ceiling.
DELETE /v1/observations/:id observations:purge Irreversibly tombstone readable evidence while retaining its id, hash and audit provenance. Active legal holds block it.
POST /v1/consolidations claims:write Derive claims from observations. Deterministic — model_used: false, meta.model: "disabled".
POST /v1/context/compile context:compile Rank authorized claims into a token budget and record the selection as a ctx_ run.
GET /v1/context/:id handoffs:read Read an actor-owned context or one delegated to the intended handoff recipient, with current visibility rechecked.
POST /v1/context/:id/feedback feedback:write Record used, useful, irrelevant, incorrect or harmful against a run or one of its items.
GET /v1/claims/:id/evidence evidence:read Resolve a claim to its supporting, contradicting and qualifying observations, plus hidden_source_count.
GET /v1/memories views:compile List authorized canonical claims with optional lexical search, lifecycle/visibility/kind filters, and a stable keyset cursor; no Atlas compile is required.

Memories list

GET /v1/memories is the dashboard’s canonical inventory. It defaults to current active and disputed claims and accepts q, subject_id, project_id, status, visibility, kind, limit (1–100, default 25), and an opaque after cursor. Authorization, retention, and validity are applied in SQL before lexical search and pagination. The response exposes has_more and next_cursor instead of a global count, so large tenants can use keyset pagination without an expensive count query. A selected memory can be opened in the dashboard’s Atlas graph for relationship inspection.

A walkthrough with real payloads is in the Quickstart; the ranking is explained on Compile context.

Claim lifecycle

Method Path Scope What it does
POST /v1/claims/:id/supersede claims:write Replace a claim with an active one named in superseded_by. Only active or disputed claims can be superseded.
POST /v1/claims/:id/revoke claims:write Withdraw a claim. Idempotent, reports already_revoked. A superseded claim cannot be revoked.
POST /v1/claims/:id/expire claims:write End validity now by setting valid_to. Idempotent, reports already_expired.

All three stop the claim appearing in compiled context and none of them delete an observation. Details on Claim lifecycle.

Checkpoints

Method Path Scope What it does
POST /v1/checkpoints checkpoints:write Upsert task state on organization + subject + agent + kind. A second write returns 200 with updated: true and the same checkpoint_id.
GET /v1/checkpoints checkpoints:read Read the current checkpoint. Requires subject_id and kind query parameters; returns 404 once expired.
GET /v1/checkpoints/:id checkpoints:read Read the exact unexpired checkpoint referenced by an eligible handoff.
DELETE /v1/checkpoints/:id checkpoints:write Delete a checkpoint row outright. Checkpoints are task state, not evidence.

kind is one of task_state, conversation, workflow, cursor. ttl_seconds is between 60 and 2,592,000, and the serialized state is capped at 64,000 characters.

Keys

Method Path Scope What it does
GET /v1/principal Return the caller’s principal, credential scopes, trust ceiling and organization role. Requires a valid API key.
POST /v1/keys keys:manage Mint a key. The raw titen_sk_… value is in the response once and never again; storage holds only its hash.
GET /v1/keys keys:manage List key metadata with status. No hashes, no secrets.
DELETE /v1/keys/:id keys:manage Revoke a key. Effective on the next request.

Directories and scoped access

Method Path Scope What it does
GET /v1/principals principals:read List the bounded non-secret principal directory. Owner/admin role required.
GET /v1/projects projects:read List projects backed by canonical records the caller may read.
GET /v1/projects/:id/references projects:read List bounded canonical references for one readable project; ~ selects explicit unscoped memory.
GET /v1/subjects subjects:read List subjects backed by canonical records the caller may read.
GET /v1/subjects/:id/references subjects:read List bounded canonical and namespaced references for one readable subject.
GET /v1/grants grants:read List active and revoked additive data grants without exposing inaccessible targets.
POST /v1/grants grants:write Delegate bounded read, write, approve, or admin authority within the caller’s current grants.
DELETE /v1/grants/:id grants:write Revoke a grant; derived keys narrow on their next request.
POST /v1/access/simulate grants:read Return visibility, grant, and final authorization gates without impersonation or record disclosure. Owner role required.

Organization authority still comes from authentication. Grants are additive, append-and-revoke records over organization, project (including explicit project:null), or subject targets. A caller must hold every permission it delegates, and a foreign or inaccessible target is indistinguishable from a missing one.

Model diagnostics

Method Path Scope What it does
GET /v1/models/config models:read Return the immutable non-secret extraction and embedding startup snapshot, including masked key state and drift.
POST /v1/models/probe models:probe Run one bounded extraction or embedding provider probe and append a metadata-only audit entry.

Diagnostics never mutate runtime configuration, reveal a credential, prompt, raw provider response, or embedding, or write canonical memory. Disabled and partially configured model tuples are reported explicitly.

Dashboard accounts

Human dashboard accounts are separate from API keys used by agents and services. Bootstrap creates username owner with a random temporary password shown once. Temporary-password login can only replace that password; a fresh login is required before the private dashboard becomes available. Established accounts with a passkey must complete a second factor after password verification.

Method Path Scope What it does
POST /v1/operator-accounts keys:manage Atomically create a human account and organization membership, returning its random temporary password once.
PATCH /v1/operator-accounts/current/password Replace the signed-in account password and revoke its dashboard sessions. Requires a valid credential.
POST /v1/operator-accounts/current/passkeys/options Create bound WebAuthn registration options for the current full dashboard session.
POST /v1/operator-accounts/current/passkeys Verify and add a passkey. The first enrollment returns eight recovery codes once.
GET /v1/operator-accounts/current/passkeys List the current account’s passkeys without credential secrets.
DELETE /v1/operator-accounts/current/passkeys/:id Revoke one passkey. Removing the last passkey requires the current password.
POST /v1/operator-accounts/current/recovery-codes Replace all recovery codes and return the new set once.
POST /v1/dashboard-sessions Exchange username and password for a short-lived dashboard credential. No bearer credential is accepted.
POST /v1/dashboard-sessions/current/passkey-options Create bound WebAuthn assertion options for a staged second-factor session.
POST /v1/dashboard-sessions/current/passkey Verify a passkey assertion and replace the staged session with a full session.
POST /v1/dashboard-sessions/current/recovery-code Consume one recovery code and replace the staged session with a full session.
DELETE /v1/dashboard-sessions/current Revoke the current dashboard credential. Requires a valid credential.

Password login uses persistent progressive delays before password verification. Unknown accounts and wrong passwords return the same response. A passkey challenge binds to the account, session, purpose, and expiry. Staged sessions cannot call product routes. Recovery and passkey revocation remain available if relying-party configuration is later disabled.

Portability

Method Path Scope What it does
GET /v1/export export:read Stream NDJSON: one header line, then one record per line. type is projects, observations or claims; limit 1–2000, default 500.
POST /v1/import import:write Load NDJSON, at most 2000 lines per request, preserving record ids so evidence links survive the move.

Order matters across requests: an observation references a project, and a claim source references an observation. Follow import_order from the export header: projects, then observations, then claims. A reference to a record that does not exist yet is 400 UNRESOLVED_REFERENCE, not a conflict; a conflict means the id already belongs to another organization. See Backup & export.

Collaboration

Method Path Scope What it does
POST /v1/workspaces workspaces:write Create a workspace. A duplicate name in one organization is 409.
GET /v1/workspaces workspaces:read List workspaces.
POST /v1/memberships memberships:write Attach a principal to the organization or a workspace with role owner, admin, member or reader.
GET /v1/memberships memberships:read List current memberships, optionally filtered by workspace_id.
DELETE /v1/memberships/:id memberships:write Soft removal: the member stops being listed, prior provenance and audit entries remain.
POST /v1/leases leases:write Acquire an exclusive lease on resource_type + resource_id. A live lease held by another principal is 409; an expired one or the same holder re-acquires.
GET /v1/leases leases:read Page unreleased leases in the authenticated organization with an opaque cursor.
DELETE /v1/leases/:id leases:write Release a lease before its TTL.
POST /v1/leases/:id/force-release leases:write Owner/admin recovery path for an organization-scoped lease.
POST /v1/handoffs handoffs:write Offer work to another principal, optionally naming a context_id and checkpoint_id.
POST /v1/handoffs/:id/resolve handoffs:write The recipient sets status to accepted or rejected. Nobody else can resolve it.
GET /v1/handoffs handoffs:read Pending handoffs addressed to the caller — the pull path for agents that cannot receive webhooks.

Lease ttl_seconds is between 10 and 86,400. More on Leases & handoffs.

MCP

Method Path Scope What it does
POST /mcp mcp:call JSON-RPC 2.0 over Streamable HTTP: initialize, notifications, ping, tools/list, tools/call. Nine tools.

Protocol versions 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05 are supported. A notification returns 202 with an empty body; a batch returns an array. Key management, membership, retention, webhook, federation, release and Atlas operations are deliberately absent from the agent tool profile; publisher, approver, operator and gateway principals use REST for those. See MCP server.

Events

Method Path Scope What it does
GET /v1/events events:read Authorized, metadata-only domain events after an opaque cursor. after, limit 1–200 default 50, kind.
GET /v1/events/:id events:read One event by id.

Event rows are written in the same atomic batch as the canonical change they describe, so an event can never exist for a write that rolled back or be missing for one that committed.

Memory Atlas

Method Path Scope What it does
POST /v1/memory-views/compile views:compile Compile a bounded authorized graph view using one of seven lenses: evidence_trace, neighborhood, conflict_freshness, workspace_graph, review_queue, scope_preview, or knowledge_release.

Read-only despite the POST; the request needs a bounded body. evidence_trace requires focus_id, neighborhood requires subject_id. Policy runs before traversal: both endpoints of every edge must be authorized, and hidden candidates contribute no edges, labels or counts. An unauthorized focus is a non-disclosing 404. The evidence trace centers that claim and can include readable source observations, complete readable context packs, and current active releases. It labels supports, contradicts, qualifies, selected-in, and released-as relationships; partial contexts and stale releases are omitted rather than partially disclosed.

workspace_graph accepts an optional workspace_id and max_nodes from 25 to 300. It returns only authorized claim and subject nodes with canonical about, related, contradicts, and supersedes edges. Truncation and withheld-edge counts are bounded aggregate state; they never disclose hidden identifiers.

Governance, retention and released knowledge

Method Path Scope What it does
POST /v1/policies governance:write Create a typed, versioned governance or retention policy. Owner/admin role required.
GET /v1/policies governance:read List authorized policies.
PATCH /v1/policies/:id governance:write Change policy status with expected-version semantics.
POST /v1/claim-approvals approvals:write Submit one current claim version and its visible supporting evidence for approval.
GET /v1/claim-approvals approvals:read List approval records visible to the operator.
POST /v1/claim-approvals/:id/decide approvals:approve Approve or reject without allowing self-approval where policy requires separation of duty.
POST /v1/channels releases:write Create a bounded channel with audiences, minimum trust and one gateway service principal.
GET /v1/channels releases:read List channel metadata without secrets or released content.
PATCH /v1/channels/:id releases:write Set a channel to active, paused or disabled with expected-version semantics.
POST /v1/knowledge-releases releases:write Draft an immutable released-content snapshot for one exact claim version.
GET /v1/knowledge-releases releases:read List authorized release rows; source evidence remains separately authorized.
POST /v1/knowledge-releases/:id/approve releases:approve Approve an eligible draft snapshot under separation-of-duty policy.
POST /v1/knowledge-releases/:id/activate releases:approve Activate an approved, still-eligible release.
POST /v1/knowledge-releases/:id/revoke releases:approve Revoke a release while retaining its history and audit trail.
POST /v1/channels/:id/context/compile channel:compile Serve only eligible released snapshots to the channel’s bound gateway principal.
POST /v1/legal-holds retention:write Place an exact claim or observation on legal hold and restore retention eligibility atomically.
POST /v1/legal-holds/:id/release retention:write Release an active legal hold.
POST /v1/retention/apply retention:write Apply bounded retrieval exclusions; active direct or dependent holds win.
POST /v1/identity-mappings identity:write Create a versioned external-to-internal identity mapping.
GET /v1/identity-mappings identity:read List authorized identity mappings.
DELETE /v1/identity-mappings/:id identity:write Retire a mapping without erasing its audit history.

verified trust describes evidence authority; it does not by itself permit disclosure. A public-facing service uses its bound channel gateway and reads released snapshots, never canonical memory. Reasoning in ADR-0002 and on Channel releases.

Audit

Method Path Scope What it does
GET /v1/audit audit:read Metadata-only audit entries with cursor pagination. after, action, limit 1–500 default 50.
GET /v1/audit/export audit:export Stream the log as NDJSON behind a titen.audit.header line. after, limit 1–5000 default 1000.

Federation

Method Path Scope What it does
POST /v1/federation/peers federation:write Register a peer with endpoint, shared_secret (16+ characters) and direction push, pull or bidirectional. One endpoint per organization.
GET /v1/federation/peers federation:read List peers with status and sync cursor.
POST /v1/federation/peers/:id/suspend federation:write Suspend a peer. Its pull and push then return 403.
POST /v1/federation/peers/:id/filters federation:write Add a filter on resource_type with optional include_kinds, exclude_subjects, min_trust. Filters bound both directions.
GET /v1/federation/peers/:id/filters federation:read List a peer’s filters.
POST /v1/federation/pull federation:write Pull up to 200 filtered events after the peer’s cursor and advance it.
POST /v1/federation/push federation:write Accept events from a peer. Requires x-titen-peer-signature: sha256=HEX over the raw body; each event reports success, conflict or rejected.
GET /v1/federation/log federation:read The exchange log for one peer_id.

Passing include_memory: true on a pull opts into signed canonical claim and evidence bundles. The destination enforces explicit claim filters, source-organization binding, trust ceilings, replay identity and complete-graph validation before an atomic import.

Webhooks and delivery

Method Path Scope What it does
POST /v1/webhooks webhooks:write Register a destination with secret (16+ characters) and a non-empty events array (up to 50; * matches every kind). The response states the signing contract and never echoes the secret.
GET /v1/webhooks webhooks:read List webhooks with status, event filter and failure count.
DELETE /v1/webhooks/:id webhooks:write Stop future delivery.
POST /v1/webhooks/:id/pause webhooks:write Suspend delivery without dropping events.
POST /v1/webhooks/:id/resume webhooks:write Resume delivery and clear the failure counter.
GET /v1/webhooks/:id/deliveries webhooks:read Metadata-only delivery state: status, attempts, last_attempt_at, next_retry_at, response_status.
POST /v1/webhooks/deliver webhooks:write Drain the delivery outbox. after, limit 1–200 default 50.

The destination must be https://, or http://localhost / http://127.0.0.1 for development; private and link-local ranges are rejected. Deliveries are signed X-Titen-Signature: sha256=HEX, an HMAC-SHA256 of the raw body, alongside X-Titen-Event and X-Titen-Delivery. Backoff is 1 minute, 5 minutes, 30 minutes, 2 hours, 12 hours, capped at 5 attempts, and a webhook is disabled after 10 consecutive failures. A unique index on (webhook, event) makes delivery exactly once per event per webhook even if a drain is replayed. More on Webhooks & events.

Indexing

Method Path Scope What it does
POST /v1/index/drain index:write Embed queued claims into the vector store. limit 1–100, default 50. Returns drained, indexed, skipped, remaining, model, dimensions, at.
POST /v1/index/verify index:write Check up to 100 active or disputed claims against the vector store and queue one durable repair for each missing id. Accepts limit and the previous next_after cursor.
POST /v1/enrichment/drain enrichment:write Drain a bounded optional extraction/reflection ledger; disabled configuration leaves canonical writes available.

Observation and claim writes queue an outbox row, and until something consumes them the vector index stays empty. This route is that consumer, pull-driven because an embedding call crosses the network and a canonical write must not wait on one. With no vector capability configured it returns 400; if the model or index is unreachable it returns 503 UNAVAILABLE with meta.pending and meta.retryable and leaves the queue untouched. GET /readyz reports background_repair so an operator can tell whether anything is calling it. See Semantic vectors.

/v1/index/verify never reads embedding values. It compares organization-scoped canonical ids with provider membership and returns checked, present, missing, repairs_queued, and next_after; the normal drain performs any queued rebuild.

Not implemented

Named here because earlier drafts documented them as if they existed: batch observation append, the old /v1/channel-releases and /v1/channel-context paths, /v1/webhook-subscriptions, /v1/webhook-deliveries as a top-level collection, and /v1/audit/events.

Compatibility

The v1 export format is versioned independently from the HTTP API.

Breaking request or response changes require a new API version or a migration path.

External field names are snake_case. Internal names are not part of the contract.

Next

↑↓ navigate↵ open