Jean Memory API
Build personal or enterprise agents with human-like memory
Live API Walkthrough
See how to use metadata tags to store and retrieve context-specific memories.
Welcome to the Jean Memory API documentation. This API provides a robust, unified memory layer for your AI applications, featuring secure API key authentication and powerful metadata filtering for advanced use cases.
Production Ready: Zero breaking changes, dual-path authentication, and enterprise-grade security.
Authentication
The API supports a dual-path authentication system to ensure both maximum flexibility for new developers and 100% backward compatibility for existing integrations like Claude Desktop.
Option 1: API Key (Recommended for API Users)
For programmatic access, include your API key in the X-Api-Key
header. This unlocks advanced features like metadata tagging and filtering.
X-Api-Key: jean_sk_s8ad0fI7x2VD2KnyLewH0e3ajuRV_1mdWGgnsBJ6VA8
Option 2: Headers (Claude Desktop & UI)
Used internally by existing integrations. Requires both x-user-id
and x-client-name
headers. This path serves a simplified toolset to ensure stability.
x-user-id: your-supabase-user-id
x-client-name: your-app-name
Unified API Endpoint
All interactions for both authentication methods use a single, unified MCP endpoint. This endpoint accepts POST requests with a JSON-RPC 2.0 payload to execute memory tools.
Architecture Diagram
This diagram illustrates how requests from different clients are routed through the unified endpoint to their respective tool schemas. Click the diagram to expand.
Available Tools
The API exposes powerful, high-performance tools for memory interaction. The exact tools and parameters available depend on your authentication method.
Client-Specific Tool Schemas
- API Users (
X-Api-Key
): Receive the full, advanced toolset includingsearch_memory_v2
and optionaltags
parameters. - Claude Desktop & UI Users: Receive a simpler, reliable set of tools without metadata parameters to prevent issues in legacy clients.
add_memories
Stores information in long-term memory. Use this to remember key facts, user preferences, or details from conversations.
Important: Non-Deterministic Behavior
Input Schema:
{
"text": {
"type": "string",
"description": "Important information to remember about the user (facts, preferences, insights, etc.)"
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "[API Users Only] Optional list of strings to categorize the memory (e.g., ['work', 'project-alpha'])."
}
}
search_memory
Performs a semantic search over memories. This is the standard search tool available to all clients and does not support tag filtering.
Input Schema:
{
"query": {
"type": "string",
"description": "Keywords or phrases to search for"
},
"limit": {
"type": "integer",
"description": "Maximum number of results to return (default: 10)"
}
}
search_memory_v2 API Users
An enhanced search tool that allows for powerful filtering by tags. This is the recommended search tool for all new development using API keys.
Input Schema:
{
"query": {
"type": "string",
"description": "Keywords or phrases to search for"
},
"limit": {
"type": "integer",
"description": "Maximum number of results to return (default: 10)"
},
"tags_filter": {
"type": "array",
"items": { "type": "string" },
"description": "Optional. A list of tags to filter results. Returns only memories containing ALL specified tags."
}
}
Example Payload:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "search_memory_v2",
"arguments": {
"query": "database performance",
"tags_filter": ["work", "project-gamma", "backend"]
}
},
"id": "req-125"
}
list_memories
Browse recently stored memories. Useful for getting a quick overview or for confirming a memory was received before it is fully indexed for search.
ask_memory
Provides a fast, conversational answer to a natural language question. Optimized for speed and should be preferred for simple queries.
deep_memory_query
A comprehensive, slow search that analyzes full documents. Use this for deep analysis that requires synthesizing information from large bodies of text. This is by far my favorite and most powerful tool.
Input Schema:
{
"search_query": {
"type": "string",
"description": "The complex, natural language question for deep analysis."
}
}
Example Payload:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "deep_memory_query",
"arguments": {
"search_query": "What is the philosophical throughline of my recent essays?"
}
},
"id": "req-126"
}
Key Concepts for Developers
Understanding these architectural concepts is crucial for building reliable and performant applications with the Jean Memory API.
Core Concept: Asynchronous Indexing
add_memories
, it is ingested immediately but is **not instantly searchable**. The memory enters a queue to be processed, embedded, and indexed into the vector database. This process is highly optimized but can take a few seconds.- Best Practice: Decouple write and read operations. Do not design workflows that add a memory and then immediately search for it. Assume an indexing delay.
- Confirmation: Use
list_memories
to confirm a memory was *received*, as it often shows entries before they are fully indexed for search.
Error Handling
The API uses standard HTTP status codes and provides detailed JSON-RPC error objects. We perform robust parameter validation on all tool calls.
Example: Invalid Parameters (HTTP 422)
HTTP 422 Unprocessable Entity
status with a -32602
error code in the body to help you debug.Example Error Response:
{
"jsonrpc": "2.0",
"error": {
"code": -32602,
"message": "Invalid parameters for tool 'add_memories': 1 validation error for ToolInput\narguments\n text\n Field required [type=missing, ...]"
},
"id": "req-123"
}
Example Use Cases for Tagging
Metadata tagging unlocks powerful workflows for sophisticated AI applications. Here are a few examples.
🏢 Multi-Tenant Applications
Isolate data between different customers or users within your application. By tagging each memory with a unique `client_id`, you can ensure that searches for one client never return data from another.
- Agent adds memory for Client A: `add_memories(text: "...", tags: ["client:acme", "user:123"])`
- Agent adds memory for Client B: `add_memories(text: "...", tags: ["client:globex", "user:456"])`
- When serving Client A, agent searches: `search_memory_v2(query: "...", tags_filter: ["client:acme"])`
📋 Project Management Agent
Build an agent that helps teams stay organized. Tag memories with project names, sprint numbers, and task types to create a searchable knowledge base for each project.
- Agent ingests a meeting summary: `add_memories(text: "...", tags: ["proj:phoenix", "sprint:3", "meeting-notes"])`
- A developer asks: "What were the action items from the last Project Phoenix sprint 3 meeting?"
- Agent searches: `search_memory_v2(query: "action items", tags_filter: ["proj:phoenix", "sprint:3"])`
Python Examples
Here's how to interact with the API using Python. Ensure your API key is set as an environment variable (`JEAN_API_KEY`).
Basic Example: Add and Search
A simple demonstration of adding a tagged memory and then retrieving it with a filtered search.
import requests
import json
import os
import time
API_KEY = os.environ.get("JEAN_API_KEY")
API_URL = "https://jean-memory-api.onrender.com/mcp/messages/"
def call_jean_api(payload):
"""Helper function to call the Jean Memory API."""
if not API_KEY:
raise ValueError("JEAN_API_KEY environment variable not set!")
headers = {
"X-Api-Key": API_KEY,
"Content-Type": "application/json"
}
try:
response = requests.post(API_URL, headers=headers, data=json.dumps(payload))
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"API call failed: {e}")
return None
"text-slate-500"># 1. Add a memory with tags
print("Adding a memory for 'project-zeta'...")
add_payload = {
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {
"name": "add_memories",
"arguments": {
"text": "The database for project-zeta needs to be migrated to Postgres 15.",
"tags": ["work", "project-zeta", "database", "backend"]
}
}
}
add_result = call_jean_api(add_payload)
print("Add result:", add_result)
"text-slate-500"># Wait for indexing
print("\nWaiting 5 seconds for indexing...")
time.sleep(5)
"text-slate-500"># 2. Search for the memory using a tag filter
print("Searching for memories tagged with 'project-zeta' and 'database'...")
search_payload = {
"jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": {
"name": "search_memory_v2",
"arguments": {
"query": "database migration",
"tags_filter": ["project-zeta", "database"]
}
}
}
search_results = call_jean_api(search_payload)
print("\nSearch results:")
print(json.dumps(search_results, indent=2))
Advanced Example: Cross-Source Context Agent
A more complex workflow where an agent uses shared, tagged memory to provide context on an issue by combining information from different sources (e.g., Slack and Jira).
"text-slate-500"># (Assumes helper function 'call_jean_api' from previous example)
"text-slate-500"># --- Step 1: An agent ingests memories from different sources ---
print("--- Step 1: Ingesting memories from Slack and Jira ---")
call_jean_api({
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": { "name": "add_memories", "arguments": {
"text": "User 'dave' in ">#bugs: 'Can't reset my password, the link is broken.'",
"tags": ["source:slack", "bug-report", "auth-flow"]
}}
})
call_jean_api({
"jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": { "name": "add_memories", "arguments": {
"text": "Jira Ticket JIRA-123 created for 'Password reset link broken'. Assigned to eng-team.",
"tags": ["source:jira", "ticket:JIRA-123", "auth-flow", "status:open"]
}}
})
print("Memories added.")
time.sleep(5) "text-slate-500"># Wait for indexing
"text-slate-500"># --- Step 2: A developer's agent seeks context on the Jira ticket ---
print("\n--- Step 2: Agent searching for all context related to 'auth-flow' ---")
search_payload = {
"jsonrpc": "2.0", "id": 3, "method": "tools/call",
"params": {
"name": "search_memory_v2",
"arguments": {
"query": "password reset",
"tags_filter": ["auth-flow"] "text-slate-500"># Find all related memories
}
}
}
search_results = call_jean_api(search_payload)
"text-slate-500"># --- Step 3: The agent synthesizes the results for the developer ---
print("\n--- Step 3: Agent synthesizing response ---")
if search_results and search_results.get('result', {}).get('results'):
context = ""
for res in search_results['result']['results']:
source = "N/A"
if res.get('metadata', {}).get('tags'):
for tag in res['metadata']['tags']:
if tag.startswith('source:'):
source = tag.split(':')[1]
context += f"- [{source.upper()}] {res['text']}\n"
final_answer = f"""
Here is the full context for the password reset issue:
{context}
This combines the original user report from Slack with the formal Jira ticket details.
"""
print(final_answer)
else:
print("Could not find any context for that ticket.")
cURL Examples
You can also interact with the API directly from your terminal using cURL.
Add a Memory with Tags
curl -X POST https://jean-memory-api.onrender.com/mcp/messages/ \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "add_memories",
"arguments": {
"text": "The Q3 financial report is due on October 15th.",
"tags": ["work", "finance", "deadline", "q3-report"]
}
},
"id": "curl-add-1"
}'
Search with a Tag Filter
curl -X POST https://jean-memory-api.onrender.com/mcp/messages/ \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "search_memory_v2",
"arguments": {
"query": "financial report",
"tags_filter": ["finance", "deadline"]
}
},
"id": "curl-search-1"
}'