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.

http
1X-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.

http
1x-user-id: your-supabase-user-id
2x-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.

POSThttps://jean-memory-api.onrender.com/mcp/messages/

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.

Loading diagram...

Metadata & Tags

The API supports powerful metadata tagging for memory segmentation and organization. This feature allows API users to categorize memories and perform sophisticated filtering for multi-tenant applications, project isolation, and context-aware search.

Adding Tagged Memories

Use the optional tags parameter in the add_memories tool to categorize your information. Tags should be an array of strings.

json
1{
2 "jsonrpc": "2.0",
3 "method": "tools/call",
4 "params": {
5 "name": "add_memories",
6 "arguments": {
7 "text": "Project Alpha uses React with TypeScript and requires daily standups at 9 AM",
8 "tags": ["work", "project-alpha", "meetings", "react"]
9 }
10 },
11 "id": "req-123"
12}

Filtering by Tags

Use the search_memory_v2 tool with the tags_filter parameter to find memories that contain specific tags.

json
1{
2 "jsonrpc": "2.0",
3 "method": "tools/call",
4 "params": {
5 "name": "search_memory_v2",
6 "arguments": {
7 "query": "meeting schedule",
8 "tags_filter": ["work", "project-alpha"]
9 }
10 },
11 "id": "req-124"
12}
⚡ Filtering Logic: AND

The filtering logic uses an AND condition. The search will only return memories that contain ALL of the tags specified in the tags_filter array.

Best Practices for Tagging

  • Consistency is Key: Use a consistent naming convention (e.g., lowercase, kebab-case: `project-alpha`).
  • Create Namespaces: Use prefixes to create logical groups (e.g., `proj:alpha`, `client:acme`, `source:slack`).
  • Be Descriptive: Keep tags short but clear. Limit to 3-5 tags per memory for optimal performance and clarity.
  • Plan for Queries: Design your tags based on how you plan to filter and retrieve the data later.

Available Tools

The API exposes powerful, high-performance tools for memory interaction. The exact tools and parameters available depend on your authentication method.

add_memories

Stores information in long-term memory. Use this to remember key facts, user preferences, or details from conversations.

Input Schema:

json
1{
2 "text": {
3 "type": "string",
4 "description": "Important information to remember about the user (facts, preferences, insights, etc.)"
5 },
6 "tags": {
7 "type": "array",
8 "items": { "type": "string" },
9 "description": "[API Users Only] Optional list of strings to categorize the memory (e.g., ['work', 'project-alpha'])."
10 }
11}

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:

json
1{
2 "query": {
3 "type": "string",
4 "description": "Keywords or phrases to search for"
5 },
6 "limit": {
7 "type": "integer",
8 "description": "Maximum number of results to return (default: 10)"
9 }
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:

json
1{
2 "query": {
3 "type": "string",
4 "description": "Keywords or phrases to search for"
5 },
6 "limit": {
7 "type": "integer",
8 "description": "Maximum number of results to return (default: 10)"
9 },
10 "tags_filter": {
11 "type": "array",
12 "items": { "type": "string" },
13 "description": "Optional. A list of tags to filter results. Returns only memories containing ALL specified tags."
14 }
15}

Example Payload:

json
1{
2 "jsonrpc": "2.0",
3 "method": "tools/call",
4 "params": {
5 "name": "search_memory_v2",
6 "arguments": {
7 "query": "database performance",
8 "tags_filter": ["work", "project-gamma", "backend"]
9 }
10 },
11 "id": "req-125"
12}

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:

json
1{
2 "search_query": {
3 "type": "string",
4 "description": "The complex, natural language question for deep analysis."
5 }
6}

Example Payload:

json
1{
2 "jsonrpc": "2.0",
3 "method": "tools/call",
4 "params": {
5 "name": "deep_memory_query",
6 "arguments": {
7 "search_query": "What is the philosophical throughline of my recent essays?"
8 }
9 },
10 "id": "req-126"
11}

Key Concepts for Developers

Understanding these architectural concepts is crucial for building reliable and performant applications with the Jean Memory API.

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 Error Response:

json
1{
2 "jsonrpc": "2.0",
3 "error": {
4 "code": -32602,
5 "message": "Invalid parameters for tool 'add_memories': 1 validation error for ToolInput\narguments\n text\n Field required [type=missing, ...]"
6 },
7 "id": "req-123"
8}

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.

Example Flow:
  1. Agent adds memory for Client A: `add_memories(text: "...", tags: ["client:acme", "user:123"])`
  2. Agent adds memory for Client B: `add_memories(text: "...", tags: ["client:globex", "user:456"])`
  3. 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.

Example Flow:
  1. Agent ingests a meeting summary: `add_memories(text: "...", tags: ["proj:phoenix", "sprint:3", "meeting-notes"])`
  2. A developer asks: "What were the action items from the last Project Phoenix sprint 3 meeting?"
  3. 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.

python
1import requests
2import json
3import os
4import time
5
6API_KEY = os.environ.get("JEAN_API_KEY")
7API_URL = "https://jean-memory-api.onrender.com/mcp/messages/"
8
9def call_jean_api(payload):
10 """Helper function to call the Jean Memory API."""
11 if not API_KEY:
12 raise ValueError("JEAN_API_KEY environment variable not set!")
13
14 headers = {
15 "X-Api-Key": API_KEY,
16 "Content-Type": "application/json"
17 }
18
19 try:
20 response = requests.post(API_URL, headers=headers, data=json.dumps(payload))
21 response.raise_for_status()
22 return response.json()
23 except requests.exceptions.RequestException as e:
24 print(f"API call failed: {e}")
25 return None
26
27"text-slate-500"># 1. Add a memory with tags
28print("Adding a memory for 'project-zeta'...")
29add_payload = {
30 "jsonrpc": "2.0", "id": 1, "method": "tools/call",
31 "params": {
32 "name": "add_memories",
33 "arguments": {
34 "text": "The database for project-zeta needs to be migrated to Postgres 15.",
35 "tags": ["work", "project-zeta", "database", "backend"]
36 }
37 }
38}
39add_result = call_jean_api(add_payload)
40print("Add result:", add_result)
41
42"text-slate-500"># Wait for indexing
43print("\nWaiting 5 seconds for indexing...")
44time.sleep(5)
45
46"text-slate-500"># 2. Search for the memory using a tag filter
47print("Searching for memories tagged with 'project-zeta' and 'database'...")
48search_payload = {
49 "jsonrpc": "2.0", "id": 2, "method": "tools/call",
50 "params": {
51 "name": "search_memory_v2",
52 "arguments": {
53 "query": "database migration",
54 "tags_filter": ["project-zeta", "database"]
55 }
56 }
57}
58search_results = call_jean_api(search_payload)
59print("\nSearch results:")
60print(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).

python
1"text-slate-500"># (Assumes helper function 'call_jean_api' from previous example)
2
3"text-slate-500"># --- Step 1: An agent ingests memories from different sources ---
4print("--- Step 1: Ingesting memories from Slack and Jira ---")
5call_jean_api({
6 "jsonrpc": "2.0", "id": 1, "method": "tools/call",
7 "params": { "name": "add_memories", "arguments": {
8 "text": "User 'dave' in ">#bugs: 'Can't reset my password, the link is broken.'",
9 "tags": ["source:slack", "bug-report", "auth-flow"]
10 }}
11})
12call_jean_api({
13 "jsonrpc": "2.0", "id": 2, "method": "tools/call",
14 "params": { "name": "add_memories", "arguments": {
15 "text": "Jira Ticket JIRA-123 created for 'Password reset link broken'. Assigned to eng-team.",
16 "tags": ["source:jira", "ticket:JIRA-123", "auth-flow", "status:open"]
17 }}
18})
19print("Memories added.")
20time.sleep(5) "text-slate-500"># Wait for indexing
21
22"text-slate-500"># --- Step 2: A developer's agent seeks context on the Jira ticket ---
23print("\n--- Step 2: Agent searching for all context related to 'auth-flow' ---")
24search_payload = {
25 "jsonrpc": "2.0", "id": 3, "method": "tools/call",
26 "params": {
27 "name": "search_memory_v2",
28 "arguments": {
29 "query": "password reset",
30 "tags_filter": ["auth-flow"] "text-slate-500"># Find all related memories
31 }
32 }
33}
34search_results = call_jean_api(search_payload)
35
36"text-slate-500"># --- Step 3: The agent synthesizes the results for the developer ---
37print("\n--- Step 3: Agent synthesizing response ---")
38if search_results and search_results.get('result', {}).get('results'):
39 context = ""
40 for res in search_results['result']['results']:
41 source = "N/A"
42 if res.get('metadata', {}).get('tags'):
43 for tag in res['metadata']['tags']:
44 if tag.startswith('source:'):
45 source = tag.split(':')[1]
46 context += f"- [{source.upper()}] {res['text']}\n"
47
48 final_answer = f"""
49Here is the full context for the password reset issue:
50
51{context}
52This combines the original user report from Slack with the formal Jira ticket details.
53"""
54 print(final_answer)
55else:
56 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

bash
1curl -X POST https://jean-memory-api.onrender.com/mcp/messages/ \
2 -H "X-Api-Key: YOUR_API_KEY" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "jsonrpc": "2.0",
6 "method": "tools/call",
7 "params": {
8 "name": "add_memories",
9 "arguments": {
10 "text": "The Q3 financial report is due on October 15th.",
11 "tags": ["work", "finance", "deadline", "q3-report"]
12 }
13 },
14 "id": "curl-add-1"
15 }'

Search with a Tag Filter

bash
1curl -X POST https://jean-memory-api.onrender.com/mcp/messages/ \
2 -H "X-Api-Key: YOUR_API_KEY" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "jsonrpc": "2.0",
6 "method": "tools/call",
7 "params": {
8 "name": "search_memory_v2",
9 "arguments": {
10 "query": "financial report",
11 "tags_filter": ["finance", "deadline"]
12 }
13 },
14 "id": "curl-search-1"
15 }'