REST API

FastAPI server exposing the Pensyve memory runtime over HTTP.

uvicorn pensyve_server.main:app --reload

Base URL: http://localhost:8000


Authentication

Auth is opt-in. Set PENSYVE_API_KEYS to a comma-separated list of keys. When set, every request must include:

X-Pensyve-Key: your-api-key

When PENSYVE_API_KEYS is unset, all endpoints are open.


Pagination

Recall and inspect endpoints support cursor-based pagination. Pass cursor (a memory ID) to fetch the next page. The response includes a cursor field — null when there are no more results.


Endpoints

GET /v1/health

Health check.

Response:

{ "status": "ok", "version": "0.1.0" }
curl http://localhost:8000/v1/health

POST /v1/entities

Create or get an entity.

Request body:

FieldTypeDefaultDescription
namestringrequiredEntity name
kindstring"user""agent", "user", "team", or "tool"

Response: 200

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "alice",
  "kind": "user"
}
curl -X POST http://localhost:8000/v1/entities \
  -H "Content-Type: application/json" \
  -d '{"name": "alice", "kind": "user"}'

POST /v1/remember

Store a semantic memory about an entity.

Request body:

FieldTypeDefaultDescription
entitystringrequiredEntity name
factstringrequiredThe fact to store
confidencenumber0.8Confidence in [0, 1]

Response: 200

{
  "id": "a1b2c3d4-...",
  "content": "Alice prefers dark mode",
  "memory_type": "semantic",
  "confidence": 0.8,
  "stability": 1.0,
  "score": null
}
curl -X POST http://localhost:8000/v1/remember \
  -H "Content-Type: application/json" \
  -d '{"entity": "alice", "fact": "Alice prefers dark mode"}'

When PENSYVE_TIER2_ENABLED=true, additional facts are automatically extracted via LLM and stored alongside the explicit fact.


POST /v1/recall

Search memories. Fuses vector, BM25, graph, recency, and other signals.

Request body:

FieldTypeDefaultDescription
querystringrequiredSearch query
entitystring | nullnullFilter to a specific entity
limitinteger5Max results per page
typesstring[] | nullnullFilter by memory type: "episodic", "semantic", "procedural"

Query parameters:

ParamTypeDescription
cursorstring | nullMemory ID to paginate from

Response: 200

{
  "memories": [
    {
      "id": "a1b2c3d4-...",
      "content": "Alice prefers dark mode",
      "memory_type": "semantic",
      "confidence": 0.8,
      "stability": 1.0,
      "score": 0.87
    }
  ],
  "contradictions": [],
  "cursor": null
}

The contradictions array is populated only when Tier 2 extraction is enabled. Each entry is {"description": "..."}.

curl -X POST http://localhost:8000/v1/recall \
  -H "Content-Type: application/json" \
  -d '{"query": "dark mode preference", "entity": "alice", "limit": 10}'

POST /v1/episodes/start

Begin tracking an interaction episode.

Request body:

FieldTypeDefaultDescription
participantsstring[]requiredEntity names of the participants

Response: 200

{
  "episode_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

Episodes expire after 30 minutes of inactivity.

curl -X POST http://localhost:8000/v1/episodes/start \
  -H "Content-Type: application/json" \
  -d '{"participants": ["alice", "my-agent"]}'

POST /v1/episodes/message

Add a message to an active episode.

Request body:

FieldTypeDefaultDescription
episode_idstringrequiredEpisode ID from /v1/episodes/start
rolestringrequiredSpeaker role (e.g. "user", "assistant")
contentstringrequiredMessage text

Response: 200

{ "status": "ok" }

Error: 404 if the episode ID is not found or has expired.

curl -X POST http://localhost:8000/v1/episodes/message \
  -H "Content-Type: application/json" \
  -d '{
    "episode_id": "f47ac10b-...",
    "role": "user",
    "content": "What is the status of project X?"
  }'

POST /v1/episodes/end

Close an episode and extract memories.

Request body:

FieldTypeDefaultDescription
episode_idstringrequiredEpisode ID
outcomestring | nullnull"success", "failure", or "partial"

Response: 200

{
  "memories_created": 3
}

Error: 404 if the episode ID is not found.

curl -X POST http://localhost:8000/v1/episodes/end \
  -H "Content-Type: application/json" \
  -d '{"episode_id": "f47ac10b-...", "outcome": "success"}'

DELETE /v1/entities/{entity_name}

Archive or permanently delete all memories for an entity.

Path parameters:

ParamTypeDescription
entity_namestringEntity name

Query parameters:

ParamTypeDefaultDescription
hard_deletebooleanfalsePermanently delete instead of archiving

Response: 200

{
  "forgotten_count": 12
}
curl -X DELETE http://localhost:8000/v1/entities/alice

# Permanent deletion
curl -X DELETE "http://localhost:8000/v1/entities/alice?hard_delete=true"

POST /v1/inspect

View all memories for an entity, grouped by type.

Request body:

FieldTypeDefaultDescription
entitystringrequiredEntity name
limitinteger50Max results per page
cursorstring | nullnullMemory ID to paginate from

Response: 200

{
  "entity": "alice",
  "episodic": [
    {
      "id": "...",
      "content": "Asked about project X",
      "memory_type": "episodic",
      "confidence": 1.0,
      "stability": 0.95,
      "score": null
    }
  ],
  "semantic": [],
  "procedural": [],
  "cursor": null
}
curl -X POST http://localhost:8000/v1/inspect \
  -H "Content-Type: application/json" \
  -d '{"entity": "alice"}'

GET /v1/stats

Memory statistics for the current namespace.

Response: 200

{
  "namespace": "default",
  "entities": 0,
  "episodic_memories": 42,
  "semantic_memories": 15,
  "procedural_memories": 3
}
curl http://localhost:8000/v1/stats

POST /v1/consolidate

Trigger background consolidation. Promotes repeated episodic memories to semantic, applies FSRS decay, and archives memories below threshold.

Request body: None.

Response: 200

{
  "promoted": 3,
  "decayed": 12,
  "archived": 1
}
curl -X POST http://localhost:8000/v1/consolidate

Error Responses

All errors return JSON:

{
  "detail": "Episode f47ac10b-... not found"
}
StatusMeaning
400Invalid request body
401Missing or invalid X-Pensyve-Key
404Resource not found (episode, entity)
422Validation error (Pydantic)
500Internal server error

Environment Variables

VariableDefaultPurpose
PENSYVE_PATH~/.pensyve/SQLite database path
PENSYVE_NAMESPACE"default"Memory namespace
PENSYVE_API_KEYSunsetComma-separated API keys
PENSYVE_TIER2_ENABLED"false"Enable LLM-based Tier 2 extraction
PENSYVE_TIER2_MODEL_PATHunsetPath to GGUF model for Tier 2