Skill pack (template.md -> AGENTS.md, .claude/skills/smriti/SKILL.md):
- §3.2 Repo reconciliation: lead with `## Repo state`'s automated drift
signals; keep the manual git log checks as the documented fallback
for checkpoints without recorded git state.
- §3.5 Backend reachability: lead with `smriti doctor` as the
structured health check; keep `curl /health` as the raw fallback.
- Bump skill pack version 2.4 -> 2.5; re-render AGENTS.md (the
.claude/skills/smriti/SKILL.md install is local-only and untracked).
- test_skill_pack: bump the expected version; add four single-line
required phrases proving the new content shipped (`Repo state`,
`smriti doctor`, "ahead of the last checkpoint", "checkpoint taken
on a different branch").
docs/API.md:
- POST /api/v4/chat/commit: add `repo_state` to the request body
example and a paragraph explaining its purpose (drift detection),
persistence (`context_blob`), and read-back path (V4 state endpoint).
- DELETE /api/v2/repos/{repo_id}: add the missing section with the
`force` query param, 204/404/409 responses, and the structured
detail shape for the 409 refusal.
CLI suite: 253 passed. AGENTS.md and SKILL.md verified byte-identical
to fresh render(target) output.
23 KiB
Smriti API Reference
This document covers the V4 (chat) and V5 (checkpoint) APIs — the current primary interfaces. V1 and V2 endpoints are legacy and not documented here; see docs/legacy/ for historical context.
Base URL: http://localhost:8000
All request and response bodies are JSON. All IDs are UUIDs.
V4 — Chat API
Prefix: /api/v4/chat
The V4 API manages Sessions, Turns, and message sending. It is the primary runtime API for the Smriti workspace.
Sessions
Create a session
POST /api/v4/chat/sessions
Creates a new Session, optionally attached to a Space and seeded from a Checkpoint.
Request body:
{
"repo_id": "<space-uuid>",
"title": "Optional title",
"provider": "openrouter",
"model": "anthropic/claude-3.5-sonnet",
"seed_from": "head"
}
| Field | Type | Required | Description |
|---|---|---|---|
repo_id |
UUID string | No | Attach to a Space. Omit for a FRESH session. |
title |
string | No | Defaults to "Session MMM DD HH:MM" |
provider |
string | No | Defaults to chat.default_provider from config |
model |
string | No | Model identifier for this provider |
seed_from |
string | No | "head" (default) seeds from latest Checkpoint; "none" starts fresh; a Checkpoint UUID seeds from that specific Checkpoint |
Response: Session object
{
"id": "uuid",
"repo_id": "uuid or null",
"title": "Session Mar 21 14:30",
"active_provider": "openrouter",
"active_model": "anthropic/claude-3.5-sonnet",
"seeded_commit_id": "uuid or null",
"created_at": "2026-03-21T14:30:00Z",
"updated_at": "2026-03-21T14:30:00Z"
}
List recent sessions
GET /api/v4/chat/sessions
Returns the 50 most recently updated Sessions, ordered by updated_at descending.
Response: Array of Session objects
Get a session
GET /api/v4/chat/sessions/{session_id}
Response: Session object
Generate a session title
POST /api/v4/chat/sessions/{session_id}/title
Uses the background intelligence model to generate a concise 3–5 word title from
the first four Turns of the Session. Updates session.title in place.
Called automatically by the frontend after the first assistant reply (if a background provider is configured).
Response: Updated Session object
List session turns
GET /api/v4/chat/sessions/{session_id}/turns
Returns all Turns for a Session, ordered by sequence_number ascending.
Response: Array of Turn objects
[
{
"id": "uuid",
"session_id": "uuid",
"role": "user",
"content": "What countries should I visit?",
"provider": "openrouter",
"model": "anthropic/claude-3.5-sonnet",
"sequence_number": 0,
"created_at": "2026-03-21T14:31:00Z"
},
{
"id": "uuid",
"session_id": "uuid",
"role": "assistant",
"content": "...",
"provider": "openrouter",
"model": "anthropic/claude-3.5-sonnet",
"sequence_number": 1,
"created_at": "2026-03-21T14:31:05Z"
}
]
Attach a session to a space
PUT /api/v4/chat/sessions/{session_id}/attach
Attaches an existing Session to a Space. Updates session.repo_id and sets
repo_id on all existing Turns in the Session.
Request body:
{
"repo_id": "<space-uuid>"
}
Response: Updated Session object
Sending messages
Send a message
POST /api/v4/chat/send
The core endpoint. Sends a user message, resolves the context based on the active mode, calls the provider, and stores both the user Turn and assistant Turn.
Request body:
{
"session_id": "<session-uuid>",
"repo_id": "<space-uuid>",
"provider": "openai",
"model": "gpt-4o",
"message": "What should I do first?",
"use_mock": false,
"memory_scope": "latest_1",
"mounted_checkpoint_id": null,
"history_base_seq": null
}
| Field | Type | Required | Description |
|---|---|---|---|
session_id |
UUID string | Yes | The active Session |
repo_id |
UUID string | No | The attached Space. Must match session.repo_id. |
provider |
string | Yes | Provider to use for this Turn |
model |
string | Yes | Model identifier |
message |
string | Yes | The user's message |
use_mock |
boolean | No | Use the deterministic mock adapter (no API key required) |
memory_scope |
string | No | "latest_1" (default) or "latest_3" |
mounted_checkpoint_id |
UUID string | No | If set, anchors context to this specific Checkpoint |
history_base_seq |
integer | No | Required when mounted_checkpoint_id is set. The sequence number of the last Turn before mounting. Only Turns with sequence_number > history_base_seq are included in context. |
Context resolution logic:
- If
mounted_checkpoint_idis set: use that Checkpoint (plus ancestors ifmemory_scopeis"latest_3"). Turn history:sequence_number > history_base_seq. - If
repo_idis set (HEAD mode): use the N most recent Checkpoints from the Space. Turn history:created_at >= latest_checkpoint.created_at. - Otherwise (FRESH): no Checkpoint context. No Turn history filter.
Response:
{
"reply": "Here is what I recommend...",
"session_id": "uuid",
"turn_count": 4,
"provider": "openai",
"model": "gpt-4o"
}
Spaces
List spaces
GET /api/v2/repos
Note: Spaces use the V2 prefix. The user-facing name is "Space"; the internal
model is RepoModel.
Response: Array of Space objects
[
{
"id": "uuid",
"name": "Australia Trip",
"description": "Planning for the 2026 trip",
"created_at": "2026-03-01T10:00:00Z",
"updated_at": "2026-03-21T14:30:00Z"
}
]
Create a space
POST /api/v2/repos
Request body:
{
"name": "Australia Trip",
"description": "Planning for the 2026 trip"
}
Response: Space object
Get a space
GET /api/v2/repos/{repo_id}
Response: Space object
Delete a space
DELETE /api/v2/repos/{repo_id}
Deletes a Space and cascades to every Checkpoint, Session, and Turn under it. Irreversible.
Query parameters:
force(boolean, defaultfalse) — required to delete a Space that still holds checkpoints.
Responses:
-
204 No Content— Space deleted (empty Space, orforce=trueon a populated Space). -
404 Not Found— Space does not exist (or belongs to a different user). -
409 Conflict— Space holds checkpoints andforce=truewas not passed. The response body carries a structured detail explaining the refusal:{ "detail": { "message": "Cannot delete space 'Australia Trip': it still holds 12 checkpoint(s). Deletion cascades to every checkpoint, session, and turn under it and cannot be undone. Re-send with ?force=true to delete the space and all its contents.", "checkpoint_count": 12, "requires_force": true } }
The 409 guard is one of three deletion-safety layers — the CLI's --force flag and the MCP smriti_delete_space tool's confirm_space argument are the other two. Every client (direct curl, the web UI, a future client) goes through this server-side rule.
Get space head state (main-only)
GET /api/v4/chat/spaces/{repo_id}/head
Returns the latest main-branch Checkpoint and latest Session for a Space. This is the legacy single-HEAD endpoint — for multi-agent workflows, prefer /state below.
Response:
{
"repo_id": "uuid",
"commit_hash": "abc1234...",
"commit_id": "uuid",
"summary": "Decided to focus on east coast cities",
"objective": "Plan a 3-week Australia itinerary",
"latest_session_id": "uuid",
"latest_session_title": "Australia Trip Planning"
}
Get space state (multi-branch, default)
GET /api/v4/chat/spaces/{repo_id}/state
The agent-facing default. Returns the main-branch continuation brief plus active non-main branches, active work claims, and a divergence signal when any branch disagrees with main on decisions. One round trip, atomic snapshot.
Response:
{
"space": {
"id": "uuid",
"name": "my-project",
"description": "..."
},
"head": { "...same shape as /head..." },
"commit": { "...full main-branch HEAD checkpoint..." },
"active_branches": [
{
"branch_name": "experiment-a",
"commit_id": "uuid",
"commit_hash": "abc1234...",
"message": "Trying alternative approach",
"author_agent": "codex-local",
"created_at": "...",
"summary": "..."
}
],
"active_claims": [
{
"id": "uuid",
"agent": "claude-code",
"branch_name": "main",
"scope": "Adding lineage test",
"intent_type": "implement",
"claimed_at": "...",
"expires_at": "...",
"base_commit_hash": "abc1234"
}
],
"divergence": {
"pairs": [
{
"branch_name": "experiment-a",
"branch_commit_hash": "def5678",
"main_only_decisions": ["Use Pydantic"],
"branch_only_decisions": ["Use dataclasses"]
}
]
}
}
active_branches is capped at 5 (most recent by created_at). divergence.pairs is capped at 2. Per-branch divergence is capped at 3 decisions per side. active_claims shows only active, non-expired claims. All three sections are omitted from the response when empty.
Provider status
List provider status
GET /api/v4/chat/providers
Returns the configuration status of all providers. Never returns API keys.
Response:
{
"openai": {
"enabled": true,
"has_key": true,
"missing_package": false,
"configured": true,
"status_label": "Ready",
"default_model": "gpt-4o"
},
"anthropic": {
"enabled": false,
"has_key": false,
"missing_package": false,
"configured": false,
"status_label": "Disabled",
"default_model": ""
},
"openrouter": {
"enabled": true,
"has_key": true,
"missing_package": false,
"configured": true,
"status_label": "Ready",
"default_model": ""
},
"background_intelligence": {
"provider": "openai",
"model": "gpt-4o-mini",
"enabled": true,
"has_key": true,
"configured": true,
"status_label": "Ready"
}
}
V5 — Checkpoint API
Prefix: /api/v5/checkpoint
The V5 API handles checkpoint drafting — the AI-assisted extraction of structured state from a conversation.
Draft a checkpoint
POST /api/v5/checkpoint/draft
Uses the background intelligence model to extract structured metadata from the active conversation. Returns a draft that the user can review, edit, and save.
Important: This endpoint respects the same context isolation as send_message.
If mounted_checkpoint_id and history_base_seq are provided, the draft is
extracted only from Turns after the isolation boundary — the same Turns the model
saw during the session.
Request body:
{
"session_id": "<session-uuid>",
"num_turns": 15,
"mounted_checkpoint_id": null,
"history_base_seq": null
}
| Field | Type | Required | Description |
|---|---|---|---|
session_id |
UUID string | Yes | The Session to draft from |
num_turns |
integer | No | Maximum number of recent Turns to include (default 15, max 100) |
mounted_checkpoint_id |
UUID string | No | If set, applies isolation boundary |
history_base_seq |
integer | No | Required when mounted_checkpoint_id is set |
Turn selection:
- If
mounted_checkpoint_idandhistory_base_seqare both provided:sequence_number > history_base_seq, limited tonum_turnsmost recent - Otherwise: all Turns in the Session, limited to
num_turnsmost recent
No previous Checkpoint context is injected into the extraction prompt. The draft reflects only what is present in the selected Turns.
Response:
{
"title": "Australia East Coast Plan",
"objective": "Decide on a 3-week itinerary for Australia focusing on the east coast",
"summary": "The user is planning a trip to Australia and has narrowed focus to Sydney, Melbourne, and the Great Barrier Reef. Budget and timing constraints have been discussed.",
"decisions": [
"Focus on east coast cities only",
"Avoid peak season (December–January)"
],
"tasks": [
"Research visa requirements",
"Compare flight options from London"
],
"open_questions": [
"Whether to include Tasmania",
"How many nights to allocate to each city"
],
"entities": [
"Sydney",
"Melbourne",
"Great Barrier Reef",
"Qantas"
],
"assumptions": [
"Budget is flexible",
"Traveling from London"
]
}
All array fields may be empty if nothing relevant was found in the conversation.
objective may be an empty string if the goal is not stated clearly enough to extract.
assumptions captures things the conversation takes for granted that were not explicitly
debated or decided.
Review a checkpoint
POST /api/v5/checkpoint/{checkpoint_id}/review
Uses the background intelligence model to review a checkpoint for reasoning consistency. Returns a list of issues and suggestions.
Issue types (V1):
| Type | Description |
|---|---|
contradiction |
Two decisions or an assumption and a decision that appear to conflict |
hidden_assumption |
Something the reasoning relies on that is not listed as an assumption or decision |
resolved_question |
An open question that appears already answered by a decision or the summary |
unused_entity |
An entity not referenced in the summary, decisions, tasks, or objective |
Response:
{
"checkpoint_id": "uuid",
"issues": [
{
"type": "hidden_assumption",
"description": "The itinerary assumes travel logistics between cities can be easily managed within the 10-day timeframe"
}
],
"suggestions": [
"Consider adding travel logistics as an explicit assumption"
]
}
Issues are capped at 5 per review. The review is conservative and prefers precision over recall.
Saving a checkpoint
Checkpoints are saved via the V4 commit endpoint, not V5.
POST /api/v4/chat/commit
Request body:
{
"repo_id": "<space-uuid>",
"session_id": "<session-uuid>",
"message": "Australia East Coast Plan",
"summary": "Decided to focus on east coast...",
"objective": "Plan a 3-week itinerary...",
"decisions": ["Focus on east coast cities only"],
"assumptions": ["Budget is flexible"],
"tasks": ["Research visa requirements"],
"open_questions": ["Whether to include Tasmania"],
"entities": ["Sydney", "Melbourne"],
"artifacts": [
{
"id": "a1b2c3d4",
"type": "text",
"label": "Draft itinerary",
"content": "Day 1: Arrive in Sydney..."
}
],
"repo_state": {
"head": "abc123def456...",
"head_short": "abc123d",
"branch": "main"
}
}
Response:
{
"id": "uuid",
"commit_hash": "abc1234def5678...",
"message": "Australia East Coast Plan",
"created_at": "2026-03-21T15:00:00Z"
}
Repo-state drift detection (repo_state). The optional repo_state field
records the git HEAD and branch of the repo the checkpoint was created from.
It is persisted under the commit's context_blob and returned by GET /api/v4/chat/spaces/{repo_id}/state as commit.context_blob.repo_state. The
CLI uses it on subsequent smriti state calls to detect drift — N commits
ahead of the recorded checkpoint, a different branch, or diverged history —
and surfaces those signals in the rendered state brief's ## Repo state
section. The field is optional: clients that cannot inspect a repo (e.g. the
MCP server, which runs in the host's arbitrary working directory) may omit
it, and the backend stores an empty context_blob.
Checkpoint history
List checkpoints for a space
GET /api/v2/repos/{repo_id}/commits
Returns all Checkpoints for a Space, ordered by creation time.
Response: Array of Checkpoint objects
[
{
"id": "uuid",
"commit_hash": "abc1234...",
"parent_commit_id": "uuid or null",
"message": "Australia East Coast Plan",
"objective": "Plan a 3-week itinerary...",
"summary": "Decided to focus on east coast...",
"decisions": ["Focus on east coast cities only"],
"assumptions": ["Budget is flexible"],
"tasks": ["Research visa requirements"],
"open_questions": ["Whether to include Tasmania"],
"entities": ["Sydney", "Melbourne"],
"artifacts": [],
"created_at": "2026-03-21T15:00:00Z"
}
]
Get a specific checkpoint
GET /api/v2/commits/{commit_id}
Response: Checkpoint object (full fields as above)
V5 — Lineage API
Prefix: /api/v5/lineage
The Lineage API handles session forking, branch tree visualization, and checkpoint comparison across branches.
Fork a session
POST /api/v5/lineage/sessions/fork
Creates a new Session branching from a specific Checkpoint. The forked session starts with a clean Turn history; its context comes from the checkpoint state snapshot.
Request body:
{
"space_id": "<space-uuid>",
"checkpoint_id": "<checkpoint-uuid>",
"branch_name": "my-branch",
"provider": "openai",
"model": "gpt-4o"
}
| Field | Type | Required | Description |
|---|---|---|---|
space_id |
UUID string | Yes | The Space to fork within |
checkpoint_id |
UUID string | Yes | The Checkpoint to fork from |
branch_name |
string | No | Branch name. Defaults to "branch-YYYY-MM-DD" |
provider |
string | No | Defaults to config default |
model |
string | No | Model for the new session |
Response:
{
"session_id": "uuid",
"branch_name": "my-branch",
"forked_from_checkpoint_id": "uuid",
"history_base_seq": 0
}
Get branch tree
GET /api/v5/lineage/spaces/{space_id}
Returns all Checkpoints and Sessions for a Space, structured for branch tree
rendering. Checkpoints carry parent_checkpoint_id for the commit ancestry chain;
Sessions carry forked_from_checkpoint_id to locate where each branch began.
Response:
{
"space_id": "uuid",
"checkpoints": [
{
"id": "uuid",
"commit_hash": "abc1234...",
"message": "Australia East Coast Plan",
"branch_name": "main",
"parent_checkpoint_id": "uuid or null",
"created_at": "...",
"summary": "...",
"objective": "..."
}
],
"sessions": [
{
"id": "uuid",
"title": "Session Mar 21",
"branch_name": "main",
"forked_from_checkpoint_id": "uuid or null",
"seeded_commit_id": "uuid or null",
"created_at": "..."
}
]
}
Compare two checkpoints
GET /api/v5/lineage/checkpoints/{a_id}/compare/{b_id}
Returns a structured diff of two Checkpoint state snapshots. Works across any two Checkpoints regardless of branch origin.
Response:
{
"checkpoint_a": {
"id": "uuid",
"commit_hash": "abc1234...",
"message": "...",
"branch_name": "main",
"summary": "...",
"objective": "...",
"decisions": ["..."],
"tasks": ["..."],
"open_questions": ["..."]
},
"checkpoint_b": { ... },
"diff": {
"summary_a": "...",
"summary_b": "...",
"objective_a": "...",
"objective_b": "...",
"decisions_only_a": ["..."],
"decisions_only_b": ["..."],
"decisions_shared": ["..."],
"tasks_only_a": ["..."],
"tasks_only_b": ["..."],
"tasks_shared": ["..."]
}
}
Get reachable checkpoints for a session
GET /api/v5/lineage/sessions/{session_id}/checkpoints
Returns the Checkpoint set reachable from a given Session. This is the authoritative query for populating the checkpoint history panel and mount-candidate list.
Reachability rules:
- Main-branch session — all Checkpoints where
branch_name == "main", newest first. - Forked session — fork-local Checkpoints (same branch) plus the fork-source Checkpoint and all its ancestors. Downstream main Checkpoints created after the fork point are explicitly excluded.
Response: Array of full Checkpoint objects (same schema as GET /api/v2/commits/{id})
V5 — Work Claims API
Prefix: /api/v5/claims
Work claims are lightweight, time-bounded declarations that an agent is actively working on something in a space. They make pre-work intent visible to other agents before work produces a checkpoint. Claims are advisory — not locks.
Create a work claim
POST /api/v5/claims
Request body:
{
"space_id": "<space-uuid>",
"agent": "claude-code",
"scope": "Adding lineage test for author_agent",
"branch_name": "main",
"base_commit_id": "<checkpoint-uuid>",
"intent_type": "implement",
"ttl_hours": 4.0
}
| Field | Type | Required | Description |
|---|---|---|---|
space_id |
UUID string | Yes | The Space to claim work in |
agent |
string | Yes | Agent identifier (e.g. claude-code, codex-local) |
scope |
string | Yes | One sentence describing the work |
branch_name |
string | No | Branch the work targets. Default "main" |
base_commit_id |
UUID string | No | The Checkpoint the agent read as HEAD |
intent_type |
string | No | One of: implement, review, investigate, docs, test. Default "implement" |
ttl_hours |
float | No | Hours until the claim expires. Default 4.0 |
Response: Claim object (status "active", claimed_at and expires_at set)
Update a claim
PATCH /api/v5/claims/{claim_id}
Marks a claim as done or abandoned. Only active claims can be updated.
Request body:
{
"status": "done"
}
| Value | Meaning |
|---|---|
done |
Work completed successfully |
abandoned |
Work intentionally stopped |
Response: Updated Claim object
Returns 409 if the claim is already done or abandoned.
List claims
GET /api/v5/claims?space_id=<uuid>
Returns active, non-expired claims for a space. Pass include_expired=true to
include done, abandoned, and expired claims.
Response: Array of Claim objects
Error responses
All endpoints return errors in this format:
{
"detail": "Session not found"
}
| Status | Meaning |
|---|---|
| 400 | Bad request — missing required field or invalid input |
| 404 | Resource not found |
| 422 | Validation error — request body did not match schema |
| 500 | Server error — typically a misconfigured or missing background provider |
| 502 | Provider error — the upstream LLM returned an error or invalid response |
Notes on V1, V2, V4, V5
/api/v1 — Transcript paste ingestion. Accepts raw text, extracts memories,
generates context packs. Not used by the current UI or CLI. Retained for
compatibility but no new development happens here. Do not build new integrations
against V1.
/api/v2 — Space CRUD and Checkpoint read endpoints. The CLI reads full
checkpoints via GET /api/v2/commits/{commit_id} and lists per-space checkpoints
via GET /api/v2/repos/{repo_id}/commits. The CommitResponse schema returns
the complete checkpoint shape including assumptions and artifacts. New
programmatic clients are welcome to use V2 read endpoints; for writes, use V4
(POST /api/v4/chat/commit) which accepts the full schema.
/api/v4 — Chat sessions, message sending, and the canonical checkpoint write
path. The chat UI's primary interaction surface and the CLI's write surface.
/api/v5 — Checkpoint drafting, review, fork, compare, lineage, and work claims.
Used by both the chat UI and the CLI/MCP server.