| title | HTTP API |
|---|---|
| description | Signet daemon HTTP API reference. |
| order | 10 |
| section | Reference |
The Signet [[daemon]] exposes a REST API on http://localhost:3850 by default.
All requests and responses use JSON unless otherwise noted. The base URL and
port are configurable via environment variables (see [[configuration]]).
Path note:
$SIGNET_WORKSPACEmeans your active Signet workspace path. Default is~/.agents, configurable viasignet workspace set <path>.
Base URL: http://localhost:3850
SIGNET_PORT — override port (default: 3850)
SIGNET_HOST — daemon host for local calls (default: 127.0.0.1)
SIGNET_BIND — bind host override (default: SIGNET_HOST)
The daemon supports three [[auth]] modes, set in agent.yaml:
local— no authentication required. All requests are trusted. This is the default for single-user local installs.team— all requests require aBearertoken in theAuthorizationheader.hybrid— requests fromlocalhostare trusted without a token; requests from any other origin require aBearertoken.
Tokens are signed JWTs with a role and optional scope. Roles and their permissions:
| Role | Permissions |
|---|---|
admin |
all permissions |
operator |
remember, recall, modify, forget, recover, documents, connectors, diagnostics, analytics |
agent |
remember, recall, modify, forget, recover, documents |
readonly |
recall only |
Token scopes (project, agent, user) restrict mutations to records
matching the scope. Admin role bypasses scope checks. Unscoped tokens have
full access within their role.
Rate limits apply in team and hybrid modes:
| Operation | Limit |
|---|---|
| forget | 30 / min |
| modify | 60 / min |
| batchForget | 5 / min |
| admin actions | 10 / min |
Errors follow a consistent shape:
{ "error": "human-readable message" }Rate-limit rejections return 429. Auth failures return 401. Permission
violations return 403. Version conflicts and state violations return 409.
Mutations blocked by the kill switch return 503.
No authentication required. Lightweight liveness check.
Response
{
"status": "healthy",
"uptime": 3600.5,
"pid": 12345,
"version": "0.1.69",
"port": 3850,
"agentsDir": "/home/user/.agents"
}Full daemon status including pipeline config, embedding provider, and a composite health score derived from diagnostics. Extraction provider runtime resolution persists startup degradation so operators can detect silent fallback or hard-blocked extraction after boot.
Response
{
"status": "running",
"version": "0.1.69",
"pid": 12345,
"uptime": 3600.5,
"startedAt": "2026-02-21T10:00:00.000Z",
"port": 3850,
"host": "localhost",
"agentsDir": "/home/user/.agents",
"memoryDb": true,
"pipelineV2": {
"enabled": true,
"paused": false,
"shadowMode": false,
"mutationsFrozen": false,
"graphEnabled": false,
"autonomousEnabled": false,
"extractionModel": "qwen3:4b"
},
"pipeline": {
"extraction": {
"running": true,
"overloaded": false,
"loadPerCpu": 0.42,
"maxLoadPerCpu": 0.8,
"overloadBackoffMs": 30000,
"overloadSince": null,
"nextTickInMs": 1200
}
},
"providerResolution": {
"extraction": {
"configured": "claude-code",
"resolved": "claude-code",
"effective": "ollama",
"fallbackProvider": "ollama",
"status": "degraded",
"degraded": true,
"fallbackApplied": true,
"reason": "Claude Code CLI not found during extraction startup preflight",
"since": "2026-03-26T06:00:00.000Z"
}
},
"health": { "score": 0.97, "status": "healthy" },
"embedding": {
"provider": "ollama",
"model": "nomic-embed-text",
"available": true
},
"bypassedSessions": 1
}The bypassedSessions field reports how many active sessions currently have
bypass enabled (see [[#Sessions]]).
Monitor providerResolution.extraction.status for degraded or blocked
states when the configured extraction provider is unavailable at startup.
When pipeline.extraction.overloaded is true, the extraction worker is
intentionally backing off for overloadBackoffMs between polls.
Returns all runtime feature flags.
Response
{
"featureName": true,
"anotherFeature": false
}Returns the identity and claims of the current request's token. In local
mode, authenticated is always false and claims is null.
Response
{
"authenticated": true,
"claims": {
"sub": "token:operator",
"role": "operator",
"scope": { "project": "my-project" },
"iat": 1740000000,
"exp": 1740086400
},
"mode": "team"
}Create a signed JWT. Requires admin permission. Rate-limited to 10
requests/min.
Request body
{
"role": "agent",
"scope": { "project": "my-project", "agent": "claude", "user": "nicholai" },
"ttlSeconds": 86400
}role is required and must be one of admin, operator, agent,
readonly. scope is optional — an empty object creates an unscoped token.
ttlSeconds defaults to the value in authConfig.defaultTokenTtlSeconds.
Response
{
"token": "<jwt>",
"expiresAt": "2026-02-22T10:00:00.000Z"
}Returns 400 if role is invalid or auth secret is unavailable (local
mode). Returns 400 if the request body is missing or malformed.
Returns all .md and .yaml files from the agents directory ($SIGNET_WORKSPACE/),
sorted by priority: agent.yaml, AGENTS.md, SOUL.md, IDENTITY.md,
USER.md, then alphabetically.
Response
{
"files": [
{ "name": "agent.yaml", "content": "...", "size": 1024 },
{ "name": "AGENTS.md", "content": "...", "size": 4096 }
]
}Write a config file. File name must end in .md or .yaml and must not
contain path separators.
Request body
{
"file": "SOUL.md",
"content": "# Soul\n..."
}Response
{ "success": true }Returns 400 for invalid file names, path traversal attempts, or wrong file
types.
Parses IDENTITY.md and returns the structured fields.
Response
{
"name": "Aria",
"creature": "fox",
"vibe": "calm and curious"
}Returns defaults ({ "name": "Unknown", "creature": "", "vibe": "" }) if the
file is missing or unreadable.
The [[memory]] API is the primary interface for reading and writing agent
memory. All write operations respect the mutationsFrozen kill switch — if
enabled, writes return 503. For a typed client wrapper, see the [[sdk]].
List memories with basic stats. Simple pagination only; for filtered search
use POST /api/memory/recall or GET /memory/search.
Requires recall permission.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 100 | Max records to return |
offset |
integer | 0 | Pagination offset |
Response
{
"memories": [
{
"id": "uuid",
"content": "User prefers dark mode",
"created_at": "2026-02-21T10:00:00.000Z",
"who": "claude-code",
"importance": 0.8,
"tags": "preference,ui",
"source_type": "manual",
"pinned": 0,
"type": "preference"
}
],
"stats": {
"total": 1247,
"withEmbeddings": 1200,
"critical": 12
}
}Create a new memory. Requires remember permission.
Content prefixes are parsed automatically:
critical: <content>— setspinned=true,importance=1.0[tag1,tag2]: <content>— sets tags
Body-level fields override prefix-parsed values.
Request body
{
"content": "User prefers vim keybindings",
"who": "claude-code",
"project": "my-project",
"importance": 0.9,
"tags": "preference,editor",
"pinned": false,
"sourceType": "manual",
"sourceId": "optional-external-id",
"agentId": "alice",
"visibility": "global"
}Only content is required. Multi-agent fields:
| Field | Description |
|---|---|
agentId |
Agent that owns this memory. Defaults to "default". |
visibility |
"global" (any permitted agent can read), "private" (owner only). Defaults to "global". |
Response
{
"id": "uuid",
"type": "preference",
"tags": "preference,editor",
"pinned": false,
"importance": 0.9,
"content": "User prefers vim keybindings",
"embedded": true,
"deduped": false
}If an identical memory (by content hash or sourceId) already exists,
deduped: true is returned with the existing record — no duplicate is
created.
Alias for POST /api/memory/remember. Accepts the same request body and
returns the same response. Requires remember permission.
Alias for POST /api/memory/remember. Used by Claude Code skill
compatibility. Requires remember permission.
Get a single memory by ID. Returns deleted memories only if the query
explicitly requests them; by default, soft-deleted records return 404.
Requires recall permission.
Response
{
"id": "uuid",
"content": "User prefers vim keybindings",
"type": "preference",
"importance": 0.9,
"tags": "preference,editor",
"pinned": 0,
"who": "claude-code",
"source_type": "manual",
"project": null,
"session_id": null,
"confidence": null,
"access_count": 3,
"last_accessed": "2026-02-21T11:00:00.000Z",
"is_deleted": 0,
"deleted_at": null,
"extraction_status": "done",
"embedding_model": "nomic-embed-text",
"version": 2,
"created_at": "2026-02-21T10:00:00.000Z",
"updated_at": "2026-02-21T10:30:00.000Z",
"updated_by": "operator"
}Full audit history for a memory in chronological order. Requires recall
permission.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 200 | Max events (cap: 1000) |
Response
{
"memoryId": "uuid",
"count": 3,
"history": [
{
"id": "hist-uuid",
"event": "created",
"oldContent": null,
"newContent": "User prefers vim keybindings",
"changedBy": "claude-code",
"actorType": "operator",
"reason": null,
"metadata": null,
"createdAt": "2026-02-21T10:00:00.000Z",
"sessionId": null,
"requestId": null
}
]
}Restore a soft-deleted memory. The recovery window is 30 days from deletion.
Requires recover permission.
Request body
{
"reason": "Accidentally deleted",
"if_version": 3
}reason is required. if_version is optional — if provided, the operation
is rejected with 409 if the current version does not match (optimistic
concurrency).
Response
{
"id": "uuid",
"status": "recovered",
"currentVersion": 3,
"newVersion": 4,
"retentionDays": 30
}Possible status values and their HTTP codes:
| Status | Code | Meaning |
|---|---|---|
recovered |
200 | Success |
not_found |
404 | Memory does not exist |
not_deleted |
409 | Memory is not deleted |
retention_expired |
409 | Outside 30-day recovery window |
version_conflict |
409 | if_version mismatch |
Update a memory's fields. At least one of content, type, tags,
importance, or pinned must be provided. Requires modify permission.
Rate-limited to 60/min.
Scoped tokens in non-local mode have their project scope checked against the
target memory's project field before the mutation is applied.
Request body
{
"content": "Updated content",
"type": "fact",
"tags": ["updated", "fact"],
"importance": 0.7,
"pinned": false,
"reason": "Correcting outdated information",
"if_version": 2,
"changed_by": "operator"
}reason is required. if_version is optional optimistic concurrency guard.
tags may be a string (comma-separated), an array of strings, or null to
clear tags.
Response
{
"id": "uuid",
"status": "updated",
"currentVersion": 2,
"newVersion": 3,
"contentChanged": true,
"embedded": true
}Possible status values and their HTTP codes:
| Status | Code | Meaning |
|---|---|---|
updated |
200 | Success |
no_changes |
200 | Patch produced no diff |
not_found |
404 | Memory does not exist |
deleted |
409 | Cannot modify a deleted memory |
version_conflict |
409 | if_version mismatch |
duplicate_content_hash |
409 | New content matches an existing memory |
Soft-delete a memory. Deleted memories can be recovered within 30 days.
Requires forget permission. Rate-limited to 30/min.
Scoped tokens have their project scope checked before the deletion. Pinned
memories require force: true. Autonomous agents (pipeline/agent actor type)
cannot force-delete pinned memories.
Request body (or query parameters)
{
"reason": "No longer relevant",
"force": false,
"if_version": 3
}reason is required, either in the body or as ?reason=... query parameter.
force defaults to false. if_version is optional.
Response
{
"id": "uuid",
"status": "deleted",
"currentVersion": 3,
"newVersion": 4
}Possible status values and their HTTP codes:
| Status | Code | Meaning |
|---|---|---|
deleted |
200 | Success |
not_found |
404 | Memory does not exist |
already_deleted |
409 | Memory is already deleted |
version_conflict |
409 | if_version mismatch |
pinned_requires_force |
409 | Pinned memory requires force: true |
autonomous_force_denied |
403 | Autonomous agents cannot force-delete |
Batch forget with preview/execute workflow. Requires forget permission.
Rate-limited to 5/min (batch forget limiter).
Requires at least one of: query, ids, or a filter field (type, tags,
who, source_type, since, until). The batch size cap is 200.
For large operations (>25 candidates), the execute mode requires a
confirm_token obtained from a prior preview call.
Request body — preview mode
{
"mode": "preview",
"query": "outdated preferences",
"type": "preference",
"tags": "old",
"who": "claude-code",
"source_type": "manual",
"since": "2025-01-01T00:00:00Z",
"until": "2026-01-01T00:00:00Z",
"limit": 20
}Or target specific IDs:
{
"mode": "preview",
"ids": ["uuid1", "uuid2"]
}Preview response
{
"mode": "preview",
"count": 3,
"requiresConfirm": false,
"confirmToken": "abc123...",
"candidates": [
{ "id": "uuid1", "score": 0.85, "pinned": false, "version": 2 }
]
}Request body — execute mode
{
"mode": "execute",
"query": "outdated preferences",
"reason": "Cleaning up stale data",
"force": false,
"confirm_token": "abc123..."
}reason is required in execute mode. confirm_token is required when
requiresConfirm was true in the preview.
Execute response
{
"mode": "execute",
"requested": 3,
"deleted": 3,
"results": [
{ "id": "uuid1", "status": "deleted", "currentVersion": 2, "newVersion": 3 }
]
}Batch update multiple memories in a single request. Requires modify
permission. Rate-limited to 60/min. Maximum 200 patches per request.
Request body
{
"reason": "Bulk correction",
"changed_by": "operator",
"patches": [
{
"id": "uuid1",
"content": "Updated content",
"reason": "Per-patch reason override",
"if_version": 2
},
{
"id": "uuid2",
"tags": ["updated"],
"importance": 0.6
}
]
}Top-level reason and changed_by are defaults applied to all patches. Each
patch can override reason individually. if_version per patch is optional.
Response
{
"total": 2,
"updated": 2,
"results": [
{
"id": "uuid1",
"status": "updated",
"currentVersion": 2,
"newVersion": 3,
"contentChanged": true,
"embedded": true
},
{
"id": "uuid2",
"status": "updated",
"currentVersion": 1,
"newVersion": 2,
"contentChanged": false
}
]
}Individual patch items that fail validation return status: "invalid_request"
with an error field. The batch continues — partial success is possible.
Hybrid search combining BM25 keyword (FTS5) and vector similarity. Results
are fused using a configurable alpha weight (cfg.search.alpha). Optional
graph boost and reranker pass are applied if enabled in pipeline config.
Requires recall permission.
Request body
{
"query": "user preferences for editor",
"limit": 10,
"type": "preference",
"tags": "editor,ui",
"who": "claude-code",
"pinned": false,
"importance_min": 0.5,
"since": "2026-01-01T00:00:00Z",
"agentId": "alice"
}Only query is required.
Response
{
"results": [
{
"id": "uuid",
"content": "User prefers vim keybindings",
"score": 0.92,
"source": "hybrid",
"type": "preference",
"tags": "preference,editor",
"pinned": false,
"importance": 0.9,
"who": "claude-code",
"project": null,
"created_at": "2026-02-21T10:00:00.000Z",
"supplementary": false
}
],
"query": "user preferences for editor",
"method": "hybrid",
"meta": {
"totalReturned": 1,
"hasSupplementary": false,
"noHits": false
}
}source per result is one of hybrid, vector, keyword, or llm_summary.
method on the response reflects whether vector search was available for
this call.
meta.totalReturned reflects the number of returned rows. meta.hasSupplementary
is true when the response includes supporting context such as an LLM summary
card or linked rationale context. meta.noHits is true when recall completed
normally but found no matching results.
When memory.pipelineV2.reranker.useExtractionModel is enabled, an
additional synthesized summary card may be prepended to results. This card
has source: "llm_summary", supplementary: true, and an id of the form
summary:<sha1-12>. It is only injected when limit >= 2 so callers
always receive at least one real memory to verify the summary against. The
card is not stored in the database and does not affect access-time tracking.
Operational note: useExtractionModel moves recall onto a live LLM
call path. When auth mode is not local, the daemon enforces a dedicated
rate-limit bucket — auth.rateLimits.recallLlm (default: 60 req/min per
token). Configure it in agent.yaml alongside the other operation limits:
auth:
mode: team
rateLimits:
recallLlm:
windowMs: 60000
max: 30No additional permission level is required beyond recall.
GET-compatible alias for POST /api/memory/recall. Forwards query parameters
to the recall endpoint. Requires recall permission.
Query parameters
| Parameter | Description |
|---|---|
q |
Search query (required) |
limit |
Max results (default: 10) |
type |
Filter by memory type |
tags |
Filter by tag (comma-sep) |
who |
Filter by author |
pinned |
1 or true to filter |
importance_min |
Minimum importance float |
since |
ISO timestamp lower bound |
Response — same shape as POST /api/memory/recall.
Legacy keyword search endpoint. Also supports filter-only queries without a
search term. Requires recall permission.
Query parameters
| Parameter | Description |
|---|---|
q |
FTS5 query string (optional) |
distinct |
who — returns distinct authors instead |
limit |
Max results (default: 20 with query, 50 without) |
type |
Filter by type |
tags |
Comma-separated tag filter |
who |
Filter by author |
pinned |
1 or true |
importance_min |
Float minimum |
since |
ISO timestamp |
When distinct=who is passed, all other parameters are ignored and the
response is { "values": ["alice", "bob"] }.
Otherwise: { "results": [...] } where each result includes id, content,
created_at, who, importance, tags, type, pinned, and optionally
score (BM25 or recency-weighted).
Vector similarity search anchored to an existing memory's embedding. Returns
memories most similar to the given record. Requires recall permission.
Query parameters
| Parameter | Description |
|---|---|
id |
Memory ID to use as the anchor (required) |
k |
Number of results (default: 10) |
type |
Optional type filter |
Response
{
"results": [
{
"id": "uuid",
"content": "...",
"type": "preference",
"tags": [],
"score": 0.87,
"confidence": null,
"created_at": "2026-02-21T10:00:00.000Z"
}
]
}Returns 404 if the anchor memory has no stored embedding.
Export all stored embeddings with their parent memory metadata. Falls back to
a legacy Python export script if the embeddings table does not exist.
Requires recall permission.
Query parameters
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
limit |
integer | 600 | 50–5000 | Page size |
offset |
integer | 0 | 0–100000 | Page offset |
vectors |
boolean | false | — | Include raw float arrays |
Response
{
"embeddings": [
{
"id": "uuid",
"content": "...",
"text": "...",
"who": "claude-code",
"importance": 0.8,
"type": "preference",
"tags": ["preference"],
"sourceType": "memory",
"sourceId": "uuid",
"createdAt": "2026-02-21T10:00:00.000Z",
"vector": [0.1, 0.2, ...]
}
],
"count": 50,
"total": 1200,
"limit": 600,
"offset": 0,
"hasMore": true
}vector is only present when vectors=true is set.
Check the configured embedding provider's availability. Results are cached for
30 seconds. Requires recall permission.
Response
{
"provider": "ollama",
"model": "nomic-embed-text",
"available": true,
"dimensions": 768,
"base_url": "http://localhost:11434",
"checkedAt": "2026-02-21T10:00:00.000Z"
}On failure, available is false and error contains a description.
Returns embedding health metrics including coverage and staleness.
Response — embedding health object with coverage percentage, stale count, and provider status.
Returns a server-computed UMAP projection of all stored embeddings.
Results are cached in the umap_cache table; cache is invalidated when
the embedding count changes. Requires recall permission.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
dimensions |
integer | 2 | Output dimensions: 2 or 3 |
If the projection is still computing, the endpoint returns 202 Accepted
with status: "computing". Poll again when ready.
Response (computed)
{
"status": "cached",
"dimensions": 2,
"count": 847,
"total": 847,
"nodes": [
{
"id": "uuid",
"x": 42.1,
"y": -18.7,
"content": "User prefers vim keybindings",
"who": "claude-code",
"importance": 0.8,
"type": "preference",
"tags": ["preference"],
"pinned": false,
"sourceType": "memory",
"sourceId": "uuid",
"createdAt": "2026-02-21T10:00:00.000Z"
}
],
"edges": [[0, 3], [0, 7]],
"cachedAt": "2026-02-21T10:05:00.000Z"
}Response (computing)
{ "status": "computing", "dimensions": 2, "count": 0, "total": 847 }The documents API ingests external content (text, URLs, files) for chunking
and embedding. Each document generates linked memory records via the pipeline.
All document endpoints require documents permission.
Submit a document for ingestion. The document is queued and processed
asynchronously. Returns 201 on success, or the existing document's ID and
status if a duplicate URL is detected.
Request body
{
"source_type": "text",
"content": "Full text content here",
"title": "My Document",
"content_type": "text/plain",
"connector_id": null,
"metadata": { "author": "nicholai" }
}For source_type: "url":
{
"source_type": "url",
"url": "https://example.com/page",
"title": "Example Page"
}source_type is required and must be text, url, or file. content is
required for text. url is required for url.
Response
{ "id": "uuid", "status": "queued" }Or if deduplicated:
{ "id": "existing-uuid", "status": "processing", "deduplicated": true }List all documents with optional status filter.
Query parameters
| Parameter | Description |
|---|---|
status |
Filter by status (queued, processing, done, failed, deleted) |
limit |
Page size (default: 50, max: 500) |
offset |
Pagination offset (default: 0) |
Response
{
"documents": [...],
"total": 42,
"limit": 50,
"offset": 0
}Each document includes all columns from the documents table.
Get a single document by ID.
Response — full document row, or 404.
List the memory records derived from this document, ordered by chunk index.
Response
{
"chunks": [
{
"id": "memory-uuid",
"content": "Chunk text...",
"type": "fact",
"created_at": "2026-02-21T10:00:00.000Z",
"chunk_index": 0
}
],
"count": 12
}Soft-delete a document and all its derived memory records. Memories linked to the document are soft-deleted one at a time with audit history.
Query parameters
| Parameter | Description |
|---|---|
reason |
Required. Deletion reason. |
Response
{ "deleted": true, "memoriesRemoved": 12 }Connectors ingest documents from external sources on a schedule or on demand.
Currently only the filesystem provider is operational; github-docs and
gdrive are registered but not yet functional.
GET requests to connector endpoints are open. POST, DELETE, and mutation
requests require admin permission (or connectors for operators).
List all registered connectors.
Response
{
"connectors": [
{
"id": "uuid",
"status": "idle",
"config_json": "{...}",
"cursor_json": "{...}",
"last_sync_at": "2026-02-21T09:00:00.000Z",
"last_error": null
}
],
"count": 1
}Register a new connector. Requires admin permission.
Request body
{
"provider": "filesystem",
"displayName": "My Notes",
"settings": {
"rootPath": "/home/nicholai/notes",
"glob": "**/*.md"
}
}provider must be filesystem, github-docs, or gdrive.
Response
{ "id": "uuid" }Returns 201.
Get a single connector's details and current state.
Trigger an incremental sync for a filesystem connector. The sync runs in the
background — poll GET /api/connectors/:id for status updates. Requires
admin permission.
Response
{ "status": "syncing" }Returns { "status": "syncing", "message": "Already syncing" } if a sync is
already running.
Trigger a full resync, discarding the cursor. Requires ?confirm=true query
parameter as a safety guard. Requires admin permission.
Response
{ "status": "syncing" }Remove a connector from the registry. Requires admin permission.
Query parameters
| Parameter | Description |
|---|---|
cascade |
true — also soft-delete documents from this connector |
Response
{ "deleted": true }Lightweight health check for a connector, including document count.
Response
{
"id": "uuid",
"status": "idle",
"lastSyncAt": "2026-02-21T09:00:00.000Z",
"lastError": null,
"documentCount": 142
}Multi-agent endpoints for managing the agent roster. All agents share one
SQLite database; memories are scoped by agent_id and visibility. The
read policy controls which other agents' global memories each agent can see.
List all registered agents. Requires no permission (local mode) or recall.
Response
{
"agents": [
{
"id": "default",
"name": "default",
"read_policy": "shared",
"policy_group": null,
"created_at": "2026-03-24T00:00:00.000Z",
"updated_at": "2026-03-24T00:00:00.000Z"
},
{
"id": "alice",
"name": "alice",
"read_policy": "isolated",
"policy_group": null,
"created_at": "2026-03-24T12:00:00.000Z",
"updated_at": "2026-03-24T12:00:00.000Z"
}
]
}read_policy is one of:
isolated— agent sees only its own memoriesshared— agent sees allvisibility=globalmemories from any agentgroup— agent seesvisibility=globalmemories from agents in the samepolicy_group
Get a single agent by name. Returns 404 if not found.
Response — same shape as a single entry in GET /api/agents.
Register a new agent. Requires remember permission.
Request body
{
"name": "alice",
"read_policy": "isolated",
"policy_group": null
}Only name is required. read_policy defaults to "isolated".
Response — the created agent record.
Remove an agent from the roster. Memories owned by the agent are marked
visibility='archived' (not deleted). Requires admin permission.
Query parameters
| Parameter | Description |
|---|---|
purge |
Set to true to permanently delete all memories where agent_id = name. |
Response
{ "success": true, "purged": false }List all installed skills from $SIGNET_WORKSPACE/skills/. Each skill must have a
SKILL.md with YAML frontmatter.
Response
{
"skills": [
{
"name": "browser-use",
"description": "Browser automation skill",
"version": "1.0.0",
"author": "browser-use",
"license": "MIT",
"user_invocable": true,
"arg_hint": "<url>",
"path": "/home/user/.agents/skills/browser-use"
}
],
"count": 3
}Search the skills.sh registry for installable skills.
Query parameters
| Parameter | Description |
|---|---|
q |
Search query string (required) |
Response
{
"results": [
{
"name": "browser-use",
"description": "browser-use/browser-use@browser-use (32.6K installs)",
"installed": false
}
]
}Get a single skill's metadata and full SKILL.md content.
Response
{
"name": "browser-use",
"description": "...",
"version": "1.0.0",
"path": "/home/user/.agents/skills/browser-use",
"content": "---\ndescription: ...\n---\n\n# Browser Use\n..."
}Returns 400 for invalid names (path traversal). Returns 404 if not
installed.
Install a skill via the configured package manager (bun, npm, or pnpm).
Runs skills add <pkg> --global --yes. Times out after 60 seconds.
Request body
{
"name": "browser-use",
"source": "browser-use/browser-use@browser-use"
}name is required. source overrides the install package name if provided.
Response
{ "success": true, "name": "browser-use", "output": "..." }Returns 500 with { "success": false, "error": "..." } on failure.
Uninstall a skill by removing its directory from $SIGNET_WORKSPACE/skills/.
Response
{ "success": true, "name": "browser-use", "message": "Removed browser-use" }Return usage analytics for skill invocations recorded by the daemon.
Query parameters
| Parameter | Description |
|---|---|
agent_id |
Optional agent scope override. Defaults to the request-scoped agent or default. |
since |
Optional ISO 8601 UTC timestamp filter, e.g. 2026-03-01T00:00:00Z. |
limit |
Optional max number of top skills to return. Default 10, range 1-100. |
Response
{
"totalCalls": 12,
"successRate": 0.917,
"topSkills": [
{
"skillName": "web-search",
"count": 5,
"successCount": 5,
"avgLatencyMs": 184
}
],
"latency": {
"p50": 160,
"p95": 310
}
}Returns 400 if since is not an ISO 8601 UTC timestamp.
List known harness config file locations and whether each exists on disk.
Response
{
"harnesses": [
{ "name": "Claude Code", "path": "/home/user/.claude/CLAUDE.md", "exists": true },
{ "name": "OpenCode", "path": "/home/user/.config/opencode/AGENTS.md", "exists": false },
{ "name": "OpenClaw (Source)", "path": "/home/user/.agents/AGENTS.md", "exists": true }
]
}Run the generate-harness-configs.py script from the scripts directory to
rebuild all harness config files from source. The script must exist at
$SIGNET_WORKSPACE/scripts/generate-harness-configs.py.
Response
{ "success": true, "message": "Configs regenerated successfully", "output": "..." }Returns 404 if the script is not found.
Secrets are stored encrypted on disk at $SIGNET_WORKSPACE/.secrets/. Values are
never returned in API responses — only names are exposed.
Store or overwrite a secret value.
Request body
{ "value": "sk-abc123..." }value must be a non-empty string.
Response
{ "success": true, "name": "OPENAI_API_KEY" }List stored secret names. Values are never included.
Response
{ "secrets": ["OPENAI_API_KEY", "GITHUB_TOKEN"] }Delete a stored secret.
Response
{ "success": true, "name": "OPENAI_API_KEY" }Returns 404 if the secret does not exist.
Execute a shell command with multiple secrets injected into the subprocess
environment. Callers pass a map of env var names to secret references —
never actual values. References can be Signet secret names or direct
1Password refs (op://vault/item/field). The daemon resolves and injects
all values before spawning.
Request body
{
"command": "curl -H 'Authorization: Bearer $OPENAI_API_KEY' https://api.openai.com/v1/models",
"secrets": {
"OPENAI_API_KEY": "OPENAI_API_KEY",
"GITHUB_TOKEN": "GITHUB_TOKEN"
}
}Both command and secrets are required. The secrets map must contain at
least one entry.
Response
{ "code": 0, "stdout": "...", "stderr": "" }Legacy single-secret variant. Execute a shell command with a single secret
injected into the subprocess environment. Prefer /api/secrets/exec for
new integrations.
Request body
{
"command": "curl -H 'Authorization: Bearer $OPENAI_API_KEY' https://api.openai.com/v1/models",
"secrets": {
"OPENAI_API_KEY": "OPENAI_API_KEY"
}
}command is required. secrets is optional — if omitted, the named secret
from the URL path is injected under its own name.
Response
{ "code": 0, "stdout": "...", "stderr": "" }Return 1Password integration status, including whether a service account token is configured and (when available) accessible vaults.
Validate and store a 1Password service account token.
Request body
{ "token": "ops_..." }Disconnect 1Password integration by removing the stored service account token secret.
List accessible vaults for the connected service account.
Import password-like fields from 1Password vault items into Signet secrets.
Request body
{
"vaults": ["Engineering"],
"prefix": "OP",
"overwrite": false
}Hook endpoints integrate with AI harness session lifecycle events. They are used by connector packages to inject memory context and extract new memories.
The x-signet-runtime-path request header (or runtimePath body field)
declares whether the caller is the plugin or legacy runtime path. The
daemon enforces that only one path can be active per session — subsequent
calls from the other path return 409.
Called at the beginning of a session. Returns context and relevant memories
for injection into the harness system prompt. Requires remember permission
(via hook routing).
Request body
{
"harness": "claude-code",
"sessionKey": "session-uuid",
"runtimePath": "plugin"
}harness is required.
Response — implementation-defined context object returned by
handleSessionStart.
Called on each user message. Returns memories relevant to the current prompt for in-context injection.
Request body
{
"harness": "claude-code",
"userMessage": "How do I set up dark mode?",
"userPrompt": "How do I set up dark mode?",
"lastAssistantMessage": "Earlier we discussed using CSS variables for theme tokens.",
"sessionKey": "session-uuid",
"transcriptPath": "/tmp/signet/session-transcript.txt",
"runtimePath": "plugin"
}harness is required.
userMessage is preferred when the harness can provide a cleaned user turn.
userPrompt, lastAssistantMessage, transcriptPath, and inline transcript
are optional.
Prompt-submit retrieval prefers structured memory recall. When structured recall is weak or empty, the daemon first attempts temporal-summary fallback (session DAG artifacts) and then transcript fallback from prior session transcripts instead of returning no context.
Called at session end. Triggers memory extraction from the transcript. Releases the session's runtime path claim.
Request body
{
"harness": "claude-code",
"sessionKey": "session-uuid",
"sessionId": "session-uuid",
"transcriptPath": "/tmp/signet/session-transcript.txt",
"runtimePath": "plugin"
}harness is required.
transcriptPath or inline transcript may be provided for transcript
capture. Signet stores a cleaned conversation-only transcript in memory
surfaces and may retain raw auditable traces separately in daemon logs.
When transcript text is available, the daemon first writes canonical
workspace-root-relative markdown lineage files under
$SIGNET_WORKSPACE/memory/:
{captured_at}--{session_token}--transcript.md{captured_at}--{session_token}--manifest.md
The manifest is mutable and may later gain a compaction_path; transcript
artifacts are immutable once written. The async summary worker later writes the
matching immutable --summary.md artifact for normal session-end jobs.
Explicit memory save from within a session. Requires remember permission.
Request body
{
"harness": "claude-code",
"content": "User wants dark mode by default",
"sessionKey": "session-uuid",
"runtimePath": "plugin"
}harness and content are required.
Explicit memory query from within a session. Requires recall permission.
Request body
{
"harness": "claude-code",
"query": "user UI preferences",
"keywordQuery": "\"dark mode\" OR theme",
"project": "/workspace/repo",
"limit": 5,
"type": "preference",
"tags": "ui,editor",
"who": "claude-code",
"since": "2026-01-01T00:00:00Z",
"until": "2026-04-01T00:00:00Z",
"expand": true,
"sessionKey": "session-uuid",
"runtimePath": "plugin"
}harness and query are required.
This route is a hook-oriented wrapper around POST /api/memory/recall. It
accepts a narrower request surface, applies hook/session policy checks, and
then forwards the supported recall filters into the shared hybrid recall path.
project on this route is forwarded as the memory project filter. It is not
remapped to recall scope.
Response
Same recall-family shape as POST /api/memory/recall, plus legacy
compatibility fields during the transition period:
{
"results": [],
"memories": [],
"count": 0,
"query": "user UI preferences",
"method": "hybrid",
"meta": {
"totalReturned": 0,
"hasSupplementary": false,
"noHits": true
}
}Special no-op cases preserve the same shape and add a flag:
{ ..., "bypassed": true }when the session is bypassed{ ..., "internal": true }for internal no-hook calls
memories and count are legacy compatibility aliases for older hook
consumers and will mirror results and results.length during the
transition period.
Called before context window compaction. Returns summary instructions for the compaction prompt.
Request body
{
"harness": "claude-code",
"sessionKey": "session-uuid",
"runtimePath": "plugin"
}harness is required.
Save a compaction summary as a memory row, as a temporal DAG artifact, and as a canonical immutable markdown compaction artifact linked back through the session manifest.
Request body
{
"harness": "claude-code",
"summary": "Session covered dark mode setup and vim configuration...",
"sessionKey": "session-uuid",
"project": "/workspace/repo",
"runtimePath": "plugin"
}harness and summary are required.
If sessionKey is present, the daemon uses it to preserve lineage:
- the memory row is agent-scoped
source_idpoints back to the session lineage- the temporal node keeps
session_key - the artifact can later be expanded through the temporal drill-down API
- transcript and temporal summary persistence are keyed by
agentId + sessionKey, so identical session keys from different agents do not collide - the canonical compaction file is written to
memory/{captured_at}--{session_token}--compaction.md - the mutable manifest for that session is backfilled with
compaction_path
If compaction fires before transcript persistence lands, callers should also
send project. The daemon uses that explicit project as the fallback lineage
scope until transcript storage catches up.
Response
{ "success": true, "memoryId": "uuid" }Trigger a mid-session memory extraction for long-lived sessions (Discord bots,
persistent agents) that never call session-end. Computes a delta since the
last extraction cursor and enqueues a summary job without releasing the session
claim.
Request body
{
"harness": "openclaw",
"sessionKey": "session-uuid",
"agentId": "agent-id",
"project": "/workspace/repo",
"transcriptPath": "/path/to/session.jsonl",
"runtimePath": "plugin"
}harness and sessionKey are required. transcript (inline string) takes
precedence over transcriptPath; both fall back to the stored session
transcript from a prior session-end or user-prompt-submit call.
The endpoint skips silently when:
- The delta since the last extraction cursor is < 500 characters
- No transcript is available
- The session is bypassed
On success the extraction cursor advances so the next call only processes new content.
Response
{ "queued": true, "jobId": "uuid" }queued: true means a summary job was enqueued; jobId identifies the
async job. The job extracts the delta and writes a temporal node scored
at 0.85 (below compaction summaries at 0.95, above chunks at 0.55). Checkpoint
jobs stay DB-native, they do not create canonical --summary.md session
artifacts.
{ "skipped": true }Returned when delta < 500 chars, no transcript is available, or the session is bypassed.
{ "queued": false }Returned by daemon implementations where summary job enqueueing is not
yet available (Phase 5). The delta was found and is above the threshold
but no job was enqueued. Callers may treat this identically to
{skipped: true} for retry purposes — the content is not consumed and
will be re-evaluated on the next call.
Return the current synthesis configuration (thresholds, model, schedule).
Request a MEMORY.md synthesis run. Implementation-defined request body
and response from handleSynthesisRequest.
Current MEMORY.md generation is a deterministic projection, not a free-form
LLM rewrite:
- scored durable memories come from the memory database
- rolling session-ledger rows come from canonical artifact frontmatter in
memory_artifacts - temporal context comes from
session_summariesDAG artifacts - the response keeps the rendered markdown in
promptfor backward compatibility, withmodel: "projection" indexBlockcontains the exact## Temporal Indexblock already included in the rendered projection
The rendered file contains these required sections:
## Global Head (Tier 1)## Thread Heads (Tier 2)## Session Ledger (Last 30 Days)## Open Threads## Durable Notes & Constraints## Temporal Index
Optional agentId / sessionKey inputs may be provided so synthesis resolves
the correct agent-scoped head.
Write a newly synthesized MEMORY.md. Backs up the existing file before
overwriting and records DB-backed head metadata used for same-agent
merge protection.
Request body
{
"content": "# Memory\n\n...",
"agentId": "optional-agent-id",
"sessionKey": "optional-session-key"
}content is required.
If another writer currently holds the active MEMORY.md lease for the same
agent head, this route returns 409.
Response
{ "success": true }The sessions API exposes active session state, including per-session bypass
toggles. When bypass is enabled for a session, all hook endpoints return
empty no-op responses with bypassed: true — but MCP tools (memory_search,
memory_store, etc.) continue to work normally.
List active sessions for the requesting agent with their bypass status.
The response merges live tracker claims with live cross-agent presence so
sessions do not disappear just because one surface has not claimed the
session yet. Results are scoped to the authenticated agent; for
cross-agent visibility use GET /api/cross-agent/presence.
Response
{
"sessions": [
{
"key": "session-uuid",
"runtimePath": "plugin",
"claimedAt": "2026-03-08T10:00:00.000Z",
"expiresAt": "2026-03-08T14:00:00.000Z",
"bypassed": false
}
],
"count": 1
}Get a single session's status by its session key.
Both raw keys (abc123) and prefixed keys (session:abc123) are accepted.
Response
{
"key": "session-uuid",
"runtimePath": "plugin",
"claimedAt": "2026-03-08T10:00:00.000Z",
"expiresAt": "2026-03-08T14:00:00.000Z",
"bypassed": false
}Returns 404 if the session key is not found.
List temporal summary nodes used for drill-down and MEMORY.md synthesis.
Results are agent-scoped.
Response
{
"summaries": [
{
"id": "sess-1",
"kind": "session",
"depth": 0,
"source_type": "summary",
"source_ref": "session-uuid",
"meta_json": "{\"source\":\"summary-worker\"}"
}
]
}Expand a temporal node by id. Returns lineage, linked memories, and transcript
context for MEMORY.md drill-down and LCM-style expansion. Expansion is
agent-scoped.
Request body
{
"id": "node-id",
"includeTranscript": true,
"transcriptCharLimit": 2000
}Response
{
"node": {
"id": "node-id",
"kind": "session",
"depth": 0,
"sourceType": "summary"
},
"parents": [],
"children": [],
"linkedMemories": [],
"transcript": {
"sessionKey": "session-uuid",
"excerpt": "..."
}
}Toggle bypass for a session. When enabled, all hook endpoints for this session
return empty no-op responses with bypassed: true. MCP tools are not affected.
Both raw keys and session:<uuid> forms are accepted.
Request body
{
"enabled": true
}enabled is required (boolean).
Response
{
"key": "session-uuid",
"bypassed": true
}Returns 404 if the session key is not found.
The git API manages automatic commit and sync of the $SIGNET_WORKSPACE/ directory.
Config is loaded from agent.yaml under the git key. Defaults: autoCommit: true, autoSync: true, syncInterval: 300s, remote: origin,
branch: main.
Return git status for the agents directory.
Response — output of getGitStatus() including branch, ahead,
behind, dirty, lastCommit.
Pull from the configured remote and branch.
Response — result of gitPull() including success, output, error.
Push the current branch to the configured remote.
Response — result of gitPush().
Pull then push — equivalent to running both operations in sequence.
Response — result of gitSync().
Return the current in-memory git configuration.
Response
{
"enabled": true,
"autoCommit": true,
"autoSync": true,
"syncInterval": 300,
"remote": "origin",
"branch": "main"
}Update runtime git configuration. Changes take effect immediately; the sync
timer is restarted if autoSync or syncInterval changes.
Request body (all fields optional)
{
"autoSync": true,
"syncInterval": 600,
"remote": "origin",
"branch": "main"
}Response
{ "success": true, "config": { ... } }The update system checks GitHub releases and the npm registry, then optionally auto-installs using the detected package manager.
Check for a newer version. Results are cached for 1 hour unless ?force=true
is passed.
Query parameters
| Parameter | Description |
|---|---|
force |
true — bypass 1-hour cache |
Response
{
"currentVersion": "0.1.69",
"latestVersion": "0.1.70",
"updateAvailable": true,
"releaseUrl": "https://github.com/Signet-AI/signetai/releases/tag/v0.1.70",
"releaseNotes": "...",
"publishedAt": "2026-02-20T12:00:00Z",
"restartRequired": false,
"pendingVersion": null,
"cached": false,
"checkedAt": "2026-02-21T10:00:00.000Z"
}Return current update configuration and runtime state.
Response
{
"autoInstall": false,
"checkInterval": 21600,
"minInterval": 300,
"maxInterval": 604800,
"pendingRestartVersion": null,
"lastAutoUpdateAt": null,
"lastAutoUpdateError": null,
"updateInProgress": false
}Modify auto-update settings. Changes are persisted to agent.yaml.
Request body (all fields optional)
{
"autoInstall": true,
"checkInterval": 43200
}checkInterval must be between 300 and 604800 seconds.
Response
{
"success": true,
"config": { "autoInstall": true, "checkInterval": 43200 },
"persisted": true,
"pendingRestartVersion": null,
"lastAutoUpdateAt": null,
"lastAutoUpdateError": null
}Install the latest version immediately. Runs the global install command for the detected package manager. A daemon restart is required to activate the update.
Response
{
"success": true,
"message": "Update installed. Restart daemon to apply.",
"output": "...",
"installedVersion": "0.1.70",
"restartRequired": true
}If already up to date, returns success: true with a message indicating no
update is needed.
Requires diagnostics permission.
Full diagnostic report across all domains. Includes a composite health score derived from database health, pipeline state, embedding availability, and mutation integrity.
Response — a multi-domain report object. Domains include database,
pipeline, embedding, mutation, fts, and composite. The composite
field looks like:
{ "score": 0.95, "status": "healthy" }Diagnostic data for a single domain. Known domains: database, pipeline,
embedding, mutation, fts, composite.
Returns 400 for unknown domains.
Administrative repair operations. All require admin permission. Operations
are rate-limited internally by the repair limiter and return 429 when the
limit is exceeded.
Requeue extraction jobs stuck in a terminal-failed state. Typically used after resolving a pipeline configuration issue.
Response
{ "action": "requeueDeadJobs", "success": true, "affected": 12, "message": "..." }Release stale pipeline job leases that have exceeded their timeout. Run this
if pipeline workers crashed and left jobs locked. Stale jobs that still have
remaining retries are returned to pending. Stale jobs that have already
reached max_attempts are moved to dead instead of being requeued again.
Response
{
"action": "releaseStaleLeases",
"success": true,
"affected": 3,
"message": "released 2 stale lease(s) back to pending and dead-lettered 1 exhausted job(s)"
}Check FTS5 index consistency against the memories table and detect legacy
tokenizer drift. Optionally repair mismatches by rebuilding the index or
recreating memories_fts with the canonical unicode61 tokenizer.
Request body (optional)
{ "repair": true }Response
{ "action": "checkFtsConsistency", "success": true, "affected": 0, "message": "..." }Trigger a manual retention decay sweep. This endpoint is currently not wired
to the pipeline worker and returns 501.
Response
{
"action": "triggerRetentionSweep",
"success": false,
"affected": 0,
"message": "Use the maintenance worker for automated sweeps..."
}Returns the count of memories that are missing vector embeddings.
Requires admin permission.
Response
{
"unembedded": 42,
"total": 1200,
"coverage": "96.5%"
}Batch re-embeds memories that are missing vector embeddings. Processes
up to batchSize memories per call. Requires admin permission.
Rate-limited — returns 429 when the limit is exceeded.
Request body
{
"batchSize": 50,
"dryRun": false
}batchSize defaults to 50. dryRun: true reports what would be
embedded without calling the embedding provider.
Response
{
"action": "reEmbedMissingVectors",
"success": true,
"affected": 42,
"message": "re-embedded 42 memories"
}Remove embedding rows that reference memories which no longer exist.
Rate-limited. Requires admin permission.
Response
{
"action": "cleanOrphanedEmbeddings",
"success": true,
"affected": 12,
"message": "cleaned 12 orphaned embeddings"
}Returns statistics on potential duplicate memories (by content hash).
Requires admin permission.
Response — object with duplicate counts and affected memory IDs.
Deduplicate memories by content hash and optionally by semantic similarity.
Rate-limited. Requires admin permission.
Request body
{
"batchSize": 50,
"dryRun": false,
"semanticEnabled": false,
"semanticThreshold": 0.95
}All fields are optional. dryRun: true reports what would be deduplicated
without making changes. semanticEnabled adds vector-similarity dedup on
top of hash-based dedup.
Response
{
"action": "deduplicateMemories",
"success": true,
"affected": 7,
"message": "deduplicated 7 memories"
}Composite pipeline status snapshot for dashboard visualization. Returns worker status, job queue counts (memory and summary), diagnostics, latency histograms, error summary, and the current pipeline mode.
Known mode values: controlled-write, shadow, frozen, paused,
disabled.
Response
{
"workers": { ... },
"queues": {
"memory": { "pending": 3, "leased": 1, "completed": 200, "failed": 0, "dead": 0 },
"summary": { "pending": 0, "leased": 0, "completed": 5, "failed": 0, "dead": 0 }
},
"diagnostics": { ... },
"latency": { ... },
"errorSummary": { ... },
"mode": "controlled-write"
}Mode is one of: disabled, frozen, shadow, paused, controlled-write.
Pause the extraction runtime in-place without restarting the daemon.
Requires admin permission and uses the admin rate limit bucket.
Returns 409 if another pause/resume transition is already running.
Response
{
"success": true,
"changed": true,
"paused": true,
"file": "/home/user/.agents/agent.yaml",
"mode": "paused"
}Resume the extraction runtime in-place without restarting the daemon.
Requires admin permission and uses the admin rate limit bucket.
Response
{
"success": true,
"changed": true,
"paused": false,
"file": "/home/user/.agents/agent.yaml",
"mode": "controlled-write"
}changed is false when the persisted pause flag already matches the
requested state.
Dreaming is a periodic knowledge-graph consolidation process that uses a smart model to merge, prune, and enrich the entity graph.
Return the current dreaming worker state, configuration, and recent passes.
Requires admin permission.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agentId |
string | no | Agent ID (default: "default") |
Response
{
"enabled": true,
"worker": { "running": true, "active": false },
"state": {
"tokensSinceLastPass": 42000,
"lastPassAt": "2026-04-01T12:00:00.000Z",
"lastPassId": "abc-123",
"lastPassMode": "incremental"
},
"config": {
"tokenThreshold": 100000,
"backfillOnFirstRun": true,
"maxInputTokens": 128000,
"maxOutputTokens": 16000,
"timeout": 300000
},
"passes": [
{
"id": "pass-uuid",
"mode": "incremental",
"status": "completed",
"startedAt": "2026-04-01T12:00:00.000Z",
"completedAt": "2026-04-01T12:05:00.000Z",
"tokensConsumed": 8000,
"mutationsApplied": 12,
"mutationsSkipped": 3,
"mutationsFailed": 1,
"summary": "Merged 3 duplicate entities, pruned 5 junk attributes",
"error": null
}
]
}Manually trigger a dreaming pass. Requires admin permission.
Returns 202 Accepted immediately and runs the pass in the background
(passes can take up to several minutes on large graphs).
Returns 409 if a pass is already running. Returns 503 if the
dreaming worker is not started.
Poll GET /api/dream/status and check passes[0].status for completion.
Request body
{
"mode": "incremental"
}mode is "incremental" (default) or "compact".
Response — 202 Accepted
{
"accepted": true,
"passId": "pass-uuid",
"status": "running",
"mode": "incremental"
}Session checkpoints track continuity state at compaction boundaries.
List session checkpoints for a project.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
project |
string | yes | Project path to filter by |
limit |
integer | no | Max results (default: 10, max: 100) |
Response
{
"checkpoints": [
{
"session_key": "abc-123",
"project": "/path/to/project",
"trigger": "periodic",
"created_at": "2026-02-21T10:00:00.000Z"
}
],
"count": 1
}Get all checkpoints for a specific session.
Response
{
"checkpoints": [ ... ],
"count": 3
}Requires analytics permission.
Aggregate request counts, memory operation totals, and per-endpoint hit counts collected since daemon start.
Response — collector-defined usage summary object.
Recent error events from the analytics collector.
Query parameters
| Parameter | Description |
|---|---|
stage |
Filter by pipeline stage (e.g., mutation) |
since |
ISO timestamp — only errors after this time |
limit |
Max errors to return |
Response
{
"errors": [ { "stage": "mutation", "message": "...", "at": "..." } ],
"summary": { "total": 5, "byStage": { "mutation": 5 } }
}Latency histograms for key operation groups: remember, recall, mutate.
Response — collector-defined latency object with p50/p95/p99 per group.
Recent structured log entries. Same data as GET /api/logs but namespaced
under analytics.
Query parameters
| Parameter | Description |
|---|---|
limit |
Max log entries (default: 100) |
level |
debug, info, warn, or error |
category |
Filter by log category (e.g., memory, pipeline) |
since |
ISO timestamp lower bound |
Response
{ "logs": [...], "count": 47 }Combined view of mutation diagnostics and recent mutation errors. Useful for auditing data integrity.
Response
{
"mutation": { ... },
"recentErrors": [ ... ],
"errorSummary": { ... }
}Session continuity scores over time. Tracks how well memory injection maintains context across sessions.
Query parameters
| Parameter | Type | Description |
|---|---|---|
project |
string | Filter by project path (optional) |
limit |
integer | Max scores to return (default: 50) |
Response
{
"scores": [
{
"id": "uuid",
"session_key": "abc-123",
"project": "/path/to/project",
"harness": "claude-code",
"score": 0.85,
"memories_recalled": 12,
"memories_used": 8,
"novel_context_count": 3,
"reasoning": "...",
"created_at": "2026-02-21T10:00:00.000Z"
}
],
"summary": {
"count": 50,
"average": 0.78,
"trend": 0.05,
"latest": 0.85
}
}Latest continuity score per project. Returns one row per project, ordered by most recent.
Response
{
"scores": [
{ "project": "/path/to/project", "score": 0.85, "created_at": "2026-02-21T10:00:00.000Z" }
]
}Telemetry endpoints expose local-only event data collected by the daemon.
No data is sent externally. If telemetry is disabled, endpoints return
enabled: false.
Query raw telemetry events.
Query parameters
| Parameter | Type | Description |
|---|---|---|
event |
string | Filter by event type (e.g., llm.generate) |
since |
string | ISO timestamp lower bound |
until |
string | ISO timestamp upper bound |
limit |
integer | Max events (default: 100) |
Response
{
"events": [
{
"event": "llm.generate",
"properties": { "inputTokens": 500, "outputTokens": 200, "durationMs": 1200 },
"timestamp": "2026-02-21T10:00:00.000Z"
}
],
"enabled": true
}Aggregated telemetry statistics since daemon start or since a given timestamp.
Query parameters
| Parameter | Type | Description |
|---|---|---|
since |
string | ISO timestamp lower bound (optional) |
Response
{
"enabled": true,
"totalEvents": 500,
"llm": {
"calls": 120,
"errors": 2,
"totalInputTokens": 60000,
"totalOutputTokens": 24000,
"totalCost": 0.45,
"p50": 800,
"p95": 2400
},
"pipelineErrors": 3
}Export raw telemetry events as newline-delimited JSON (NDJSON).
Query parameters
| Parameter | Type | Description |
|---|---|---|
since |
string | ISO timestamp lower bound (optional) |
limit |
integer | Max events (default: 10000) |
Response — Content-Type: application/x-ndjson. Each line is a
JSON-serialized telemetry event. Returns 404 if telemetry is not enabled.
Requires analytics permission.
Build a chronological timeline for a memory entity, combining mutation history, log events, and errors associated with the given ID.
Response — timeline object with ordered events from buildTimeline().
Same as GET /api/timeline/:id but wraps the result in an export envelope
with version and timestamp metadata.
Response
{
"meta": {
"version": "0.1.69",
"exportedAt": "2026-02-21T10:00:00.000Z",
"entityId": "uuid"
},
"timeline": { ... }
}Return recent structured log entries from the in-memory log buffer.
Query parameters
| Parameter | Description |
|---|---|
limit |
Max entries (default: 100) |
level |
Minimum level: debug, info, warn, error |
category |
Filter by category string |
since |
ISO timestamp — only logs after this time |
Response
{ "logs": [...], "count": 100 }Server-Sent Events stream of live log output. Each event is a JSON-serialized
LogEntry. The connection sends an initial {"type":"connected"} event and
then emits entries in real time as the daemon generates them.
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
Each SSE event:
data: {"level":"info","category":"memory","message":"Memory saved","at":"..."}
The stream stays open until the client disconnects.
Model Context Protocol endpoint using Streamable HTTP transport (stateless). Supports POST (send messages), GET (SSE stream), and DELETE (session teardown).
Exposes memory tools: memory_search, memory_store, memory_get,
memory_list, memory_modify, memory_forget. See docs/MCP.md for full
tool documentation.
POST /mcp — Send MCP JSON-RPC messages. Returns JSON or SSE stream.
GET /mcp — Open an SSE stream for server-initiated notifications.
DELETE /mcp — Terminate MCP session (no-op in stateless mode).
List all scheduled tasks with their last run status.
Response
{
"tasks": [{
"id": "uuid",
"name": "Review open PRs",
"prompt": "Review all open pull requests",
"cron_expression": "0 9 * * *",
"harness": "claude-code",
"working_directory": "/path/to/project",
"enabled": 1,
"last_run_at": "2026-02-23T09:00:00Z",
"next_run_at": "2026-02-24T09:00:00Z",
"last_run_status": "completed",
"last_run_exit_code": 0
}],
"presets": [
{"label": "Every 15 min", "expression": "*/15 * * * *"},
{"label": "Hourly", "expression": "0 * * * *"},
{"label": "Daily 9am", "expression": "0 9 * * *"},
{"label": "Weekly Mon 9am", "expression": "0 9 * * 1"}
]
}Create a new scheduled task.
Request body
{
"name": "Review open PRs",
"prompt": "Review all open pull requests and summarize findings",
"cronExpression": "0 9 * * *",
"harness": "claude-code",
"workingDirectory": "/path/to/project"
}Response (201)
{"id": "uuid", "nextRunAt": "2026-02-24T09:00:00Z"}Get a single task with its 20 most recent runs.
Update a task's name, prompt, cron, harness, working directory, or enabled state.
Delete a task and all its run history (cascade).
Trigger an immediate manual run. Returns 202 with runId. Returns 409 if
the task already has a running execution. Skill usage analytics are
attributed using non-breaking task scope hints when available.
Paginated run history. Supports limit and offset query parameters.
Server-Sent Events stream of live task output. Replays buffered output on connect, then streams new events in real time. Sends keepalive comments every 15 seconds.
Event types
| Type | Description |
|---|---|
connected |
Initial connection confirmation |
run-started |
A run has begun (includes runId) |
run-output |
Stdout or stderr chunk (stream field) |
run-completed |
Run finished (includes exitCode) |
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
Serves the SvelteKit dashboard as a single-page application. Static files are
served from the built dashboard directory. Any path without a file extension
falls back to index.html for client-side routing.
If the dashboard build is not found, a minimal HTML fallback page is served with links to key API endpoints.