The REST API runs on FastAPI. No SDK required — just HTTP.

Start the Server

pip install pensyve uvicorn
uvicorn pensyve_server.main:app --reload

Server runs at http://localhost:3000. Hit /docs for the interactive Swagger UI.

Health Check

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

Remember a Fact

curl -X POST http://localhost:3000/v1/remember \
  -H "Content-Type: application/json" \
  -d '{
    "entity": "alice",
    "fact": "Prefers dark mode and vim keybindings",
    "confidence": 0.9
  }'
{
  "id": "a1b2c3d4-...",
  "content": "Prefers dark mode and vim keybindings",
  "memory_type": "semantic",
  "confidence": 0.9,
  "stability": 1.0,
  "score": null
}

confidence defaults to 0.8 if omitted.

Recall Memories

curl -X POST http://localhost:3000/v1/recall \
  -H "Content-Type: application/json" \
  -d '{
    "query": "editor preferences",
    "entity": "alice",
    "limit": 5
  }'
{
  "memories": [
    {
      "id": "a1b2c3d4-...",
      "content": "Prefers dark mode and vim keybindings",
      "memory_type": "semantic",
      "confidence": 0.9,
      "stability": 1.0,
      "score": 0.87
    }
  ],
  "contradictions": [],
  "cursor": null
}

Filtering and Pagination

# Only semantic memories
curl -X POST http://localhost:3000/v1/recall \
  -H "Content-Type: application/json" \
  -d '{"query": "vim", "types": ["semantic"], "limit": 10}'

# Cursor-based pagination — pass the cursor from the previous response
curl -X POST http://localhost:3000/v1/recall?cursor=a1b2c3d4-... \
  -H "Content-Type: application/json" \
  -d '{"query": "vim", "limit": 5}'

Recall Grouped (for LLM readers)

When you're feeding recalled memories to another LLM as context, use POST /v1/recall_grouped instead. The server runs the same RRF fusion pipeline and then clusters the top-limit memories by source episode in one round trip — saving you from rebuilding session blocks client-side.

curl -X POST http://localhost:3000/v1/recall_grouped \
  -H "Content-Type: application/json" \
  -d '{
    "query": "How many books did I buy?",
    "limit": 50,
    "order": "chronological"
  }'
{
  "groups": [
    {
      "session_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "session_time": "2026-01-01T10:00:00+00:00",
      "group_score": 0.92,
      "memories": [
        {
          "id": "a1b2c3d4-...",
          "content": "user: I bought three books yesterday",
          "memory_type": "episodic",
          "confidence": 1.0,
          "stability": 0.8,
          "score": 0.92
        }
      ]
    }
  ]
}

session_id is null for semantic and procedural memories — they surface as singleton groups so you can iterate the result uniformly. Set order to "relevance" to put the highest-scoring session first instead.

Episodes

Episodes track multi-turn interactions. Three-step flow: start, add messages, end.

Start

curl -X POST http://localhost:3000/v1/episodes/start \
  -H "Content-Type: application/json" \
  -d '{"participants": ["alice", "assistant"]}'
{ "episode_id": "e5f6a7b8-..." }

Add Messages

curl -X POST http://localhost:3000/v1/episodes/message \
  -H "Content-Type: application/json" \
  -d '{
    "episode_id": "e5f6a7b8-...",
    "role": "user",
    "content": "I always use dark mode"
  }'

curl -X POST http://localhost:3000/v1/episodes/message \
  -H "Content-Type: application/json" \
  -d '{
    "episode_id": "e5f6a7b8-...",
    "role": "assistant",
    "content": "Noted for future sessions"
  }'

End

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

Episodes expire after 30 minutes if not ended. outcome is optional — valid values are "success", "failure", or "partial".

Other Endpoints

Create Entity

curl -X POST http://localhost:3000/v1/entities \
  -H "Content-Type: application/json" \
  -d '{"name": "alice", "kind": "user"}'

Inspect

View all memories for an entity, grouped by type:

curl -X POST http://localhost:3000/v1/inspect \
  -H "Content-Type: application/json" \
  -d '{"entity": "alice", "limit": 50}'

Consolidate

Trigger memory promotion, decay, and archival:

curl -X POST http://localhost:3000/v1/consolidate
{ "promoted": 2, "decayed": 5, "archived": 1 }

Forget

# Soft delete (archive)
curl -X DELETE http://localhost:3000/v1/entities/alice

# Hard delete
curl -X DELETE "http://localhost:3000/v1/entities/alice?hard_delete=true"

Stats

curl http://localhost:3000/v1/stats
{
  "namespace": "default",
  "entities": 0,
  "episodic_memories": 12,
  "semantic_memories": 8,
  "procedural_memories": 3
}

Authentication

Auth is off by default. To enable it, set the PENSYVE_API_KEYS environment variable:

export PENSYVE_API_KEYS="key-one,key-two"
uvicorn pensyve_server.main:app --reload

Then include the key in every request:

curl -X POST http://localhost:3000/v1/recall \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer key-one" \
  -d '{"query": "preferences"}'

Requests without a valid key return 403.

Environment Variables

VariableDefaultPurpose
PENSYVE_PATH~/.pensyve/SQLite database location
PENSYVE_NAMESPACEdefaultMemory namespace
PENSYVE_API_KEYS(unset)Comma-separated API keys
PENSYVE_TIER2_ENABLEDfalseEnable LLM-based extraction