supermemory Documentation

Memory and context engine + app that is extremely fast, scalable, and can be run fully locally. The Memory API for the AI era.

RAW Doc

Technical Documentation: supermemoryai/supermemory

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (README.md)


Supermemory is the memory and context layer for AI. #1 on LongMemEval, LoCoMo, and ConvoMem — the three major benchmarks for AI memory.

We are a research lab building the engine, plugins and tools around it.

Your AI forgets everything between conversations. Supermemory fixes that.

It automatically learns from conversations, extracts facts, builds user profiles, handles knowledge updates and contradictions, forgets expired information, and delivers the right context at the right time. Full RAG, connectors, file processing — the entire context stack, one system.

🧠 Memory Extracts facts from conversations. Handles temporal changes, contradictions, and automatic forgetting.
👤 User Profiles Auto-maintained user context — stable facts + recent activity. One call, ~50ms.
🔍 Hybrid Search RAG + Memory in a single query. Knowledge base docs and personalized context together.
🔌 Connectors Google Drive · Gmail · Notion · OneDrive · GitHub — auto-sync with real-time webhooks.
📄 Multi-modal Extractors PDFs, images (OCR), videos (transcription), code (AST-aware chunking). Upload and it works.

All of this is in our single memory structure and ontology.


Use Supermemory

| 🧑‍💻 I use AI tools

Give Claude Code, Muse Code, Cursor, Codex and OpenCode persistent memory across every conversation with a plugin or the MCP server.

Your AI remembers your preferences, projects, past discussions — and gets smarter over time.

→ Jump to User setup | 🔧 I'm building AI products

Add memory, RAG, user profiles, and connectors to your agents and apps with a single API.

No vector DB config. No embedding pipelines. No chunking strategies.

→ Jump to developer quickstart |
| --- | --- |
| 🖥️ I want to run it myself

State-of-the-art memory, on your machine. One binary. Zero config. Bring any model — or run fully offline with Ollama.

bash
curl -fsSL https://supermemory.ai/install | bash

→ Jump to Supermemory local | |


Give your AI memory

Plugins and the MCP server give any compatible AI assistant persistent memory. One install, and your AI remembers you.

Supermemory Plugins

Supermemory comes built with plugins for Claude Code, Muse Code, Cursor, Codex, OpenCode, OpenClaw, and Hermes.

These plugins are implementations of the supermemory API, and they are open source!

You can find them here:

MCP

Server URL:

text
https://mcp.supermemory.ai/mcp
json
{
  "mcpServers": {
    "supermemory": {
      "url": "https://mcp.supermemory.ai/mcp"
    }
  }
}

Read more about our MCP here - https://supermemory.ai/docs/supermemory-mcp/mcp

What your AI gets

Tool What it does
memory Save or forget information. Your AI calls this automatically when you share something worth remembering.
recall Search memories by query. Returns relevant memories + your user profile summary.
context Injects your full profile (preferences, recent activity) into the conversation at start. In Cursor and Claude Code, just type /context.

How it works

Once installed, Supermemory runs in the background:

  1. You talk to your AI normally. Share preferences, mention projects, discuss problems.
  2. Supermemory extracts and stores the important stuff. Facts, preferences, project context — not noise.
  3. Next conversation, your AI already knows you. It recalls what you're working on, how you like things, what you discussed before.

Memory is scoped with projects (container tags) so you can separate work and personal context, or organize by client, repo, or anything else.

Supported clients

Claude Desktop · Cursor · Windsurf · VS Code · Claude Code · OpenCode · OpenClaw · Hermes

The MCP server is open source — view the source.

Manual configuration

Add this to your MCP client config:

json
{
  "mcpServers": {
    "supermemory": {
      "url": "https://mcp.supermemory.ai/mcp"
    }
  }
}

Build with Supermemory (API)

If you're building AI agents or apps, Supermemory gives you the entire context stack through one API — memory, RAG, user profiles, connectors, and file processing.

Install

bash
npm install supermemory    # or: pip install supermemory

Quickstart

typescript
import Supermemory from "supermemory";

const client = new Supermemory();

// Store a conversation
await client.add({
  content: "User loves TypeScript and prefers functional patterns",
  containerTag: "user_123",
});

// Get user profile + relevant memories in one call
const { profile, searchResults } = await client.profile({
  containerTag: "user_123",
  q: "What programming style does the user prefer?",
});

// profile.static  → ["Loves TypeScript", "Prefers functional patterns"]
// profile.dynamic → ["Working on API integration"]
// searchResults   → Relevant memories ranked by similarity
python
from supermemory import Supermemory

client = Supermemory()

client.add(
    content="User loves TypeScript and prefers functional patterns",
    container_tag="user_123"
)

result = client.profile(container_tag="user_123", q="programming style")

print(result.profile.static)   # Long-term facts
print(result.profile.dynamic)  # Recent context

Supermemory automatically extracts memories, builds user profiles, and returns relevant context. No embedding pipelines, no vector DB config, no chunking strategies.

Framework integrations

Drop-in wrappers for every major AI framework:

typescript
// Vercel AI SDK
import { withSupermemory } from "@supermemory/tools/ai-sdk";
const model = withSupermemory(openai("gpt-4o"), { containerTag: "user_123", customId: "conv-1" });

// Mastra
import { withSupermemory } from "@supermemory/tools/mastra";
const agent = new Agent(withSupermemory(config, "user-123", { mode: "full" }));

Vercel AI SDK · LangChain · LangGraph · OpenAI Agents SDK · Mastra · Agno · Claude Memory Tool · n8n

Search modes

typescript
// Hybrid (default) — RAG + Memory in one query
const results = await client.search({
  q: "how do I deploy?",
  containerTag: "user_123",
  searchMode: "hybrid",
});
// Returns deployment docs (RAG) + user's deploy preferences (Memory)

// Memories only
const results = await client.search({
  q: "user preferences",
  containerTag: "user_123",
  searchMode: "memories",
});

User profiles

Traditional memory relies on search — you need to know what to ask for. Supermemory automatically maintains a profile for every user:

typescript
const { profile } = await client.profile({ containerTag: "user_123" });

// profile.static  → ["Senior engineer at Acme", "Prefers dark mode", "Uses Vim"]
// profile.dynamic → ["Working on auth migration", "Debugging rate limits"]

One call. ~50ms. Inject into your system prompt and your agent instantly knows who it's talking to.

Connectors

Auto-sync external data into your knowledge base:

Google Drive · Gmail · Notion · OneDrive · GitHub · Web Crawler

Real-time webhooks. Documents automatically processed, chunked, and searchable.

API at a glance

Method Purpose
client.add() Store content — text, conversations, URLs, HTML
client.profile() User profile + optional search in one call
client.search() Hybrid search across memories and documents (searchMode)
client.search.documents() Document search with metadata filters (legacy v3 response shape)
client.documents.uploadFile() Upload PDFs, images, videos, code
client.documents.list() List and filter documents
client.settings.update() Configure memory extraction and chunking

Full API reference → supermemory.ai/docs


Supermemory local — run it yourself

State-of-the-art memory, on your machine. One binary. Zero config.

bash
curl -fsSL https://supermemory.ai/install | bash
# or
npx supermemory local
bash
supermemory-server

First boot sets up the embedded Supermemory graph engine, local embeddings, and your credentials, then prints an API key. The full Memory API — documents, memories, user profiles, hybrid search — runs against http://localhost:6767.

typescript
const client = new Supermemory({
  apiKey: "sm_...",
  baseURL: "http://localhost:6767", // that's the only change
});
  • Bring any model — OpenAI, Anthropic, Gemini, Groq, or any OpenAI-compatible endpoint. An interactive wizard walks you through it on first boot.
  • Embeddings — local Xenova/bge-base-en-v1.5 by default (no API key); optionally OpenAI, Gemini, or Ollama. Same provider stack as cloud.
  • Offline text memories if you want — point it at Ollama (gpt-oss:20b works great), disable telemetry with SUPERMEMORY_DISABLE_TELEMETRY=1, and use local text or file inputs after the first embedding-model download. URL ingestion uses a hosted reader service.
  • Your data, one directory — everything lives in ~/.supermemory, easy to back up or move.
  • Same API as the platform — prototype locally, ship on the hosted platform by changing baseURL.

Run with Docker

Prebuilt images support Linux amd64 and arm64. You don’t need to build or clone the server source.

For a new installation, run these commands in an empty directory and set your own LLM provider key:

bash
release_url=https://github.com/supermemoryai/supermemory/releases
curl -fL "$release_url/download/server-v0.0.9/compose.yaml" -o compose.yaml
export OPENAI_API_KEY=your_openai_api_key_here
export SUPERMEMORY_VERSION=0.0.9
docker compose up -d --wait --wait-timeout 180

The API listens on http://localhost:6767. Data and credentials persist in the supermemory_supermemory-data volume. Get your local API key with docker compose exec supermemory cat /data/api-key; requests require it even on localhost.

Read the Docker setup and upgrade instructions for persistent provider configuration, backups and upgrades. Keep the existing volume and keys when upgrading; don’t run docker compose down -v.

Read the self-hosting docs — quickstart, configuration, embeddings, and local vs. Enterprise.


Benchmarks

Supermemory is state of the art across all major AI memory benchmarks:

Benchmark What it measures Result
LongMemEval Long-term memory across sessions with knowledge updates #1
LoCoMo Fact recall across extended conversations (single-hop, multi-hop, temporal, adversarial) #1
ConvoMem Personalization and preference learning #1

On LongMemEval, supermemory reaches 95% Recall@15 while adding only ~720 tokens of context — a 99.4% context reduction (99.6% at @10, 99.8% at @5). Recall by category: Knowledge Updates 99%, Assistant recall 100%, User recall 97%, Multi-session 93%, Temporal Reasoning 91%, Preference 90%.

We also built the Supermemory Filesystem (SMFS), which uses 3.0× fewer tokens on Claude (24M vs 72M) and 1.75× fewer on Codex across the 110-question xAFS benchmark. See the full write-ups on our research page.

We also built MemoryBench — an open-source framework for standardized, reproducible benchmarks of memory providers. Compare Supermemory, Mem0, Zep, and others head-to-head:

bash
bun run src/index.ts run -p supermemory -b longmemeval -j gpt-4o -r my-run

Benchmarking your own memory solution

We provide an Agent skill for companies to benchmark their own context and memory solutions against supermemory.

text
npx skills add supermemoryai/memorybench

Simply run this and do /benchmark-context - Supermemory will automatically do the work for you!


How memory works under the hood

text
Your app / AI tool
        ↓
   Supermemory
        │
        ├── Memory Engine     Extracts facts, tracks updates, resolves contradictions,
        │                     auto-forgets expired info
        ├── User Profiles     Static facts + dynamic context built from engine, always fresh
        ├── Hybrid Search     RAG + Memory in one query
        ├── Connectors        Real-time sync from Google Drive, Gmail, Notion, GitHub...
        └── File Processing   PDFs, images, videos, code → searchable chunks

Memory is not RAG. RAG retrieves document chunks — stateless, same results for everyone. Memory extracts and tracks facts about users over time. It understands that "I just moved to SF" supersedes "I live in NYC." Supermemory runs both together by default, so you get knowledge base retrieval and personalized context in every query. Read more about this here - https://supermemory.ai/docs/concepts/memory-vs-rag

Automatic forgetting. Supermemory knows when memories become irrelevant. Temporary facts ("I have an exam tomorrow") expire after the date passes. Contradictions are resolved automatically. Noise never becomes permanent memory.




Apps Docs Index (`apps/docs/index.mdx`)


title: "Introduction" mode: "custom"

import { AGENT_PROMPT } from "/snippets/agent-prompt.jsx"
import { HERO_TS, HERO_PY, HERO_CURL } from "/snippets/hero-samples.jsx"
import { AgentMarks, CopyPromptButton, PromptPanel } from "/snippets/agent-prompt-panel.jsx"

export const HomeLink = ({ href, icon, children }) => (

text
{children}
)

export const PathCard = ({ title, description, href, image, children }) => (

text
<a className="sm-home-card-media" href={href} aria-label={title}>
  
</a>

  <a className="sm-home-card-title" href={href}>{title}</a>
  {description}
  {children}

)

export const ListCard = ({ title, children }) => (

text
{title}
  {children}

)

text
<h1 className="sm-home-title sm-home-title-split">
    Memory and continual learning for the world’s best agents.
  </h1>
  
    Supermemory gives your agent long-term memory of every user. Paste one prompt into your coding agent and it wires memory into your project.
  
  
    <CopyPromptButton prompt={AGENT_PROMPT} label="Copy prompt" copiedLabel="Copied" className="sm-home-btn sm-home-btn-primary" />
    [Read the quickstart](/quickstart)
  
  <AgentMarks />
Platform

Choose how you use supermemory

Start with the path that fits your app and your team.
text
<HomeLink href="/quickstart" icon="/icons/hugeicons/play-circle.svg">Quickstart</HomeLink>
    <HomeLink href="/concepts/how-it-works" icon="/icons/hugeicons/layers-01.svg">How it works</HomeLink>
    <HomeLink href="/api-reference/overview" icon="/icons/hugeicons/source-code.svg">API reference</HomeLink>
  
  
    <HomeLink href="/supermemory-mcp/mcp" icon="/icons/hugeicons/plug-01.svg">Supermemory MCP</HomeLink>
    <HomeLink href="/integrations/claude-code" icon="/icons/hugeicons/command-line.svg">Claude Code</HomeLink>
    <HomeLink href="/integrations/cursor" icon="/icons/hugeicons/cursor-01.svg">Cursor</HomeLink>
  
  
    <HomeLink href="/self-hosting/quickstart" icon="/icons/hugeicons/play-circle.svg">Local quickstart</HomeLink>
    <HomeLink href="/self-hosting/configuration" icon="/icons/hugeicons/sliders-horizontal.svg">Configuration</HomeLink>
    <HomeLink href="/self-hosting/providers" icon="/icons/hugeicons/server-stack-01.svg">Model providers</HomeLink>
Products

Explore features

Browse memory, retrieval, profiles, and connectors.
text
<ListCard title="Memory and retrieval">
    <HomeLink href="/concepts/graph-memory" icon="/icons/hugeicons/brain-01.svg">Graph memory</HomeLink>
    <HomeLink href="/concepts/super-rag" icon="/icons/hugeicons/search-01.svg">SuperRAG</HomeLink>
    <HomeLink href="/recall/search" icon="/icons/hugeicons/file-search.svg">Search memories</HomeLink>
    <HomeLink href="/concepts/user-profiles" icon="/icons/hugeicons/user.svg">User profiles</HomeLink>
    <HomeLink href="/concepts/filtering" icon="/icons/hugeicons/filter.svg">Filtering</HomeLink>
    <HomeLink href="/concepts/memory-vs-rag" icon="/icons/hugeicons/balance-scale.svg">Memory vs RAG</HomeLink>
  </ListCard>
  <ListCard title="Build and connect">
    <HomeLink href="/ingestion/add-memories" icon="/icons/hugeicons/plus-sign.svg">Add memories</HomeLink>
    <HomeLink href="/connectors/overview" icon="/icons/hugeicons/plug-01.svg">Connectors</HomeLink>
    <HomeLink href="/smfs/overview" icon="/icons/hugeicons/folder-01.svg">SMFS</HomeLink>
    <HomeLink href="/concepts/container-tags" icon="/icons/hugeicons/tag-01.svg">Namespaces</HomeLink>
    <HomeLink href="/concepts/customization" icon="/icons/hugeicons/sliders-horizontal.svg">Customization</HomeLink>
    <HomeLink href="/migration/from-mem0" icon="/icons/hugeicons/arrow-data-transfer-horizontal.svg">Migrate from mem0</HomeLink>
  </ListCard>

Apps Docs Quickstart (`apps/docs/quickstart.mdx`)


title: "Quickstart" icon: "/icons/hugeicons/play.svg"

By the end of this page you will:

  1. Ingest a conversation (how personal memory actually arrives)
  2. Ingest a document (how knowledge for RAG arrives)
  3. Retrieve three ways — document search (RAG), memory graph traversal, and user profile
  4. Drop it into a chat harness that remembers across restarts

Same namespace for everything. One engine, three ways out. A namespace is what v3/v4 called a container tag.

Get an API key

Grab a key from the developer console — API Keys → Create API Key.

console.supermemory.ai is where keys and usage live.

```bash TypeScript npm i supermemory export SUPERMEMORY_API_KEY="sm_..." ```
bash python
pip install supermemory
export SUPERMEMORY_API_KEY="sm_..."
bash curl
export SUPERMEMORY_API_KEY="sm_..."

1. Ingest a conversation

Real apps do not push four isolated one-liners as separate “memories.” They send conversation turns — often the full session — under a stable id so the pipeline can extract facts and link entities.

We’ll use one user (user_4f8a) and one chat session. The turns never say “Sarah is my VP of Product” — that connection is what the graph should resolve later.

```typescript TypeScript import { Supermemory } from "supermemory";

const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
const user = "user_4f8a"; // the namespace

const conversation = user: Just got back from Tokyo — the team offsite went great. assistant: Glad it went well! Anything stand out? user: Sarah presented the Q3 roadmap at the offsite. assistant: Sounds like a big moment for her. user: She's being promoted to VP of Product. assistant: Congrats to Sarah — that's huge. user: I need a gift idea for my VP of Product. assistant: Happy to help brainstorm something personal..trim();

const conv = await supermemory.add(user, {
content: conversation,
id: "chat_offsite_2026", // one session → one document
metadata: { type: "conversation" },
dreaming: "instant", // process this document now — see note below
});

console.log(conv.id, conv.status); // e.g. "queued"

text
```python Python
from supermemory import Supermemory

client = Supermemory()
user = "user_4f8a"

conversation = """
user: Just got back from Tokyo — the team offsite went great.
assistant: Glad it went well! Anything stand out?
user: Sarah presented the Q3 roadmap at the offsite.
assistant: Sounds like a big moment for her.
user: She's being promoted to VP of Product.
assistant: Congrats to Sarah — that's huge.
user: I need a gift idea for my VP of Product.
assistant: Happy to help brainstorm something personal.
""".strip()

conv = client.add(
    user,
    content=conversation,
    id="chat_offsite_2026",  # one session → one document
    metadata={"type": "conversation"},
    dreaming="instant",  # process this document now, see note below
)
print(conv.id, conv.status)  # e.g. "queued"
bash curl
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/document" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "user: Just got back from Tokyo — the team offsite went great.\nassistant: Glad it went well! Anything stand out?\nuser: Sarah presented the Q3 roadmap at the offsite.\nassistant: Sounds like a big moment for her.\nuser: She is being promoted to VP of Product.\nassistant: Congrats to Sarah — that is huge.\nuser: I need a gift idea for my VP of Product.\nassistant: Happy to help brainstorm something personal.",
    "id": "chat_offsite_2026",
    "metadata": { "type": "conversation" },
    "dreaming": "instant"
  }'

add returns immediately with status: "queued". Processing is still async — wait until done before searching.

**`dreaming: "instant"`** — By default, dreaming is `"dynamic"`: Supermemory batches related documents so memories form from coherent units, so a fresh namespace can show zero memories and an empty profile for several minutes. For this quickstart (and any path where you need memories/profiles right away), pass **`dreaming: "instant"`** so the document is processed on its own as soon as it finishes indexing. That bills one extra operation per document. See [Processing Modes](/ingestion/add-memories#processing-modes).

2. Ingest a document

Now add knowledge the agent should ground on — a short internal note the conversation never fully spelled out. This is the SuperRAG / document path.

```typescript TypeScript const handbook = ` # Team notes — gifts & recognition

When someone is promoted to VP or above, the company recommends a thoughtful gift
in the $75–$150 range. Experiences tied to recent team milestones land better
than generic swag.

For product leadership, books on platform strategy or a dinner near the last
offsite city are common picks. Tokyo offsites often inspire travel-themed gifts.
`.trim();

const doc = await supermemory.add(user, {
content: handbook,
id: "doc_gift_policy",
metadata: { type: "document", source: "handbook" },
taskType: "superrag",
});

console.log(doc.id, doc.status);

text
```python Python
handbook = """
# Team notes — gifts & recognition

When someone is promoted to VP or above, the company recommends a thoughtful gift
in the $75–$150 range. Experiences tied to recent team milestones land better
than generic swag.

For product leadership, books on platform strategy or a dinner near the last
offsite city are common picks. Tokyo offsites often inspire travel-themed gifts.
""".strip()

doc = client.add(
    user,
    content=handbook,
    id="doc_gift_policy",
    metadata={"type": "document", "source": "handbook"},
    task_type="superrag",
)
print(doc.id, doc.status)
bash curl
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/document" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "# Team notes — gifts & recognition\n\nWhen someone is promoted to VP or above, the company recommends a thoughtful gift in the $75–$150 range. Experiences tied to recent team milestones land better than generic swag.\n\nFor product leadership, books on platform strategy or a dinner near the last offsite city are common picks. Tokyo offsites often inspire travel-themed gifts.",
    "id": "doc_gift_policy",
    "metadata": { "type": "document", "source": "handbook" },
    "taskType": "superrag"
  }'

3. Wait until both are `done`

Poll document status. With dreaming: "instant", once system.status is done the document is indexed and memories for that document should be available for search and profiles (queued → extracting → … → done).

Note that the preferred way is to have dreaming: dynamic. supermemory charges one extra operation for instant dreaming. Instant is good for one off tests, setup, debugging and benchmarking.

```typescript TypeScript async function waitUntilDone(id: string) { for (;;) { const d = await supermemory.documents.get(user, id); if (d.system.status === "done" || d.system.status === "failed") return d; await new Promise((r) => setTimeout(r, 1500)); } }

await waitUntilDone(conv.id);
await waitUntilDone(doc.id);
console.log("ready to search");

text
```python Python
import time

def wait_until_done(doc_id: str):
    while True:
        d = client.documents.get(user, doc_id)
        if d.system.status in ("done", "failed"):
            return d
        time.sleep(1.5)

wait_until_done(conv.id)
wait_until_done(doc.id)
print("ready to search")
bash curl
# replace DOC_ID with each document id from the add responses
curl "https://api.supermemory.ai/ns/user_4f8a/document/DOC_ID" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
# repeat until "system": { "status": "done" }

Short text with instant dreaming usually finishes in a few seconds. Larger PDFs take longer. If you omit dreaming (default "dynamic"), document RAG can work after done while memory extraction may still be batching — use "instant" when the next step is memory search or profiles. system.dreamingStatus turns done once a document's memories are ready.

4. Three ways to get context back

A. Document search (RAG)

Chunk-level retrieval over raw knowledge — use when you need what the docs say.

```typescript TypeScript const rag = await supermemory.search(user, { query: "gift ideas for a VP promotion after a Tokyo offsite", searchMode: "chunks", limit: 3, });

for (const hit of rag.results) {
console.log(hit.similarity, hit.chunk);
}

text
```python Python
rag = client.search(
    user,
    query="gift ideas for a VP promotion after a Tokyo offsite",
    search_mode="chunks",
    limit=3,
)

for hit in rag.results:
    print(hit.similarity, hit.chunk)
bash curl
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/search" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "gift ideas for a VP promotion after a Tokyo offsite",
    "searchMode": "chunks",
    "limit": 3
  }'

You should see chunks from the handbook (budget range, Tokyo offsite angle) — document grounding, not personal facts.

Omit searchMode (or set it to "hybrid", the default) to get extracted memories and document chunks together:

typescript
await supermemory.search(user, {
  query: "gift ideas for a VP promotion after a Tokyo offsite",
  searchMode: "hybrid",
  limit: 5,
});

B. Memory graph traversal

Search extracted memories with related edges. This is the entity-chain moment: gift → VP of Product → Sarah → Tokyo offsite.

```typescript TypeScript const memories = await supermemory.search(user, { query: "What gift should I get, and why?", include: { related: true }, searchMode: "memories", limit: 5, });

console.log(JSON.stringify(memories, null, 2));

text
```python Python
memories = client.search(
    user,
    query="What gift should I get, and why?",
    search_mode="memories",
    include={"related": True},
    limit=5,
)
print(memories.to_json())
bash curl
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/search" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "What gift should I get, and why?",
    "searchMode": "memories",
    "include": { "related": true },
    "limit": 5
  }'

Abbreviated shape:

json
{
  "results": [
    {
      "id": "mem_8c1f",
      "memory": "Sarah is being promoted to VP of Product",
      "similarity": 0.81,
      "included": {
        "related": {
          "parents": [
            { "id": "mem_2a90", "memory": "Sarah presented the Q3 roadmap at the Tokyo offsite" }
          ],
          "children": [
            { "id": "mem_b713", "memory": "User needs a gift idea for their VP of Product, Sarah" }
          ]
        }
      }
    }
  ],
  "searchTime": 287
}

You never wrote “Sarah is my VP of Product” as one sentence. The graph connected sessions of speech. Deep dive: graph memory.

C. User profile

Profiles are the always-on summary (static + recent dynamic) of an entity (or a namespace) - what you inject every turn without re-searching the world.

```typescript TypeScript const { profile } = await supermemory.profile(user);

console.log("static:", profile.static.map((m) => m.memory));
console.log("dynamic:", profile.dynamic.map((m) => m.memory));

text
```python Python
profile = client.profile(user).profile

print("static:", [m.memory for m in profile.static])
print("dynamic:", [m.memory for m in profile.dynamic])
bash curl
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/profile" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

The v5 profile call takes no query. When you also want query-ranked memories, run a search next to it (the harness below does exactly that).

There is a lot more to profiles - with Buckets, for example, you can make supermemory learn and categorize incoming information for learning specific things.

Path Use when
Document search Ground in policies, docs, handbooks
Memory + related Personal facts, entity links, “what’s true about this user”
Profile Cheap always-on context every LLM turn

Same namespace → same context pool. See Memory vs RAG.

5. Put it in a harness

There is no single required harness. The pattern is the same wherever you run the model: read context (profile / search / docs), generate, write the turn back under a stable id so the session stays one document.

Here are two example shapes — pick whatever matches your stack.

Example: explicit profile + search + add

``` /* Detailed source-code truncated for AI context efficiency. */ ```
python python
# pip install supermemory openai
from supermemory import Supermemory
from openai import OpenAI

memory = Supermemory()
llm = OpenAI()
user = "user_4f8a"
session_id = "chat_live_session"

while True:
    question = input("you: ")

    profile = memory.profile(user).profile

    related = memory.search(user, query=question, search_mode="memories", limit=5)

    # optional: also pull document chunks for grounding
    rag = memory.search(user, query=question, search_mode="chunks", limit=3)

    context = "\n".join(
        [
            "## Profile (static)",
            *[m.memory for m in profile.static],
            "## Profile (dynamic)",
            *[m.memory for m in profile.dynamic],
            "## Related memories",
            *[r.memory for r in related.results if r.memory],
            "## Docs",
            *[r.chunk for r in rag.results if r.chunk],
        ]
    )

    res = llm.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": f"You help this user. Context:\n{context}"},
            {"role": "user", "content": question},
        ],
    )
    answer = res.choices[0].message.content or ""
    print(f"assistant: {answer}")

    # append this turn into the same conversation document
    memory.add(
        user,
        content=f"user: {question}\nassistant: {answer}",
        id=session_id,
    )

Example: Vercel AI SDK

Same pattern, wrapped: withSupermemory injects context and can save the conversation for you. Details: AI SDK integration.

typescript
// npm install ai @ai-sdk/openai @supermemory/tools
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { withSupermemory } from "@supermemory/tools/ai-sdk";
import * as readline from "node:readline/promises";

const model = withSupermemory(openai("gpt-4o"), {
  namespace: "user_4f8a",
  id: "chat_live_session", // keep stable for the whole session
  mode: "full", // profile + query search
});

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });

while (true) {
  const prompt = await rl.question("you: ");
  const { text } = await generateText({ model, prompt });
  console.log(`assistant: ${text}`);
}

Try:

text
you: What gift should I get for the person being promoted?
assistant: You're looking for something for Sarah — she's being promoted to VP of
Product after presenting the Q3 roadmap in Tokyo. Your handbook suggests $75–$150
and something tied to the offsite; a Tokyo-inspired experience or platform-strategy
book would fit…

Kill it, restart it

Ctrl+C the process, start again with the same namespace (and optional same id for the live session). Ask:

text
you: who's getting promoted?
assistant: Sarah — she's being promoted to VP of Product.

Nothing was reloaded from your process. Memory and docs live in supermemory.

Mental model

text
INGEST                         WAIT                         RETRIEVE
  ──────                         ────                         ────────
  Conversation (id)         →    system.status === done  →    Memory graph (+ related)
  Document (id)             →    system.status === done  →    Document search (RAG)
                                                          →    Profile (static + dynamic)
                                                                   │
                                                                   ▼
                                                              Chat harness

Where next

Conversations, files, URLs, `id` updates, and status. Hybrid vs memories, filters, thresholds, rerank. How relations and entity chains are produced. Static vs dynamic, and when to inject a profile every turn. withSupermemory modes, id, addMemory. Isolation for multi-tenant products.

Apps Docs Api Reference Overview (`apps/docs/api-reference/overview.mdx`)


title: "API reference" icon: "/icons/hugeicons/plug-socket.svg"

This is the legacy v3/v4 reference. v3 and v4 are deprecated and will be shut down on **December 31, 2026**. Move to [v5](/v5/api-reference/overview) before then. The [migration guide](/migration/api-v5) maps every legacy call to its v5 equivalent.

This is the contract-level reference for Supermemory: methods, paths, parameters, and the playground.

For narrative guides (when to use what, patterns, SDKs), start with the Quickstart and Using supermemory.

Base URL

text
https://api.supermemory.ai

Self-hosted: use your instance URL (for example http://localhost:6767). See Self-hosting.

Authentication

All endpoints use a Bearer API key. Create one in the developer console.

bash
Authorization: Bearer sm_...

Details: API keys & auth.

Mental model

Group What it does
Ingest Add documents, files, batches, and conversations into the pipeline
Documents Get status, list, update, delete, chunks, and file URLs
Search Semantic recall — memories, documents, or hybrid
Profiles Static + dynamic facts for a container (user / entity)
Memories Create, list, update, and forget extracted memory entries
Container tags Multi-tenant settings, merge, and delete for a container
Connections OAuth connectors (Drive, Notion, Gmail, …) and sync
Settings Org-level customization, buckets, and reset

Same containerTag scopes ingest, search, and profiles — one engine, multiple ways out.

Suggested order

  1. Ingest — POST /v3/documents (SDK: client.add)
  2. Documents — GET /v3/documents/{id} until status: "done"
  3. Search — POST /v4/search
  4. Profiles — POST /v4/profile

Full walkthrough with conversation + document examples: Quickstart.

SDKs

Official clients wrap this API:

  • TypeScript: npm install supermemory
  • Python: pip install supermemory

See Supermemory SDK.

The API's OpenAPI generator supplies official TypeScript / Python SDK samples via x-codeSamples, plus cURL. Playground snippets use the deployed spec, so their availability depends on the API version serving it.

SDK generation is migrating off Stainless SaaS to stlc soon; documented OpenAPI samples will then be produced by the SDK build instead of a hand-maintained map.

OpenAPI

Spec (live): https://api.supermemory.ai/v3/openapi


Apps Docs Connectors Overview (`apps/docs/connectors/overview.mdx`)


title: "Connectors overview" sidebarTitle: "Overview" icon: "/icons/hugeicons/layers-01.svg"

Connect external platforms to automatically sync documents into supermemory. Supported connectors include Google Drive, Gmail, Notion, OneDrive, GitHub, Granola and Web Crawler with real-time synchronization and intelligent content processing.

New v5 connectors belong to one namespace. The namespace goes in the URL (/ns/{namespace}/connectors) and is the first argument of namespace-scoped SDK calls. Organization-wide listing uses connectors.listAll() in TypeScript or connectors.list_all() in Python without a namespace argument. Legacy connectors can span multiple namespaces or have no namespace.

Supported connectors

**Google Docs, Slides, Sheets**
text
Real-time sync via webhooks. Supports shared drives, nested folders, and collaborative documents.
**Email Threads**
text
Real-time sync via Pub/Sub webhooks. Syncs threads with full conversation history and metadata.
**Pages, Databases, Blocks**
text
Instant sync of workspace content. Handles rich formatting, embeds, and database properties.
**Word, Excel, PowerPoint**
text
Scheduled sync every 4 hours. Supports personal and business accounts with file versioning.
**GitHub Repositories**
text
Real-time incremental sync via webhooks. Supports documentation files in repositories.
**Meeting notes and transcripts**
text
Syncs AI meeting notes, summaries, attendees, and transcripts from your Granola workspace.
**Web Pages, Documentation**
text
Crawl websites automatically with robots.txt compliance. Scheduled recrawling keeps content up to date.

Quick start

1. Create a Connector

typescript typescript
import { Supermemory } from "supermemory"

const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

const connector = await supermemory.connectors.create("user-123", {
  provider: "notion",
  redirectUrl: "https://yourapp.com/callback",
  documentLimit: 5000,
})

// Send the user to authorization.url to finish OAuth
console.log("Connector:", connector.id)
console.log("Auth URL:", connector.authorization?.url)
console.log("Expires at:", connector.authorization?.expiresAt)
// Output: Auth URL: https://api.supermemory.ai/v3/connections/auth/redirect?state=...
python python
from supermemory import Supermemory
import os

client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

connector = client.connectors.create(
    "user-123",
    request={
        "provider": "notion",
        "redirect_url": "https://yourapp.com/callback",
        "document_limit": 5000,
    },
)

# Send the user to authorization.url to finish OAuth
print(f"Connector: {connector.id}")
print(f"Auth URL: {connector.authorization.url}")
print(f"Expires at: {connector.authorization.expires_at}")
# Output: Auth URL: https://api.supermemory.ai/v3/connections/auth/redirect?state=...
bash curl
curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "notion",
    "redirectUrl": "https://yourapp.com/callback",
    "documentLimit": 5000
  }'

# Response: {
#   "id": "PTzGiUYei7pgzg5buzZHgA",
#   "authorization": {
#     "url": "https://api.supermemory.ai/v3/connections/auth/redirect?state=...",
#     "expiresAt": "2024-01-15T11:30:00.000Z"
#   }
# }

2. Handle OAuth callback

Send the user to authorization.url before authorization.expiresAt. When the user authorizes, the provider returns them to your redirectUrl and the first sync starts. GitHub, Gmail and Google Drive (with the default syncScope: "selected") first send the user through Supermemory's hosted picker to choose what to sync. A pending OAuth connector is not returned by list until the user finishes authorization; get returns it with system.status: "pending_authorization".

Config providers (Web Crawler, S3, Granola) return authorization: null and start syncing immediately.

3. Monitor sync status

typescript typescript
import { Supermemory } from "supermemory"

const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

// Read one connector with its recent sync runs
const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
  include: "syncs",
})

console.log("Provider:", connector.provider)
console.log("Account:", connector.account)
console.log("Last sync:", connector.latestRun?.system.status, connector.latestRun?.error)
console.log("Last synced at:", connector.system.lastSuccessfulSyncAt)
console.log("Documents:", connector.documentCount)
console.log("Recent runs:", connector.syncs)

// List the documents the connector synced into the namespace
const docs = await supermemory.list("user-123", "documents")
console.log(`Synced ${docs.pagination.totalItems} documents`)
// Output: Synced 45 documents
python python
from supermemory import Supermemory
import os

client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

# Read one connector with its recent sync runs
connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])

print(f"Provider: {connector.provider}")
print(f"Account: {connector.account}")
print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}")
if connector.latest_run and connector.latest_run.error:
    print(connector.latest_run.error)
print(f"Last synced at: {connector.system.last_successful_sync_at}")
print(f"Documents: {connector.document_count}")
print(f"Recent runs: {connector.syncs}")

# List the documents the connector synced into the namespace
docs = client.list("user-123", "documents")
print(f"Synced {docs.pagination.total_items} documents")
# Output: Synced 45 documents
bash curl
# Read one connector with its recent sync runs
curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

# Response: {
#   "id": "PTzGiUYei7pgzg5buzZHgA",
#   "provider": "notion",
#   "namespace": "user-123",
#   "account": "[email protected]",
#   "documentLimit": 5000,
#   "documentCount": 45,
#   "latestRun": { "errorCode": null, "error": null, "system": { "status": "completed", "startedAt": "...", "completedAt": "..." } },
#   "system": { "status": "active", "createdAt": "2024-01-15T10:30:00.000Z", "lastSuccessfulSyncAt": "2024-01-15T10:45:00.000Z" },
#   "syncs": [...]
# }

# List synced documents in the namespace
curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

# Response: {"documents": [...], "pagination": {"totalItems": 45, ...}}

How connectors work

Authentication flow

  1. Create Connector: Call POST /ns/{namespace}/connectors with a provider. OAuth providers return an authorization link; config providers (Web Crawler, S3, Granola) take a config object and return authorization: null
  2. User Authorization: Send the user to authorization.url when the provider requires it
  3. Automatic Setup: The connector is established. Save any required label, repository, or file selection before content sync can proceed; providers without a selection step start their initial sync after setup
  4. Continuous Sync: Real-time updates via webhooks + scheduled sync every 4 hours (or scheduled recrawling for Web Crawler)

Document processing pipeline

mermaid
graph TD
    A[External Document] --> B[Webhook/Schedule Trigger]
    B --> C[Content Extraction]
    C --> D[Chunking & Embedding]
    D --> E[Index in Supermemory]

    E --> F[Searchable Memory]
    E --> G[Document Search]

Sync mechanisms

Provider Real-time Sync Scheduled Sync Manual Sync
Google Drive ✅ Webhooks (7-day expiry) ✅ Every 4 hours ✅ On-demand
Gmail ✅ Pub/Sub (7-day expiry) ✅ Every 4 hours ✅ On-demand
Notion ✅ Webhooks ✅ Every 4 hours ✅ On-demand
OneDrive ✅ Webhooks (30-day expiry) ✅ Every 4 hours ✅ On-demand
GitHub ✅ Webhooks ✅ Every 4 hours ✅ On-demand
Granola ❌ Not supported ❌ Not supported ✅ On-demand
Web Crawler ❌ Not supported ✅ Scheduled recrawling (7+ days) ✅ On-demand

Connector Management

List Connectors

typescript typescript
import { Supermemory } from "supermemory"

const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

// Connectors in one namespace
const { connectors } = await supermemory.connectors.list("org-123")

for (const connector of connectors) {
  console.log(`${connector.provider}: ${connector.account} (${connector.id})`)
  console.log(`Documents: ${connector.documentCount} of ${connector.documentLimit}`)
  console.log(`Last sync: ${connector.latestRun?.system.status ?? "never"}`)
}

// Connectors across every namespace in the organization
const all = await supermemory.connectors.listAll()
python python
from supermemory import Supermemory
import os

client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

# Connectors in one namespace
connectors = client.connectors.list("org-123").connectors

for connector in connectors:
    print(f"{connector.provider}: {connector.account} ({connector.id})")
    print(f"Documents: {connector.document_count} of {connector.document_limit}")
    print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}")

# Connectors across every namespace in the organization
all_connectors = client.connectors.list_all()
bash curl
# Connectors in one namespace
curl "https://api.supermemory.ai/ns/org-123/connectors" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

# Response: {
#   "connectors": [
#     {
#       "id": "PTzGiUYei7pgzg5buzZHgA",
#       "provider": "notion",
#       "namespace": "org-123",
#       "account": "[email protected]",
#       "documentLimit": 5000,
#       "documentCount": 45,
#       "latestRun": { "system": { "status": "completed", ... }, "error": null },
#       "system": { "status": "active", "createdAt": "2024-01-15T10:30:00.000Z", "lastSuccessfulSyncAt": "2024-01-15T10:45:00.000Z" }
#     }
#   ],
#   "pagination": { "currentPage": 1, "limit": 10, "totalItems": 1, "totalPages": 1 }
# }

# Connectors across every namespace in the organization
curl "https://api.supermemory.ai/connectors" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

Trigger a Sync

A connector syncs on its own schedule. Start one now with connectors.sync. The call returns 409 while a sync for that connector is already running.

typescript typescript
const run = await supermemory.connectors.sync("org-123", "PTzGiUYei7pgzg5buzZHgA")

console.log(run.status)
// Output: queued
bash curl
curl -X POST "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

# Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}

Update a Connector

Change the document limit, or the selection for providers that support one, with connectors.update. A new selection replaces the old one. See Managing Connector Selection.

typescript typescript
const connector = await supermemory.connectors.update("org-123", "PTzGiUYei7pgzg5buzZHgA", {
  documentLimit: 8000,
})

console.log(connector.documentLimit)
// Output: 8000
bash curl
curl -X PATCH "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"documentLimit": 8000}'

# Response: the updated connector object

Delete Connectors

DELETE /ns/{namespace}/connectors/{id} accepts an optional deleteDocuments query parameter:

Parameter Type Default Description
deleteDocuments boolean true When true, all documents imported by the connector are permanently deleted. When false, the connector is removed but documents are kept.
Setting `deleteDocuments=false` is useful when you want to disconnect an integration without losing the memories that were already imported.
typescript typescript
import { Supermemory } from "supermemory"

const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

// Delete the connector and all imported documents (default)
await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")

// Delete the connector but keep imported documents
await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
  deleteDocuments: false,
})
python python
from supermemory import Supermemory
import os

client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

# Delete the connector and all imported documents (default)
client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")

# Delete the connector but keep imported documents
client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
bash curl
# Delete the connector and all imported documents (default)
curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

# Delete the connector but keep imported documents
curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?deleteDocuments=false" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
There is no delete-by-provider route in v5. Find the connector with `connectors.list(namespace)` and delete it by `id`.

Custom OAuth applications

By default, Supermemory uses its own OAuth applications to connect to third-party providers. You can use your own OAuth app credentials for tighter control over data access, which is useful for enterprise customers. Custom OAuth credentials are an organization setting and are not part of the v5 connector routes.

  1. Create the OAuth application on the provider's developer console:
  2. For Google Drive specifically: choose application type Web application, and enable the Google Drive API under "APIs and Services" in the Cloud Console. Google also requires verification/approval before custom keys work in production.
  3. Set the redirect URL to https://api.supermemory.ai/v3/connections/auth/callback/{provider} (for example, .../auth/callback/google-drive).
Enabling custom keys for a provider applies to all new connectors for that provider. Existing connectors will need to be re-authorized.

Apps Docs Memorybench Overview (`apps/docs/memorybench/overview.mdx`)


title: "MemoryBench" sidebarTitle: "Overview" icon: "/icons/hugeicons/test-tube-01.svg"

Benchmarking memory systems is hard, and most comparisons you'll find aren't apples-to-apples — different datasets, different judges, different prompts, cherry-picked runs. MemoryBench (supermemoryai/memorybench, MIT licensed) is the open-source framework we built to fix that: the same benchmark questions, the same pipeline, and the same judges run against every provider, so a comparison actually means something.

We open-sourced it so you don't have to take our word for anything — you can run it yourself, against your own memory implementation, on the datasets that match your use case.

Clone it, run it against your own data, or read the source for exactly how each provider is scored.

Benchmark your own memory system

MemoryBench ships a Claude Code skill that automates the entire process of benchmarking a custom memory implementation — yours — against Supermemory, Mem0, and Zep. Point it at your code and it handles discovery, integration, and the run:

  1. Asks a few questions about your setup (provider name, where your memory code lives, which benchmark, which competitors, how many questions)
  2. Analyzes your memory code to find its init, ingest, and search methods
  3. Generates a provider adapter and registers it with the framework
  4. Runs the full benchmark against your chosen competitors
  5. Reports accuracy, latency, and context-token results side by side
bash
# From your project root
/memorybench

No manual TypeScript required to get a first result — see Adding a Provider for what the skill generates and how to do it by hand.


How it works

Every run goes through the same checkpointed pipeline, regardless of provider or benchmark:

text
INGEST → SEARCH → ANSWER → EVALUATE → REPORT

Ingestion can take hours for large datasets, and API calls fail — so every phase checkpoints independently and a run resumes from the last completed step instead of starting over.

Chunk-based semantic search LLM-powered memory extraction Knowledge graph construction
Benchmark Tests Source
LoCoMo Fact recall across extended, multi-session conversations — single-hop, multi-hop, temporal, adversarial snap-research/locomo
LongMemEval Long-term memory across sessions, including knowledge that gets updated mid-conversation xiaowu0162/longmemeval
ConvoMem Personalization, preference learning, and reference resolution within a conversation Salesforce/ConvoMem

None of these fit your use case? Build your own benchmark — providers, benchmarks, and judges are all pluggable.

Judging is judge-agnostic too — score a run with GPT-4o, Claude, Gemini, or any model you configure, so results aren't an artifact of one evaluator's bias.


Test on scenarios that actually match your product, not just the built-in datasets. Register your own memory system so it can be scored and compared. MemScore, judge scoring, and how to read a report. Found a bug, or want a provider/benchmark we don't support yet?

Apps Docs Self Hosting Overview (`apps/docs/self-hosting/overview.mdx`)


title: "Supermemory local" sidebarTitle: "Overview" icon: "/icons/hugeicons/server-stack-01.svg"

Supermemory runs on your own hardware. It's the same memory engine behind the hosted platform — ingestion, memory extraction, hybrid semantic search, and the full API — as a single self-contained binary.

```bash curl curl -fsSL https://supermemory.ai/install | bash ```
bash npx
npx supermemory local

No Docker. No database to provision. No config files. It boots in seconds with everything built in. The SDKs and other components in the public repository are open source; the downloadable self-hosted server binary is built from a separate, non-public codebase.

Zero config, actually

Run the binary with nothing set and you get a complete memory system:

  • The Supermemory graph engine, embedded — created automatically on first boot. No database to stand up, no connection strings.
  • Built-in local embeddings — default Xenova/bge-base-en-v1.5 (768d) on your machine, no API key. Opt into OpenAI, Gemini, or Ollama instead — see Embeddings.
  • An API key, generated for you — printed on first boot, ready to paste into any SDK.
  • The full Memory API — /ns/{namespace}/document, /ns/{namespace}/search, /ns/{namespace}/profile, namespaces, the works.

The only thing you bring is a model. In production, Supermemory runs its own proprietary models, purpose-tuned for long-horizon data understanding and memory extraction. Self-hosted, the same pipeline runs on whatever model you point it at — OpenAI, Anthropic, Gemini, Groq, or any OpenAI-compatible endpoint. Bring a key and go. Or don't bring one at all:

Runs fully offline

Supermemory works with any OpenAI-compatible endpoint, which means it runs end-to-end on your machine with a local model — Ollama, LM Studio, vLLM, llama.cpp. gpt-oss-20b is a great fit:

bash
OPENAI_BASE_URL=http://localhost:11434/v1 \
OPENAI_API_KEY=ollama \
OPENAI_MODEL=gpt-oss:20b \
supermemory-server

Local graph engine, local embeddings, local LLM. Text-memory processing can stay on your machine after the embedding model's first download. Supermemory collects telemetry; you can disable it with SUPERMEMORY_DISABLE_TELEMETRY=1. Webpage ingestion sends the URL to Firecrawl and needs FIRECRAWL_API_KEY; use local text inputs for offline operation. Also set SUPERMEMORY_NO_UPDATE_CHECK=1 if the server must not check for releases.

Drop-in with your existing code

The v5 SDKs (`supermemory` 5.x on npm and PyPI) need `supermemory-server` v0.0.9 or later. Older servers only speak v3/v4; run `supermemory-server upgrade` first.

The self-hosted server speaks the same API as the hosted platform. Point any Supermemory SDK at it with a one-line change:

typescript
const client = new Supermemory({
  apiKey: "sm_...", // printed on first boot
  baseUrl: "http://localhost:6767",
})

Everything in the Memory API docs works the same way. The coding plugins do too — Claude Code, Muse Code, Codex, and OpenCode all target your local server with SUPERMEMORY_API_URL=http://localhost:6767 (Muse: set baseUrl in .muse/supermemory.json, because hook env is cleared).

Self-hosted vs. the platform

Self-hosted is free within its lite license limit and useful for local development and privacy-sensitive workloads. The server binary is not open source; the hosted platform is where the full product lives:

Self-hosted Platform
Full Memory API ✅ ✅
Hybrid semantic search ✅ ✅
Embeddings Local default (or OpenAI / Gemini / Ollama) Managed
File ingestion (PDFs, images) ✅ (PDFs need GEMINI_API_KEY; images use Gemini or Vertex AI) ✅
Connectors (Google Drive, Notion, Gmail, OneDrive) — ✅
Supermemory MCP — ✅
Memory extraction Your model, your key Proprietary long-horizon models — higher quality, cheaper at scale
Infrastructure Your machine Globally distributed, scales with you

If you outgrow a single machine — or want connectors, MCP, and the best-tuned extraction pipeline — switch to the platform with its baseUrl and a hosted API key. Changing the connection does not transfer your local data. Running this for a team or organization? See Local vs. Enterprise.

Next steps

Install, run, and store your first memory in under two minutes Every environment variable: LLM providers, storage, auth, tuning Local default, remote providers, multilingual, dimension lock

Apps Docs Self Hosting Quickstart (`apps/docs/self-hosting/quickstart.mdx`)


title: "Self-hosting quickstart" sidebarTitle: "Quickstart" icon: "/icons/hugeicons/play.svg"

Install

```bash curl -fsSL https://supermemory.ai/install | bash ``` ```bash npx supermemory local ``` ```bash bunx supermemory local ```

The installer detects your OS and architecture, downloads the right binary, verifies it, and (when run interactively) prompts you for an LLM API key. Supported platforms: macOS (Apple Silicon & Intel), Linux (x64 & arm64).

Pin or change versions

Pass an explicit version to install (or roll back to) a specific release instead of latest:

bash
curl -fsSL https://supermemory.ai/install | bash -s -- 0.0.3
Before rolling back, back up your [data directory](#where-things-live). The installer replaces the binary, but an older server may not understand data or schema changes made by a newer release.

Release tags are server-v<version> on GitHub Releases (for example server-v0.0.3).

To move to the newest release later:

bash
supermemory-server upgrade

The binary may also print an “update available” notification on startup. If you intentionally pinned an older version (for example while debugging a regression), you can ignore that message until you are ready to upgrade.

Run

bash
supermemory-server

First boot sets everything up — the embedded Supermemory graph engine, local embeddings, and your credentials:

text
╭───────────────────────────────────────────────────────────────╮
│  → supermemory ready                                          │
│                                                               │
│    url         http://localhost:6767                          │
│    database    encrypted local storage (~/.supermemory)       │
│    embeddings  local · Xenova/bge-base-en-v1.5 · 768d         │
│    workflow    rivet                                          │
│    api key     sm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx              │
│    org id      xxxxxxxxxxxxxxxxxxxxxx                         │
│    boot        4.2s                                           │
╰───────────────────────────────────────────────────────────────╯
  every request needs this key: Authorization: Bearer <api key>

Save that API key — it's your bearer token for every request.

In production, Supermemory runs proprietary models tuned for long-horizon data understanding. Self-hosted, you bring any model: if no provider key is set, first boot launches an interactive setup wizard — pick a provider (OpenAI, Anthropic, Gemini, Groq, or any OpenAI-compatible endpoint like Ollama), paste your key, and it's saved encrypted for every future launch. After the LLM key, you can optionally pick an embedding model (press Enter to keep local `Xenova/bge-base-en-v1.5`). See [all providers](/self-hosting/configuration#llm-providers), [embeddings](/self-hosting/embeddings), and [fully-offline local models](/self-hosting/configuration#fully-offline-with-local-models). **Docker / non-interactive:** set an LLM key via env and, if you don’t want local embeddings, set `SUPERMEMORY_EMBEDDING_PROVIDER` / `MODEL` / `DIMENSIONS`. There is no wizard without a TTY.

For the prebuilt Docker image, follow Run Supermemory with Docker for installation, authentication, backups, and upgrades.

Add your first memory

The v5 SDKs (`supermemory` 5.x on npm and PyPI) need `supermemory-server` v0.0.9 or later. Older servers only speak v3/v4; run `supermemory-server upgrade` first. ```typescript import { Supermemory } from "supermemory"

const client = new Supermemory({
apiKey: "sm_...",
baseUrl: "http://localhost:6767",
})

await client.add("user_dhravya", {
content: "I'm Dhravya. I love building dev tools and I'm allergic to peanuts.",
dreaming: "instant",
})

text
</Tab>
<Tab title="Python">
```python
from supermemory import Supermemory

client = Supermemory(
    api_key="sm_...",
    base_url="http://localhost:6767",
)

client.add(
    "user_dhravya",
    content="I'm Dhravya. I love building dev tools and I'm allergic to peanuts.",
    dreaming="instant",
)
```bash curl http://localhost:6767/ns/user_dhravya/document \ -H "Authorization: Bearer sm_..." \ -H "Content-Type: application/json" \ -d '{ "content": "I am Dhravya. I love building dev tools and I am allergic to peanuts.", "dreaming": "instant" }' ```

Search it

```typescript const { results } = await client.search("user_dhravya", { query: "what food should I avoid?", }) ``` ```python results = client.search("user_dhravya", query="what food should I avoid?") ``` ```bash curl http://localhost:6767/ns/user_dhravya/search \ -H "Authorization: Bearer sm_..." \ -H "Content-Type: application/json" \ -d '{ "query": "what food should I avoid?" }' ```

That's it. Everything in the Memory API — documents, memories, user profiles, spaces, filtering — works identically against your local server.

Where things live

By default, all state lives in a single directory you can back up or move:

Path Contents
~/.supermemory/ (or $SUPERMEMORY_DATA_DIR; ./.supermemory/ before v0.0.9) The Supermemory graph engine's data, API key, auth secret, embedding model cache
~/.supermemory/env.enc Provider keys and embedding choices saved by the first-boot wizard, encrypted and loaded on every launch
~/.supermemory/env Plaintext API keys written by the installer. On launch, the server copies parsed values into env.enc before attempting to remove this file; it can remain if migration or removal fails

Next steps

LLM providers, local models, performance tuning Local default, OpenAI / Gemini / Ollama, multilingual The full API — it all works against your local server

Apps Docs Smfs Overview (`apps/docs/smfs/overview.mdx`)


title: "SMFS" sidebarTitle: "Overview" icon: "/icons/hugeicons/database-01.svg"

SMFS mounts your Supermemory container as a real directory. Agents read it with ls, cat, and grep. No SDK to learn, no client to wire up, no embeddings to think about.

SMFS is open source and free for everyone.

Why a filesystem

Every model already knows how a filesystem works. It can ls, cat, grep, find, redirect with >, pipe with |. You don't have to teach it a new API surface, and the grammar carries across runtimes.

The catch: a filesystem on its own isn't great for memory. Search means walking the tree. Long files burn through context. The model has to hold the directory structure in its head. None of that scales as memory grows.

SMFS fixes the catch. The shell is real, but underneath:

  • Semantic grep by default. One call surfaces what matters across the whole container, ranked by meaning. Pass any flag and you fall through to the real grep for exact matches.
  • Memory paths get distilled. Files marked as memory paths are extracted and indexed by Supermemory. They don't bloat the model's context.
  • Virtual profile.md. A live digest of the container at the mount root. The model can cat profile.md for a one-shot summary instead of walking every file.
  • Bidirectional sync runs in the background. Local reads hit cache; writes push to Supermemory.

You get filesystem ergonomics without paying the filesystem tax in tokens.

Two ways to use SMFS

Pick by where your agent runs.

For agents and tools with a real filesystem. Claude Code, Cursor, devcontainers, Docker, Codespaces. NFSv3 on macOS, FUSE on Linux. For agents running serverless or at the edge. Cloudflare Workers, AWS Lambda, Vercel, Modal. A virtual bash where the filesystem is your container. Available as [`@supermemory/bash`](/smfs/bash-tool) for TypeScript and [`supermemory-bash`](/smfs/bash-tool-python) for Python.

Use SMFS with your sandbox provider

Already using a sandbox or agent platform? Jump straight to the guide for your provider.

Isolated Linux sandboxes with millisecond boot times. Mount SMFS inside or use the bash tool from your orchestrating code. Firecracker microVMs for AI code execution. Install SMFS directly or use a custom template with it pre-installed. The most popular TypeScript agent framework. Add memory as a tool with one function call. Edge-first agents. Use the bash tool in Workers, or mount SMFS in Cloudflare Containers.

Next steps

One curl, one mount, you're done. Drop SMFS into a TypeScript or Python agent without mounting anything. Full working apps you can clone and run — legal docs, support agents, and more.

Apps Docs Snippets Api V5 Overview (`apps/docs/snippets/api-v5-overview.mdx`)

Migrate by hand

This is a breaking API migration. Do not change only the URL: fields moved, search defaults changed, response envelopes changed, and some legacy operations have no v5 replacement.

The API base URL and bearer keys do not change.

Where parameters live: GET options go in the query string. POST, PATCH, and PUT options go in the JSON body, or as form fields on file uploads; the one exception is list pagination (page, limit, sort, order), which stays in the query string. DELETE options such as moveTo go in the query string, while bulk deletes send their ids in the JSON body. Most routes reject options sent in the wrong place with 400. DELETE /ns/{namespace} does not: it ignores a JSON body, so a moveTo sent in the body permanently deletes the namespace instead of moving it. v5 application routes are unversioned; /v5/reference is the interactive v5 reference, not an API path prefix.

Search for `/v3/`, `/v4/`, `containerTag`, `containerTags`, `customId`, `entityContext`, `filterByMetadata`, `filters`, and legacy SDK methods such as `client.search.execute`, `client.profile`, and `client.connections.*`. Move the legacy `containerTag` into `/ns/{namespace}`. Never infer scope from a document or memory ID, and never send multiple namespaces to one v5 request. Apply [ingestion](/migration/api-v5-document-writes), [updates](/migration/api-v5-document-updates), [content management](/migration/api-v5-document-reads), [search](/migration/api-v5-recall), [profiles](/migration/api-v5-profiles), [forgetting](/migration/api-v5-memory-forgetting), [namespaces](/migration/api-v5-settings), [organization](/migration/api-v5-organization), and [filter](/migration/api-v5-filters) changes independently. Migrate envelopes, includes, pagination, profile buckets, system fields, and partial-error handling before switching traffic. Follow the [verification and rollout guide](/migration/api-v5-rollout). Compare identity and behavior—not raw JSON ordering—and set changed defaults explicitly during rollout. Switch traffic, monitor failures and semantic drift, then remove legacy compatibility code only after that domain passes verification.

Upgrade the SDK

The v5 TypeScript SDK ships as the same supermemory package. Install it, then replace the legacy client:

bash
npm i supermemory
```ts Legacy import Supermemory from "supermemory"

const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
await client.add({ content: "new turn", containerTag: "user_1" })

text
```ts v5
import { Supermemory } from "supermemory"

const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
await supermemory.add("user_1", { content: "new turn" })

URL values are positional (namespace first, then id where there is one); everything else goes in one object. Legacy containerTag never appears in a body again. The Python SDK speaks v5 from 5.0.0 with the same shape and keyword arguments: client.add("user_1", content="new turn").

Endpoint map

Legacy v5 SDK method
POST /v3/documents POST /ns/{namespace}/document supermemory.add
POST /v3/documents/batch POST /ns/{namespace}/document/batch supermemory.documents.batchAdd
POST /v3/documents/file POST /ns/{namespace}/document/file supermemory.documents.uploadFile
GET/PATCH /v3/documents/{id} GET/PATCH /ns/{namespace}/document/{id} supermemory.documents.get / supermemory.documents.update
File replace or partial file update POST/PATCH /ns/{namespace}/document/file/{id} supermemory.documents.replaceWithFile / supermemory.documents.updateFile
Single or bulk document delete DELETE /ns/{namespace}/document supermemory.documents.delete
Legacy document or memory lists POST /ns/{namespace}/list/{type} supermemory.list
POST /v3/search or /v4/search POST /ns/{namespace}/search supermemory.search
POST /v4/profile POST /ns/{namespace}/profile supermemory.profile
POST /v4/profile/buckets GET/PUT/DELETE /ns/{namespace}/profile/buckets supermemory.profiles.getBuckets / setBuckets / deleteBuckets
Legacy memory forget routes DELETE /ns/{namespace}/memories... supermemory.memories.forget / supermemory.memories.forgetMatching
Container-tag settings and lifecycle /namespaces and /ns/{namespace} supermemory.namespaces.list / get / update / delete
GET/PATCH /v3/settings GET/PATCH /organization supermemory.organization.get / update

Connectors

Connector routes also move under the namespace. Legacy containerTags arrays become one namespace per connector.

Legacy v5 SDK method
POST /v3/connections/list GET /ns/{namespace}/connectors or GET /connectors supermemory.connectors.list / supermemory.connectors.listAll
POST /v3/connections/{provider} POST /ns/{namespace}/connectors supermemory.connectors.create
GET /v3/connections/{connectionId} GET /ns/{namespace}/connectors/{id} supermemory.connectors.get
POST /v3/connections/{connectionId}/configure PATCH /ns/{namespace}/connectors/{id} supermemory.connectors.update
DELETE /v3/connections/{connectionId} DELETE /ns/{namespace}/connectors/{id} supermemory.connectors.delete
POST /v3/connections/{provider}/import POST /ns/{namespace}/connectors/{id}/sync supermemory.connectors.sync
```ts Legacy const connection = await client.connections.create("notion", { containerTags: ["user_1"], redirectUrl: "https://app.example.com/connected", }) ```
ts v5
const { id, authorization } = await supermemory.connectors.create("user_1", {
  provider: "notion",
  redirectUrl: "https://app.example.com/connected"
})

authorization is null for config providers (web-crawler, s3, granola), which start syncing immediately. connectors.sync returns { id, status: "queued" } and 409 when a sync is already running.


Apps Docs V5 Api Reference Overview (`apps/docs/v5/api-reference/overview.mdx`)


title: "API reference" sidebarTitle: "Overview" icon: "/icons/hugeicons/plug-socket.svg"

import MigrateCli from "/snippets/migrate-cli.mdx";

v5 makes namespace scope explicit in the URL and consolidates overlapping legacy operations.

text
https://api.supermemory.ai
  /ns/{namespace}/document
  /ns/{namespace}/search
  /ns/{namespace}/profile
  /ns/{namespace}/list/{type}
  /ns/{namespace}/memories
  /namespaces
  /organization

Use the v5 reference for the stable v5 API. /reference always points to the latest public version.

Install the TypeScript SDK

bash
npm i supermemory
ts
import { Supermemory } from "supermemory"

const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

URL values are positional arguments (namespace first, then id where there is one), and everything else goes in one object: supermemory.add("user_123", { content }). Each reference page below shows the SDK call next to the equivalent curl. The Python SDK (pip install supermemory, 5.0.0 and later) follows the same shape with keyword arguments: client.add("user_123", content="...").

Translate every v3/v4 request and response deterministically. Authenticate with the same bearer API keys used by legacy endpoints.

Apps Docs Agents And Mcp (`apps/docs/agents-and-mcp.mdx`)


title: "Agents, skills and MCP" sidebarTitle: "Agents, skills and MCP" icon: "/icons/hugeicons/robotic.svg"

This page is for building with Supermemory using coding agents: scaffolding a project, following the real API, and searching product docs.

This is not the consumer memory MCP that gives Claude or Cursor long-term memory about you. That is a separate product: Supermemory MCP.

Path How For
CLI npx supermemory Setup, smoke tests, agent-driven integration
Skill npx skills add … --skill supermemory Teach the agent the real API surface
Docs MCP https://supermemory.ai/docs/mcp Search these docs while the agent codes

CLI

Agents (and humans) can set things up from the terminal easily using our CLI

bash
npx supermemory

Useful for coding agents:

bash
npx supermemory setup              # detect project, launch/print integration flow
npx supermemory setup --prompt     # print integration prompt only
npx supermemory help --json        # agent-readable command catalog
npx supermemory help --all
npx supermemory migrate            # move a v3/v4 project to v5 with your agent

Also available for smoke tests against your key: add, search, profile, docs, namespaces, config, whoami. Auth via first-run credentials or SUPERMEMORY_API_KEY.

bash
npx supermemory add "User prefers TypeScript" --namespace user_123
npx supermemory search "language preference" --namespace user_123
npx supermemory profile --namespace user_123

Skill

Install the official skill so the agent uses the real endpoints, auth, and namespace rules instead of hallucinating APIs:

bash
npx skills add https://github.com/supermemoryai/skills --skill supermemory

Source: github.com/supermemoryai/skills.

For coding agents, use all three together: the skill, the docs MCP and `npx supermemory setup`.

Docs MCP

Remote MCP that lets the agent search Supermemory documentation while it implements an integration.

Server URL:

text
https://supermemory.ai/docs/mcp

Setup by client

Add to `~/.cursor/mcp.json`:
text
```json
{
  "mcpServers": {
    "supermemory-docs": {
      "url": "https://supermemory.ai/docs/mcp"
    }
  }
}
```
```bash claude mcp add --transport http supermemory-docs https://supermemory.ai/docs/mcp ```
text
Or project `.mcp.json`:

```json
{
  "mcpServers": {
    "supermemory-docs": {
      "type": "http",
      "url": "https://supermemory.ai/docs/mcp"
    }
  }
}
```
```bash codex mcp add supermemory-docs --url https://supermemory.ai/docs/mcp ```
text
Or `~/.codex/config.toml`:

```toml
[mcp_servers.supermemory-docs]
url = "https://supermemory.ai/docs/mcp"
```
```json { "mcp": { "supermemory-docs": { "type": "remote", "url": "https://supermemory.ai/docs/mcp", "enabled": true } } } ``` Add to `.vscode/mcp.json`:
text
```json
{
  "servers": {
    "supermemory-docs": {
      "type": "http",
      "url": "https://supermemory.ai/docs/mcp"
    }
  }
}
```
```json { "mcpServers": { "supermemory-docs": { "url": "https://supermemory.ai/docs/mcp" } } } ```
text
Stdio-only clients can proxy:

```json
{
  "mcpServers": {
    "supermemory-docs": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://supermemory.ai/docs/mcp"]
    }
  }
}
```

Starter prompt (docs + setup)

text
You are integrating Supermemory into my app.

- Use the supermemory-docs MCP (or https://supermemory.ai/docs/llms.txt) before inventing endpoints.
- Prefer `npx supermemory setup` / the supermemory skill for correct auth, namespace, and SDK usage.
- Canonical writes: POST /ns/{namespace}/document · search: POST /ns/{namespace}/search · profile: POST /ns/{namespace}/profile
- Auth: Authorization: Bearer $SUPERMEMORY_API_KEY only
- Always scope with one namespace in the URL path on write and search; there is no namespace field in the body
- SDK: import { Supermemory } from "supermemory"; version 5 or later; the namespace is the first argument: client.add(namespace, { content }), client.search(namespace, { query })
- For demos use dreaming: "instant" when memories must be ready right after status done
- Already on Supermemory v3/v4? Run `npx supermemory@latest migrate` instead of rewriting calls by hand

Integrate prompt (optional)

If the skill is not installed, paste a fuller prompt so the agent asks the right product questions:

``` /* Detailed source-code truncated for AI context efficiency. */ ````

Memory MCP (different product)

Want your assistant to remember you across chats (save/recall/profile in Claude, Cursor, etc.)? That is the Memory MCP, not the docs MCP:

→ Supermemory MCP

Next steps

Conversation + document ingest, RAG, graph, profile, harness. Persistent memory for assistants — separate from docs setup. Claude Code, OpenClaw, Codex, Hermes, and more. withSupermemory and memory tools in app code.

Apps Docs Authentication (`apps/docs/authentication.mdx`)


title: "API keys & auth" sidebarTitle: "API keys" icon: "/icons/hugeicons/key-01.svg"

API keys

All API requests require authentication using a Bearer token. Get your API key from the Developer Platform.

Include your key in all requests:

bash curl
curl -X POST https://api.supermemory.ai/ns/user_123/search \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  -d '{"query": "hello"}'
typescript typescript
import { Supermemory } from "supermemory";

const supermemory = new Supermemory({ apiKey: "YOUR_API_KEY" });
python python
from supermemory import Supermemory

client = Supermemory(api_key="YOUR_API_KEY")

Connector branding

When users connect external services (Google Drive, Notion, OneDrive), they see a "Log in to Supermemory" prompt by default. You can replace this with your own app name by providing your own OAuth credentials.

This works for Google Drive, Notion, and OneDrive. See the full setup in Customization.


Scoped API keys

Scoped keys are restricted to one namespace (what v3/v4 called a container tag), or to a fixed list of namespaces. They can read and write content only inside those namespaces, subject to the key's read/write permission and member restrictions — use them to give a client, session, or tenant limited access without shipping your org master key.

Namespace scoping is not read-only access. Writable scoped keys can update and delete documents and forget memories. Deleting or moving a namespace additionally requires an org admin or owner with write access to the source and, for moves, the destination.

Pairs with namespaces for multi-tenant isolation.

Allowed endpoints: /ns/{namespace}/document, /ns/{namespace}/list, /ns/{namespace}/search, /ns/{namespace}/profile, and /ns/{namespace}/memories; /namespaces and /ns/{namespace}; and the legacy /v3/documents, /v3/memories, /v4/memories, /v4/conversations, /v3/search, /v4/search, /v4/profile, /v3/container-tags/list, and /v3/session routes. Any other route returns 403.

Scoped keys cannot read billing, manage org settings, or mint further keys.

Create a scoped key

The scoped-key endpoint still takes the namespace in a field named containerTag.

bash
curl https://api.supermemory.ai/v3/auth/scoped-key \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  -d '{
    "containerTag": "my-project",
    "name": "my-key-name",
    "expiresInDays": 30
  }'

Parameters

Parameter Required Default Description
containerTag Yes, unless containerTags is set — The namespace to scope the key to. Up to 100 characters: letters, numbers, hyphens, underscores, colons
containerTags No — Scope the key to several namespaces instead of one. Takes precedence over containerTag
name No scoped_{containerTag} Display name for the key
expiresInDays No — 1–365 days
rateLimitMax No 500 Max requests per window (1–10,000)
rateLimitTimeWindow No 60000 Window in milliseconds (1–3,600,000)

Response

json
{
  "key": "sm_orgId_...",
  "id": "key-id",
  "name": "scoped_my-project",
  "containerTag": "my-project",
  "containerTags": ["my-project"],
  "expiresAt": "2026-03-08T00:00:00.000Z",
  "allowedEndpoints": ["/v3/session", "/v3/documents", "/v3/memories", "/v4/memories", "/v4/conversations", "/ns/*/memories", "/v3/search", "/v4/search", "/v4/profile", "/v3/container-tags/list", "/ns/*/document", "/ns/*/list", "/ns/*/search", "/ns/*/profile", "/namespaces$", "/ns$", "/ns/*$"]
}

Use the returned key like a normal API key — it just will not work outside its namespace.

Disable a scoped key

Revoke with the id from creation. Subsequent requests get 401. Memories and namespaces are not deleted.

bash
curl https://api.supermemory.ai/v3/auth/scoped-key/KEY_ID \
  --request DELETE \
  --header 'Authorization: Bearer YOUR_API_KEY'
json
{ "success": true }

Apps Docs Install (`apps/docs/install.md`)

You are integrating Supermemory into my application. Supermemory provides user memory, semantic search, and automatic knowledge extraction for AI applications.

You can always reference the documentation by using the SearchSupermemoryDocs MCP or running a web search tool for content on supermemory.ai/docs.

STEP 1: ASK ME THESE QUESTIONS

  1. What are you building?

    • Personal chatbot/assistant
    • Team knowledge base
    • Customer support bot
    • Document Q&A
    • Other
  2. How do you want to integrate?

    • Vercel AI SDK (@supermemory/tools)
    • OpenAI plugins
    • Direct SDK (supermemory npm/pip)
    • Direct API calls
  3. Data model?

    • Individual users only → containerTag: userId
    • Organizations only → containerTag: orgId
    • Both users AND orgs → ask for strategy
  4. Do you want USER PROFILES?
    User profiles are automatically-maintained facts about users (what they like, what they're working on, preferences).

    • Yes (RECOMMENDED) → Use client.profile() to get context
    • No → Just use search
  5. How should I retrieve context?

    • OPTION A: One call with search included → profile({ containerTag, q: userMessage })
    • OPTION B: Separate calls → profile() for facts, search() for memories

STEP 2: INSTALL

bash
# Get API key: https://console.supermemory.ai
npm install supermemory  # or: pip install supermemory
# For Vercel AI SDK: npm install @supermemory/tools
export SUPERMEMORY_API_KEY="sm_..."

STEP 3: CONFIGURE SETTINGS (DO THIS FIRST)

typescript
// PATCH https://api.supermemory.ai/v3/settings
fetch('https://api.supermemory.ai/v3/settings', {
  method: 'PATCH',
  headers: {
    'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    shouldLLMFilter: true,
    filterPrompt: `This is a [your app description]. containerTag is [userId/orgId]. We store [what data].`
  })
})

STEP 4: CONTAINER TAG STRATEGY

Based on their data model answer:

USER-ONLY APP:

typescript
containerTag: userId

ORG-ONLY APP:

typescript
containerTag: orgId  // Org members share memories

BOTH (ask which):

typescript
// Option A: Unique per user-org combination
containerTag: `${userId}-${orgId}`

// Option B: Org-scoped with user metadata
containerTag: orgId, metadata: { userId }

// Option C: User-scoped with org metadata
containerTag: userId, metadata: { orgId }

STEP 5: INTEGRATION CODE

Based on their integration choice:

VERCEL AI SDK

typescript
import { streamText } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
import { supermemoryTools } from '@supermemory/tools/ai-sdk'

// Option 1: Agent tools (recommended for agentic flows)
const result = await streamText({
  model: anthropic('claude-3-5-sonnet-20241022'),
  prompt: userMessage,
  tools: supermemoryTools(process.env.SUPERMEMORY_API_KEY, {
    containerTags: [userId]
  })
})
// Agent gets searchMemories, addMemory, fetchMemory tools

// Option 2: Profile middleware (automatic context injection)
import { withSupermemory } from '@supermemory/tools/ai-sdk'
const modelWithMemory = withSupermemory(anthropic('claude-3-5-sonnet-20241022'), {
  containerTag: userId,
  customId: 'conversation-1',
})

const result = await generateText({
  model: modelWithMemory,
  messages: [{ role: 'user', content: userMessage }]
})
// Profile is automatically injected into context

DIRECT SDK (WITH PROFILES)

typescript
import Supermemory from 'supermemory'

const client = new Supermemory()

// Before each LLM call:
const { profile, searchResults } = await client.profile({
  containerTag: userId,
  q: userMessage  // Include this if they chose OPTION A (one call)
                  // Omit if they chose OPTION B (separate calls)
})

// Build context
const context = `
Static facts: ${profile.static.join('\n')}
Recent context: ${profile.dynamic.join('\n')}
${searchResults ? `Memories: ${searchResults.results.map(r => r.content).join('\n')}` : ''}
`

// Send to LLM
const messages = [
  { role: 'system', content: `User context:\n${context}` },
  { role: 'user', content: userMessage }
]

// After LLM responds:
await client.memories.add({
  content: `user: ${userMessage}\nassistant: ${response}`,
  containerTag: userId
})

DIRECT SDK (NO PROFILES)

typescript
import Supermemory from 'supermemory'

const client = new Supermemory()

// Search for relevant memories
const results = await client.search({
  q: userMessage,
  containerTag: userId,
  searchMode: 'hybrid',  // Searches memories + document chunks
  limit: 5
})

// Build context
const context = results.results.map(r => r.content).join('\n')

// Send to LLM with context
const messages = [
  { role: 'system', content: `Relevant context:\n${context}` },
  { role: 'user', content: userMessage }
]

// Store the conversation
await client.memories.add({
  content: `user: ${userMessage}\nassistant: ${response}`,
  containerTag: userId
})

PYTHON VERSION

python
from supermemory import Supermemory

client = Supermemory()

# With profiles (if they want it)
profile_data = client.profile(
    container_tag=user_id,
    q=user_message  # Include if OPTION A, omit if OPTION B
)

context = f"""
Static: {chr(10).join(profile_data.profile.static)}
Dynamic: {chr(10).join(profile_data.profile.dynamic)}
"""

# Store conversation
client.add(content=f"user: {user_message}\\nassistant: {response}", container_tag=user_id)

DIRECT API

bash
# Add memory
curl -X POST https://api.supermemory.ai/v3/documents \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content": "conversation", "containerTag": "userId"}'

# Get profile
curl -X POST https://api.supermemory.ai/v4/profile \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"containerTag": "userId", "q": "search query"}'

# Search
curl -X POST https://api.supermemory.ai/v4/search \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "query", "containerTag": "userId", "searchMode": "hybrid"}'

STEP 6: FILE UPLOADS (if they need it)

typescript
// Files are automatically extracted (PDFs, images with OCR, videos with transcription)
const formData = new FormData()
formData.append('file', fileBlob)
formData.append('containerTag', userId)

await fetch('https://api.supermemory.ai/v3/documents/file', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: formData
})

// Processing is async - check status before assuming searchable
// GET /v3/documents/{documentId}

STEP 7: SEARCH MODES

typescript
// HYBRID (recommended) - searches memories + document chunks
searchMode: 'hybrid'

// MEMORIES ONLY - just extracted memories, no original text
searchMode: 'memories'

STEP 8: METADATA FILTERS (if they need secondary filtering)

typescript
await client.search({
  q: query,
  containerTag: userId,
  filters: {
    AND: [
      { key: 'type', value: 'conversation', type: 'string_equal' },
      { key: 'timestamp', value: '2024', type: 'string_contains' }
    ]
  }
})

KEY POINTS:

  1. Configure settings FIRST with filterPrompt
  2. User profiles = automatic facts about users (profile.static + profile.dynamic)
  3. profile({ containerTag, q }) combines profile + search in ONE call
  4. Search modes: 'hybrid' (recommended) or 'memories'
  5. File extraction is automatic - no config needed
  6. Store conversations after each interaction
  7. containerTag should match what you put in filterPrompt

TESTING:

bash
# 1. Configure settings
curl -X PATCH https://api.supermemory.ai/v3/settings \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"shouldLLMFilter": true, "filterPrompt": "..."}'

# 2. Add test memory
curl -X POST https://api.supermemory.ai/v3/documents \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content": "Test", "containerTag": "test_user"}'

# 3. Get profile
curl -X POST https://api.supermemory.ai/v4/profile \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"containerTag": "test_user"}'

NOW:

  1. Ask me the 5 questions above
  2. Generate complete working code based on my answers
  3. Include installation, settings config, and full integration

DOCS: https://supermemory.ai/docs


Apps Docs Invite Team Members (`apps/docs/invite-team-members.mdx`)


title: "Invite team members" sidebarTitle: "Invite Team Members" contentType: "How-to" icon: "/icons/hugeicons/user-multiple.svg"

Invite colleagues from the Supermemory console so they can access your organization’s workspace. Choose their role and access, then share an invitation link they can accept with their own account.

Before you begin

Check these requirements before you invite someone:

  • You’re an Owner or Admin in the organization.
  • Your organization has a plan that supports team invitations. Pro and higher include this feature.
  • You have an available member seat. Members and unexpired pending invitations count toward your limit.
  • You know the email address your colleague uses to sign in to Supermemory.

If Invite member is disabled, hover over it to check the plan or member limit. Open Billing to upgrade if needed.

Create and share an invitation

Create the invitation in the organization your colleague needs to join:

Open the [console](https://console.supermemory.ai) and select your organization from the top-left organization menu. Open **Settings** → **Team**.
text
<Frame caption="Team settings lists members and pending invitations. This screenshot uses a local Acme demo organization.">
  
</Frame>
Click **Invite member**. Enter the email address your colleague uses to sign in. Select a role:
text
- **Member**: Use this role for workspace access without team or billing administration.
- **Admin**: Use this role for full access, including members, billing, and settings.

For a **Member**, keep **Full access** to include all containers. On Scale or Enterprise, choose **Restricted** to limit access to selected [container tags](/concepts/container-tags). Add at least one tag and select **Read** or **Write** for each tag.

<Frame caption="The Invite member dialog defaults to Member and Full access.">
  
</Frame>
Click **Send invite**. Find your colleague’s email under **Pending invitations**. Click their link in the **Invite link** column to copy it, then share it privately with that colleague.
text
The invitation expires after seven days. To cancel it, click the **×** at the end of its row.

Accept the invitation

Ask your colleague to complete these steps:

  1. Open the invitation link you shared.
  2. Log in or create an account with the email address you invited.
  3. Check the organization name and role, then click ACCEPT.

If they see This invitation is not for you, ask them to click LOG IN WITH A DIFFERENT ACCOUNT. They need to sign in with the invited email address. If the invitation has expired or you canceled it, create a new invitation and share its new link.

Return to Settings → Team and confirm your colleague appears under Members. Their invitation no longer appears under Pending invitations after they accept.


Apps Docs Using Supermemory (`apps/docs/using-supermemory.mdx`)


title: "Using Supermemory" sidebarTitle: "Overview" icon: "/icons/hugeicons/compass-01.svg"

import { Journey, JourneyStep, JourneyItem } from "/snippets/journey.mdx";

Everything in this section is one of four steps. Same loop whether you're building personal memory, RAG over docs, or both on the same namespace.

Raw input automatically becomes memories (the knowledge graph) and indexed document chunks (for RAG). You don't choose one or the other; both come from the same write. Retrieval then gives you three ways to read that pool back. If you're unsure which fits, read Memory vs RAG.

Where next

Walk the whole loop end to end with one working example. What happens between `add()` and a memory showing up in search. The isolation boundary every ingest and retrieve call is scoped to (v3/v4 called it a container tag). When to reach for search, profiles, or both. Run standardized, reproducible evals against Supermemory and other providers — including your own.

Apps Docs Api Reference Connections (`apps/docs/api-reference/connections.mdx`)


title: "Connections" sidebarTitle: "Overview" icon: "/icons/hugeicons/book-open-01.svg"

Connections pull content from Notion, Google Drive, Gmail, OneDrive, S3, GitHub, and more.

Area Endpoints
Create / delete POST/DELETE /v3/connections/{provider}
List / get POST /v3/connections/list, GET …/{connectionId}
Configure / resources POST …/configure, GET …/resources
Sync / documents POST …/import, POST …/documents

Guides: Connectors overview · provider pages under Connectors


Apps Docs Api Reference Container Tags (`apps/docs/api-reference/container-tags.mdx`)


title: "Container tags" sidebarTitle: "Overview" icon: "/icons/hugeicons/book-open-01.svg"

v5 calls these **namespaces**. Same values. New integrations should use [v5 namespaces](/v5/api-reference/namespaces).

containerTag is the primary multi-tenant key (user id, workspace id, etc.). These endpoints manage settings and lifecycle for a tag.

Endpoint Use when
GET /v3/container-tags/{containerTag} Read tag settings
PATCH /v3/container-tags/{containerTag} Update tag settings
DELETE /v3/container-tags/{containerTag} Delete a container and its data
POST /v3/container-tags/merge Merge exactly two listed tags into either listed tag or a new target tag
GET /v3/container-tags/merge/{mergeId} Poll merge status

Guide: Container tags · Filtering


Apps Docs Api Reference Documents (`apps/docs/api-reference/documents.mdx`)


title: "Documents" sidebarTitle: "Overview" icon: "/icons/hugeicons/book-open-01.svg"

Documents are the unit of ingestion. Adds return immediately with status: "queued"; poll until done before relying on search or profiles.

Endpoint Use when
GET /v3/documents/{id} Status + metadata for one document
POST /v3/documents/list Filter and paginate documents
GET /v3/documents/processing Currently processing items
PATCH /v3/documents/{id} Update content or metadata
DELETE /v3/documents/{id} Delete by id or customId
DELETE /v3/documents/bulk Bulk delete
GET /v3/documents/{id}/chunks Inspect RAG chunks
GET /v3/documents/{id}/file-url Presigned URL for uploaded files

Guide: Document operations


Apps Docs Api Reference Ingest (`apps/docs/api-reference/ingest.mdx`)


title: "Ingest" sidebarTitle: "Overview" icon: "/icons/hugeicons/book-open-01.svg"

Send raw content into the processing pipeline. Supermemory extracts memories, chunks for RAG, and updates profiles asynchronously.

Endpoint Use when
POST /v3/documents Text, URLs, or structured content
POST /v3/documents/file Binary file upload
POST /v3/documents/batch Many documents in one request
POST /v4/conversations Chat sessions with turn-aware ingest

Guides: Add memories · Quickstart

Use a stable `customId` (conversation id, doc id) so re-sends upsert instead of duplicating. Pass `dreaming: "instant"` when the next step is memory search or profiles.

Apps Docs Api Reference Memories (`apps/docs/api-reference/memories.mdx`)


title: "Memories" sidebarTitle: "Overview" icon: "/icons/hugeicons/book-open-01.svg"

These endpoints operate on extracted memories, not raw documents.

Endpoint Use when
POST /v4/memories Write memories directly (skip document pipeline)
POST /v4/memories/list List with history / versions
PATCH /v4/memories Update (creates a new version)
DELETE /v4/memories Forget a specific memory
POST /v4/memories/forget-matching Forget by natural-language match

For document-level CRUD, use Documents. For pipeline ingest, use Ingest.

Guide: Memory operations


Apps Docs Api Reference Profiles (`apps/docs/api-reference/profiles.mdx`)


title: "Profiles" sidebarTitle: "Profiles overview" icon: "/icons/hugeicons/id.svg"

Profiles summarize what Supermemory knows about a user or entity in a containerTag.

Endpoint Use when
POST /v4/profile Fetch static + dynamic profile for a container
POST /v4/profile/buckets Profile organized by custom buckets

Guides: User profiles API · Concepts · Buckets


Apps Docs Api Reference Search (`apps/docs/api-reference/search.mdx`)


title: "Recall" sidebarTitle: "Overview" icon: "/icons/hugeicons/book-open-01.svg"

Get context back out of Supermemory: search extracted memories / documents, or fetch a user profile.

Endpoint Role
POST /v4/search Primary recall — searchMode: memories, documents, or hybrid
POST /v3/search Document / SuperRAG-oriented search
POST /v4/profile Static + dynamic profile for a container
POST /v4/profile/buckets Profile organized by custom buckets

Prefer v4 with searchMode: "hybrid" unless you only need document chunks or only extracted memories.

Guides: Search · User profiles · SuperRAG · Memory vs RAG


Apps Docs Api Reference Settings (`apps/docs/api-reference/settings.mdx`)


title: "Settings" sidebarTitle: "Overview" icon: "/icons/hugeicons/book-open-01.svg"

Org-level configuration for extraction, customization, and profile buckets.

Endpoint Use when
GET /v3/settings Read org settings
PATCH /v3/settings Update org settings
POST /v3/settings/suggest-buckets Suggest profile buckets
POST /v3/settings/reset Reset organization data (destructive)

Guide: Customization


Apps Docs Concepts Container Tags (`apps/docs/concepts/container-tags.mdx`)


title: "Namespaces" sidebarTitle: "Namespaces" icon: "/icons/hugeicons/folder-01.svg" keywords: ["container tag", "container tags", "containerTag", "containerTags", "namespace"]

A namespace is the primary way you organize and isolate memories in Supermemory. It is a string identifier you choose, and it scopes every call: add, search, list, update, and delete all happen inside one namespace.

Namespaces were called **container tags** (`containerTag`) in v3 and v4. Same values, nothing moved. See the [v5 migration guide](/migration/api-v5).

Every memory in user_alex lives in its own isolated space, completely separate from memories in user_jordan. This is what makes Supermemory safe to use in multi-tenant applications: one user can never see another user's memories unless you explicitly query both namespaces.

Bucket memories by user, project, agent, workspace, or any boundary that makes sense for your app. Each namespace maps to its own vector index, so search and retrieval never leak across boundaries.

How it works

When you add a document to a namespace, Supermemory creates that namespace (scoped to your organization) if it does not exist yet. You don't need to provision anything ahead of time. The first write to a new namespace creates it, and later writes reuse it.

typescript typescript
import { Supermemory } from "supermemory";

const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });

// First call auto-creates the "user_alex" namespace
await supermemory.add("user_alex", {
  content: "Alex prefers dark mode and concise answers",
});

// Later, retrieve only Alex's memories
const results = await supermemory.search("user_alex", {
  query: "what are the user's UI preferences?",
});
bash curl
curl -X POST "https://api.supermemory.ai/ns/user_alex/document" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "content": "Alex prefers dark mode and concise answers" }'

curl -X POST "https://api.supermemory.ai/ns/user_alex/search" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "what are the user'\''s UI preferences?" }'

Under the hood, each namespace is backed by a dedicated vector index. Embeddings, chunks, and memory entries for one namespace are stored and searched independently of every other namespace. There is no shared index to filter through, which is why isolation is strict rather than best-effort.

A namespace is an **opaque identifier you choose**. Supermemory does not parse meaning out of it: `user_123`, `project_mobile`, and `org:acme:team:growth` are all equally valid. Pick a convention that mirrors the access boundaries in your own application.

Naming rules

Namespaces are validated on every request. A namespace must:

  • Be 100 characters or less
  • Contain only alphanumeric characters, hyphens (-), underscores (_), and colons (:)

Matching pattern: ^[a-zA-Z0-9_:-]+$

typescript
// ✅ Valid
"user_123"
"project-mobile-app"
"org:acme:user:john"
"tenant_42_workspace_7"

// ❌ Invalid — spaces, slashes, and other symbols are rejected
"user 123"
"project/mobile"
"team@acme"

The colon is intentionally allowed so you can build hierarchical namespaces (for example org:acme:user:john) that encode several levels of structure in a single identifier.


Scope lives in the URL

In v5 the namespace is a path segment, not a body field. Every scoped endpoint starts with /ns/{namespace}/, and every scoped SDK call takes namespace as its first argument.

Legacy (v3/v4) v5
containerTag string in the request body /ns/{namespace} in the URL
containerTags array Not supported. One namespace per request.
A request can only touch one namespace. To read across several namespaces, make one request per namespace and merge the results in your application.

Where namespaces are used

The same namespace flows through the entire lifecycle of a memory. Pass it consistently and your data stays neatly partitioned.

Operation Behavior
Add Writes the document into the namespace (auto-creating it).
Search Restricts retrieval to the namespace.
List Returns only documents, chunks, or memories in the namespace.
Update / Delete Targets the document inside the namespace.
typescript
// Add
await supermemory.add("project_q1", { content: "Q1 planning notes" });

// Search within the same namespace
await supermemory.search("project_q1", { query: "planning" });

// List every document in the namespace
await supermemory.list("project_q1", "documents");

Access control

Namespaces are also an authorization boundary, not just an organizational one. Two mechanisms can restrict which namespaces a given caller may touch:

  • API key scopes — an API key can be limited to a specific set of namespaces, and a key can be read-only.
  • Member restrictions — an organization member can be granted access to only certain namespaces, with read or write permission per namespace.

When a request is restricted, Supermemory validates the namespace in the URL against the caller's allowed set:

  • Requesting a namespace outside the allowed set returns 403 Forbidden.
  • A write (add/update/delete) to a read-only namespace returns 403 Forbidden.

This means you can hand out an API key that is physically incapable of reading or writing another tenant's data, enforced at the data layer rather than in your application code.


Managing namespaces

The namespaces.* SDK methods read and change a namespace itself, as opposed to the documents inside it.

Method What it does
supermemory.namespaces.list() Page through namespaces with documentCount and memoryCount.
supermemory.namespaces.get(namespace) Read one namespace and its supportingContext.
supermemory.namespaces.update(namespace, { supportingContext }) Set a context prompt applied when processing documents in this namespace. Useful for steering extraction per project or tenant.
supermemory.namespaces.delete(namespace) Delete the namespace, its namespace-owned memory records, and documents exclusive to it. Shared documents are retained.
supermemory.namespaces.delete(namespace, { moveTo }) Move the content into another namespace, then remove this one. Queued; returns an operationId.

supportingContext is what v3/v4 called entityContext. The legacy "merge" operation is now a delete with moveTo, one source namespace per request.

Namespace deletion and moves require an org admin or owner with write access to the source namespace; moves also require write access to the destination. The default sm_project_default namespace cannot be permanently deleted. See deletion scope and erasure limits.

typescript typescript
await supermemory.namespaces.update("project_research", {
  supportingContext: "This project contains research papers about machine learning.",
});

const { namespaces, pagination } = await supermemory.namespaces.list();
// namespaces: [{ id, namespace, documentCount, memoryCount, description, system }]

// Consolidate: move everything from the old namespace, then remove it
await supermemory.namespaces.delete("project_research_old", {
  moveTo: "project_research",
});
// { status: "queued", operationId, namespace, moveTo }
bash curl
curl -X PATCH "https://api.supermemory.ai/ns/project_research" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "supportingContext": "This project contains research papers about machine learning." }'

curl "https://api.supermemory.ai/namespaces" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

curl -X DELETE "https://api.supermemory.ai/ns/project_research_old?moveTo=project_research" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

Choosing a convention

Pick a naming scheme that maps onto the isolation boundaries your application actually needs.

Pattern Example Use case
User isolation user_{userId} Per-user memory in a consumer app
Project grouping project_{projectId} Project- or workspace-scoped content
Agent scoping agent_{agentId} Separate long-term memory per AI agent
Hierarchical org:{orgId}:user:{userId} Multi-level, multi-tenant SaaS
Keep namespaces **deterministic** — derive them directly from IDs you already have (a user ID, a tenant ID) so you can always reconstruct the right namespace at query time without a lookup.

Next steps

Combine namespaces with metadata filters for precise retrieval. Mint keys that can only touch one namespace — multi-tenant clients without the org master key. See namespaces in action across the add API.

Apps Docs Concepts Content Types (`apps/docs/concepts/content-types.mdx`)


title: "Supported content types" sidebarTitle: "Multi-modal ingestion" icon: "/icons/hugeicons/files-01.svg"

Supermemory automatically extracts and indexes content from various formats. There are two entry points: supermemory.add() for text and URLs, supermemory.documents.uploadFile() for actual files. Both take the target namespace as the first argument. See Add Memories to learn how to ingest content via the API.

Text content

Raw text, conversations, notes, or any string content.

typescript
await supermemory.add("user_123", {
  content: "User prefers dark mode and uses vim keybindings",
});

Best for: Chat messages, user preferences, notes, logs, transcripts.


URLs & Web pages

Send a URL and Supermemory fetches, extracts, and indexes the content.

typescript
await supermemory.add("documentation", {
  content: "https://docs.example.com/api-reference"
});

Extracts: Article text, headings, metadata. Strips navigation, ads, boilerplate.


Documents

PDF

Files are binary, so they go through uploadFile, not add — pass a stream, not base64. The upload is multipart, so metadata is a JSON string here:

typescript
import fs from 'fs';

await supermemory.documents.uploadFile("user_123", {
  file: fs.createReadStream('report.pdf'),
  metadata: JSON.stringify({ title: "Q4 Financial Report" }),
});

Extracts: Text, tables, headers. OCR for scanned documents.

Advanced document extraction

Scale and Enterprise include advanced extraction for PDFs: page-by-page OCR with descriptions of figures and diagrams merged into the extracted text. This helps make the visual content in reports, research papers, and technical documents searchable alongside their text.

Free, Pro, and Max continue to support standard PDF extraction, including OCR for scanned documents. The advanced extraction tier applies to PDFs; it does not change support for other file types.

Use the same uploadFile call shown above. Supermemory selects the extraction tier automatically from your organization's plan; no additional request parameter is needed. If advanced extraction cannot process a PDF, Supermemory falls back to standard extraction, so figure and diagram descriptions are not guaranteed for every document.

See Billing & usage for plan availability.

Microsoft Office

Word, Excel, and PowerPoint files upload the same way — Supermemory detects the type from the file itself:

typescript
await supermemory.documents.uploadFile("user_123", {
  file: fs.createReadStream('roadmap.docx'),
  metadata: JSON.stringify({ title: "Product Roadmap" }),
});

Google Workspace

Automatically handled via Google Drive connector:

  • Google Docs
  • Google Sheets
  • Google Slides

Code & markdown

Both are plain text, so they go through add like any other string content — no file upload needed:

typescript
// Markdown
await supermemory.add("user_123", {
  content: markdownContent,
  metadata: { title: "README.md" },
});

// Code (language auto-detected)
await supermemory.add("user_123", {
  content: codeContent,
  metadata: { language: "typescript" },
});

Extracts: Structure, headings, code blocks with syntax awareness.

See Super RAG for how Supermemory optimizes chunking for each content type.


Images

Set fileType: "image" and mimeType so Supermemory knows exactly how to process it. Both are multipart form fields, sent in the same object as file and metadata:

typescript
await supermemory.documents.uploadFile("user_123", {
  file: fs.createReadStream('diagram.png'),
  metadata: JSON.stringify({ title: "Architecture Diagram" }),
  fileType: "image",
  mimeType: "image/png",
});

Extracts: OCR text, visual descriptions, diagram interpretations.

Supported: PNG, JPG, JPEG, WebP, GIF


Audio & video

Video and audio each have a dedicated fileType ("video", "audio"); set it with mimeType when inference is not enough:

typescript
// Video
await supermemory.documents.uploadFile("user_123", {
  file: fs.createReadStream('demo.mp4'),
  metadata: JSON.stringify({ title: "Product Demo" }),
  fileType: "video",
  mimeType: "video/mp4",
});

// Audio
await supermemory.documents.uploadFile("user_123", {
  file: fs.createReadStream('call-recording.mp3'),
  metadata: JSON.stringify({ title: "Customer Call Recording" }),
  fileType: "audio",
  mimeType: "audio/mpeg",
});

Extracts: Transcription, speaker detection, topic segmentation.

Supported: MP3, WAV, M4A, MP4, WebM


Structured data

JSON and CSV are text — stringify and send them through add(), no file upload needed.

JSON

typescript
await supermemory.add("user_123", {
  content: JSON.stringify(userData),
  metadata: { title: "User Profile Data", format: "json" },
});

CSV

typescript
await supermemory.add("user_123", {
  content: csvContent,
  metadata: { title: "Sales Data Q4", format: "csv" },
});

File upload

For any binary file, use uploadFile — it accepts a stream, not base64:

typescript
import fs from 'fs';

await supermemory.documents.uploadFile("user_123", {
  file: fs.createReadStream('./document.pdf'),
  metadata: JSON.stringify({ title: "document.pdf" }),
});

No Node fs access? uploadFile also accepts a web File or Blob:

typescript
await supermemory.documents.uploadFile("user_123", {
  file: new File(['my bytes'], 'file.txt'),
});

await supermemory.documents.uploadFile("user_123", {
  file: await fetch('https://somesite/file').then((r) => r.blob())
});

Auto-Detection

add() tells URLs and plain text apart on its own — no extra flag needed:

typescript
// URL detected automatically
await supermemory.add("user_123", { content: "https://example.com/page" });

// Plain text detected automatically
await supermemory.add("user_123", { content: "User said they prefer email contact" });

For files, uploadFile detects type from the file itself in most cases. fileType and mimeType only exist to force specific processing when inference is not enough — set them for images, video, and audio.


Content limits

Type Max Size
Text 1MB
Files 50MB
URLs Fetched content up to 10MB

Typical processing time: text is near-instant; PDFs take 1-5s; images 2-10s; video 10s+; webpages 1-3s. Text content is split on sentence boundaries, with about 200 characters of overlap carried between chunks.

For large files, consider chunking or using [connectors](/connectors/overview) for automatic sync.

Next steps

Upload content via the API How content is chunked and indexed

Apps Docs Concepts Customization (`apps/docs/concepts/customization.mdx`)


title: "Customizing for your use case" sidebarTitle: "Customizing" icon: "/icons/hugeicons/settings-02.svg"

Configure how Supermemory processes and retrieves content for your specific use case.

Organizational Context

Tell Supermemory what your organization does and what content matters during ingestion. This steers what gets extracted into memories across every namespace. In v3/v4 this field was called filterPrompt.

typescript
// Example: Brand guidelines assistant
await supermemory.organization.update({
  organizationalContext: `You are ingesting content for Brand.ai's brand guidelines system.

    Index:
    - Official brand values and mission statements
    - Approved tone of voice guidelines
    - Logo usage and visual identity docs
    - Approved messaging and taglines

    Skip:
    - Draft documents and work-in-progress
    - Outdated brand materials (pre-2024)
    - Internal discussions about brand changes
    - Competitor analysis docs`,
});
```typescript organizationalContext: `Personal AI assistant. Prioritize recent content, action items, and personal context. Exclude spam and duplicates.` ``` ```typescript organizationalContext: `Customer support agent. Prioritize verified solutions, official docs, and resolved tickets. Exclude internal discussions and PII.` ``` ```typescript organizationalContext: `Legal research assistant. Prioritize precedents, current regulations, and approved contract language. Exclude privileged communications.` ``` ```typescript organizationalContext: `Financial analysis assistant. Prioritize latest reports, verified data, and regulatory filings. Exclude speculative data and MNPI.` ``` ```typescript organizationalContext: `Healthcare information assistant. Prioritize evidence-based guidelines and FDA-approved info. Exclude PHI and outdated recommendations.` ``` ```typescript organizationalContext: `Developer documentation assistant. Prioritize current APIs, working examples, and best practices. Exclude deprecated APIs and test fixtures.` ```

The value replaces the existing context in full. Send null to clear it. The legacy shouldLLMFilter, categories, includeItems, and excludeItems settings are not part of the v5 organization resource.


Supporting Context

Guide memory extraction for a specific namespace. Organizational context is org-wide; supporting context is per namespace. In v3/v4 this field was called entityContext.

typescript
await supermemory.add("session_abc123", {
  content: "User asked about logo variations for dark backgrounds...",
  supportingContext: `Design exploration conversation between [email protected] and Brand.ai assistant.
    Focus on John's design preferences and brand requirements.`,
});
Set the supporting context for a namespace without uploading content.
typescript
await supermemory.namespaces.update("session_abc123", {
  supportingContext: `Design exploration conversation between [email protected] and Brand.ai assistant.
    Focus on John's design preferences and brand requirements.`,
});
Supporting context persists on the namespace and combines with the org-level organizational context.
The Chunk size and Connector branding sections below use the legacy settings resource. Their `client.settings.update(...)` examples require a legacy SDK that exposes `settings`; SDK 5.x users should call `PATCH /v3/settings` directly with an org admin or owner credential. These options are not fields on the v5 organization resource.

Chunk size

Control how documents are split into searchable pieces. Smaller chunks = more precise retrieval but less context per result.

typescript
await client.settings.update({
  chunkSize: 512  // -1 for default
});
Use Case Chunk Size Why
Citations & references 256-512 Precise source attribution
Q&A / Support 512-1024 Balanced context
Long-form analysis 1024-2048 More context per chunk
Default -1 Supermemory's optimized default
Smaller chunks generate more memories per document. Larger chunks provide more context but may reduce precision.

Connector branding

Show "Log in to YourApp" instead of "Log in to Supermemory" when users connect external services. See Connectors Overview for the full list of supported integrations.

1. Create OAuth credentials in [Google Cloud Console](https://console.cloud.google.com/) 2. Redirect URI: `https://api.supermemory.ai/v3/connections/auth/callback/google-drive`
text
```typescript
await client.settings.update({
  googleDriveCustomKeyEnabled: true,
  googleDriveClientId: "your-client-id.apps.googleusercontent.com",
  googleDriveClientSecret: "your-client-secret"
});
```
1. Create integration at [Notion Developers](https://developers.notion.com/) 2. Redirect URI: `https://api.supermemory.ai/v3/connections/auth/callback/notion`
text
```typescript
await client.settings.update({
  notionCustomKeyEnabled: true,
  notionClientId: "your-notion-client-id",
  notionClientSecret: "your-notion-client-secret"
});
```
1. Register app in [Azure Portal](https://portal.azure.com/) 2. Redirect URI: `https://api.supermemory.ai/v3/connections/auth/callback/onedrive`
text
```typescript
await client.settings.update({
  onedriveCustomKeyEnabled: true,
  onedriveClientId: "your-azure-app-id",
  onedriveClientSecret: "your-azure-client-secret"
});
```

API reference

typescript
// Read organization settings
const { organizationalContext, namespaceCount } = await supermemory.organization.get();

// Update organization context
await supermemory.organization.update({ organizationalContext: "..." });

// Read and update one namespace
const ns = await supermemory.namespaces.get("session_abc123");
await supermemory.namespaces.update("session_abc123", {
  supportingContext: "...",
});
Organization settings are organization-wide and require an organization administrator. Changes apply to new content only—existing memories aren't reprocessed.

Next steps

See your custom settings in action Set up automatic syncing from external platforms

Apps Docs Concepts Filtering (`apps/docs/concepts/filtering.mdx`)


title: "Organizing & filtering memories" sidebarTitle: "Metadata filtering" icon: "/icons/hugeicons/filter.svg"

Supermemory provides two ways to organize your memories:

**Organize memories** into isolated spaces by user, project, or workspace **Query memories** by custom properties like category, status, or date

Both can be used independently or together for precise filtering.


Namespaces

Namespaces create isolated memory spaces. Use them to separate memories by user, project, or any logical boundary. The namespace is part of the URL (/ns/{namespace}/...), so namespace-scoped SDK calls take it as the first argument.

Adding Memories to a Namespace

typescript
await supermemory.add("user_123", {
  content: "Meeting notes from Q1 planning",
});

Searching a Namespace

typescript
const results = await supermemory.search("user_123", {
  query: "planning notes",
  searchMode: "chunks",
});
Each search is scoped to a single namespace. Passing `"user_123"` as the namespace restricts results to memories stored in that namespace.
Pattern Example Use Case
User isolation user_{userId} Per-user memories
Project grouping project_{projectId} Project-specific content
Hierarchical org_{orgId}_team_{teamId} Multi-level organization
```typescript // Multi-tenant SaaS - isolate by organization and user await supermemory.add("org_acme_user_john", { content: "Company policy document", });
text
// Search only within that user's org context
const results = await supermemory.search("org_acme_user_john", {
  query: "vacation policy",
  searchMode: "chunks",
});

// Project-based isolation
await supermemory.add("project_mobile_app", {
  content: "Sprint 5 retrospective notes",
});

// Time-based segmentation
await supermemory.add("user_cfo_2024_q1", {
  content: "Q1 2024 financial report",
});
```

The namespace is a path parameter on every v5 call (`add`, `search`, `list`, `profile`, `documents.*`). There is no array form; one request touches one namespace.

Metadata

Metadata lets you attach custom properties to memories and filter by them later.

Adding memories with metadata

typescript
await supermemory.add("user_123", {
  content: "Technical design document for auth system",
  metadata: {
    category: "engineering",
    priority: "high",
    year: 2024,
  },
});

Searching with a Filter

A filter is a typed expression. One condition is an object with field, operator, and value. Several conditions are combined with an and or or node that lists them in operands:

typescript
const results = await supermemory.search("user_123", {
  query: "design document",
  filter: {
    operator: "and",
    operands: [
      { field: "category", operator: "eq", value: "engineering" },
      { field: "priority", operator: "eq", value: "high" },
    ],
  },
  searchMode: "chunks",
});

A single condition needs no wrapper:

typescript
filter: { field: "status", operator: "eq", value: "published" }

Operators

Operator Value type Example
eq, neq string, number, boolean { field: "status", operator: "eq", value: "published" }
gt, gte, lt, lte number { field: "priority", operator: "gte", value: 5 }
contains, notContains string { field: "title", operator: "contains", value: "react" }
arrayContains, arrayNotContains string { field: "tags", operator: "arrayContains", value: "important" }

String conditions accept an optional caseSensitive boolean. Numeric values are JSON numbers, not numeric strings.

Combining filters

Nest and and or nodes for complex queries:

typescript
const results = await supermemory.search("user_123", {
  query: "meeting notes",
  filter: {
    operator: "and",
    operands: [
      { field: "type", operator: "eq", value: "meeting" },
      {
        operator: "or",
        operands: [
          { field: "team", operator: "eq", value: "engineering" },
          { field: "team", operator: "eq", value: "product" },
        ],
      },
    ],
  },
  searchMode: "chunks",
});

Excluding results

Use the negative operators (neq, notContains, arrayNotContains) to exclude matches:

typescript
const results = await supermemory.search("user_123", {
  query: "documentation",
  filter: { field: "status", operator: "neq", value: "draft" },
  searchMode: "chunks",
});
**Substring match, case-insensitive:** ```typescript // Find documents with "machine learning" in the description const results = await supermemory.search("user_123", { query: "AI research", filter: { field: "description", operator: "contains", value: "machine learning", caseSensitive: false, }, searchMode: "chunks", }); ```
text
**Numeric comparisons:**
```typescript
// Find high-priority items created after a specific date
const results = await supermemory.search("user_123", {
  query: "tasks",
  filter: {
    operator: "and",
    operands: [
      { field: "priority", operator: "gte", value: 7 },
      { field: "created_timestamp", operator: "gte", value: 1704067200 }, // Unix timestamp
    ],
  },
  searchMode: "chunks",
});
```

**Array membership:**
```typescript
// Find documents where a specific user is a participant
const results = await supermemory.search("user_123", {
  query: "meeting notes",
  filter: { field: "participants", operator: "arrayContains", value: "[email protected]" },
  searchMode: "chunks",
});
```

**Complex nested filters:**
```typescript
// (category = "tech" OR category = "science") AND status != "archived"
const results = await supermemory.search("user_123", {
  query: "research papers",
  filter: {
    operator: "and",
    operands: [
      {
        operator: "or",
        operands: [
          { field: "category", operator: "eq", value: "tech" },
          { field: "category", operator: "eq", value: "science" },
        ],
      },
      { field: "status", operator: "neq", value: "archived" },
    ],
  },
  searchMode: "chunks",
});
```
**User's work documents from 2024:** ```typescript const results = await supermemory.search("user_123", { query: "quarterly report", filter: { operator: "and", operands: [ { field: "category", operator: "eq", value: "work" }, { field: "type", operator: "eq", value: "report" }, { field: "year", operator: "eq", value: 2024 }, ], }, searchMode: "chunks", }); ```
text
**Team meeting notes with specific participants:**
```typescript
const results = await supermemory.search("project_alpha", {
  query: "sprint planning",
  filter: {
    operator: "and",
    operands: [
      { field: "type", operator: "eq", value: "meeting" },
      {
        operator: "or",
        operands: [
          { field: "participants", operator: "arrayContains", value: "alice" },
          { field: "participants", operator: "arrayContains", value: "bob" },
        ],
      },
    ],
  },
  searchMode: "chunks",
});
```

**Exclude drafts and deprecated content:**
```typescript
const results = await supermemory.search("user_123", {
  query: "documentation",
  filter: {
    operator: "and",
    operands: [
      { field: "status", operator: "neq", value: "draft" },
      { field: "content", operator: "notContains", value: "deprecated" },
      { field: "tags", operator: "arrayNotContains", value: "archived" },
    ],
  },
  searchMode: "chunks",
});
```

The same filter expression works on list and profile calls:

typescript
// Page through matching documents
const { documents, pagination } = await supermemory.list("user_123", "documents", {
  filter: { field: "category", operator: "eq", value: "engineering" },
});

// Build the profile only from matching memories
const { profile } = await supermemory.profile("user_123", {
  filter: { field: "source", operator: "eq", value: "onboarding" },
});

Quick reference

When adding memories

typescript
await supermemory.add("user_123", { // Isolation
  content: "Your content here",
  metadata: { key: "value" },         // Custom properties
});

When searching

typescript
const results = await supermemory.search("user_123", { // Scopes results to this namespace
  query: "search query",
  filter: { field: "status", operator: "eq", value: "published" }, // Optional
  searchMode: "chunks",
});

Metadata key rules

  • Allowed characters: a-z, A-Z, 0-9, _, -, .
  • No spaces or special characters
  • Keys are literal: customer.plan is one key named customer.plan, not a nested path

Query complexity limits

  • Maximum 5 levels of nested and/or expressions
  • Maximum 200 operands per logical group
If you need more conditions than these limits allow, break your query into multiple requests or use broader search terms with post-processing.

Reading One Document's Chunks

Scoping a search to a single document (docId) is not part of v5. To work with the chunks of one long document, fetch the document with its chunks attached:

typescript
const doc = await supermemory.documents.get("user_123", "doc_123", {
  include: ["chunks"],
});

Next steps

Apply filters in search queries Add content with namespaces and metadata

Apps Docs Concepts Graph Memory (`apps/docs/concepts/graph-memory.mdx`)


title: "Graph memory" sidebarTitle: "Graph memory" icon: "/icons/hugeicons/share-08.svg"

How understanding is stored and stays true over time.

Supermemory builds a living knowledge graph of facts on top of other facts — not a static folder of embeddings, and not classic entity–relation–entity triples you maintain by hand.

The pipeline that turns a chat or file into memories is How it works.

This page is the model: what a memory is, how edges form, and why agents utilize supermemory's graph

Try it

Get a key from the developer console — API Keys → Create API Key — then add a memory and pull it back with related edges:

typescript
import { Supermemory } from "supermemory";

const supermemory = new Supermemory({ apiKey: "sm_..." }); // from console.supermemory.ai → API Keys

await supermemory.add("user_123", {
  content: "Alex mentioned he just started at Stripe",
  dreaming: "instant", // memories land in the graph right away
});

const { results } = await supermemory.search("user_123", {
  query: "where does Alex work?",
  include: { related: true },
  searchMode: "memories",
});

Full walkthrough with a live example: Quickstart.

Documents vs memories

Documents Memories
What Raw input you send Facts Supermemory extracts
Examples PDF, chat log, Drive file, URL “Alex is a PM at Stripe”
Role Source of truth for RAG / SuperRAG Personal and entity state over time
Lifecycle You add / update / delete Graph updates, extends, derives, forgets

Think of documents as books you hand the system. Memories are the insights it keeps — connected to each other as new content arrives.

Uploading a long PDF does more than store bytes: Supermemory derives many memories and links them to what it already knows about that entity or user. Chunks of the document remain available for [SuperRAG](/concepts/super-rag) grounding.

Properties and rules of memories

  1. Memories are atomic - Each memory has enough information and context about one particular topic
  2. They always build on top of each other - with updates, the model knows the history, a memory extends from other memories, and new facts are derived (derives relation) from existing knowledge.

Memory relationships

When content is processed, new facts connect to existing ones through three relationship types.

Updates — information changes

New fact replaces what was true before for search purposes; history can remain for audit.

text
Memory 1: "Alex works at Google as a software engineer"
Memory 2: "Alex just started at Stripe as a PM"
         → Memory 2 UPDATES Memory 1

isLatest (and related graph fields) keep retrieval on the current fact without erasing the past.

Extends — information enriches

New fact adds detail without invalidating the old one.

text
Memory 1: "Alex works at Stripe as a PM"
Memory 2: "Alex focuses on payments and leads a team of 5"
         → Memory 2 EXTENDS Memory 1

Both stay valid; context gets richer.

Derives — information infers

Supermemory infers a fact you never stated in one place, from patterns across memories.

text
Memory 1: "Alex is a PM at Stripe"
Memory 2: "Alex frequently discusses payment APIs and fraud detection"
         → Derived: "Alex likely works on Stripe's core payments product"

That is the same class of “entity chain” you see in the quickstart (gift → VP of Product → Sarah → Tokyo offsite). Search can expose edges via include: { related: true }; they come back under included.related — see Search API.

Automatic extraction (one input → many facts)

Input:

Had a great call with Alex. He's enjoying the new PM role at Stripe, though the payments work is intense. He moved to Seattle for the job—Capitol Hill. Wants dinner next time I'm in town.

Example extracted memories:

  • Alex works at Stripe as a PM
  • Alex works on payments infrastructure (extends role)
  • Alex lives in Seattle, Capitol Hill
  • Alex wants to meet for dinner (episodic)

You do not define schema or draw edges. You add content; the graph updates.

Dreaming keeps the graph alive

Ingest is not a one-shot snapshot. After (and alongside) indexing, dreaming continues building the graph: extracting facts, linking related memories, resolving updates, and producing derives you never stated in one place.

By default Supermemory uses dreaming: "dynamic" — related documents are grouped so memories form from coherent units (e.g. a real multi-turn session), not each isolated write in isolation. That is why production quality is higher when you keep a stable id on conversations and let dynamic dreaming do its job.

Use dreaming: "instant" when this document must hit the graph immediately (demos, “search right after add”). That path processes the document alone and costs an extra operation.

How to set the flag, statuses, and when done means what: How it works → Dreaming and Processing modes.

Memory types

Type Example Behavior
Facts “Alex is a PM at Stripe” Persists until updated
Preferences “Alex prefers morning meetings” Strengthens with repetition
Episodes “Met Alex for coffee Tuesday” Decays unless significant

Automatic forgetting

  • Time-based — temporary facts drop after they expire (“exam tomorrow”, “meeting at 3pm today”).
  • Contradiction — updates win for “what’s true now.”
  • Noise filtering — casual, non-meaningful chatter is less likely to become durable memory.

For explicit product controls (forget, review low-confidence derives), see Forget & update and Memory review.

What you don’t do

You do not hand-maintain the graph. You:

  1. Ingest under a namespace
  2. Wait for the pipeline when needed
  3. Search or load a profile
typescript
await supermemory.add("user_123", {
  content: "Alex mentioned he just started at Stripe",
});

const { results } = await supermemory.search("user_123", {
  query: "where does Alex work?",
  include: { related: true },
  searchMode: "memories",
});
// Prefer latest work fact (Stripe); history remains in the graph.
// Related edges come back under results[i].included.related
If you need… Go to
Pipeline statuses, dreaming, documents in How it works
Memory vs document retrieval Memory vs RAG · SuperRAG
Always-on summary of a user Profiles
Isolation / tenants Multi-tenancy
API: add / search / forget Ingestion · Search · Forget & update
Ingest pipeline, statuses, and outputs. When to use memory vs document retrieval. Static + dynamic context built from the graph. See entity chains in a full conversation + document flow.

Apps Docs Concepts How It Works (`apps/docs/concepts/how-it-works.mdx`)


title: "How Supermemory works" sidebarTitle: "How it works" icon: "/icons/hugeicons/cpu.svg"

At its core, supermemory is powered by a custom learning model and a graph database that we built internally.

Decides what and how to learn, what is important, when to forget, creating relations, etc. Where the learnings are actually stored, optimized for search. Fact-based temporal graph that has Vector, FTS, and graph built in.

But, you don't have to think about the above. The interface for users is as simple as it gets.

Get started in under a minute

From the [developer console](https://console.supermemory.ai) — **API Keys → Create API Key**. `console.supermemory.ai` is where keys and usage live. Install the SDK, drop in your key, add a memory, and search it — right below, or the full [ingest → retrieve loop](/using-supermemory). ```typescript TypeScript import { Supermemory } from "supermemory";

const supermemory = new Supermemory({ apiKey: "sm_..." }); // from console.supermemory.ai → API Keys

await supermemory.add("user_123", {
content: "The user loves Paris.",
dreaming: "instant", // memories available right after processing
});

const { results } = await supermemory.search("user_123", {
query: "where does the user want to travel?",
});

text
```python Python
# pip install supermemory
from supermemory import Supermemory

client = Supermemory(api_key="sm_...")  # from console.supermemory.ai → API Keys

client.add(
    "user_123",
    content="The user loves Paris.",
    dreaming="instant",  # memories available right after processing
)

results = client.search("user_123", query="where does the user want to travel?")
bash curl
curl -X POST "https://api.supermemory.ai/ns/user_123/document" \
  -H "Authorization: Bearer sm_..." \
  -H "Content-Type: application/json" \
  -d '{ "content": "The user loves Paris.", "dreaming": "instant" }'

curl -X POST "https://api.supermemory.ai/ns/user_123/search" \
  -H "Authorization: Bearer sm_..." \
  -H "Content-Type: application/json" \
  -d '{ "query": "where does the user want to travel?" }'

What you send: documents

A document is raw input — whatever you hand Supermemory:

  • Conversation transcripts and messages
  • Text, markdown, HTML
  • PDFs, images, audio/video, code
  • URLs and connector items (Drive, Notion, Gmail, …)

You do not pre-chunk or pick an embedding model. See Multi-modal ingestion for formats, and Add context for the API.

Supermemory handles the ingestion and extraction for you. This also gives us a big advantage for quality - The engine extracts it in an optimized way with Contextual Chunking and other features for better quality search and memory generation.

Use a stable id when the same conversation or file will be updated later (sessions, connector syncs). That identity also drives diff billing on re-ingest.

What the pipeline does

Stage What happens
Queued Accepted; waiting to run
Extracting Text / OCR / transcription / page fetch
Chunking Splits content for retrieval (type-aware where needed)
Embedding Vectors for similarity search
Indexing Makes chunks and derived structure searchable
Done Document path is ready for search
typescript
const doc = await supermemory.add("user_123", {
  content: conversationText,
  id: "chat_session_1",
});

// Poll until ready
const { system } = await supermemory.documents.get("user_123", doc.id);
// system.status → "queued" | "extracting" | ... | "done" | "failed"

Larger PDFs and long video take longer. Short chat turns usually finish in seconds.

Dreaming (how memories enter the graph)

A document with status done has its chunks indexed for search. Memories (the graph's facts, updates and derived facts) come from a second phase called dreaming.

This is when the content is passed through the memory model and merged, arranged and organized for the future.

Pass dreaming on add (a field in the JSON body, next to content in the SDK):

Mode Default? Behavior When to use
dynamic Yes Related documents are grouped so memories form from coherent units, not one isolated write at a time. Graph quality is higher for real multi-turn / multi-doc flows. Memory extraction may continue after status: "done". Production agents, connectors, ongoing sessions
instant No This document is dreamed on its own, right away. Memories are available as soon as processing finishes for that document. Bills one extra operation per document. Demos, quickstarts, “I need the graph now”
typescript
// Production default — omit or set explicitly
await supermemory.add("user_123", {
  content: conversationText,
  id: "chat_session_1",
  dreaming: "dynamic",
});

// Need memories immediately (e.g. tutorial)
await supermemory.add("user_123", {
  content: conversationText,
  id: "chat_session_1",
  dreaming: "instant",
});

Rule of thumb: use dynamic in real apps for better quality and cost, because batching lets new memories connect to related ones. Use instant when the very next step is a memory search or profile that must already reflect this document, as in the quickstart.

How those memories connect and stay true over time is Graph memory. API detail: Processing modes.

What you get out

After the pipeline runs, the same document leads to three things -> Chunks, Memories and Profile. (in the same namespace):

Output Role Go deeper
Document chunks Grounding in the raw source (RAG / SuperRAG) SuperRAG, Search API
Memories Extracted facts in a living graph — updates, links, time Graph memory
Profile A sample of memories, static + dynamic summary for always-on context Profiles, Profile API

Supermemory does more than store the file. It derives memories (what it understood) and keeps chunks (the source), so you can both personalize and ground answers. That distinction is the core of Memory vs RAG.

Isolation and identity

  • namespace — hard isolation boundary (user, tenant, project), carried in the URL. See Namespaces.
  • Metadata — soft dimensions inside a namespace for filtering. See Metadata filtering.
  • Scoped API keys — credentials that cannot cross a namespace. See API keys.

Next steps

How facts connect, update, and stay true over time. Formats, extractors, and what you can send. API: add, id, files, dreaming, status. Query documents and memories after the pipeline finishes.

Apps Docs Concepts Memory Vs Rag (`apps/docs/concepts/memory-vs-rag.mdx`)


title: "Memory vs RAG: Understanding the difference" sidebarTitle: "Memory vs RAG" icon: "/icons/hugeicons/balance-scale.svg"

Most developers confuse RAG (Retrieval-Augmented Generation) with agent memory. They're not the same thing, and using RAG for memory is why your agents keep forgetting important context. Let's understand the fundamental difference.

The core problem

When building AI agents, developers often treat memory as just another retrieval problem. They store conversations in a vector database, embed queries, and hope semantic search will surface the right context.

This approach fails because memory isn't about finding similar text—it's about understanding relationships, temporal context, and user state over time.

Documents vs memories in Supermemory

Supermemory makes a clear distinction between these two concepts:

Documents: Raw knowledge

Documents are the raw content you send to Supermemory—PDFs, web pages, text files. They represent static knowledge that doesn't change based on who's accessing it.

Characteristics:

  • Stateless: A document about Python programming is the same for everyone
  • Unversioned: Content doesn't track changes over time
  • Universal: Not linked to specific users or entities
  • Searchable: Perfect for semantic similarity search

Use Cases:

  • Company knowledge bases
  • Technical documentation
  • Research papers
  • General reference material

Memories: Contextual understanding

Memories are the insights, preferences, and relationships extracted from documents and conversations. They're tied to specific users or entities and evolve over time.

Characteristics:

  • Stateful: "User prefers dark mode" is specific to that user
  • Temporal: Tracks when facts became true or invalid
  • Personal: Linked to users, sessions, or entities
  • Relational: Understands connections between facts

Use Cases:

  • User preferences and history
  • Conversation context
  • Personal facts and relationships
  • Behavioral patterns

Why RAG fails as memory

Let's look at a real scenario that illustrates the problem:

``` Day 1: "I love Adidas sneakers" Day 30: "My Adidas broke after a month, terrible quality" Day 31: "I'm switching to Puma" Day 45: "What sneakers should I buy?" ``` ```python # RAG sees these as isolated embeddings query = "What sneakers should I buy?"
text
# Semantic search finds closest match
result = vector_search(query)
# Returns: "I love Adidas sneakers" (highest similarity)

# Agent recommends Adidas 🤦
```

**Problem**: RAG finds the most semantically similar text but misses the temporal progression and causal relationships.
```python # Supermemory understands temporal context query = "What sneakers should I buy?"
text
# Memory retrieval considers:
# 1. Temporal validity (Adidas preference is outdated)
# 2. Causal relationships (broke → disappointment → switch)
# 3. Current state (now prefers Puma)

# Agent correctly recommends Puma ✅
```

**Solution**: Memory systems track when facts become invalid and understand causal chains.

The technical difference

RAG: Semantic similarity

text
Query → Embedding → Vector Search → Top-K Results → LLM

RAG excels at finding information that's semantically similar to your query. It's stateless—each query is independent.

Memory: Contextual graph

text
Query → Entity Recognition → Graph Traversal → Temporal Filtering → Context Assembly → LLM

Memory systems build a knowledge graph that understands:

  • Entities: Users, products, concepts
  • Relationships: Preferences, ownership, causality
  • Temporal Context: When facts were true
  • Invalidation: When facts became outdated

When to use each

- Static documentation - Knowledge bases - Research queries - General Q&A - Content that doesn't change per user - User preferences - Conversation history - Personal facts - Behavioral patterns - Anything that evolves over time

Real-world examples

E-commerce assistant

Stores product catalogs, specifications, reviews
text
```python
# Good for RAG
"What are the specs of iPhone 15?"
"Compare Nike and Adidas running shoes"
"Show me waterproof jackets"
```
Tracks user preferences, purchase history, interactions
text
```python
# Needs Memory
"What size do I usually wear?"
"Did I like my last purchase?"
"What's my budget preference?"
```

Customer support bot

FAQ documents, troubleshooting guides, policies
text
```python
# Good for RAG
"How do I reset my password?"
"What's your return policy?"
"Troubleshooting WiFi issues"
```
Previous issues, user account details, conversation context
text
```python
# Needs Memory
"Is my issue from last week resolved?"
"What plan am I on?"
"You were helping me with..."
```

How Supermemory handles both

Supermemory provides a unified platform that correctly handles both patterns:

1. Document storage (RAG)

python
# Add a document for RAG-style retrieval
client.add(
    "product_knowledge",  # Shared namespace, not tied to a user
    content="iPhone 15 has a 48MP camera and A17 Pro chip",
    dreaming="instant"
)

2. Memory creation

python
# Add a user-specific memory
client.add(
    "user_123",  # User-specific namespace
    content="User prefers Android over iOS",
    dreaming="instant",
    metadata={
        "type": "preference",
        "confidence": "high"
    }
)

3. Hybrid retrieval

python
# Search is scoped to one namespace, so query each side on its own
user = client.profile("user_123")  # User's Android preference (memory)

specs = client.search(
    "product_knowledge",  # Latest Android phone specs (document chunks)
    query="What phone should I recommend?",
    search_mode="chunks",
)

# Hand both to the model: user.profile + specs.results

The bottom line

**Key Insight**: RAG answers "What do I know?" while Memory answers "What do I remember about you?"

Stop treating memory like a retrieval problem. Your agents need both:

  • RAG for accessing knowledge
  • Memory for understanding users

Supermemory provides both capabilities in a unified platform, ensuring your agents have the right context at the right time.


Next steps

How memory relationships work Our managed RAG solution Start ingesting content Query your memories and documents

Apps Docs Concepts Multi Tenancy Examples (`apps/docs/concepts/multi-tenancy-examples.mdx`)


title: "Multi-tenancy examples" sidebarTitle: "Examples" icon: "/icons/hugeicons/check-list.svg"

A few common shapes multi-tenancy takes in practice, combining namespaces for isolation with metadata filters for organization within a boundary.


Personal agent

A single namespace per user is enough — there's no shared data to leak, so metadata is optional.

typescript
await supermemory.add(userId, { // one namespace per user
  content: "User prefers morning workouts and vegetarian meals",
});

const results = await supermemory.search(userId, {
  query: "workout preferences",
});

Company agent (shared + personal memory)

A company-wide assistant usually needs two kinds of namespaces: one shared namespace the whole org reads from, and one personal namespace per employee that nobody else can see.

typescript
// Shared org knowledge — visible to everyone at the company
await supermemory.add("org_acme_shared", {
  content: "Q3 roadmap: ship the mobile app redesign by end of August",
  metadata: { team: "product", type: "roadmap" },
});

// Personal memory — only this employee's agent should see this
await supermemory.add("org_acme_user_alex", {
  content: "Prefers async updates over meetings",
});

Inside the shared namespace, use metadata to scope queries to a team rather than creating a namespace per team:

typescript
const results = await supermemory.search("org_acme_shared", {
  query: "roadmap updates",
  filter: { field: "team", operator: "eq", value: "product" },
  searchMode: "chunks",
});

An employee's agent typically queries both namespaces — their personal one plus the shared one — and merges the results, since a request is scoped to exactly one namespace.


Email assistant

One namespace per user, with metadata carrying email-specific properties like label, sender, or folder — so the assistant can answer things like "find the Spotify email tagged Promotional".

typescript
await supermemory.add(userId, {
  content: "Your Spotify Premium receipt for July — $11.99 charged",
  metadata: {
    source: "gmail",
    sender: "[email protected]",
    label: "Promotional",
  },
});

const results = await supermemory.search(userId, {
  query: "spotify",
  filter: {
    operator: "and",
    operands: [
      { field: "source", operator: "eq", value: "gmail" },
      { field: "label", operator: "eq", value: "Promotional" },
    ],
  },
  searchMode: "chunks",
});

Multi-tenant support platform

Each customer gets their own namespace, and metadata tracks ticket-level fields like status and priority — so "open, high-priority tickets" is a filter, not a new namespace, and it can never accidentally include another customer's tickets.

typescript
await supermemory.add("org_customer_442", {
  content: "Customer reports checkout button unresponsive on Safari",
  metadata: { status: "open", priority: "high", channel: "chat" },
});

const results = await supermemory.search("org_customer_442", {
  query: "checkout issue",
  filter: {
    operator: "and",
    operands: [
      { field: "status", operator: "eq", value: "open" },
      { field: "priority", operator: "eq", value: "high" },
    ],
  },
  searchMode: "chunks",
});

Next steps

Why namespaces and metadata are separate mechanisms. How isolation works, naming rules, and access control. Metadata filter operators, combining `and`/`or`, and query limits.

Apps Docs Concepts Multi Tenancy (`apps/docs/concepts/multi-tenancy.mdx`)


title: "Multi-tenancy overview" sidebarTitle: "Overview" icon: "/icons/hugeicons/user-multiple.svg"

Most apps built on Supermemory serve more than one user, customer, or tenant out of a single Supermemory organization. Multi-tenancy is how you keep those memories apart — so User A's data is never visible to User B, and so you can still slice and query within a user's own data by things like category, status, or date.

Supermemory gives you two complementary tools for this:

**Isolation.** A namespace is a hard boundary. Memories in one namespace are never returned by a search scoped to another namespace. **Organization.** Metadata is a set of custom key/value properties on a memory that you filter by — category, priority, date, participants, anything you define.

They solve different problems, and most production apps use both together.


Why two mechanisms

It's tempting to reach for one tool and make it do everything, but namespaces and metadata aren't interchangeable — they answer different questions.

Question Answer
"Which tenant does this memory belong to?" Namespace
"Within this tenant's memories, which ones match status: open?" Metadata filter
"Can this API key even see tenant X's data?" Namespace (enforced as an access boundary)
"Find memories tagged engineering created after March" Metadata filter

A namespace decides whether a memory is reachable at all for a given request. Metadata decides which of the reachable memories match. Filtering never crosses a namespace boundary — you can't use metadata to peek into another tenant's namespace.


How they work together

A typical multi-tenant write scopes the memory to a tenant with a namespace, then attaches metadata for finer-grained querying later:

typescript
await supermemory.add("org_acme", { // isolates to the "acme" tenant
  content: "Customer requested a refund for order #4821",
  metadata: {
    category: "support",
    status: "open",
    priority: "high",
  },
});

And a search combines both: the namespace restricts which tenant's data is in scope, and the filter narrows down which memories within that tenant come back:

typescript
const results = await supermemory.search("org_acme", {
  query: "refund request",
  filter: {
    operator: "and",
    operands: [
      { field: "category", operator: "eq", value: "support" },
      { field: "status", operator: "eq", value: "open" },
    ],
  },
  searchMode: "chunks",
});
The namespace is **required** on every call (it is part of the URL) and validated as an access boundary. Metadata filters are **optional** — a search with just `namespace` and no `filter` still only returns that tenant's memories.

Choosing your boundary

The namespace is the layer that should map to your actual tenancy model — pick the level that matches what "one isolated space" means in your app:

Pattern Example Use case
Per-user user_{userId} Consumer app, personal memory per user
Per-tenant/org org_{orgId} B2B SaaS, one namespace per customer org
Hierarchical org:{orgId}:user:{userId} Multi-level — isolate by org, and optionally drill into a user within it
Per-project project_{projectId} Workspace- or project-scoped content

The simplest form is one namespace per user, derived from an ID you already have:

typescript
await supermemory.add(userId, { // one namespace per user
  content: "User prefers morning workouts",
});

const results = await supermemory.search(userId, {
  query: "workout preferences",
});

Everything within that boundary — categories, statuses, dates, custom fields — is metadata, not a new namespace. Don't create a new namespace for every property you want to filter on; that's what metadata is for.


Access control

Namespaces aren't just organizational — they're enforced as an authorization boundary. API keys and org members can be restricted to specific namespaces, so a request for a namespace outside the caller's allowed set is rejected with 403 Forbidden rather than silently filtered. See Namespaces → Access control for the details.


Next steps

Personal agents, company agents, email assistants, and support platforms. How isolation works, naming rules, and access control. Metadata filter operators, combining `and`/`or`, and query limits. Mint keys that can only touch one namespace.

Apps Docs Concepts Rules (`apps/docs/concepts/rules.mdx`)


title: "Rules of supermemory" sidebarTitle: "Rules of supermemory" icon: "/icons/hugeicons/justice-scale-01.svg"

Supermemory provides powerful primitives and the full context stack for building AI agents. This page collects rules of thumb from building and running supermemory in production. They aren't hard constraints, just shortcuts that save you time, cost, and confusing search results.

Thinking about ingestion

What to ingest, and what not to

Send what you would send to a human for memory

Treat supermemory as a database for human-like understanding of knowledge and search. You should be feeding it unstructured data like documents, chat conversations, or even images, videos, and websites. You should not be ingesting database records or CSVs, since those are more structured.

Although supermemory does support learning from long-horizon structured data, typically the right approach is to give an agent tools to traverse the structure directly.

Agents benefit most from having a general idea of the topic alongside tools to look through the data. For example, knowing "this company uses PostHog and has three products (API, Console, and Landing Page)" helps the agent navigate the PostHog data more effectively.

A quick test for where information belongs

Context Test result Where it goes
"Sarah prefers async updates and is being promoted to VP of Product" A colleague would remember this supermemory: memory search + profile
The Q3 planning doc, support tickets, the API changelog A colleague would look it up by meaning supermemory: ingested as documents, recalled with document search
Invoice #4821, total $1,340.50, status paid Queried by ID, summed in reports your database
"Answer in the user's language. Never quote internal pricing." Every request needs it, verbatim system prompt

Two things about this table that trip people up.

"Remember" and "look up" are both supermemory, but different reads. You ingest documents; the pipeline derives memories from them and maintains a profile per namespace. supermemory.search(namespace, { query, searchMode: "memories" }) recalls the derived facts. supermemory.search(namespace, { query, searchMode: "chunks" }) recalls the source material itself. A support agent usually needs both: memories for "this customer runs self-hosted and already tried reinstalling", documents for the actual troubleshooting guide.

Supermemory is not your system of record. There's no SQL over memories, no joins, no aggregates, no querying by primary key. Keep transactional data in your database, and ingest the narrative around it ("the customer disputed invoice #4821 and churned over it") so your AI understands what the rows mean.

When you know you only want search, you can cut costs by 5x. Just set taskType when ingesting:

typescript typescript
await supermemory.add("test", {
  content: "testing",
  taskType: "superrag",
});
python python
client.add(
    "test",
    content="testing",
    task_type="superrag",
)
bash curl
curl -X POST "https://api.supermemory.ai/ns/test/document" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "content": "testing", "taskType": "superrag" }'

Use hybrid mode when searching over SuperRag content

hybrid mode (the v5 default) makes it much easier to get complete results from supermemory when you have both memories and documents.

typescript typescript
const { results } = await supermemory.search("test", {
  query: "test",
  searchMode: "hybrid",
});
python python
results = client.search(
    "test",
    query="test",
    search_mode="hybrid",
)
bash curl
curl -X POST "https://api.supermemory.ai/ns/test/search" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "test", "searchMode": "hybrid" }'

Each entry in results carries either memory or chunk:

ts
{ results: ({ memory: string } | { chunk: string })[], searchTime: number }

Use item.memory || item.chunk when reading results.

Keep documents medium-sized

While supermemory can handle documents with 400k+ tokens, sending smaller, self-contained documents produces better-quality learnings. The internal learning agent and "dreaming" jobs reflect on memories to build relations between them. If documents are too long, fewer memories get generated and fewer relations get made.

We also recommend ingesting documents sequentially within a single namespace where possible, since that's how supermemory determines what came first (used for updates relations and temporal reasoning).

Handling single-threaded chatbots

Many agent harnesses, like openclaw, hermes, and other single-threaded custom agents, run one long conversation with compaction. Some tips for managing single-threaded (and other long-running) conversations:

  1. Send an id when you can: a sessionId, conversationId, document ID, or any representation of a "session" in your application.
  2. Generate one if you don't have one, e.g. the current 4-hour window: ${new Date().toISOString().slice(0,10)}-${new Date().getHours()>>2}. Adjust the window size based on traffic per namespace.
  3. Send the same prefix: keep the start of the document identical across ingests under the same id so supermemory can diff cleanly. You can either resend the full growing transcript each time, or send only the new turns since your last ingest. Just don't mix the two for the same id.
text
Ingestion 1:
Assistant: Hey, how are you?
User: I'm fine.

Ingestion 2 (full transcript):
Assistant: Hey, how are you?
User: I'm fine.
Assistant: Anything I can help with today?

Ingestion 2 (delta only):
Assistant: Anything I can help with today?

You're only billed for the new (diff) content you send, so doing this well improves performance, cuts cost, and keeps usage simple.

Architecture and design

Let supermemory handle the learning

Don't pass content through an additional LLM before sending it to supermemory. Supermemory does that learning automatically. Because the engine already knows what it knows, it can contextually summarize, update, and forget information as needed.

Configure what you want it to learn

Ground it with supportingContext (called entityContext in v3/v4) to prevent drift over time. Picture a third person watching a conversation between two people: what do they remember, and about whom? Giving supermemory context about the entity itself helps ground its learnings and prevents drift and decay over time.

typescript typescript
const user = auth.user.name;
await supermemory.add(user, {
  content: "Hey, I'm doing great!",
  supportingContext: `User is ${user}, talking to assistant Kira`,
}); // -> supermemory learns "Dhravya is doing great"
python python
user = auth.user.name
client.add(
    user,
    content="Hey, I'm doing great!",
    supporting_context=f"User is {user}, talking to assistant Kira",
)  # -> supermemory learns "Dhravya is doing great"
bash curl
curl -X POST "https://api.supermemory.ai/ns/dhravya/document" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Hey, I'\''m doing great!",
    "supportingContext": "User is dhravya, talking to assistant Kira"
  }'

Use namespaces, don't over-stuff a single one

Use a namespace wherever there's a hard permission boundary.

  • Don't: ingest everything into one namespace and filter through it with metadata.
  • Do: give each user their own namespace (namespace: userId), and still filter by metadata inside it if needed.

There's little correlation between the number of items in a namespace and its quality or latency. Supermemory is built for multi-tenant workloads and supports up to 1M documents and 10M memories per namespace.

Use metadata filtering for detailed scoping inside namespaces

You'll often want to ingest and search with filtering inside a single namespace. Say the engineering team ingests this:

typescript typescript
await supermemory.add("org-supermemory", {
  content: "The team prefers TypeScript",
  metadata: { team: "Engineering" },
  group: { team: "Engineering" },
});
python python
client.add(
    "org-supermemory",
    content="The team prefers TypeScript",
    metadata={"team": "Engineering"},
    group={"team": "Engineering"},
)
bash curl
curl -X POST "https://api.supermemory.ai/ns/org-supermemory/document" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "The team prefers TypeScript",
    "metadata": { "team": "Engineering" },
    "group": { "team": "Engineering" }
  }'

Tip: group (called filterByMetadata in v3/v4) ensures a fact like "the team prefers TypeScript" is only built on top of the engineering team's knowledge.

Later, the research team ingests this into the same namespace (POST /ns/org-supermemory/document) but with different metadata:

json
{
  "content": "The team prefers Python",
  "metadata": { "team": "Research" },
  "group": { "team": "Research" }
}

This keeps research's and engineering's memories from mixing, even though they share a namespace. When searching:

typescript typescript
const results = await supermemory.search("org-supermemory", {
  query: "preferred language",
  filter: { field: "team", operator: "eq", value: "Research" },
  searchMode: "chunks",
}); // -> "python"
python python
results = client.search(
    "org-supermemory",
    query="preferred language",
    search_mode="chunks",
    filter={"field": "team", "operator": "eq", "value": "Research"},
)  # -> "python"
bash curl
curl -X POST "https://api.supermemory.ai/ns/org-supermemory/search" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "preferred language",
    "searchMode": "chunks",
    "filter": { "field": "team", "operator": "eq", "value": "Research" }
  }'

Thinking about harness

Think about how to bring memory back into the harness itself.

Embrace a little noise

You might want to hyper-optimize everything that goes into the model's prompt, but counterintuitively, you sometimes want to embrace noise, since true personalization comes from distinctive information.

Example: a user says "hi" and the LLM responds "Hey Dhravya! How's it going? How's the new office coming along?" instead of something generic.

Supermemory is designed for this: it returns an average of 10 tokens per fact, so even 50 facts is just 500 tokens of context, cheap enough to stay generous.

Tools, hooks, and making the choice

Think about how supermemory fits into your harness. Example, a personal agent:

  • Session start hook → load profile
  • On-message hook → enrich the prompt with search
  • On-stop hook → save the conversation

Play around with these options in our playground, and read more in this post on memory at the harness level.


Apps Docs Concepts Super Rag (`apps/docs/concepts/super-rag.mdx`)


title: "SuperRAG (managed RAG as a service)" sidebarTitle: "SuperRAG" icon: "/icons/hugeicons/flash.svg"

Supermemory doesn't just store your content—it transforms it into optimized, searchable knowledge. Every upload goes through an intelligent pipeline that extracts, chunks, and indexes content in the ideal way for its type.

Automatic content intelligence

When you add content, Supermemory:

  1. Detects the content type — PDF, code, markdown, images, video, etc.
  2. Extracts content optimally — Uses type-specific extraction (OCR for images, transcription for audio)
  3. Chunks intelligently — Applies the right chunking strategy for the content type
  4. Generates embeddings — Creates vector representations for semantic search
  5. Builds relationships — Connects new knowledge to existing memories
typescript
// Just upload — Supermemory handles the rest
await supermemory.documents.uploadFile("docs_kb", {
  file: fs.createReadStream('technical-documentation.pdf'),
  metadata: JSON.stringify({ title: "Technical Documentation" }),
});

No chunking strategies to configure. No embedding models to choose. It just works.


Ingesting as pure SuperRAG (`taskType: "superrag"`)

By default, every supermemory.add() call runs on the memory path (taskType: "memory"): Supermemory chunks and embeds the content for retrieval, and runs it through the memory pipeline — extracting facts, updating the profile, and linking it into the knowledge graph.

If you're ingesting content that's purely reference material — documentation, a large PDF, a knowledge base article — and you don't need Supermemory to derive personal facts or update a profile from it, set taskType: "superrag" in the request body. It skips the memory pipeline entirely and only does the chunk → embed → index work needed to make the content searchable.

typescript typescript
await supermemory.add("docs_kb", { // e.g. a long internal wiki page
  content: "...",
  taskType: "superrag",
});
python python
client.add(
    "docs_kb",
    content="...",  # e.g. a long internal wiki page
    task_type="superrag",
)
bash curl
curl -X POST "https://api.supermemory.ai/ns/docs_kb/document" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "content": "...", "taskType": "superrag" }'
taskType: "memory" (default) taskType: "superrag"
Chunking, embedding, indexing ✅ ✅ — searchable immediately via searchMode: "chunks"
Fact extraction into memories ✅ ❌ skipped
Profile (static/dynamic/buckets) updates ✅ ❌ skipped
Graph linking (updates/extends/derives) ✅ ❌ skipped
Price per ingested token Full rate 5x cheaper
`taskType: "superrag"` is a **5x discount on ingested tokens** — `sm_superrag_text`/`sm_superrag_rich` are priced at 20% of `sm_tokens_text`/`sm_tokens_rich`. See [Billing → Memory vs SuperRAG tokens](/overview/billing#memory-vs-superrag-tokens) for the exact rates. Content ingested as `superrag` is retrievable via chunk search (`searchMode: "chunks"`), but it will **never** surface as a memory, contribute to a user's profile, or connect into the knowledge graph. Use it for reference material you want searchable, not for anything that should shape what Supermemory knows about a user — that still needs the default `taskType: "memory"`.

When you're searching over a mix of both, searchMode: "hybrid" (below) is what pulls memory-path facts and superrag-path document chunks into one result set. More ingestion guidance: Rules of supermemory → Ingest with SuperRag when you just need search.


Smart chunking by content type

Different content types need different chunking strategies. Supermemory applies the optimal approach automatically:

Documents (PDF, DOCX)

PDFs and documents are chunked by semantic sections — headers, paragraphs, and logical boundaries. This preserves context better than arbitrary character splits.

text
├── Executive Summary (chunk 1)
├── Introduction (chunk 2)
├── Section 1: Architecture
│   ├── Overview (chunk 3)
│   └── Components (chunk 4)
└── Conclusion (chunk 5)

Code

When content is recognized as Markdown, fenced code blocks are kept whole when they fit in a chunk. Larger blocks are split and re-wrapped in the original fence, including its language tag. Overlap can contain part of a previous line, so chunks are not guaranteed to be independently valid code.

Raw source files and content not recognized as Markdown use plain-text chunking. A lone fenced block may not be enough for automatic Markdown detection.

Web pages

URLs are fetched, cleaned of navigation/ads, and chunked by article structure — headings, paragraphs, lists.

Markdown

Chunked by heading hierarchy, preserving the document structure.

See Content Types for the full list of supported formats.


Hybrid memory + RAG

Supermemory combines the best of both approaches in every search:

- Finds similar document chunks - Great for knowledge retrieval - Stateless — same results for everyone - Extracts and tracks user facts - Understands temporal context - Personalizes results per user

With searchMode: "hybrid" (the default), you get both:

typescript
const results = await supermemory.search("user_123", {
  query: "how do I deploy the app?",
  searchMode: "hybrid",
});

// Returns:
// - Deployment docs from your knowledge base (RAG)
// - User's previous deployment preferences (Memory)
// - Their specific environment configs (Memory)

Search optimization

Two flags give you fine-grained control over result quality:

Reranking

Re-scores results using a cross-encoder model for better relevance. rerank takes "none" (default), "order", or "aggregate":

typescript
const results = await supermemory.search("user_123", {
  query: "complex technical question",
  rerank: "order", // +~100ms, significantly better ranking
});

When to use: Complex queries, technical documentation, when precision matters more than speed.

Query rewriting

Expands your query to capture more relevant results:

typescript
const results = await supermemory.search("user_123", {
  query: "how to auth",
  rewriteQuery: true, // Expands to "authentication login oauth jwt..."
});

When to use: Short queries, user-facing search, when recall matters.


Why it's "Super"

Traditional RAG SUPER RAG
Manual chunking config Automatic per content type
One-size-fits-all splits AST-aware code chunking
Just document retrieval Hybrid memory + documents
Static embeddings Relationship-aware graph
Generic search Rerank + query rewriting

You focus on building your product. Supermemory handles the RAG complexity.


Next steps

All supported formats and how they're processed The full processing pipeline When to use each approach Search parameters and optimization Exact meter rates for memory vs SuperRAG tokens `taskType` and other ingestion parameters

Apps Docs Concepts User Profiles (`apps/docs/concepts/user-profiles.mdx`)


title: "User profiles" sidebarTitle: "Profiles" icon: "/icons/hugeicons/user-circle.svg"

User profiles are automatically maintained collections of facts about your users that Supermemory builds from all their interactions. Think of it as a persistent "about me" document that's always up-to-date.

Each namespace gets its own profile.

Note: It's called "user" profile, but in reality it can be anything - an agent, organization, etc.

No search needed — comprehensive user info always ready Profiles update as users interact with your system

Why Profiles?

Traditional memory systems rely entirely on search:

Problem Search Only With Profiles
Context retrieval 3-5 queries 1 call
Response time 200-500ms 50-100ms
Basic user info Requires specific queries Always available

Search is too narrow: When you search for "project updates", you miss that the user prefers bullet points, works in PST, and uses specific terminology.

Profiles provide the foundation: Instead of searching for basic context, profiles give your LLM a complete picture of who the user is.

A pure search architecture means every turn pays a search(prompt) round trip before the agent can respond. A profile is attached once and sits alongside every user prompt and agent output — no extra call, no latency, and no risk of the query missing something important.


Non-literal-matching use cases

Semantic search retrieves content that's similar to the query — it's built for questions like "what did we discuss about the migration?" It's a poor fit for facts that should be known regardless of what's being asked, because there's rarely a query that's semantically close to them.

The clearest example is the user's own name. If someone tells your agent "call me Dhravya, not my full name" once during onboarding, that fact has almost nothing in common — vector-wise — with "help me plan a trip to Japan" or "review this PR." A search for either of those queries will not surface the name preference, because search only returns what's relevant to the query, and a name preference isn't relevant to trip planning or code review — it should just always be there.

typescript
// Weeks earlier, during onboarding
await supermemory.add("user_123", {
  content: "Call me Dhravya, not my full first name",
});

// Later — an unrelated query
const { results } = await supermemory.search("user_123", {
  query: "help me plan a trip to Japan",
});
// The name preference won't be in `results` — it's not semantically
// related to trip planning, so search correctly leaves it out.

// But it's always in the profile, independent of the query:
const { profile } = await supermemory.profile("user_123");
console.log(profile.static.map((m) => m.memory)); // ["User goes by Dhravya, not their full name", ...]

This is the general pattern: names, pronouns, timezone, tone/format preferences, role, and other facts that should color every response — not just responses to a matching query — belong in the profile, not left to be caught by search. If your agent needs to "just know" something at all times, that's a strong signal it belongs in the profile rather than relying on a lucky semantic match.


Static vs dynamic

Profiles separate two types of information:

Static profile

Long-term, stable facts:

  • "Sarah is a senior software engineer at TechCorp"
  • "Sarah specializes in distributed systems"
  • "Sarah prefers technical docs over video tutorials"

Dynamic profile

Recent context and temporary states:

  • "Sarah is migrating the payment service to microservices"
  • "Sarah is preparing for a conference talk next month"
  • "Sarah is debugging a memory leak in auth service"

Buckets

Static and dynamic split facts by how long-lived they are. Buckets split them by topic — a third, independent axis you define, like preferences, goals, or work. As content is ingested, a classifier sorts each fact into the buckets it matches.

Every org starts with a default preferences bucket, which is always present — custom buckets are added alongside it, never in place of it. Add your own in console settings at the organization level, or per namespace with supermemory.profiles.setBuckets() — namespace buckets are add-only, so a namespace always keeps every org-level bucket.

typescript
// Define a namespace-level bucket (name → description)
await supermemory.profiles.setBuckets("user_123", {
  buckets: { goals: "Goals and targets the user is actively working toward" },
});

const { profile } = await supermemory.profile("user_123", { // optional — omit for all configured buckets
  buckets: ["preferences", "goals"],
});

console.log(profile.buckets.preferences); // [{ id, memory }, ...]
console.log(profile.buckets.goals);

Bucket descriptions steer the classifier, so a precise description ("explicit first-person preferences only, exclude inferred traits") produces cleaner buckets than a vague one. Buckets are separate from organizationalContext (called filterPrompt in v3/v4), which steers what gets extracted at all — buckets only organize facts that already made it into the profile.

Request bucketed profiles, create buckets at the org or namespace level, and see validation limits.

How it works

Profiles are built automatically through ingestion:

  1. Ingest content — Users add documents, chat, or any content
  2. Extract facts — AI analyzes content for facts about the user
  3. Update profile — System adds, updates, or removes facts
  4. Always current — Profiles reflect the latest information
You don't manually manage profiles — they build themselves as users interact. Start by [adding content](/ingestion/add-memories) to see profiles in action.

Profiles don't replace search — they complement it:

  • Profile = broad foundation (who the user is, preferences, background)
  • Search = specific details (exact memories matching a query)

Example

User asks: "Can you help me debug this?"

Without profiles: LLM has no context about expertise, projects, or preferences.

With profiles: LLM knows:

  • Senior engineer (adjust technical level)
  • Working on payment service (likely context)
  • Prefers CLI tools (tool suggestions)
  • Recent memory leak issues (possible connection)

Filtering profiles

Not many people realize this, but profiles support the same metadata filtering as memory and document search. A profile is synthesized from the underlying memories in a namespace, so any filter expression you'd pass to search also narrows which memories are eligible to contribute to static, dynamic, and buckets.

typescript
// Only build the profile from memories tagged as onboarding data
const { profile } = await supermemory.profile("user_123", {
  filter: { field: "source", operator: "eq", value: "onboarding" },
});

This is useful when a namespace mixes memories from several sources or contexts and you only want one of them reflected in the profile — for example, a support agent that should only see profile facts derived from support tickets, not from an internal wiki synced into the same namespace:

typescript
const { profile } = await supermemory.profile("org_customer_442", {
  filter: { field: "channel", operator: "eq", value: "support_ticket" },
});

A v5 profile call takes no search query. If you also need query-ranked memories, make a separate supermemory.search() call with the same filter. See Filtering Profiles for the full parameter reference.


Use cases

Personalized AI assistants

Profiles provide: expertise level, communication preferences, tools used, current projects.

typescript
const systemPrompt = `You are assisting ${userName}.

Background: ${profile.static.map((m) => m.memory).join('\n')}
Current focus: ${profile.dynamic.map((m) => m.memory).join('\n')}

Adjust responses to their expertise and preferences.`;

Customer support

Profiles provide: product usage, previous issues, tech proficiency.

  • No more "let me look up your account"
  • Agents immediately understand context
  • AI support references past interactions naturally

Educational platforms

Profiles provide: learning style, completed courses, strengths/weaknesses.

Development tools

Profiles provide: preferred languages, coding style, current project context.


Next steps

Fetch and use profiles via the API Create and configure topical buckets How the underlying knowledge graph works Automatic profile injection with AI SDK Build profiles by adding content

Apps Docs Connectors Github (`apps/docs/connectors/github.mdx`)


title: "GitHub connector" sidebarTitle: "GitHub" icon: "/images/github-icon.svg"

Connect GitHub repositories to sync documentation files into your Supermemory knowledge base with OAuth authentication, webhook support, and automatic incremental syncing.

The GitHub connector requires a **Scale Plan** or **Enterprise Plan**.

Quick setup

1. Create GitHub Connector

```typescript import { Supermemory } from "supermemory"
text
const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

const connector = await supermemory.connectors.create("user-123", {
  provider: "github",
  redirectUrl: "https://yourapp.com/auth/github/callback",
  documentLimit: 5000,
})

// Send the user to GitHub to authorize
if (connector.authorization) window.location.href = connector.authorization.url
console.log("Auth URL expires at:", connector.authorization?.expiresAt)
```
```python from supermemory import Supermemory import os
text
client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

connector = client.connectors.create(
    "user-123",
    request={
        "provider": "github",
        "redirect_url": "https://yourapp.com/auth/github/callback",
        "document_limit": 5000,
    },
)

# Send the user to GitHub to authorize
print(f"Redirect to: {connector.authorization.url}")
print(f"Auth URL expires at: {connector.authorization.expires_at}")
```
```bash curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "github", "redirectUrl": "https://yourapp.com/auth/github/callback", "documentLimit": 5000 }'
text
# Response: {
#   "id": "PTzGiUYei7pgzg5buzZHgA",
#   "authorization": {
#     "url": "https://api.supermemory.ai/v3/connections/auth/redirect?state=...",
#     "expiresAt": "2024-01-15T11:30:00.000Z"
#   }
# }
```
**OAuth Scopes:** The GitHub connector requires these scopes: - `repo` - Access to private and public repositories - `user:email` - Access to user's email address - `admin:repo_hook` - Manage webhooks for incremental sync

2. Handle OAuth callback

Send the user to authorization.url before authorization.expiresAt. After the user grants permissions, Supermemory sends them to a hosted repository picker; once they choose repositories, they return to your redirectUrl. A pending OAuth connector is not returned by list until the user finishes authorization; get returns it with system.status: "pending_authorization".

3. Select Repositories

Unlike most connectors, GitHub requires repository selection before syncing begins. There are two ways to set it:

  • Hosted picker: read the connector with include: "picker" and send the user to picker.url. The link works once and expires at picker.expiresAt.
  • Direct selection: if you already know the repository ids, pass them in selection.repos with connectors.update. A new selection replaces the old one and starts a sync.
See [Managing Connector Selection](/connectors/managing-resources) for the generic selection API that GitHub, Gmail and Google Drive share. ```typescript // Option A: send the user to the hosted picker const withPicker = await supermemory.connectors.get("user-123", connectorId, { include: "picker", returnUrl: "https://yourapp.com/settings/integrations" })
text
if (withPicker.picker) window.location.href = withPicker.picker.url

// Option B: set the repositories directly by id
await supermemory.connectors.update("user-123", connectorId, {
  selection: {
    repos: ["123456789", "987654321"],
  },
})

console.log("Repository sync initiated")
```
```python # Option A: send the user to the hosted picker with_picker = client.connectors.get( "user-123", connector_id, include=["picker"], return_url="https://yourapp.com/settings/integrations", )
text
if with_picker.picker:
    print(f"Redirect to: {with_picker.picker.url}")

# Option B: set the repositories directly by id
client.connectors.update(
    "user-123",
    connector_id,
    selection={"repos": ["123456789", "987654321"]},
)

print("Repository sync initiated")
```
```bash # Option A: get a one-time hosted picker URL curl "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?include=picker&returnUrl=https%3A%2F%2Fyourapp.com%2Fsettings%2Fintegrations" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response includes: "picker": { "url": "https://...", "expiresAt": "2024-01-15T11:30:00.000Z" }

# Option B: set the repositories directly by id
curl -X PATCH "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "selection": {
      "repos": ["123456789", "987654321"]
    }
  }'
```
The legacy `GET /v3/connections/{id}/resources` repository listing has no v5 route. The hosted picker shows the user their repositories and saves the selection for you.

Supported document types

The GitHub connector syncs documentation and text files with the following extensions:

  • Markdown files: .md, .mdx, .markdown
  • Text files: .txt
  • reStructuredText: .rst
  • AsciiDoc: .adoc
  • Org-mode: .org

Files are indexed as github_markdown document type in Supermemory.

Only text-based documentation files are synced. Binary files, images, and code files (`.js`, `.py`, `.go`, etc.) are excluded by default to focus on searchable documentation content.

Incremental sync with webhooks

The GitHub connector automatically sets up webhooks for real-time incremental syncing. When files are pushed or deleted in configured repositories, Supermemory is notified immediately.

**Batch Processing:** Webhook events are processed in batches with a 10-minute delay to optimize performance and prevent excessive syncing during rapid commits. This means changes pushed to your repository will be reflected in Supermemory within approximately 10 minutes.

How it works

  1. Webhook Setup: When you set the selection, a webhook is automatically installed in each repository
  2. Push Events: When commits are pushed to the default branch, changed documentation files are synced
  3. Delete Events: When documentation files are deleted, they're removed from your Supermemory knowledge base
  4. Incremental Updates: Only changed files are processed, keeping sync fast and efficient

Webhook security

Webhooks are secured using HMAC-SHA256 signature validation with constant-time comparison. Supermemory automatically validates that webhook events come from GitHub before processing them. Each repository gets a unique webhook secret for maximum security.

```typescript // Check sync status and selection const connector = await supermemory.connectors.get("user-123", connectorId, { include: "syncs", })
text
console.log("Webhooks:", connector.capabilities.webhooks)
console.log("Last synced:", connector.system.lastSuccessfulSyncAt)
console.log("Last sync:", connector.latestRun?.system.status, connector.latestRun?.error)
console.log("Repositories:", connector.selection?.repos)
console.log("Recent runs:", connector.syncs)
```
```python # Check sync status and selection connector = client.connectors.get("user-123", connector_id, include=["syncs"])
text
print(f"Webhooks: {connector.capabilities.webhooks}")
print(f"Last synced: {connector.system.last_successful_sync_at}")
print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}")
if connector.latest_run and connector.latest_run.error:
    print(connector.latest_run.error)
repos = (connector.selection or {}).get("repos") or []
print(f"Repositories: {[r.name for r in repos]}")
print(f"Recent runs: {connector.syncs}")
```
```bash # Get connector details including recent sync runs curl "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?include=syncs" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ```

Connector Management

List All Connectors

```typescript // List GitHub connectors in a namespace const { connectors } = await supermemory.connectors.list("user-123")
text
connectors
  .filter(connector => connector.provider === "github")
  .forEach(connector => {
    console.log(`Provider: ${connector.provider}`)
    console.log(`ID: ${connector.id}`)
    console.log(`Account: ${connector.account}`)
    console.log(`Created: ${connector.system.createdAt}`)
    console.log(`Document limit: ${connector.documentLimit}`)
    console.log(`Repositories: ${connector.selection?.repos?.length ?? 0}`)
    console.log("---")
  })
```
```python # List GitHub connectors in a namespace connectors = client.connectors.list("user-123", provider="github").connectors
text
for connector in connectors:
    print(f"Provider: {connector.provider}")
    print(f"ID: {connector.id}")
    print(f"Account: {connector.account}")
    print(f"Created: {connector.system.created_at}")
    print(f"Document limit: {connector.document_limit}")
    repos = (connector.selection or {}).get("repos") or []
    print(f"Repositories: {len(repos)}")
    print("---")
```
```bash # List connectors in a namespace curl "https://api.supermemory.ai/ns/user-123/connectors" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ```

Update Repository Selection

You can update which repositories are synced at any time. The new selection replaces the old one and starts a sync.

```typescript // Add or remove repositories await supermemory.connectors.update("user-123", connectorId, { selection: { repos: ["123456789", "987654321"], }, })
text
console.log("Repository selection updated")
```
```python # Add or remove repositories client.connectors.update( "user-123", connector_id, selection={"repos": ["123456789", "987654321"]}, )
text
print("Repository selection updated")
```
```bash # Update repository selection curl -X PATCH "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "selection": { "repos": ["123456789", "987654321"] } }' ```
When you update the repository selection: - New repositories are added and synced immediately - Removed repositories have their webhooks deleted - Existing documents from removed repositories remain in Supermemory unless you delete them manually

Delete Connector

```typescript // Delete the connector and its imported documents (default) await supermemory.connectors.delete("user-123", connectorId)
text
// Delete the connector but keep the imported documents
await supermemory.connectors.delete("user-123", connectorId, {
  deleteDocuments: false,
})
```
```python # Delete the connector and its imported documents (default) client.connectors.delete("user-123", connector_id)
text
# Delete the connector but keep the imported documents
client.connectors.delete("user-123", connector_id, delete_documents=False)
```
```bash # Delete the connector and its imported documents (default) curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Delete the connector but keep the imported documents
curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?deleteDocuments=false" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
Deleting a GitHub connector will: - Stop all future syncs from configured repositories - Remove all webhooks from the repositories - Revoke the OAuth authorization - **Permanently delete all synced documents** from your Supermemory knowledge base (unless you pass `deleteDocuments: false` to keep them)

Manual sync

Trigger a manual synchronization for all selected repositories. The call returns 409 while a sync for that connector is already running.

```typescript const run = await supermemory.connectors.sync("user-123", connectorId)
text
console.log(run.status)
// Output: queued
```
```python run = client.connectors.sync("user-123", connector_id)
text
print(run.status)
# Output: queued
```
```bash curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}/sync" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```

Advanced configuration

Custom OAuth application

For white-label deployments or custom branding you can connect with your own GitHub OAuth app. Custom OAuth credentials are an organization setting and are not part of the v5 connector routes. See Custom OAuth Applications.

**Setting up a GitHub OAuth App:**
  1. Go to GitHub Settings → Developer settings → OAuth Apps
  2. Click "New OAuth App"
  3. Set Authorization callback URL to: https://api.supermemory.ai/v3/connections/auth/callback/github
  4. Copy the Client ID and generate a Client Secret

After configuration, all new GitHub connectors will use your custom OAuth app instead of Supermemory's default app.


Apps Docs Connectors Gmail (`apps/docs/connectors/gmail.mdx`)


title: "Gmail connector" sidebarTitle: "Gmail" icon: "/icons/hugeicons/mail-01.svg"

Connect Gmail to automatically sync email threads into your supermemory knowledge base. Supports real-time updates via Google Cloud Pub/Sub webhooks and incremental synchronization.

**Max Plan Required:** The Gmail connector is available on Max plan and above.

Quick setup

1. Create Gmail Connector

```typescript import { Supermemory } from "supermemory"
text
const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

const connector = await supermemory.connectors.create("user-123", {
  provider: "gmail",
  redirectUrl: "https://yourapp.com/auth/gmail/callback",
  documentLimit: 5000,
})

// Send the user to Google to authorize
if (connector.authorization) window.location.href = connector.authorization.url
console.log("Auth URL expires at:", connector.authorization?.expiresAt)
```
```python from supermemory import Supermemory import os
text
client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

connector = client.connectors.create(
    "user-123",
    request={
        "provider": "gmail",
        "redirect_url": "https://yourapp.com/auth/gmail/callback",
        "document_limit": 5000,
    },
)

# Send the user to Google to authorize
print(f"Redirect to: {connector.authorization.url}")
print(f"Auth URL expires at: {connector.authorization.expires_at}")
```
```bash curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "gmail", "redirectUrl": "https://yourapp.com/auth/gmail/callback", "documentLimit": 5000 }'
text
# Response: {
#   "id": "PTzGiUYei7pgzg5buzZHgA",
#   "authorization": {
#     "url": "https://api.supermemory.ai/v3/connections/auth/redirect?state=...",
#     "expiresAt": "2024-01-15T11:30:00.000Z"
#   }
# }
```

2. Handle OAuth callback

Send the user to authorization.url before authorization.expiresAt. After the user grants permissions, Supermemory sends them to a hosted label picker; once they choose labels, they return to your redirectUrl and the initial sync begins. Nothing syncs until at least one label is selected. A pending OAuth connector is not returned by list until the user finishes authorization; get returns it with system.status: "pending_authorization".

To change the labels later, read the connector with include: "picker" and send the user to picker.url, or set label ids directly with connectors.update(namespace, id, { selection: { labels: ["INBOX"] } }). See Managing Connector Selection.

3. Check Connector Status

```typescript // Get connector details with recent sync runs const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", { include: "syncs", })
text
console.log("Connected email:", connector.account)
console.log("Last sync:", connector.latestRun?.system.status)
console.log("Threads synced:", connector.documentCount)

// List synced email threads in the namespace
const docs = await supermemory.list("user-123", "documents")

console.log(`Synced ${docs.pagination.totalItems} email threads`)
```
```python # Get connector details with recent sync runs connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])
text
print(f"Connected email: {connector.account}")
print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}")
print(f"Threads synced: {connector.document_count}")

# List synced email threads in the namespace
docs = client.list("user-123", "documents")

print(f"Synced {docs.pagination.total_items} email threads")
```
```bash # Get connector details with recent sync runs curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# List synced email threads in the namespace
curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```
There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.

What gets synced

Email threads

Gmail threads (conversations) are synced as individual documents with all messages included:

  • Thread content converted to structured markdown
  • All messages within each thread preserved in order
  • Message metadata: subject, from, to, cc, bcc, date
  • HTML content converted to clean markdown
  • Attachment metadata: filename, mime type, size (attachments are referenced, not stored)

Document metadata

Each synced thread includes searchable metadata:

Field Description
type Always gmail_thread
subject Email subject line
threadId Gmail thread ID
from Sender, as the raw From header (for example Team <[email protected]>)
to Recipient addresses, comma-separated
date Date of first message
messageCount Number of messages in thread
attachmentCount Number of attachments (if any)
attachmentNames List of attachment filenames

You can filter searches using these metadata fields:

typescript
const results = await supermemory.search("user-123", {
  query: "project update",
  filter: {
    operator: "and",
    operands: [
      { field: "type", operator: "eq", value: "gmail_thread" },
      { field: "from", operator: "contains", value: "[email protected]" },
    ],
  },
  searchMode: "chunks",
})

Connector Management

List All Connectors

```typescript // List all connectors in a namespace const { connectors } = await supermemory.connectors.list("user-123")
text
connectors.forEach(connector => {
  console.log(`Provider: ${connector.provider}`)
  console.log(`ID: ${connector.id}`)
  console.log(`Account: ${connector.account}`)
  console.log(`Created: ${connector.system.createdAt}`)
  console.log(`Document limit: ${connector.documentLimit}`)
  console.log("---")
})
```
```python # List all connectors in a namespace connectors = client.connectors.list("user-123").connectors
text
for connector in connectors:
    print(f"Provider: {connector.provider}")
    print(f"ID: {connector.id}")
    print(f"Account: {connector.account}")
    print(f"Created: {connector.system.created_at}")
    print(f"Document limit: {connector.document_limit}")
    print("---")
```
```bash # List all connectors in a namespace curl "https://api.supermemory.ai/ns/user-123/connectors" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ```

Delete Connector

```typescript // Delete the connector and its imported documents (default) await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
// Delete the connector but keep the imported documents
await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
  deleteDocuments: false,
})
```
```python # Delete the connector and its imported documents (default) client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
# Delete the connector but keep the imported documents
client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
```
```bash # Delete the connector and its imported documents (default) curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Delete the connector but keep the imported documents
curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?deleteDocuments=false" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
Deleting a connector will: - Stop all future syncs from Gmail - Remove the OAuth authorization - Delete the synced documents unless you pass `deleteDocuments: false`

Manual sync

Trigger a manual synchronization. The call returns 409 while a sync for that connector is already running.

```typescript const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
console.log(run.status)
// Output: queued
```
```python run = client.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
print(run.status)
# Output: queued
```
```bash curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```

Sync mechanism

Gmail connector supports multiple sync methods:

Feature Behavior
Real-time sync Via Google Cloud Pub/Sub webhooks (7-day expiry, auto-renewed)
Scheduled sync Every 4 hours
Manual sync On-demand via API
Incremental sync Uses Gmail historyId to fetch only changed threads

How real-time sync works

  1. After labels are selected, supermemory registers a Gmail API "watch" subscription when Pub/Sub is configured
  2. Gmail sends notifications to a Google Cloud Pub/Sub topic when emails change
  3. supermemory receives these notifications and fetches updated threads
  4. Watch subscriptions expire after 7 days and are automatically renewed
Real-time sync monitors the selected labels, including labels outside **INBOX**. If multiple connectors use the same mailbox, its watch covers their combined label selections.

Permissions & scopes

The Gmail connector requests the following OAuth scopes:

Scope Purpose
gmail.readonly Read-only access to Gmail messages and threads
userinfo.email Access to user's email address for connector identification
**Read-only Access:** The Gmail connector only reads emails. It cannot send, delete, or modify any emails in the user's account.

Limitations

**Important Limitations:** - **Plan requirement**: Requires Max Plan or above - **INBOX only** for real-time sync: Only INBOX label triggers real-time updates; other labels sync via scheduled sync - **Watch expiration**: Gmail watch subscriptions expire after 7 days (automatically renewed by supermemory) - **Document limit**: Default limit is 10,000 threads per connector (configurable via `documentLimit` parameter) - **Attachments**: Attachment metadata is stored, but attachment content is not downloaded - **Rate limits**: Gmail API rate limits may affect sync speed for accounts with many emails

Troubleshooting

OAuth fails or missing refresh token

If OAuth fails or the connector stops syncing:

  1. Delete the existing connector
  2. Create a new connector
  3. Ensure the user completes the full OAuth flow with consent
typescript
// Re-create the connector to get fresh tokens
await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
  deleteDocuments: false,
})

const next = await supermemory.connectors.create("user-123", {
  provider: "gmail",
  redirectUrl: "https://yourapp.com/auth/gmail/callback"
})

// User must re-authenticate
if (next.authorization) window.location.href = next.authorization.url

Emails not syncing in real-time

If real-time sync isn't working:

  • Scheduled sync (every 4 hours) and manual sync still work
  • Real-time sync requires supermemory's Pub/Sub infrastructure
  • Check if the connector was created recently (watch registration happens on creation)
  • Trigger a manual sync to verify the connector is working

Permission denied errors

If you see permission errors:

  • Ensure the user granted the required Gmail scopes during OAuth
  • Verify your organization has Max Plan or above access
  • Check if the user revoked app access in their Google Account settings

Apps Docs Connectors Google Drive (`apps/docs/connectors/google-drive.mdx`)


title: "Google Drive connector" sidebarTitle: "Google Drive" icon: "/images/google-drive-icon.svg"

Connect Google Drive to sync documents into your Supermemory knowledge base with OAuth authentication and custom app support.

Sync scope

Default for new connectors: after OAuth, the user completes a folder and file picker (Google Docs, Sheets, Slides, and PDFs). Only items they select are synced and updated until they change the selection (for example from the Supermemory console).

Whole Drive: set config.syncScope to "full" when creating the connector so the entire Drive syncs without the picker.

Explicit scoped mode: set config.syncScope to "selected" for the picker flow, or rely on the default for new connects.

If you use scoped sync and the user has not finished the picker yet, **scheduled or manual sync may skip that connector** until a selection is saved on the connector.

Quick setup

1. Create Google Drive Connector

```typescript import { Supermemory } from "supermemory"
text
const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

const connector = await supermemory.connectors.create("user-123", {
  provider: "google-drive",
  redirectUrl: "https://yourapp.com/auth/google-drive/callback",
  documentLimit: 3000,
  config: { syncScope: "selected" },
})

// Send the user to Google to authorize
if (connector.authorization) window.location.href = connector.authorization.url
console.log("Auth URL expires at:", connector.authorization?.expiresAt)
```
```python from supermemory import Supermemory import os
text
client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

connector = client.connectors.create(
    "user-123",
    request={
        "provider": "google-drive",
        "redirect_url": "https://yourapp.com/auth/google-drive/callback",
        "document_limit": 3000,
        "config": {"sync_scope": "selected"},
    },
)

# Send the user to Google to authorize
print(f"Redirect to: {connector.authorization.url}")
print(f"Auth URL expires at: {connector.authorization.expires_at}")
```
```bash curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "google-drive", "redirectUrl": "https://yourapp.com/auth/google-drive/callback", "documentLimit": 3000, "config": { "syncScope": "selected" } }'
text
# Response: {
#   "id": "PTzGiUYei7pgzg5buzZHgA",
#   "authorization": {
#     "url": "https://api.supermemory.ai/v3/connections/auth/redirect?state=...",
#     "expiresAt": "2024-01-15T11:30:00.000Z"
#   }
# }
```
For **whole Drive** sync, send `"config": { "syncScope": "full" }` on the same `POST /ns/{namespace}/connectors` request instead of `"selected"`.

2. Handle OAuth callback

Send the user to authorization.url before authorization.expiresAt. After the user grants permissions, Google redirects through Supermemory to finish the connector. A pending OAuth connector is not returned by list until the user finishes authorization; get returns it with system.status: "pending_authorization".

With scoped sync (syncScope omitted or "selected"), the user is sent to Supermemory’s hosted file and folder picker; they must complete that step before syncs run. With syncScope: "full", Supermemory redirects to your redirectUrl without the picker.

You can open the picker again later for an existing connector. Read the connector with include: "picker" and send the user to picker.url; the link works once and expires at picker.expiresAt. Pass returnUrl to choose where the picker sends the user afterwards.

```typescript const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", { include: "picker", returnUrl: "https://yourapp.com/settings/integrations" })
text
if (connector.picker) window.location.href = connector.picker.url
```
```bash curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=picker&returnUrl=https%3A%2F%2Fyourapp.com%2Fsettings%2Fintegrations" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response includes: "picker": { "url": "https://...", "expiresAt": "2024-01-15T11:30:00.000Z" }
```

If you already know the Drive file and folder ids, set them directly. A new selection replaces the old one and starts a sync.

```typescript await supermemory.connectors.update("user-123", "PTzGiUYei7pgzg5buzZHgA", { selection: { files: ["1AbCdEfGhIjKlMnOpQrStUvWxYz"], folders: ["0BxYzAbCdEfGhIjKlMnOpQrStU"], }, }) ``` ```bash curl -X PATCH "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "selection": { "files": ["1AbCdEfGhIjKlMnOpQrStUvWxYz"], "folders": ["0BxYzAbCdEfGhIjKlMnOpQrStU"] } }' ```

3. Check Connector Status

```typescript // Get connector details with recent sync runs const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", { include: "syncs", })
text
console.log("Account:", connector.account)
console.log("Selection:", connector.selection)
console.log("Last sync:", connector.latestRun?.system.status)

// List synced documents in the namespace
const { documents } = await supermemory.list("user-123", "documents")
```
```python # Get connector details with recent sync runs connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])
text
print(f"Account: {connector.account}")
print(f"Selection: {connector.selection}")
print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}")

# List synced documents in the namespace
documents = client.list("user-123", "documents").documents
```
```bash # Get connector details with recent sync runs curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# List synced documents in the namespace
curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```
There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.

Supported document types

Based on the API type definitions, Google Drive documents are identified with these types:

  • google_doc - Google Docs
  • google_slide - Google Slides
  • google_sheet - Google Sheets

PDF and Markdown files stored in Drive are synced too.

Drive documents are converted to markdown before ingestion. This conversion is lossy — some formatting may not be preserved.

Connector Management

List All Connectors

```typescript // List all connectors in a namespace const { connectors } = await supermemory.connectors.list("user-123")
text
connectors.forEach(connector => {
  console.log(`Provider: ${connector.provider}`)
  console.log(`ID: ${connector.id}`)
  console.log(`Account: ${connector.account}`)
  console.log(`Created: ${connector.system.createdAt}`)
  console.log(`Document limit: ${connector.documentLimit}`)
  console.log("---")
})
```
```python # List all connectors in a namespace connectors = client.connectors.list("user-123").connectors
text
for connector in connectors:
    print(f"Provider: {connector.provider}")
    print(f"ID: {connector.id}")
    print(f"Account: {connector.account}")
    print(f"Created: {connector.system.created_at}")
    print(f"Document limit: {connector.document_limit}")
    print("---")
```
```bash # List all connectors in a namespace curl "https://api.supermemory.ai/ns/user-123/connectors" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response example:
# {
#   "connectors": [
#     {
#       "id": "PTzGiUYei7pgzg5buzZHgA",
#       "provider": "google-drive",
#       "namespace": "user-123",
#       "account": "[email protected]",
#       "documentLimit": 3000,
#       "documentCount": 120,
#       "latestRun": { "system": { "status": "completed", ... }, "error": null },
#       "system": { "status": "active", "createdAt": "2024-01-15T10:30:00.000Z", "lastSuccessfulSyncAt": "..." }
#     }
#   ],
#   "pagination": { "currentPage": 1, "limit": 10, "totalItems": 1, "totalPages": 1 }
# }
```

Delete Connector

```typescript // Delete the connector and its imported documents (default) await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
// Delete the connector but keep the imported documents
await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
  deleteDocuments: false,
})
```
```python # Delete the connector and its imported documents (default) client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
# Delete the connector but keep the imported documents
client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
```
```bash # Delete the connector and its imported documents (default) curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Delete the connector but keep the imported documents
curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?deleteDocuments=false" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
Deleting a connector will: - Stop all future syncs from Google Drive - Remove the OAuth authorization - Delete the synced documents unless you pass `deleteDocuments: false`

Manual sync

Trigger a manual synchronization. The call returns 409 while a sync for that connector is already running.

```typescript const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
console.log(run.status)
// Output: queued
```
```python run = client.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
print(run.status)
# Output: queued
```
```bash curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```

Advanced configuration

Custom OAuth application

You can connect with your own Google OAuth app. Custom OAuth credentials are an organization setting and are not part of the v5 connector routes. See Custom OAuth Applications for the setup steps and callback URL.

**Important Notes:** - Document processing happens asynchronously - Use one namespace per user or tenant - Check `latestRun.system.status` and `latestRun.error` for failed syncs

Apps Docs Connectors Granola (`apps/docs/connectors/granola.mdx`)


title: "Granola connector" sidebarTitle: "Granola" icon: "/images/granola.svg"

Connect Granola to sync AI meeting notes and transcripts into your Supermemory knowledge base. The connector uses a Granola API key, so there is no OAuth redirect flow.

The Granola connector requires a **Pro Plan** or higher in Supermemory and a Granola plan that can create API keys. In Granola, create one from **Settings > Connectors > API keys**.

Quick setup

From the console

  1. Open the Supermemory Console.
  2. Go to Connectors.
  3. Find the Granola row and click Connect.
  4. Paste your Granola API key.
  5. Optionally set a document limit and namespace.
  6. Click Connect.

The console creates the connector and starts the initial sync automatically.

The console limits connector setup to 500 documents. Use the API setup below for higher `documentLimit` values, up to 10,000.

With the API

```typescript import { Supermemory } from "supermemory"
text
const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

const connector = await supermemory.connectors.create("org-123", {
  provider: "granola",
  config: {
    apiKey: process.env.GRANOLA_API_KEY!,
  },
  documentLimit: 1000,
})

// Granola doesn't require OAuth; authorization is null and the first sync starts now
console.log("Granola connector:", connector.id)
```
```python from supermemory import Supermemory import os
text
client = Supermemory(api_key=os.environ["SUPERMEMORY_API_KEY"])

connector = client.connectors.create(
    "org-123",
    request={
        "provider": "granola",
        "config": {
            "api_key": os.environ["GRANOLA_API_KEY"],
        },
        "document_limit": 1000,
    },
)

# Granola doesn't require OAuth; authorization is None and the first sync starts now
print(f"Granola connector: {connector.id}")
```
```bash curl -X POST "https://api.supermemory.ai/ns/org-123/connectors" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "granola", "config": { "apiKey": "'"$GRANOLA_API_KEY"'" }, "documentLimit": 1000 }'
text
# Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "authorization": null}
```

Supermemory validates the Granola API key before creating the connector. The initial sync starts automatically after the connector is created.

Configuration options

For Granola, provider-specific fields go inside the config object. The namespace is in the URL, and documentLimit stays top-level in the body.

Parameter Location Required Description
apiKey config.apiKey Yes Granola API key from Settings > Connectors > API keys
documentLimit top-level No Maximum notes to sync per connector (default: 10,000)
The API key is stored encrypted and is never returned by `connectors.get` or `connectors.list`.

What gets synced

Granola notes are synced as markdown documents. Each document can include:

  • Note title
  • Meeting time, attendees, and meeting URL when Granola returns them
  • AI-generated summary when present
  • Full transcript when present

Document metadata

Each synced note includes searchable metadata:

Field Description
type Always granola
title Granola note title
createdAt Granola note creation timestamp
updatedAt Granola note update timestamp
url Granola note URL, when available
attendees Attendee names or emails, when available
meetingStart Calendar event start time, when available

You can filter searches using these metadata fields:

typescript
const results = await supermemory.search("org-123", {
  query: "customer onboarding discussion",
  filter: {
    operator: "and",
    operands: [
      { field: "type", operator: "eq", value: "granola" },
      { field: "attendees", operator: "arrayContains", value: "[email protected]" },
    ],
  },
  searchMode: "chunks",
})

Connector Management

Check Sync Status

```typescript const connector = await supermemory.connectors.get("org-123", "PTzGiUYei7pgzg5buzZHgA", { include: "syncs", })
text
console.log("Last sync:", connector.latestRun?.system.status, connector.latestRun?.error)
console.log("Notes synced:", connector.documentCount)
```
```bash curl "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ```

Delete Connector

```typescript await supermemory.connectors.delete("org-123", "PTzGiUYei7pgzg5buzZHgA") ``` ```python client.connectors.delete("org-123", "PTzGiUYei7pgzg5buzZHgA") ``` ```bash curl -X DELETE "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` By default, deleting a connector removes all synced documents from Supermemory. To keep documents, pass `deleteDocuments: false` (`DELETE /ns/{namespace}/connectors/{id}?deleteDocuments=false`).

Manual sync

The call returns 409 while a sync for that connector is already running.

```typescript await supermemory.connectors.sync("org-123", "PTzGiUYei7pgzg5buzZHgA") ``` ```python client.connectors.sync("org-123", "PTzGiUYei7pgzg5buzZHgA") ``` ```bash curl -X POST "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```

Sync behavior

Feature Behavior
Initial sync Fetches Granola notes up to documentLimit
Incremental sync Uses Granola updated_at timestamps to fetch notes changed since the previous sync
Transcript handling Fetches each note with transcript content included
Sync schedule Initial sync after connector creation + manual triggers
Document limit 10,000 notes per connector (default)

Troubleshooting

Error Solution
Granola API key is required Include a non-empty config.apiKey value when creating the connector
Granola API key is invalid Create a new key in Granola and reconnect
Could not reach Granola API Retry after checking Granola API availability and network access
Missing notes Check documentLimit; if the workspace has more notes than the limit, only notes up to the limit are imported

Apps Docs Connectors Managing Resources (`apps/docs/connectors/managing-resources.mdx`)


title: 'Managing Connector Selection' sidebarTitle: 'Managing Resources' icon: 'folder-sync'

**Selectable connectors:** GitHub (repositories), Gmail (labels) and Google Drive (files and folders) let you choose what to sync. Other connectors sync everything and return `selection: null`.

Some connectors let you select which resources to sync. v5 exposes this through two calls on the connector itself: read it with include: "picker" to get a hosted picker URL, or send the ids directly with connectors.update.

Open the Hosted Picker

GET /ns/{namespace}/connectors/{id}?include=picker

Returns a one-time picker URL. Send the user there; Supermemory lists their resources, saves the selection and starts a sync. Pass returnUrl to choose where the picker sends the user when they finish.

```typescript Typescript import { Supermemory } from "supermemory"

const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

const connector = await supermemory.connectors.get("user-123", connectorId, {
include: "picker",
returnUrl: "https://yourapp.com/settings/integrations"
})

console.log("Picker URL:", connector.picker?.url)
console.log("Expires at:", connector.picker?.expiresAt)
console.log("Selectable:", connector.capabilities.selectable)

text
```python Python
from supermemory import Supermemory
import os

client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

connector = client.connectors.get(
    "user-123",
    connector_id,
    include=["picker"],
    return_url="https://yourapp.com/settings/integrations",
)

print(f"Picker URL: {connector.picker.url if connector.picker else None}")
print(f"Expires at: {connector.picker.expires_at if connector.picker else None}")
print(f"Selectable: {connector.capabilities.selectable}")
bash curl
curl "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?include=picker&returnUrl=https%3A%2F%2Fyourapp.com%2Fsettings%2Fintegrations" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

Query parameters

  • include: picker for the hosted picker URL, syncs for recent sync runs, or syncs,picker for both
  • returnUrl: Optional. Where the hosted picker sends the user when they finish

Response

json
{
  "id": "PTzGiUYei7pgzg5buzZHgA",
  "provider": "github",
  "namespace": "user-123",
  "capabilities": { "selectable": true, "webhooks": true },
  "selection": {
    "repos": [
      { "id": "123456789", "name": "your-org/documentation" }
    ]
  },
  "picker": {
    "url": "https://...",
    "expiresAt": "2024-01-15T11:30:00.000Z"
  }
}
The picker link works once, in any browser. `picker` is `null` for providers that do not support selection. A Google Drive connector with full-drive sync can still open the picker; saving a selection switches it to scoped sync. Because the link changes what syncs, `include=picker` needs an admin key with write access to the namespace; otherwise the request returns `403`.

Set the Selection Directly

PATCH /ns/{namespace}/connectors/{id}

Send the ids to sync, grouped by kind. The new selection replaces the current one and starts a sync. Webhooks are registered for the selected resources.

```typescript Typescript import { Supermemory } from "supermemory"

const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

const connector = await supermemory.connectors.update("user-123", connectorId, {
selection: {
repos: ["123456789", "987654321"],
},
})

console.log("Selection:", connector.selection)
console.log("Last sync:", connector.latestRun?.system.status)

text
```python Python
from supermemory import Supermemory
import os

client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

connector = client.connectors.update(
    "user-123",
    connector_id,
    selection={"repos": ["123456789", "987654321"]},
)

print(f"Selection: {connector.selection}")
print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}")
bash curl
curl -X PATCH "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "selection": {
      "repos": ["123456789", "987654321"]
    }
  }'

Request body

json
{
  "selection": {
    "repos": ["123456789"]
  }
}

The selection key depends on the provider:

  • GitHub: repos (repository ids)
  • Gmail: labels (label ids, for example INBOX)
  • Google Drive: files and folders (Drive ids)

You can send documentLimit in the same body. Each call must include selection, documentLimit, or both.

Response

The updated connector object, including the saved selection as { id, name } pairs.

json
{
  "id": "PTzGiUYei7pgzg5buzZHgA",
  "provider": "github",
  "selection": {
    "repos": [
      { "id": "123456789", "name": "your-org/documentation" },
      { "id": "987654321", "name": "your-org/api-docs" }
    ]
  },
  "latestRun": { "errorCode": null, "error": null, "system": { "status": "running", "startedAt": "...", "completedAt": null } }
}

Error Responses

  • 400: Connector does not support selection, the selection is empty or has unknown ids, or the connector needs to be reconnected
  • 401: Unauthorized
  • 403: The key lacks admin role or write access to the connector's namespaces
  • 404: Connector not found
  • 409: Connector is still waiting for the user to finish authorizing
**Automatic Sync:** After a selection is saved, a sync starts for the connector. You don't need to call `connectors.sync` afterwards.

Example: GitHub repository selection

Here's a complete example for GitHub:

```typescript Typescript // 1. Create the connector const created = await supermemory.connectors.create("user-123", { provider: "github", redirectUrl: "https://yourapp.com/callback" })

// 2. Send the user to GitHub. After they authorize, Supermemory shows the hosted
// repository picker, then returns them to redirectUrl
if (created.authorization) window.location.href = created.authorization.url

// 3. To change repositories later, open the hosted picker again
const connector = await supermemory.connectors.get("user-123", created.id, {
include: "picker",
returnUrl: "https://yourapp.com/settings/integrations"
})
if (connector.picker) window.location.href = connector.picker.url

// 4. Or, if you already know the repository ids, set them directly
await supermemory.connectors.update("user-123", created.id, {
selection: { repos: selectedRepositoryIds },
})

text
```python Python
# 1. Create the connector
created = client.connectors.create(
    "user-123",
    request={
        "provider": "github",
        "redirect_url": "https://yourapp.com/callback",
    },
)

# 2. Send the user to GitHub. After they authorize, Supermemory shows the hosted
#    repository picker, then returns them to redirect_url
print(f"Redirect to: {created.authorization.url}")

# 3. To change repositories later, open the hosted picker again
connector = client.connectors.get(
    "user-123",
    created.id,
    include=["picker"],
    return_url="https://yourapp.com/settings/integrations",
)
if connector.picker:
    print(f"Redirect to: {connector.picker.url}")

# 4. Or, if you already know the repository ids, set them directly
client.connectors.update(
    "user-123",
    created.id,
    selection={"repos": selected_repository_ids},
)
bash curl
# 1. Create the connector
curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"provider": "github", "redirectUrl": "https://yourapp.com/callback"}'

# 2. ... (user authorizes at authorization.url and picks repositories in the hosted picker) ...

# 3. To change repositories later, get a one-time hosted picker URL
curl "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?include=picker" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

# 4. Or set the repositories directly
curl -X PATCH "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "selection": {
      "repos": ["123456789"]
    }
  }'

Apps Docs Connectors Notion (`apps/docs/connectors/notion.mdx`)


title: "Notion connector" sidebarTitle: "Notion" icon: "/images/notion-icon.svg"

Connect Notion workspaces to automatically sync pages, databases, and content blocks into your Supermemory knowledge base. Supports real-time updates, rich formatting, and database properties.

Quick setup

1. Create Notion Connector

```typescript import { Supermemory } from "supermemory"
text
const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

const connector = await supermemory.connectors.create("user-123", {
  provider: "notion",
  redirectUrl: "https://yourapp.com/auth/notion/callback",
  documentLimit: 2000,
})

// Send the user to Notion to authorize
if (connector.authorization) window.location.href = connector.authorization.url
```
```python from supermemory import Supermemory import os
text
client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

connector = client.connectors.create(
    "user-123",
    request={
        "provider": "notion",
        "redirect_url": "https://yourapp.com/auth/notion/callback",
        "document_limit": 2000,
    },
)

# Send the user to Notion to authorize
print(f"Redirect to: {connector.authorization.url}")
```
```bash curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "notion", "redirectUrl": "https://yourapp.com/auth/notion/callback", "documentLimit": 2000 }'
text
# Response: {
#   "id": "PTzGiUYei7pgzg5buzZHgA",
#   "authorization": {
#     "url": "https://api.supermemory.ai/v3/connections/auth/redirect?state=...",
#     "expiresAt": "2024-01-15T11:30:00.000Z"
#   }
# }
```

2. Handle OAuth flow

Send the user to authorization.url before authorization.expiresAt. After the user grants workspace access, Notion redirects to your redirectUrl and the first sync starts. A pending OAuth connector is not returned by list until the user finishes authorization; get returns it with system.status: "pending_authorization".

3. Monitor sync progress

```typescript // Check connector details and recent sync runs const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", { include: "syncs", })
text
console.log("Connected workspace:", connector.account)
console.log("Last sync:", connector.latestRun?.system.status)
console.log("Documents synced:", connector.documentCount)

// List synced pages and databases
const { documents } = await supermemory.list("user-123", "documents")
```
```python # Check connector details and recent sync runs connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])
text
print(f"Connected workspace: {connector.account}")
print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}")
print(f"Documents synced: {connector.document_count}")

# List synced pages and databases
documents = client.list("user-123", "documents").documents
```
```bash # Get connector details with recent sync runs curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response includes connector details:
# {
#   "id": "PTzGiUYei7pgzg5buzZHgA",
#   "provider": "notion",
#   "namespace": "user-123",
#   "account": "[email protected]",
#   "documentLimit": 2000,
#   "documentCount": 120,
#   "latestRun": { "system": { "status": "completed", ... }, "error": null },
#   "system": { "status": "active", "createdAt": "2024-01-15T10:00:00Z", "lastSuccessfulSyncAt": "2024-01-15T10:30:00Z" },
#   "syncs": [...]
# }

# List synced documents in the namespace
curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

# Response: {"documents": [{"title": "Product Roadmap", "system": {"status": "done", ...}, ...}], "pagination": {...}}
```
There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.

Document limit

Each connector has a documentLimit (optional when creating the connector; allowed range 1–10,000). For Notion, each sync run asks the Notion Search API for pages shared with the integration, ordered by last edited time, newest first, and stops after that many pages (or when Search has no more results).

  • Full sync: If your workspace has more shareable pages than documentLimit, the rest are not included in that run. Pages that look “missing” are often older or less recently edited relative to that ordering. Increase documentLimit or trigger another sync after pages change if you need broader coverage.
  • Incremental sync: Only pages edited after the previous sync are candidates; each one still counts toward the same documentLimit. If more pages changed than the limit since last sync, only the first batch in that newest-first order is returned for that run.

Nested and child pages still count as normal pages in Search if the integration can access them—they are not skipped because they are nested. The limit applies to how many pages are fetched per sync, not to depth.

Change the limit later with supermemory.connectors.update(namespace, id, { documentLimit }).

Manual Sync

```typescript const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
console.log(run.status)
// Output: queued
```
```bash curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
# 409 if a sync is already running
```

Supported content types

Notion pages

  • Rich text blocks with formatting preserved
  • Nested pages and hierarchical structure
  • Embedded content (images, videos, files)
  • Code blocks with syntax highlighting
  • Callouts and quotes converted to markdown

Notion databases

  • Database entries synced as individual documents
  • Properties included in metadata
  • Relations between database entries
  • Formulas and rollups calculated values
  • Multi-select and select properties

Block types

Block Type Processing Markdown Output
Text Formatting preserved **bold**, *italic*, ~~strikethrough~~
Heading Hierarchy maintained # H1, ## H2, ### H3
Code Language detected python\ncode here\n
Quote Blockquote format > quoted text
Callout Custom formatting > 💡 **Note:** callout text
List Structure preserved - item 1\n - nested item
Table Markdown tables `
Image Referenced with metadata ``
Embed Link with context [Embedded Content](url)

Delete Connector

Remove a Notion connector when no longer needed:

```typescript // Delete the connector and its imported documents (default) await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
// Delete the connector but keep the imported documents
await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
  deleteDocuments: false,
})
```
```python # Delete the connector and its imported documents (default) client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
# Delete the connector but keep the imported documents
client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
```
```bash # Delete the connector and its imported documents (default) curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Delete the connector but keep the imported documents
curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?deleteDocuments=false" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
Deleting a connector will: - Stop all future syncs from Notion - Remove the OAuth authorization - Delete the synced documents unless you pass `deleteDocuments: false`

Advanced configuration

Custom Notion integration

For production deployments you can connect with your own Notion integration. Custom OAuth credentials are an organization setting and are not part of the v5 connector routes. See Custom OAuth Applications for the setup steps and callback URL.

Workspace permissions

Notion connector respects workspace permissions:

Permission Level Sync Behavior
Admin Full workspace access
Member Pages with read access
Guest Only shared pages
No Access Removed from index

Database integration

Database properties

Notion database properties are mapped to metadata keys named notion_prop_, lowercased with spaces replaced by underscores (a Status property becomes notion_prop_status). The page title is stored as title, and every page has type: "notion_doc".

typescript
// List documents in the namespace (there is no per-connector document list in v5)
const { documents } = await supermemory.list("user-123", "documents")

// Find database entries by a property value
const projectEntries = documents.filter(doc =>
  doc.metadata?.notion_prop_status === "In Progress"
)

// Database properties become searchable metadata
const projectWithStatus = await supermemory.search("user-123", {
  query: "machine learning project",
  filter: {
    operator: "and",
    operands: [
      { field: "notion_prop_status", operator: "eq", value: "In Progress" },
      { field: "notion_prop_priority", operator: "eq", value: "High" },
    ],
  },
  searchMode: "chunks",
})

Optimization strategies

  1. Set documentLimit high enough for your workspace size (see Document limit)
  2. Use one namespace per user or tenant for efficient organization
  3. Monitor database sync performance for large datasets
  4. Share only the pages you need with the integration so each sync stays small
  5. Handle webhook delays gracefully in your application
**Notion-Specific Benefits:** - Real-time sync via webhooks for instant updates - Rich formatting and block structure preserved - Database properties become searchable metadata - Hierarchical page structure maintained - Collaborative workspace support **Important Limitations:** - Complex block formatting may be simplified in markdown conversion - Large databases can take significant time to sync initially - Workspace permissions affect which content is accessible - Notion API rate limits may affect sync speed for large workspaces - Embedded files and images are referenced, not stored directly

Apps Docs Connectors Onedrive (`apps/docs/connectors/onedrive.mdx`)


title: "OneDrive connector" sidebarTitle: "OneDrive" icon: "/images/microsoft-icon.svg"

Connect OneDrive to automatically sync Word documents, Excel spreadsheets, and PowerPoint presentations into your Supermemory knowledge base. Supports both personal and business accounts with scheduled synchronization.

Quick setup

1. Create OneDrive Connector

typescript typescript
import { Supermemory } from "supermemory"

const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

const connector = await supermemory.connectors.create("user-123", {
  provider: "onedrive",
  redirectUrl: "https://yourapp.com/auth/onedrive/callback",
  documentLimit: 1500,
})

// Send the user to Microsoft to authorize
if (connector.authorization) window.location.href = connector.authorization.url
// Output: Redirects to https://api.supermemory.ai/v3/connections/auth/redirect?state=...
python python
from supermemory import Supermemory
import os

client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

connector = client.connectors.create(
    "user-123",
    request={
        "provider": "onedrive",
        "redirect_url": "https://yourapp.com/auth/onedrive/callback",
        "document_limit": 1500,
    },
)

# Send the user to Microsoft to authorize
print(f"Redirect to: {connector.authorization.url}")
# Output: Redirect to: https://api.supermemory.ai/v3/connections/auth/redirect?state=...
bash curl
curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "onedrive",
    "redirectUrl": "https://yourapp.com/auth/onedrive/callback",
    "documentLimit": 1500
  }'

# Response: {
#   "id": "PTzGiUYei7pgzg5buzZHgA",
#   "authorization": {
#     "url": "https://api.supermemory.ai/v3/connections/auth/redirect?state=...",
#     "expiresAt": "2024-01-15T11:30:00.000Z"
#   }
# }

2. Handle Microsoft OAuth

Send the user to authorization.url before authorization.expiresAt. After the user grants permissions, Microsoft redirects to your redirectUrl and the initial sync begins. A pending OAuth connector is not returned by list until the user finishes authorization; get returns it with system.status: "pending_authorization".

3. Monitor sync status

```typescript Typescript // Check connector details with recent sync runs const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", { include: "syncs", })
text
console.log("Account:", connector.account)
console.log("Last sync:", connector.latestRun?.system.status)
console.log("Documents synced:", connector.documentCount)

// List synced Office documents in the namespace
const { documents } = await supermemory.list("user-123", "documents")
```
python python
# Check connector details with recent sync runs
  connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])

  print(f"Account: {connector.account}")
  print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}")
  print(f"Documents synced: {connector.document_count}")

  # List synced Office documents in the namespace
  documents = client.list("user-123", "documents").documents
  ```
```bash cURL
  # Get connector details with recent sync runs
  curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
    -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

  # List synced Office documents in the namespace
  curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
    -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{}'
  ```
</CodeGroup>

<Note>
There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.
</Note>

## Supported document types

### Microsoft Word documents
- **Rich text formatting** converted to markdown
- **Headers and styles** preserved as markdown hierarchy
- **Images and charts** extracted and referenced
- **Tables** converted to markdown tables

### Excel spreadsheets
- **Worksheet data** converted to structured markdown
- **Multiple sheets** processed separately
- **Charts and graphs** extracted as images
- **Formulas** converted to calculated values
- **Cell formatting** simplified in markdown

### PowerPoint presentations
- **Slide content** converted to structured markdown
- **Speaker notes** included when present
- **Images and media** extracted and referenced
- **Embedded objects** processed when possible

## Sync mechanism

Webhooks lead to real-time syncing of changes in documents. You may also manually trigger a sync.

### Manual sync trigger

The call returns `409` while a sync for that connector is already running.

<CodeGroup>
```typescript Typescript
  const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")

  console.log(run.status)
  // Output: queued
  ```
```python Python
  run = client.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")

  print(run.status)
  # Output: queued
  ```
```bash cURL
  curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
    -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

  # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
  ```
</CodeGroup>

## Delete Connector

Remove a OneDrive connector when no longer needed:

<CodeGroup>

```typescript Typescript
// Delete the connector and its imported documents (default)
await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")

// Delete the connector but keep the imported documents
await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
deleteDocuments: false,
})
python python
# Delete the connector and its imported documents (default)
client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")

# Delete the connector but keep the imported documents
client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
bash curl
# Delete the connector and its imported documents (default)
curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

# Delete the connector but keep the imported documents
curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?deleteDocuments=false" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
Deleting a connector will: - Stop all future syncs from OneDrive - Remove the OAuth authorization - Delete the synced documents unless you pass `deleteDocuments: false`

Advanced configuration

Custom Microsoft app

For production deployments you can connect with your own Microsoft application. Custom OAuth credentials are an organization setting and are not part of the v5 connector routes. See Custom OAuth Applications for the setup steps and callback URL.

Optimization tips

  1. Set realistic document limits based on storage and usage
  2. Monitor sync health regularly with latestRun and include: "syncs"
  3. Trigger manual syncs when immediate updates are needed
  4. Consider account type when setting expectations
**OneDrive-Specific Benefits:** - Supports both personal and business Microsoft accounts - Processes all major Office document formats - Preserves document structure and formatting - Handles large enterprise document collections **Important Limitations:** - Large Office documents may take significant time to process - Complex Excel formulas may not convert perfectly to markdown - Microsoft API rate limits may slow sync for large accounts

Apps Docs Connectors S3 (`apps/docs/connectors/s3.mdx`)


title: "S3 Connector" sidebarTitle: "S3" icon: "/icons/hugeicons/database-01.svg"

Connect Amazon S3 buckets or S3-compatible storage services (MinIO, DigitalOcean Spaces, Cloudflare R2, Tigris) to sync files into your Supermemory knowledge base.

The S3 connector requires a **Scale Plan** or higher. You can also create S3 connectors directly from the [Supermemory Console](https://console.supermemory.ai).

Quick setup

```typescript import { Supermemory } from "supermemory"
text
const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

const connector = await supermemory.connectors.create("org-123", {
  provider: "s3",
  config: {
    bucket: "my-documents-bucket",
    region: "us-east-1",
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
  },
})

// S3 doesn't require OAuth; authorization is null and the first sync starts now
console.log("Connector ID:", connector.id)
```
```python from supermemory import Supermemory import os
text
client = Supermemory(api_key=os.environ["SUPERMEMORY_API_KEY"])

connector = client.connectors.create(
    "org-123",
    request={
        "provider": "s3",
        "config": {
            "bucket": "my-documents-bucket",
            "region": "us-east-1",
            "access_key_id": os.environ["AWS_ACCESS_KEY_ID"],
            "secret_access_key": os.environ["AWS_SECRET_ACCESS_KEY"],
        },
    },
)

# S3 doesn't require OAuth; authorization is None and the first sync starts now
print(f"Connector ID: {connector.id}")
```
```bash curl -X POST "https://api.supermemory.ai/ns/org-123/connectors" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "s3", "config": { "bucket": "my-documents-bucket", "region": "us-east-1", "accessKeyId": "AKIAIOSFODNN7EXAMPLE", "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" } }'
text
# Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "authorization": null}
```

Configuration options

For S3, provider-specific fields go inside the config object. The namespace is in the URL, and documentLimit stays top-level in the body.

Parameter Location Required Description
bucket config.bucket Yes S3 bucket name
region config.region Yes AWS region (e.g., us-east-1). Use auto for S3-compatible providers that don't expose AWS-style regions (MinIO, R2, Tigris).
accessKeyId config.accessKeyId Yes AWS access key ID or S3-compatible service key
secretAccessKey config.secretAccessKey Yes AWS secret access key
endpoint config.endpoint No Custom endpoint for S3-compatible services
prefix config.prefix No Key prefix filter (e.g., documents/)
documentLimit top-level No Maximum documents to sync (default: 10,000)
Credentials are stored encrypted and are never returned by `connectors.get` or `connectors.list`. The `config` in responses contains only non-secret fields such as `bucket`, `region`, `endpoint` and `prefix`.

S3-compatible services

Use config.endpoint to connect to S3-compatible storage. These services don't use AWS-style regions, so set config.region to auto. The value is still required for request signing but the service ignores it.

typescript
// MinIO
const connector = await supermemory.connectors.create("minio-sync", {
  provider: "s3",
  config: {
    bucket: "my-bucket",
    region: "auto",
    accessKeyId: "minio-key",
    secretAccessKey: "minio-secret",
    endpoint: "https://minio.example.com",
  },
})

Common S3-compatible endpoint values:

Service config.endpoint config.region
DigitalOcean Spaces https://nyc3.digitaloceanspaces.com nyc3
Cloudflare R2 https://<account-id>.r2.cloudflarestorage.com auto
Tigris https://t3.storage.dev auto

Cloudflare R2 example:

typescript
const connector = await supermemory.connectors.create("r2-sync", {
  provider: "s3",
  config: {
    bucket: "my-bucket",
    region: "auto",
    accessKeyId: process.env.R2_ACCESS_KEY_ID!,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
    endpoint: "https://<account-id>.r2.cloudflarestorage.com",
  },
})
For S3-compatible services, `config.endpoint` is the base S3 endpoint. Do not include the bucket name in the endpoint URL; pass the bucket separately as `config.bucket`.

Prefix filtering

Sync only files within a specific path:

typescript
const connector = await supermemory.connectors.create("engineering-docs", {
  provider: "s3",
  config: {
    bucket: "company-data",
    region: "us-east-1",
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
    prefix: "documents/engineering/", // Only syncs files under this path
  },
})

Multi-Tenant Buckets

A connector syncs into exactly one namespace. For a bucket shared by many tenants, create one connector per tenant namespace and point each at that tenant's path with config.prefix:

typescript
await supermemory.connectors.create("user-123", {
  provider: "s3",
  config: {
    bucket: "user-files",
    region: "us-east-1",
    accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
    prefix: "users/user-123/",
  },
})

Connector Management

Check Sync Status

```typescript const connector = await supermemory.connectors.get("org-123", "PTzGiUYei7pgzg5buzZHgA", { include: "syncs", })
text
console.log("Bucket:", connector.config?.bucket)
console.log("Last sync:", connector.latestRun?.system.status, connector.latestRun?.error)
console.log("Files synced:", connector.documentCount)
```
```bash curl "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ```

Delete Connector

```typescript await supermemory.connectors.delete("org-123", "PTzGiUYei7pgzg5buzZHgA") ``` ```bash curl -X DELETE "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` By default, deleting a connector removes all synced documents from Supermemory. To keep documents, pass `deleteDocuments: false` (`DELETE /ns/{namespace}/connectors/{id}?deleteDocuments=false`).

Manual sync

The call returns 409 while a sync for that connector is already running.

```typescript await supermemory.connectors.sync("org-123", "PTzGiUYei7pgzg5buzZHgA") ``` ```bash curl -X POST "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```

Sync behavior

Feature Behavior
Initial sync Fetches all files matching prefix filter
Incremental sync Only files modified since last sync
Sync schedule Every 4 hours + manual triggers
Document limit 10,000 files per connector (default)

IAM permissions

Minimum required permissions:

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:ListBucket"],
      "Resource": [
        "arn:aws:s3:::your-bucket-name",
        "arn:aws:s3:::your-bucket-name/*"
      ]
    }
  ]
}

Error codes

Supermemory checks the credentials against the bucket before it creates the connector. If that check fails, POST /ns/{namespace}/connectors returns 400 with one of these messages. The code column is the status S3 returned.

S3 status Message Solution
401 Authentication failed Verify access key and secret
403 Access denied Check IAM permissions and bucket policy
404 Bucket not found Verify bucket name and region

Apps Docs Connectors Troubleshooting (`apps/docs/connectors/troubleshooting.mdx`)


title: "Connector troubleshooting" sidebarTitle: "Troubleshooting" icon: "/icons/hugeicons/wrench-01.svg"

Quick guide to resolve common connector issues with authentication, syncing, and permissions.

Quick health check

Check if your connectors are working properly:

typescript typescript
const { connectors } = await supermemory.connectors.list("user-123")

connectors.forEach(connector => {
  console.log(`${connector.provider}: ${connector.account} - last sync ${connector.latestRun?.system.status ?? "never"}`)
})

// Inspect a failing connector's recent runs and the items that failed
const failing = connectors.filter(connector => connector.latestRun?.system.status === "failed")
for (const connector of failing) {
  const detail = await supermemory.connectors.get("user-123", connector.id, {
    include: "syncs",
  })
  console.log(`⚠️ ${connector.provider}: ${detail.latestRun?.error}`)
  console.log(detail.syncs)
}

// Check for stuck documents in the namespace
const { documents } = await supermemory.list("user-123", "documents")
const stuck = documents.filter(doc => doc.system.status === "failed")
if (stuck.length > 0) {
  console.log(`⚠️ ${stuck.length} documents failed to process`)
}
python python
connectors = client.connectors.list("user-123").connectors

for connector in connectors:
    status = connector.latest_run.system.status if connector.latest_run else "never"
    print(f"{connector.provider}: {connector.account} - last sync {status}")

# Inspect a failing connector's recent runs and the items that failed
failing = [c for c in connectors if c.latest_run and c.latest_run.system.status == "failed"]
for connector in failing:
    detail = client.connectors.get("user-123", connector.id, include=["syncs"])
    print(f"⚠️ {connector.provider}: {detail.latest_run.error}")
    print(detail.syncs)

# Check for stuck documents in the namespace
documents = client.list("user-123", "documents").documents
stuck = [doc for doc in documents if doc.system.status == "failed"]
if stuck:
    print(f"⚠️ {len(stuck)} documents failed to process")
bash curl
# List connectors in the namespace
curl "https://api.supermemory.ai/ns/user-123/connectors" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

# Read one connector with its recent sync runs
curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

# Check document status in the namespace
curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filter": {"field": "type", "operator": "eq", "value": "notion_doc"}}'
There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace; read each document's `system.status`.

Common issues

Connector Missing From List

Problem: connectors.create returned an id, but connectors.list does not show it

Solution: A pending OAuth connector is not returned by list until the user finishes authorization. connectors.get still returns it with system.status: "pending_authorization". Send the user to authorization.url before authorization.expiresAt. If the link expired, create the connector again.

OAuth callback fails

Problem: "Invalid redirect URI" error after user grants permissions

Solution: The provider always redirects to Supermemory's callback, not to your redirectUrl. If you use a custom OAuth application, its redirect URI must be exactly https://api.supermemory.ai/v3/connections/auth/callback/{provider}. Your redirectUrl is where Supermemory sends the user after it handles that callback, and must be an http or https URL:

typescript
const connector = await supermemory.connectors.create("user-123", {
  provider: "notion",
  redirectUrl: "https://yourapp.com/auth/notion/callback"
})

Prevention:

  • Use HTTPS for production URLs
  • Register https://api.supermemory.ai/v3/connections/auth/callback/{provider} in your custom OAuth app settings
  • Test the flow in development first

Documents not syncing

Problem: Documents stuck in "queued" or "extracting" status for over 30 minutes

Solution: Trigger a manual sync:

typescript
// Force a sync for the connector
await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
// 409 means a sync is already running; wait for latestRun.system.status to change

If documents consistently fail:

  • Read latestRun.error and include: "syncs" for the failed items
  • Check if files are over 50MB (may timeout)
  • Verify you have permission to access the documents
  • Ensure the document type is supported

Permission denied errors

Problem: Some documents show "permission denied" or aren't syncing

Solution: Re-authenticate with proper permissions:

typescript
// Delete and recreate the connector
await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
  deleteDocuments: false,
})

const next = await supermemory.connectors.create("user-123", {
  provider: "google-drive",
  redirectUrl: "https://yourapp.com/callback"
})

// User must re-authenticate
if (next.authorization) window.location.href = next.authorization.url

Sync takes too long

Problem: Hundreds of documents taking hours to sync

Solution: Set reasonable document limits:

typescript
const connector = await supermemory.connectors.create("user-123", {
  provider: "onedrive",
  redirectUrl: "https://yourapp.com/callback",
  documentLimit: 500, // Start with fewer documents
})

// Raise it later without reconnecting
await supermemory.connectors.update("user-123", connector.id, {
  documentLimit: 2000,
})

Provider-specific issues

Google Drive

Shared Drive Issues

Shared drives require special permissions. Make sure:

  • User has access to the shared drive
  • OAuth app has drive.readonly scope
  • User is a member of the shared drive

Picker Not Completed

With the default scoped sync, syncs may skip the connector until the user finishes the file picker. Read the connector with include: "picker" and send the user to picker.url.

Notion

Database Not Syncing

Notion databases need explicit permission. If databases aren't syncing:

  1. Go to Notion workspace settings
  2. Find your integration under "Connections"
  3. Click on the integration
  4. Select specific pages/databases to share
  5. Re-sync after granting access

Workspace Access

For full workspace access, a workspace admin must:

  1. Approve the integration
  2. Grant access to all pages
  3. Enable "Read content" permission

OneDrive

Business vs Personal Accounts

Business accounts may have additional restrictions:

  • Admin consent might be required
  • Some SharePoint sites may be restricted
  • Compliance policies may block certain files

Gmail

Real-time Sync Not Working

If emails aren't syncing in real-time but scheduled/manual sync works:

  1. Real-time sync uses Google Cloud Pub/Sub webhooks
  2. Watch subscriptions expire after 7 days (supermemory auto-renews)
  3. Only INBOX label triggers real-time updates
  4. Trigger a manual sync to verify the connector is healthy:
typescript
await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")

Missing Refresh Token

If Gmail stops syncing after initial setup:

  1. The user may have revoked app access in Google Account settings
  2. Delete and recreate the connector
  3. Ensure user completes full OAuth consent flow
typescript
// Re-authenticate to get fresh tokens
await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
  deleteDocuments: false,
})

const next = await supermemory.connectors.create("user-123", {
  provider: "gmail",
  redirectUrl: "https://yourapp.com/callback"
})

Max Plan Required Error

Gmail connector requires Max Plan or above. If you see access errors:

  • Verify your organization has the required plan
  • Contact support to upgrade your plan

Best practices

  1. Set reasonable document limits - Start with 500-1000 documents
  2. Use one namespace per user or tenant - Makes debugging easier
  3. Monitor latestRun - Check weekly for failed syncs
  4. Handle rate limits gracefully - Implement exponential backoff
  5. Test OAuth in development - Ensure redirect URLs work before production

Apps Docs Connectors Web Crawler (`apps/docs/connectors/web-crawler.mdx`)


title: "Web Crawler connector" sidebarTitle: "Web Crawler" icon: "/icons/hugeicons/globe-02.svg"

Connect websites to automatically crawl and sync web pages into your Supermemory knowledge base. The web crawler respects robots.txt rules, includes SSRF protection, and automatically recrawls sites on a schedule.

The web crawler connector requires a **Scale Plan** or **Enterprise Plan**.

Quick setup

1. Create Web Crawler Connector

```typescript import { Supermemory } from "supermemory"
text
const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })

const connector = await supermemory.connectors.create("user-123", {
  provider: "web-crawler",
  config: {
    startUrl: "https://docs.example.com",
    crawlDepth: 3,
  },
  documentLimit: 5000,
})

// Web crawler doesn't require OAuth; crawling starts immediately
console.log("Connector ID:", connector.id)
// Note: connector.authorization is null for web-crawler
```
```python from supermemory import Supermemory import os
text
client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))

connector = client.connectors.create(
    "user-123",
    request={
        "provider": "web-crawler",
        "config": {
            "start_url": "https://docs.example.com",
            "crawl_depth": 3,
        },
        "document_limit": 5000,
    },
)

# Web crawler doesn't require OAuth; crawling starts immediately
print(f"Connector ID: {connector.id}")
# Note: connector.authorization is None for web-crawler
```
```bash curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider": "web-crawler", "config": { "startUrl": "https://docs.example.com", "crawlDepth": 3 }, "documentLimit": 5000 }'
text
# Response: {
#   "id": "PTzGiUYei7pgzg5buzZHgA",
#   "authorization": null
# }
```

Configuration options

Parameter Location Required Description
startUrl config.startUrl Yes Public URL where the crawl starts
crawlDepth config.crawlDepth No How many links deep to follow (1 to 5, default 3)
documentLimit top-level No Maximum pages to sync (1 to 10,000)

2. Connector Established

Unlike OAuth connectors, the web crawler doesn't require user authorization. authorization is null, the connector is visible immediately, and crawling begins automatically.

3. Monitor sync progress

```typescript // Check connector details with recent sync runs const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", { include: "syncs", })
text
console.log("Start URL:", connector.config?.startUrl)
console.log("Last sync:", connector.latestRun?.system.status)
console.log("Pages synced:", connector.documentCount)

// List synced web pages in the namespace
const docs = await supermemory.list("user-123", "documents")

console.log(`Synced ${docs.pagination.totalItems} documents`)
```
```python # Check connector details with recent sync runs connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])
text
print(f"Start URL: {(connector.config or {}).get('startUrl')}")
print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}")
print(f"Pages synced: {connector.document_count}")

# List synced web pages in the namespace
docs = client.list("user-123", "documents")

print(f"Synced {docs.pagination.total_items} documents")
```
```bash # Get connector details with recent sync runs curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response includes connector details:
# {
#   "id": "PTzGiUYei7pgzg5buzZHgA",
#   "provider": "web-crawler",
#   "namespace": "user-123",
#   "config": {"startUrl": "https://docs.example.com", "crawlDepth": 3},
#   "documentLimit": 5000,
#   "documentCount": 240,
#   "latestRun": { "system": { "status": "completed", ... }, "error": null },
#   "system": { "status": "active", "createdAt": "2024-01-15T10:00:00Z", "lastSuccessfulSyncAt": "..." }
# }

# List synced documents in the namespace
curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

# Response: {"documents": [{"title": "Getting Started", "system": {"status": "done", ...}, ...}], "pagination": {...}}
```
There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.

Supported content types

Web pages

  • HTML content extracted and converted to markdown
  • Same-domain crawling only (respects hostname boundaries)
  • Robots.txt compliance - respects disallow rules
  • Content filtering - only HTML pages (skips non-HTML content)

URL requirements

The web crawler only processes valid public URLs:

  • Must be a public URL (not localhost, private IPs, or internal domains)
  • Must be accessible from the internet
  • Must return HTML content (non-HTML files are skipped)

Sync mechanism

The web crawler uses scheduled recrawling rather than real-time webhooks:

  • Initial Crawl: Begins immediately after connector creation
  • Scheduled Recrawling: Automatically recrawls sites that haven't been synced in 7+ days
  • No Real-time Updates: Unlike other connectors, web crawler doesn't support webhook-based real-time sync
The recrawl schedule is automatically assigned when the connector is created. Sites are recrawled periodically to keep content up to date, but updates are not instantaneous.

Manual Recrawl

Start a crawl now. The call returns 409 while a sync for that connector is already running.

```typescript const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
console.log(run.status)
// Output: queued
```
```bash curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```

Connector Management

List All Connectors

```typescript // List all web crawler connectors in a namespace const { connectors } = await supermemory.connectors.list("user-123")
text
const webCrawlerConnectors = connectors.filter(
  connector => connector.provider === "web-crawler"
)

webCrawlerConnectors.forEach(connector => {
  console.log(`Start URL: ${connector.config?.startUrl}`)
  console.log(`Connector ID: ${connector.id}`)
  console.log(`Created: ${connector.system.createdAt}`)
})
```
```python # List all web crawler connectors in a namespace connectors = client.connectors.list("user-123", provider="web-crawler").connectors
text
for connector in connectors:
    print(f"Start URL: {(connector.config or {}).get('startUrl')}")
    print(f"Connector ID: {connector.id}")
    print(f"Created: {connector.system.created_at}")
```
```bash # List all connectors in a namespace curl "https://api.supermemory.ai/ns/user-123/connectors" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Response: {
#   "connectors": [
#     {
#       "id": "PTzGiUYei7pgzg5buzZHgA",
#       "provider": "web-crawler",
#       "namespace": "user-123",
#       "documentLimit": 5000,
#       "config": {"startUrl": "https://docs.example.com", "crawlDepth": 3},
#       "system": { "status": "active", "createdAt": "2024-01-15T10:30:00.000Z", "lastSuccessfulSyncAt": "..." }
#     }
#   ],
#   "pagination": { "currentPage": 1, "limit": 10, "totalItems": 1, "totalPages": 1 }
# }
```

Delete Connector

Remove a web crawler connector when no longer needed:

```typescript // Delete the connector and its crawled pages (default) await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
// Delete the connector but keep the crawled pages
await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
  deleteDocuments: false,
})
```
```python # Delete the connector and its crawled pages (default) client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
text
# Delete the connector but keep the crawled pages
client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
```
```bash # Delete the connector and its crawled pages (default) curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
text
# Delete the connector but keep the crawled pages
curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?deleteDocuments=false" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
Deleting a connector will: - Stop all future crawls of the website - Remove the connector configuration - Delete the crawled pages unless you pass `deleteDocuments: false`

Security & compliance

SSRF protection

Built-in protection against Server-Side Request Forgery (SSRF) attacks:

  • Blocks private IP addresses (10.x.x.x, 192.168.x.x, 172.16-31.x.x)
  • Blocks localhost and internal domains
  • Blocks cloud metadata endpoints
  • Only allows public, internet-accessible URLs

URL validation

All URLs are validated before crawling:

  • Must be valid HTTP/HTTPS URLs
  • Must be publicly accessible
  • Must return HTML content
  • Response size limited to 10MB
**Important Limitations:** - Requires Scale Plan or Enterprise Plan - Only crawls same-domain URLs - Scheduled recrawling means updates are not real-time - Large websites may take significant time to crawl initially - Robots.txt restrictions may prevent crawling some pages - URLs must be publicly accessible (no authentication required)

Apps Docs Ingestion Add Memories (`apps/docs/ingestion/add-memories.mdx`)


title: "Ingesting context to supermemory" sidebarTitle: "API" icon: "/icons/hugeicons/plus-sign.svg"

Send any raw content to Supermemory — conversations, documents, files, URLs. We extract the memories automatically. Pass id to identify content and avoid duplicates, and taskType: "superrag" if you just need it searchable, not remembered — that's 5x cheaper per token.

Every write goes to one namespace (what v3/v4 called a container tag). The namespace sits in the URL path, never in the body.

Quick start

```typescript import { Supermemory } from "supermemory";
text
const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });

// Add text content
await supermemory.add("user_123", {
  content: "Machine learning enables computers to learn from data",
  metadata: { category: "ai" },
});

// Add a URL (auto-extracted)
await supermemory.add("user_123", {
  content: "https://youtube.com/watch?v=dQw4w9WgXcQ"
});
```
```python from supermemory import Supermemory
text
client = Supermemory()

# Add text content
client.add(
    "user_123",
    content="Machine learning enables computers to learn from data",
    metadata={"category": "ai"},
)

# Add a URL (auto-extracted)
client.add("user_123", content="https://youtube.com/watch?v=dQw4w9WgXcQ")
```
```bash curl -X POST "https://api.supermemory.ai/ns/user_123/document" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "Machine learning enables computers to learn from data", "metadata": {"category": "ai"} }' ```

Response:

json
{ "id": "abc123", "status": "queued" }
If an irrecoverable processing error occurs, the document's status becomes `failed`. Send the content again with the same `id` to reprocess it.

Updating content

Use id to update existing documents or conversations. When you send content with the same id, Supermemory intelligently processes only what's new.

Two ways to update:

Option 1: Send only the new content

typescript
// First request
await supermemory.add("user_sarah", {
  content: "user: Hi, I'm Sarah.\nassistant: Nice to meet you!",
  id: "conv_123",
});

// Later: send only new messages
await supermemory.add("user_sarah", {
  content: "user: What's the weather?\nassistant: It's sunny today.",
  id: "conv_123", // Same ID — Supermemory links them
});

Option 2: Send the full updated content

typescript
// Supermemory detects the diff and only processes new parts
await supermemory.add("user_sarah", {
  content: "user: Hi, I'm Sarah.\nassistant: Nice to meet you!\nuser: What's the weather?\nassistant: It's sunny today.",
  id: "conv_123",
});

Both work — choose what fits your architecture.

Replace entire document

To completely replace a document's content (not append), use documents.update():

typescript
// Replace the entire document content
await supermemory.documents.update("user_sarah", "doc_id_123", {
  content: "Completely new content replacing everything",
  metadata: { version: 2 },
});

This triggers full reprocessing of the document. If you only update metadata (no content change), the document is updated in place with no reindexing.

Formatting conversations

Format your conversations however you want. Supermemory handles any string format:

typescript
// Simple string
content: "user: Hello\nassistant: Hi there!"

// JSON stringify
content: JSON.stringify(messages)

// Template literal
content: messages.map(m => `${m.role}: ${m.content}`).join('\n')

// Any format — just make it a string
content: formatConversation(messages)

Upload files

Upload PDFs, images, and documents directly. This is a multipart request, so metadata is a JSON string here, not an object.

```typescript import { openAsBlob } from "node:fs";
text
await supermemory.documents.uploadFile("user_123", {
  file: await openAsBlob("document.pdf"),
  metadata: JSON.stringify({ source: "upload" }),
});
```
```python import json
text
with open("document.pdf", "rb") as file:
    client.documents.upload_file(
        "user_123",
        file=file,
        metadata=json.dumps({"source": "upload"}),
    )
```
```bash curl -X POST "https://api.supermemory.ai/ns/user_123/document/file" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -F "[email protected]" \ -F 'metadata={"source":"upload"}' ```

Supported file types

Type Formats Processing
Documents PDF, DOC, DOCX, TXT, MD Text extraction, OCR for scans
Images JPG, PNG, GIF, WebP OCR text extraction
Spreadsheets CSV, Google Sheets Structured data extraction
Videos YouTube URLs, MP4 Auto-transcription

Limits: 50MB max file size

Scale and Enterprise include [advanced document extraction for PDFs](/concepts/content-types#advanced-document-extraction), with page-by-page OCR and descriptions of figures and diagrams. It is selected automatically from your organization's plan; no extra upload parameter is needed. Free, Pro, and Max keep standard PDF extraction, including OCR for scans.

Parameters

namespace is the first argument on the SDK call and the URL path on the HTTP route. Every other parameter, including taskType and dreaming, goes in the request object (the JSON body over HTTP). The route accepts no query parameters; sending one returns 400.

Parameter Type Description
namespace string Required. Group by user/project. Goes in the URL path. Required for user profiles
content string Required. Any raw content — text, conversations, URLs, HTML
id string Recommended. Your ID for the content (conversation ID, doc ID). Enables updates and deduplication. The response id echoes it back
metadata object Key-value pairs for filtering (strings, numbers, booleans, or arrays of strings)
group object Filter which existing memories are used as context during ingestion. See Filtered Writes
supportingContext string Context for memory extraction on this namespace. Max 1500 chars. See Customization
date string ISO 8601 date the content was originally created. Use when backfilling. See Backfill historical data
dreaming "dynamic" | "instant" Processing mode. Default "dynamic". "instant" processes each document on its own and bills one extra operation. See Processing Modes
taskType "memory" | "superrag" Pipeline to run. Default "memory". "superrag" skips fact extraction and profile updates, doing only chunk/embed/index — at 5x cheaper per token. See SuperRAG ingestion
**Content Types:** ```typescript // Any text — conversations, notes, documents { content: "Meeting notes from today's standup" } { content: JSON.stringify(messages) }
text
// URLs (auto-detected and extracted)
{ content: "https://example.com/article" }
{ content: "https://youtube.com/watch?v=abc123" }

// Markdown, HTML, or any format
{ content: "# Project Docs\n\n## Features\n- Real-time sync" }
```

**Namespaces** (first argument; letters, numbers, `-`, `_`, and `:`, up to 100 characters):
```typescript
// By user
await supermemory.add("user_123", { content: "..." });

// By project
await supermemory.add("project_alpha", { content: "..." });

// Hierarchical
await supermemory.add("org_456_team_backend", { content: "..." });
```

**IDs (Recommended):**
```typescript
// Use IDs from your system
{ id: "conv_abc123" }        // Conversation ID
{ id: "doc_456" }            // Document ID
{ id: "thread_789" }         // Thread ID
{ id: "meeting_2024_01_15" } // Meeting ID

// Updates: same id = same document
// Supermemory only processes new/changed content
await supermemory.add("user_123", {
  content: "Updated content...",
  id: "doc_456", // Links to existing document
});
```

**Metadata:**
```typescript
{
  metadata: {
    source: "slack",
    author: "john",
    priority: 1,
    reviewed: true
  }
}
```
- No nested objects
- Values: string, number, boolean, or an array of strings

**Supporting Context:**
```typescript
// Guide memory extraction for this namespace
await supermemory.add("session_abc123", {
  content: "...",
  supportingContext: `Design exploration conversation between [email protected] and Brand.ai assistant.
    Focus on John's design preferences and brand requirements.`,
});
```
- Max 1500 characters
- Persists on the namespace
- Combines with org-level organizational context

Processing modes

Dreaming: dynamic vs instant

The dreaming parameter controls how Supermemory turns a document into memories.

  • "dynamic" (default) — groups related documents together so memories form from coherent, logical units rather than one isolated entry at a time. A fresh namespace can show zero memories and an empty profile for several minutes.
  • "instant" — processes each document on its own right away, and bills one extra operation per document. Use it for quickstarts, tests, and any flow that reads memories or a profile right after add.
typescript
await supermemory.add("user_123", {
  content: "...",
  dreaming: "instant",
});

Memory vs SuperRAG ingestion

The taskType parameter controls whether that content also feeds the memory pipeline.

  • "memory" (default) — chunks/embeds for search and extracts facts, updates the user's profile, and links into the graph.
  • "superrag" — chunks/embeds for search only. No fact extraction, no profile updates. Priced at 5x cheaper per token than "memory".
typescript
await supermemory.add("user_123", {
  content: "...",
  taskType: "superrag",
});

Use "superrag" for reference material you want searchable but that shouldn't shape what Supermemory knows about a user. Full explanation: SuperRAG → Ingesting as pure SuperRAG.


Filtered writes

By default, when you add content, Supermemory uses all existing memories in the namespace as context for generating new memories. With filtered writes, you can scope this context to only memories from documents matching specific metadata.

This is useful when you have many documents in a namespace but want new memories to build on top of a specific subset — for example, only memories from a particular source, category, or user.

The metadata itself is still written to the document, but the memories will only be built on top of what's already there matching the filter. ```typescript await supermemory.add("user_123", { content: "New research findings on transformer architectures...", metadata: { category: "ml", source: "arxiv" }, group: { category: "ml" }, }); ``` ```python client.add( "user_123", content="New research findings on transformer architectures...", metadata={"category": "ml", "source": "arxiv"}, group={"category": "ml"}, ) ``` ```bash curl -X POST "https://api.supermemory.ai/ns/user_123/document" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "New research findings on transformer architectures...", "metadata": {"category": "ml", "source": "arxiv"}, "group": {"category": "ml"} }' ```

How it works

When group is provided:

  • Profile memories (static context) are filtered by the existing memories' metadata
  • Similar memories used as context during ingestion are filtered the same way
  • The new document's own metadata is written normally — the filter only affects which existing memories are used as context

`group` parameter

Key Type Description
group Record<string, string | number | boolean | string[]> Key-value pairs to filter existing memories by their own metadata, rather than a live join to source-document metadata
  • Scalar values (string, number, boolean) match exactly
  • Array values match if any value in the array matches (OR logic)
  • Multiple keys are combined with AND logic
typescript
// Match documents where category is "ml" AND source is either "arxiv" or "pubmed"
await supermemory.add("user_123", {
  content: "...",
  group: {
    category: "ml",
    source: ["arxiv", "pubmed"],
  },
});

Processing pipeline

When you add content, Supermemory:

  1. Validates your request
  2. Stores the document and queues for processing
  3. Extracts content (OCR, transcription, web scraping)
  4. Chunks into searchable memories
  5. Embeds for vector search
  6. Indexes for retrieval

Track progress with GET /ns/{namespace}/document/{id}:

typescript
const doc = await supermemory.documents.get("user_123", "abc123");
console.log(doc.system.status); // "queued" | "extracting" | "chunking" | "embedding" | "done" | "failed"
Process multiple documents with rate limiting:
text
```typescript
async function batchUpload(documents: Array<{id: string, content: string}>) {
  const results = [];

  for (const doc of documents) {
    try {
      const result = await supermemory.add("batch_import", {
        content: doc.content,
        id: doc.id,
      });
      results.push({ id: doc.id, success: true, docId: result.id });
    } catch (error) {
      results.push({ id: doc.id, success: false, error });
    }

    // Rate limit: 1 second between requests
    await new Promise(r => setTimeout(r, 1000));
  }

  return results;
}
```

**Tips:**
- Batch size: 3-5 documents at once
- Delay: 1-2 seconds between requests
- Use `id` to track and deduplicate
- For large backfills use `documents.batchAdd` instead. See [Backfill historical data](/ingestion/batch-ingest-historical-data)
| Status | Error | Cause | |--------|-------|-------| | 400 | Bad request | Missing required fields, invalid parameters, unexpected query parameters | | 401 | Unauthorized | Invalid or missing API key | | 402 | Payment required | Insufficient balance or document quota | | 403 | Forbidden | No write access to this namespace | | 409 | Conflict | A namespace merge is in progress | | 429 | Rate limited | Too many requests | | 500 | Internal error | Processing failure |
text
```typescript
try {
  await supermemory.add("user_123", { content: "..." });
} catch (error) {
  // Back off on 429, top up on 402, fix the request on 400, retry later on 5xx
  console.error("add failed:", error);
}
```
**Delete by IDs (one or many, Supermemory or your own ids):** ```typescript const { count, errors } = await supermemory.documents.delete("user_123", { ids: ["doc_1", "doc_2", "doc_3"], }); ```
text
**Delete everything in a namespace:**
```typescript
// Delete all content for a user
await supermemory.namespaces.delete("user_123");
```

Deletes are permanent — no recovery.

Next steps


Apps Docs Ingestion Batch Ingest Historical Data (`apps/docs/ingestion/batch-ingest-historical-data.mdx`)


title: "How to backfill historical data into Supermemory" sidebarTitle: "Backfill historical data" icon: "/icons/hugeicons/clock-01.svg"

Use POST /ns/{namespace}/document/batch to backfill exports, emails, messages, or other dated records.

Sort the source data oldest to newest, add `date` to every document.

Backfill in batches

Backfill dated content by setting date on each document, sorting the source records oldest to newest, and sending them in batches. Each request can contain up to 600 documents.

Endpoint: POST /ns/{namespace}/document/batch

typescript typescript
import { Supermemory } from "supermemory";

type SourceDocument = {
  id: string;
  content: string;
  createdAt: string;
};

const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
const batchSize = 100;

async function backfillHistoricalData(sourceDocuments: SourceDocument[]) {
  const documents = sourceDocuments
    .map((document) => ({
      content: document.content,
      id: document.id,
      date: new Date(document.createdAt).toISOString()
    }))
    .sort((a, b) => a.date.localeCompare(b.date));

  for (let offset = 0; offset < documents.length; offset += batchSize) {
    const result = await supermemory.documents.batchAdd("historical_import", {
      documents: documents.slice(offset, offset + batchSize),
    });

    if (result.failed > 0) {
      throw new Error(`${result.failed} documents failed to ingest`);
    }
  }
}
python python
from datetime import datetime, timezone
from supermemory import Supermemory

client = Supermemory()
batch_size = 100

def to_utc(value: str) -> str:
    parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
    if parsed.tzinfo is None:
        raise ValueError("created_at must include a timezone")
    return parsed.astimezone(timezone.utc).isoformat().replace("+00:00", "Z")

def backfill_historical_data(source_documents: list[dict[str, str]]) -> None:
    documents = sorted(
        [
            {
                "content": document["content"],
                "id": document["id"],
                "date": to_utc(document["created_at"]),
            }
            for document in source_documents
        ],
        key=lambda document: document["date"],
    )

    for offset in range(0, len(documents), batch_size):
        result = client.documents.batch_add(
            "historical_import",
            documents=documents[offset : offset + batch_size],
        )

        if result.failed > 0:
            raise RuntimeError(f"{result.failed} documents failed to ingest")
bash curl
curl -X POST "https://api.supermemory.ai/ns/historical_import/document/batch" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "documents": [
      { "content": "first message", "id": "msg_001", "date": "2023-01-05T09:00:00Z" },
      { "content": "second message", "id": "msg_002", "date": "2023-01-06T14:30:00Z" }
    ]
  }'

results lists accepted documents first, in request order, then failed ones, so match each result by id. Inspect count (accepted), failed, and every item in results; a batch can contain successful and failed items together.

Optional: wait for processing to finish

Endpoint: GET /ns/{namespace}/document/{id}

The batch endpoint returns after accepting the documents. If a later step depends on the documents being indexed, poll the returned document IDs until system.status is done. If that later step also needs the extracted memories, send the batch with dreaming: "instant"; under the default dynamic mode memory extraction is batched and can lag done by minutes. Each document reports system.dreamingStatus: "done" once its memories are ready.

typescript typescript
async function waitUntilDone(ids: string[]) {
  while (true) {
    const documents = await Promise.all(
      ids.map((id) => supermemory.documents.get("historical_import", id))
    );

    if (documents.some((document) => document.system.status === "failed")) {
      throw new Error("A document failed to process");
    }

    if (documents.every((document) => document.system.status === "done")) {
      return;
    }
    await new Promise((resolve) => setTimeout(resolve, 10_000));
  }
}
python python
import time

def wait_until_done(ids: list[str]) -> None:
    while True:
        documents = [
            client.documents.get("historical_import", document_id) for document_id in ids
        ]

        if any(document.system.status == "failed" for document in documents):
            raise RuntimeError("A document failed to process")

        if all(document.system.status == "done" for document in documents):
            return

        time.sleep(10)
bash curl
curl "https://api.supermemory.ai/ns/historical_import/document/msg_001" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
# repeat until "system": { "status": "done" }

Apps Docs Ingestion Document Operations (`apps/docs/ingestion/document-operations.mdx`)


title: "Document operations" sidebarTitle: "Documents" icon: "/icons/hugeicons/files-02.svg"

Manage documents after ingestion using the SDK. Every call is scoped to one namespace (what v3/v4 called a container tag).

List documents

Retrieve paginated documents with filtering. One list call serves documents, chunks, and memories; pick with type.

```typescript const { documents } = await supermemory.list("user_123", "documents", { limit: 10, });
text
documents.forEach(d => {
  console.log(d.id, d.title, d.system.status);
});
```
```python page = client.list("user_123", "documents", limit=10)
text
for doc in page.documents:
    print(doc.id, doc.title, doc.system.status)
```
```bash curl -X POST "https://api.supermemory.ai/ns/user_123/list/documents?limit=10" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ```

Response:

json
{
  "documents": [
    {
      "id": "doc_abc123",
      "title": "Meeting notes",
      "type": "text",
      "summary": "Team sync covering the Q3 roadmap",
      "metadata": { "source": "slack" },
      "url": null,
      "system": {
        "status": "done",
        "dreamingStatus": "done",
        "createdAt": "2024-01-15T10:30:00Z",
        "updatedAt": "2024-01-15T10:30:00Z"
      }
    }
  ],
  "chunks": [],
  "memories": [],
  "pagination": {
    "currentPage": 1,
    "limit": 10,
    "totalItems": 25,
    "totalPages": 3
  }
}

Every response contains documents, chunks, memories, and pagination. Only the array for the requested type is filled.

Parameters

namespace and type are required. Over HTTP, pagination and sorting are query parameters and filter and include go in the JSON body; the SDK takes them all in one options object after namespace and type. Unknown query or body keys return 400.

Parameter Type Default Description
type string required documents, chunks, or memories
limit number 10 Items per page (maximum 100)
page number 1 Page number
sort string createdAt createdAt or updatedAt for documents and memories; createdAt or position for chunks
order string desc desc (newest) or asc (oldest)
body.filter object — Metadata filter. See Organizing & Filtering
body.include.content boolean false Documents only: include last published extracted text as content
body.include.forgotten boolean false Memories only: include forgotten or expired rows that remain latest, returned with isForgotten: true. Normal forgetting makes a memory non-latest, so it still will not appear in this list

Include extracted content

Document lists omit content by default. To include the last published extracted text, including text from uploaded files, set include.content in the JSON body:

bash
curl -X POST "https://api.supermemory.ai/ns/user_123/list/documents?limit=10" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"include":{"content":true}}'

content is null until the first extraction publishes. When you replace a file, the previous text remains until the new extraction publishes. It also remains if the replacement fails. Including content can make pages large.

```typescript async function getAllDocuments(namespace: string) { const all = []; let page = 1;
text
while (true) {
  const { documents, pagination } = await supermemory.list(namespace, "documents", {
    limit: 100,
    page,
  });

  all.push(...documents);
  if (page >= pagination.totalPages) break;
  page++;
}

return all;

}

text
</Accordion>

<Accordion title="Filter by metadata">
```typescript
const { documents } = await supermemory.list("user_123", "documents", {
  filter: {
    operator: "and",
    operands: [
      { field: "status", operator: "eq", value: "reviewed" },
      { field: "priority", operator: "eq", value: "high" },
    ],
  },
});

Get document

Get a specific document with its processing status. Pass include to include its chunks and memories.

```typescript const doc = await supermemory.documents.get("user_123", "doc_abc123", { include: ["chunks", "memories"], // optional });
text
console.log(doc.system.status);  // "queued" | "extracting" | ... | "done" | "failed"
console.log(doc.content);
```
```python doc = client.documents.get( "user_123", "doc_abc123", include=["chunks", "memories"], # optional )
text
print(doc.system.status)  # "queued" | "extracting" | ... | "done" | "failed"
print(doc.content)
```
```bash curl "https://api.supermemory.ai/ns/user_123/document/doc_abc123?include=chunks&include=memories" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ```

The id may be the Supermemory document ID or the id you supplied at ingestion. It resolves only inside the namespace in the path.

The current HTTP response also includes `system.dreamingStatus`. TypeScript SDK 5.0.1 and Python SDK 5.0.0 do not expose that field in their generated models; Python's `system.dreaming_status` is not available. Use the raw HTTP response when monitoring memory generation with these versions, or upgrade the TypeScript SDK to 5.0.2 for typed `system.dreamingStatus`. `system.status: "done"` alone does not mean memories are ready.

Processing status

Status Description
queued Waiting to process
extracting Extracting content (OCR, transcription)
chunking Breaking into searchable pieces
embedding Creating vector representations
done Ready for search
failed Processing failed

Treat any status other than done or failed as still in progress.

Memory generation status

system.dreamingStatus tracks memory generation separately from system.status.

Status Description
dreaming Memories for the latest content are queued, being generated, or waiting for a retry
done Memories are generated, or the document needed none

With the default dreaming: "dynamic", Supermemory batches related documents before forming memories, so status can be done minutes before dreamingStatus is. If your next step reads memories or profiles, wait for dreamingStatus to be done too.

```typescript async function waitForProcessing(namespace: string, id: string) { while (true) { const doc = await supermemory.documents.get(namespace, id);
text
if (doc.system.status === "done") return doc;
  if (doc.system.status === "failed") throw new Error("Processing failed");

  await new Promise(r => setTimeout(r, 2000));
}

}

text
</Accordion>

---

## Update document

Update a document's content or metadata. **Content changes** trigger full reprocessing; **metadata-only changes** (e.g. updating `accepted`, `version`) do not reindex. The body accepts any non-empty subset of `content`, `metadata`, `supportingContext`, or `date`. Metadata merges key by key. `group` is an ingestion-side setting: it picks which existing memories serve as context while content is processed, so changing it only takes effect when the document is re-ingested. Send it together with `content`.

<Tabs>
<Tab title="TypeScript">
  ```typescript
  await supermemory.documents.update("user_123", "doc_abc123", {
    content: "Updated content here",
    metadata: { version: 2, reviewed: true },
  });
  ```
</Tab>
<Tab title="Python">
  ```python
  client.documents.update(
      "user_123",
      "doc_abc123",
      content="Updated content here",
      metadata={"version": 2, "reviewed": True},
  )
  ```
</Tab>
<Tab title="cURL">
  ```bash
  curl -X PATCH "https://api.supermemory.ai/ns/user_123/document/doc_abc123" \
    -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"content": "Updated content here", "metadata": {"version": 2}}'
  ```
</Tab>
</Tabs>

For file-backed documents use `documents.updateFile` (PATCH: partial, `file` optional, metadata merges key by key) or `documents.replaceWithFile` (POST: full replace, `file` required, omitted metadata keys are cleared).

```typescript
await supermemory.documents.updateFile("user_123", "doc_abc123", {
metadata: JSON.stringify({ reviewed: true }),
});

await supermemory.documents.replaceWithFile("user_123", "doc_abc123", {
file,
metadata: JSON.stringify({ revision: 2 }),
});

Delete documents

Permanently remove documents. One call deletes 1 to 100 documents by Supermemory or caller-defined ID.

```typescript // One or many const { count, errors } = await supermemory.documents.delete("user_123", { ids: ["doc_1", "doc_2", "doc_3"], });
text
// Delete a whole namespace (all content for a user)
await supermemory.namespaces.delete("user_123");
```
```python # One or many result = client.documents.delete("user_123", ids=["doc_1", "doc_2", "doc_3"]) print(result.count, result.errors)
text
# Delete a whole namespace (all content for a user)
client.namespaces.delete("user_123")
```
```bash # One or many curl -X DELETE "https://api.supermemory.ai/ns/user_123/document" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ids": ["doc_1", "doc_2", "doc_3"]}'
text
# Delete a whole namespace
curl -X DELETE "https://api.supermemory.ai/ns/user_123" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

Inspect both count and per-ID errors; HTTP success can include partial failures. Only documents whose system.status is done or failed can be deleted; a document that is still processing is reported in errors as Document is still processing. Memories derived from a deleted document are forgotten.

Deletes are permanent — no recovery.

Processing queue

There is no separate processing endpoint. List documents and read system.status and system.dreamingStatus on each item in the HTTP response. The SDK examples below count documents still being indexed; use raw HTTP for memory-generation status with the SDK versions noted above.

```typescript const { documents } = await supermemory.list("user_123", "documents", { limit: 100, }); const processing = documents.filter(d => d.system.status !== "done" && d.system.status !== "failed"); console.log(`${processing.length} documents processing`); ``` ```python page = client.list("user_123", "documents", limit=100) processing = [d for d in page.documents if d.system.status not in ("done", "failed")] print(f"{len(processing)} documents processing") ``` ```bash curl -X POST "https://api.supermemory.ai/ns/user_123/list/documents?limit=100" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ```

Next steps


Apps Docs Integrations Agent Framework (`apps/docs/integrations/agent-framework.mdx`)


title: "Microsoft Agent Framework" sidebarTitle: "MS Agent Framework" icon: "/images/microsoft-icon.svg"

Microsoft's Agent Framework is a Python framework for building AI agents with tools, handoffs, and context providers. Supermemory integrates natively as a context provider, tool set, or middleware — so your agents remember users across sessions.

What you can do

  • Automatically inject user memories before every agent run (context provider)
  • Give agents tools to search and store memories on their own
  • Intercept chat requests to add memory context via middleware
  • Combine all three for maximum flexibility

Setup

Install adapter version >=2.0.0,<3 with the OpenAI client package:

bash
pip install "supermemory-agent-framework>=2.0.0,<3" agent-framework-openai

Or with uv:

bash
uv add "supermemory-agent-framework>=2.0.0,<3" agent-framework-openai

The adapter requires supermemory>=5.0.0,<6. The OpenAI examples also require the separate agent-framework-openai package and use its current OpenAIChatClient Responses API client. Older supported framework cores can have different OpenAI client names and model arguments.

Set up your environment:

bash
# .env
SUPERMEMORY_API_KEY=your-supermemory-api-key
OPENAI_API_KEY=your-openai-api-key

Get your Supermemory API key from console.supermemory.ai.


Connection

All integration points share a single AgentSupermemory connection. This ensures the same API client, container tag, and conversation ID are used across middleware, tools, and context providers.

python
from supermemory_agent_framework import AgentSupermemory

conn = AgentSupermemory(
    api_key="your-supermemory-api-key",  # or set SUPERMEMORY_API_KEY env var
    container_tag="user-123",            # memory scope (e.g., user ID)
    conversation_id="session-abc",       # optional, auto-generated if omitted
    entity_context="The user is a Python developer.",  # optional
)

Connection options

Parameter Type Default Description
api_key str env var Supermemory API key. Falls back to SUPERMEMORY_API_KEY
container_tag str "msft_agent_chat" Memory scope (e.g., user ID)
conversation_id str auto-generated Groups messages into a conversation
entity_context str None Custom context about the user, prepended to memories

Pass this connection to any integration:

python
middleware = SupermemoryChatMiddleware(conn, options=...)
tools = SupermemoryTools(conn)
provider = SupermemoryContextProvider(conn, mode="full")

Existing callers and data

Keep the existing container_tag and conversation_id arguments. The adapter sends the container tag as the v5 namespace and the unchanged conversation_<conversation_id> as the document id. Choose a distinct container tag per user or tenant: the default msft_agent_chat scope is shared. Repeated writes append/diff into the same conversation document; they do not replace previous turns. entity_context remains context prepended to retrieved memories, not an ingestion setting.

Existing Agent Framework session exports remain loadable because the provider does not change its scoped state. Reconstruct the connection with the same identifiers after loading a session; connections and API credentials are not part of provider state. This adapter migration does not move existing server-side data. Confirm old data is available in the corresponding v5 namespace using the v5 migration guide; the adapter does not delete or rewrite legacy data.


The most idiomatic integration. Follows the same pattern as Agent Framework's built-in Mem0 provider — memories are automatically fetched before the LLM runs and conversations can be stored afterward.

python
import asyncio
from agent_framework import AgentSession
from agent_framework.openai import OpenAIChatClient
from supermemory_agent_framework import AgentSupermemory, SupermemoryContextProvider

async def main():
    conn = AgentSupermemory(container_tag="user-123")

    provider = SupermemoryContextProvider(conn, mode="full")

    agent = OpenAIChatClient(model="gpt-5").as_agent(
        name="MemoryAgent",
        instructions="You are a helpful assistant with memory.",
        context_providers=[provider],
    )

    session = AgentSession()
    response = await agent.run(
        "What's my favorite programming language?",
        session=session,
    )
    print(response.text)

asyncio.run(main())

How it works

  1. before_run() — Searches Supermemory for the user's profile and relevant memories, then injects them into the session context as additional instructions
  2. after_run() — If store_conversations=True, saves the conversation to Supermemory so future sessions have more context

Configuration options

Parameter Type Default Description
connection AgentSupermemory required Shared connection
mode str "full" "profile", "query", or "full"
store_conversations bool False Save conversations after each run
context_prompt str built-in Custom prompt describing the memories
verbose bool False Enable detailed logging

Memory tools

Give agents explicit control over memory operations. The agent decides when to search or store information.

python
import asyncio
from agent_framework.openai import OpenAIChatClient
from supermemory_agent_framework import AgentSupermemory, SupermemoryTools

async def main():
    conn = AgentSupermemory(container_tag="user-123")
    tools = SupermemoryTools(conn)

    agent = OpenAIChatClient(model="gpt-5").as_agent(
        name="MemoryAgent",
        instructions="""You are a helpful assistant with memory.
When users share preferences, save them. When they ask questions, search memories first.""",
    )

    response = await agent.run(
        "Remember that I prefer Python over JavaScript",
        tools=tools.get_tools(),
    )
    print(response.text)

asyncio.run(main())

Available tools

The agent gets three tools:

  • search_memories — Search for relevant memories by query
  • add_memory — Store new information for later recall
  • get_profile — Fetch the user's full profile (static + dynamic facts)

Tool JSON envelopes are preserved. Profiles still expose string arrays for static, dynamic, and bucket facts. A query passed to get_profile triggers a separate v5 search, returned under search_results with results, timing, and total (the number returned). Search results keep their top-level updated_at timestamp and include v5 metadata; legacy optional fields not returned by v5 remain present as null because their values cannot be reconstructed. The deprecated Python-only include_full_docs argument remains ignored and is not exposed to the model.


Chat middleware

Intercept chat requests to automatically inject memory context. Useful when you want memory injection without the session-based context provider pattern.

python
import asyncio
from agent_framework.openai import OpenAIChatClient
from supermemory_agent_framework import (
    AgentSupermemory,
    SupermemoryChatMiddleware,
    SupermemoryMiddlewareOptions,
)

async def main():
    conn = AgentSupermemory(container_tag="user-123")

    middleware = SupermemoryChatMiddleware(
        conn,
        options=SupermemoryMiddlewareOptions(
            mode="full",
            add_memory="always",
        ),
    )

    agent = OpenAIChatClient(model="gpt-5").as_agent(
        name="MemoryAgent",
        instructions="You are a helpful assistant.",
        middleware=[middleware],
    )

    response = await agent.run("What's my favorite programming language?")
    await middleware.wait_for_background_tasks()
    print(response.text)

asyncio.run(main())

Memory modes

python
SupermemoryContextProvider(conn, mode="full")  # or "profile" / "query"
Mode What it fetches Best for
"profile" User profile (static + dynamic facts) only Personalization without query overhead
"query" Memories relevant to the current message only Targeted recall, no profile data
"full" (default) Profile + query search combined Maximum context

V5 profile retrieval does not accept a search query. Full mode makes separate profile and search requests and deduplicates overlapping facts before injection. Profile-associated searches keep the legacy memory-only mode and 0.6 threshold; the explicit search tool keeps hybrid mode and 0.6. Writes are asynchronous; an accepted document may not be immediately searchable, and this migration does not change the SDK's default processing mode.


Example: support agent with memory

A support agent that remembers customers across sessions:

text
/* Detailed source-code truncated for AI context efficiency. */

This example stores conversations through the provider only. Enabling middleware storage as well can submit overlapping content; use both only when intentional.


Error handling

The package provides specific exception types:

Tools return operation failures as JSON (success: false, error). Provider retrieval/storage and middleware retrieval failures are logged and let the agent continue without memories. Middleware background write failures are logged rather than raised by wait_for_background_tasks(); a timeout on that wait still raises asyncio.TimeoutError.

python
from supermemory_agent_framework import (
    AgentSupermemory,
    SupermemoryConfigurationError,
    SupermemoryAPIError,
    SupermemoryNetworkError,
)

try:
    conn = AgentSupermemory()  # no API key set
except SupermemoryConfigurationError as e:
    print(f"Missing API key: {e}")
Exception When
SupermemoryConfigurationError Missing API key or invalid config
SupermemoryAPIError API returned an error response
SupermemoryNetworkError Connection failure
SupermemoryTimeoutError Request timed out
SupermemoryMemoryOperationError Memory add/search failed

How automatic profiling works Filtering and search modes Memory for OpenAI Agents SDK Memory for LangChain apps

Apps Docs Integrations Agno (`apps/docs/integrations/agno.mdx`)


title: "Agno" sidebarTitle: "Agno" icon: "/icons/hugeicons/brain-01.svg"

Agno agents are stateless by default. Each conversation starts fresh. Supermemory changes that - your agents can remember users, recall past conversations, and build on previous interactions.

What you can do

  • Give agents access to user profiles and conversation history
  • Store agent interactions for future sessions
  • Let agents search memories to answer questions with context

Setup

Install the packages:

bash
pip install agno supermemory python-dotenv

Set up your environment:

bash
# .env
SUPERMEMORY_API_KEY=your-supermemory-api-key
OPENAI_API_KEY=your-openai-api-key

Get your Supermemory API key from console.supermemory.ai.

Basic integration

Fetch user context before running an agent, then store the interaction after.

text
/* Detailed source-code truncated for AI context efficiency. */

Core concepts

User profiles

Supermemory keeps two buckets of user info:

  • Static facts: Things that stay consistent (name, preferences, expertise)
  • Dynamic context: What they're focused on lately
python
result = memory.profile("user_123")

print([m.memory for m in result.profile.static])   # ["Vegetarian", "Allergic to nuts"]
print([m.memory for m in result.profile.dynamic])  # ["Learning Italian cuisine", "Meal prepping"]

Storing memories

Save interactions so future sessions have context:

python
def store_chat(user_id: str, user_msg: str, agent_response: str):
    memory.add(
        user_id,
        content=f"User asked: {user_msg}\nAgent said: {agent_response}",
        metadata={"type": "conversation"},
        dreaming="instant"
    )

Searching memories

Look up past interactions:

python
results = memory.search(
    "user_123",
    query="pasta recipes we discussed",
    search_mode="hybrid",  # Searches memories + document chunks
    limit=5
)

for r in results.results:
    print(r.memory or r.chunk)

Example: personal assistant with memory

An assistant that actually knows who it's talking to. Preferences stick around. Past conversations inform new ones.

text
/* Detailed source-code truncated for AI context efficiency. */

Using Agno tools with memory

Give your agent tools that can search and store memories directly.

python
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools import tool
from supermemory import Supermemory

memory = Supermemory()

@tool
def search_memory(query: str, user_id: str) -> str:
    """Search for information in the user's memory.

    Args:
        query: What to look for
        user_id: The user's ID
    """
    results = memory.search(user_id, query=query, limit=5)

    if not results.results:
        return "Nothing relevant found in memory."

    return "\n".join([r.memory or r.chunk for r in results.results])

@tool
def remember(content: str, user_id: str) -> str:
    """Store something important about the user.

    Args:
        content: What to remember
        user_id: The user's ID
    """
    memory.add(user_id, content=content, dreaming="instant")
    return f"Remembered: {content}"

agent = Agent(
    name="memory_agent",
    model=OpenAIChat(id="gpt-4o"),
    tools=[search_memory, remember],
    description="""You are an assistant with memory.

When users share preferences or important info, use the remember tool.
When they ask about past conversations, search your memory first.""",
    markdown=True
)

Image context with memory

Agno handles images too. When users share photos, you can store what the agent saw for later.

python
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.media import Image
from pathlib import Path
from supermemory import Supermemory

memory = Supermemory()

def analyze_and_remember(user_id: str, image_path: str, question: str) -> str:
    """Analyze an image, answer a question, and store the context."""

    agent = Agent(
        name="vision_agent",
        model=OpenAIChat(id="gpt-4o"),
        description="You analyze images and answer questions about them.",
        markdown=True
    )

    # Get the agent's analysis
    response = agent.run(question, images=[Image(filepath=Path(image_path))])

    # Store the interaction with image context
    memory.add(
        user_id,
        content=f"User shared an image and asked: {question}\nAnalysis: {response.content}",
        metadata={"type": "image_analysis", "image": image_path},
        dreaming="instant"
    )

    return response.content

Metadata for filtering

Tags let you narrow down searches:

python
# Store with metadata
memory.add(
    "user_123",
    content="User prefers dark mode interfaces",
    dreaming="instant",
    metadata={
        "type": "preference",
        "category": "ui",
        "source": "onboarding"
    }
)

# Search with filters
results = memory.search(
    "user_123",
    query="interface preferences",
    filter={
        "operator": "and",
        "operands": [
            {"field": "type", "operator": "eq", "value": "preference"},
            {"field": "category", "operator": "eq", "value": "ui"}
        ]
    }
)

How automatic profiling works Filtering and search modes Memory for LangChain apps Multi-agent systems with memory

Apps Docs Integrations Ai Sdk (`apps/docs/integrations/ai-sdk.mdx`)


title: "Vercel AI SDK" sidebarTitle: "Vercel AI SDK" icon: "/icons/hugeicons/triangle.svg"

import MigrateCli from "/snippets/migrate-cli.mdx";

The Supermemory AI SDK provides native integration with Vercel's AI SDK through two approaches: User Profiles for automatic personalization and Memory Tools for agent-based interactions.

Check out the NPM page for more details

Installation

bash
npm install @supermemory/tools

Quick comparison

Approach Use Case Setup
User Profiles Personalized LLM responses with automatic user context Simple middleware
Memory Tools AI agents that need explicit memory control Tool definitions

User profiles with middleware

Automatically inject user profiles into every LLM call for instant personalization.

typescript
import { generateText } from "ai"
import { withSupermemory } from "@supermemory/tools/ai-sdk"
import { openai } from "@ai-sdk/openai"

const modelWithMemory = withSupermemory(openai("gpt-5"), {
  namespace: "user-123",
  id: "conversation-456",
})

const result = await generateText({
  model: modelWithMemory,
  messages: [{ role: "user", content: "What do you know about me?" }]
})

Required fields

Both namespace and id are required.

  • namespace — who the memories belong to. Use a stable identifier per user, workspace, or tenant (e.g. "user-123", "acme-workspace"). Memory search and writes are scoped to this tag.
  • id — which conversation this turn belongs to. Use it to group messages from the same chat session into a single document (e.g. "chat-2026-04-25", a thread ID, or a UUID per session).
**Memory saving is enabled by default** (`addMemory: "always"`). New conversations are persisted automatically. To opt out, set `addMemory: "never"`:
typescript
const modelWithMemory = withSupermemory(openai("gpt-5"), {
  namespace: "user-123",
  id: "conversation-456",
  addMemory: "never",
})

Memory search modes

Profile Mode (Default) - Retrieves the user's complete profile:

typescript
const model = withSupermemory(openai("gpt-4"), { namespace: "user-123", id: "conv-1", mode: "profile" })

Query Mode - Searches memories based on the user's message:

typescript
const model = withSupermemory(openai("gpt-4"), { namespace: "user-123", id: "conv-1", mode: "query" })

Full Mode - Combines profile AND query-based search:

typescript
const model = withSupermemory(openai("gpt-4"), { namespace: "user-123", id: "conv-1", mode: "full" })

Custom prompt templates

Customize how memories are formatted. The template receives userMemories, generalSearchMemories, and searchResults (raw array for filtering by metadata):

typescript
import { withSupermemory, type MemoryPromptData } from "@supermemory/tools/ai-sdk"

const claudePrompt = (data: MemoryPromptData) => `
<context>
  <user_profile>
    ${data.userMemories}
  </user_profile>
  <relevant_memories>
    ${data.generalSearchMemories}
  </relevant_memories>
</context>
`.trim()

const model = withSupermemory(anthropic("claude-3-sonnet"), {
  namespace: "user-123",
  id: "conv-1",
  mode: "full",
  promptTemplate: claudePrompt,
})

Verbose logging

typescript
const model = withSupermemory(openai("gpt-4"), {
  namespace: "user-123",
  id: "conv-1",
  verbose: true,
})
// Console output shows memory retrieval details

When Supermemory errors (default: continue without memories)

If the Supermemory API returns an error, is unreachable, or retrieval hits the internal time limit, memory injection is skipped. skipMemoryOnError defaults to true, so the LLM call still runs with the original prompt (no injected memories). Use verbose: true if you want console output when that happens.

To fail the call when memory retrieval fails instead, set skipMemoryOnError: false:

typescript
const model = withSupermemory(openai("gpt-5"), {
  namespace: "user-123",
  id: "conv-1",
  skipMemoryOnError: false,
})

Persisting tool calls (default: off)

By default, saved conversations include only user and assistant text — tool calls and tool results are dropped, since tool payloads are often large and low-signal and would pollute memory extraction. To persist the full tool round trip (tool calls with their arguments, plus tool results, in their original order), set includeToolCalls: true:

typescript
const model = withSupermemory(openai("gpt-5"), {
  namespace: "user-123",
  id: "conv-1",
  includeToolCalls: true,
})

Memory tools

Add memory capabilities to AI agents with search, add, and fetch operations.

typescript
import { streamText } from "ai"
import { createAnthropic } from "@ai-sdk/anthropic"
import { supermemoryTools } from "@supermemory/tools/ai-sdk"

const anthropic = createAnthropic({ apiKey: "YOUR_ANTHROPIC_KEY" })

const result = await streamText({
  model: anthropic("claude-3-sonnet"),
  prompt: "Remember that my name is Alice",
  tools: supermemoryTools("YOUR_SUPERMEMORY_KEY")
})

Available tools

Search Memories - Semantic search through user memories:

typescript
const result = await streamText({
  model: openai("gpt-5"),
  prompt: "What are my dietary preferences?",
  tools: supermemoryTools("API_KEY")
})
// AI will call: searchMemories({ informationToGet: "dietary preferences" })

Add Memory - Store new information:

typescript
const result = await streamText({
  model: anthropic("claude-3-sonnet"),
  prompt: "Remember that I'm allergic to peanuts",
  tools: supermemoryTools("API_KEY")
})
// AI will call: addMemory({ memory: "User is allergic to peanuts" })

Using individual tools

For more control, import tools separately:

typescript
import {
  searchMemoriesTool,
  addMemoryTool
} from "@supermemory/tools/ai-sdk"

const result = await streamText({
  model: openai("gpt-5"),
  prompt: "What do you know about me?",
  tools: {
    searchMemories: searchMemoriesTool("API_KEY", { namespace: "personal" }),
    createEvent: yourCustomTool,
  }
})

Tool results

typescript
// searchMemories result
{ success: true, results: [...], count: 5 }

// addMemory result
{ success: true, memory: { id: "mem_123", ... } }

Apps Docs Integrations Cartesia (`apps/docs/integrations/cartesia.mdx`)


title: "Cartesia" sidebarTitle: "Cartesia (voice)" icon: "/images/cartesia.svg"

Supermemory integrates with Cartesia, providing long-term memory capabilities for voice AI agents. Your Cartesia applications will remember past conversations and provide personalized responses based on user history.

Installation

To use Supermemory with Cartesia, install the required dependencies:

bash
pip install supermemory-cartesia

Set up your API key as an environment variable:

bash
export SUPERMEMORY_API_KEY=your_supermemory_api_key

You can obtain an API key from console.supermemory.ai.

Configuration

Supermemory integration is provided through the SupermemoryCartesiaAgent wrapper class:

python
from supermemory_cartesia import SupermemoryCartesiaAgent
from line.llm_agent import LlmAgent, LlmConfig

# Create base LLM agent
base_agent = LlmAgent(
    model="anthropic/claude-haiku-4-5-20251001",
    api_key=os.getenv("ANTHROPIC_API_KEY"),
    config=LlmConfig(
        system_prompt="""You are a helpful voice assistant with memory.""",
        introduction="Hello! Great to talk with you again!",
    ),
)

# Wrap with Supermemory
memory_agent = SupermemoryCartesiaAgent(
    agent=base_agent,
    api_key=os.getenv("SUPERMEMORY_API_KEY"),
    container_tag="user-123",
    custom_id="session-456",  # Required: groups all messages in same document
    config=SupermemoryCartesiaAgent.MemoryConfig(
        mode="full",            # "profile" | "query" | "full"
        search_limit=10,        # Max memories to retrieve
        search_threshold=0.3,   # Relevance threshold (0.0-1.0)
    ),
)

Agent wrapper pattern

The SupermemoryCartesiaAgent wraps your existing LlmAgent to add memory capabilities:

python
from line.voice_agent_app import VoiceAgentApp

async def get_agent(env, call_request):
    # Extract container_tag from call metadata (typically user ID)
    container_tag = call_request.metadata.get("user_id", "default-user")

    # Create base agent
    base_agent = LlmAgent(...)

    # Wrap with memory
    memory_agent = SupermemoryCartesiaAgent(
        agent=base_agent,
        container_tag=container_tag,
        custom_id=call_request.call_id,  # Required: groups all messages in same document
    )

    return memory_agent

# Create voice agent app
app = VoiceAgentApp(get_agent=get_agent)

How it works

When integrated with Cartesia Line, Supermemory provides two key functionalities:

1. Memory retrieval

When a UserTurnEnded event is detected, Supermemory retrieves relevant memories:

  • Static Profile: Persistent facts about the user
  • Dynamic Profile: Recent context and preferences
  • Search Results: Semantically relevant past memories

2. Context enhancement

Retrieved memories are formatted and injected into the agent's system prompt before processing, giving the model awareness of past conversations.

3. Background storage

Conversations are automatically stored in Supermemory (non-blocking) for future retrieval.

Memory modes

Mode Static Profile Dynamic Profile Search Results Use Case
"profile" Yes Yes No Personalization without search
"query" No No Yes Finding relevant past context
"full" Yes Yes Yes Complete memory (default)

Configuration options

You can customize how memories are retrieved and used:

MemoryConfig

python
SupermemoryCartesiaAgent.MemoryConfig(
    mode="full",               # Memory mode (default: "full")
    search_limit=10,           # Max memories to retrieve (default: 10)
    search_threshold=0.1,      # Similarity threshold 0.0-1.0 (default: 0.1)
    system_prompt="Based on previous conversations:\n\n",
)
Parameter Type Default Description
search_limit int 10 Maximum number of memories to retrieve per query
search_threshold float 0.1 Minimum similarity threshold for memory retrieval
mode str "full" Memory retrieval mode: "profile", "query", or "full"
system_prompt str "Based on previous conversations:\n\n" Prefix text for memory context

Agent parameters

python
SupermemoryCartesiaAgent(
    agent=base_agent,           # Required: Cartesia Line LlmAgent
    container_tag="user-123",   # Required: Primary container tag (e.g., user ID)
    custom_id="session-456",    # Required: Groups all messages in same document
    add_memory="always",        # Optional: "always" (default) or "never"
    container_tags=["org-acme", "prod"],  # Optional: Additional tags
    api_key=os.getenv("SUPERMEMORY_API_KEY"),  # Optional: defaults to env var
    config=MemoryConfig(...),   # Optional: memory configuration
    base_url=None,              # Optional: custom API endpoint
)
Parameter Type Required Description
agent LlmAgent Yes The Cartesia Line agent to wrap
container_tag str Yes Primary container tag for memory scoping (e.g., user ID)
custom_id str Yes Groups all messages in the same document (e.g., call ID, conversation ID)
add_memory str No Memory persistence mode: "always" (default) or "never"
container_tags List[str] No Additional container tags for organization (e.g., ["org", "prod"])
api_key str No Supermemory API key (or set SUPERMEMORY_API_KEY env var)
config MemoryConfig No Advanced configuration
base_url str No Custom API endpoint

Container tags

Container tags allow you to organize memories across multiple dimensions:

python
memory_agent = SupermemoryCartesiaAgent(
    agent=base_agent,
    container_tag="user-alice",           # Primary: user ID
    container_tags=["org-acme", "prod"],  # Additional: organization, environment
)

Memories are stored with all tags:

json
{
  "content": "User: What's the weather?\nAssistant: It's sunny today!",
  "container_tags": ["user-alice", "org-acme", "prod"],
  "metadata": { "platform": "cartesia" }
}

Automatic document grouping

The SDK automatically groups all messages from the same conversation into a single Supermemory document using custom_id:

python
memory_agent = SupermemoryCartesiaAgent(
    agent=base_agent,
    container_tag="user-alice",
    custom_id=call_request.call_id,  # Required: Groups all messages together
)

How it works:

  • The custom_id parameter groups all messages into the same Supermemory document
  • Typically you use the call ID or conversation ID from Cartesia
  • All messages from that conversation are appended to the same document
  • This ensures conversation continuity and proper memory generation

Example: Basic voice agent with memory

Here's a complete example of a Cartesia Line voice agent with Supermemory integration:

python
import os
from line.llm_agent import LlmAgent, LlmConfig
from line.voice_agent_app import VoiceAgentApp
from supermemory_cartesia import SupermemoryCartesiaAgent

async def get_agent(env, call_request):
    # Extract container_tag from call metadata (typically user ID)
    container_tag = call_request.metadata.get("user_id", "default-user")

    # Create base LLM agent
    base_agent = LlmAgent(
        model="anthropic/claude-haiku-4-5-20251001",
        api_key=os.getenv("ANTHROPIC_API_KEY"),
        config=LlmConfig(
            system_prompt="""You are a helpful voice assistant with memory.""",
            introduction="Hello! Great to talk with you again!",
        ),
    )

    # Wrap with Supermemory
    memory_agent = SupermemoryCartesiaAgent(
        agent=base_agent,
        api_key=os.getenv("SUPERMEMORY_API_KEY"),
        container_tag=container_tag,
        custom_id=call_request.call_id,  # Required: Groups all messages
    )

    return memory_agent

# Create voice agent app
app = VoiceAgentApp(get_agent=get_agent)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

Example: Advanced agent with tools

Here's an example with custom tools and multi-tag support:

python
import os
from line.llm_agent import LlmAgent, LlmConfig
from line.tools import LoopbackTool
from line.voice_agent_app import VoiceAgentApp
from supermemory_cartesia import SupermemoryCartesiaAgent

# Define custom tool
async def get_weather(location: str) -> str:
    return f"The weather in {location} is sunny, 72°F"

weather_tool = LoopbackTool(
    name="get_weather",
    description="Get current weather for a location",
    function=get_weather
)

async def get_agent(env, call_request):
    container_tag = call_request.metadata.get("user_id", "default-user")
    org_id = call_request.metadata.get("org_id")

    # Create LLM agent with tools
    base_agent = LlmAgent(
        model="gemini/gemini-2.5-flash-preview-09-2025",
        tools=[weather_tool],
        config=LlmConfig(
            system_prompt="You are a personal assistant with memory and tools.",
            introduction="Hi! How can I help you today?"
        )
    )

    # Wrap with Supermemory
    memory_agent = SupermemoryCartesiaAgent(
        agent=base_agent,
        api_key=os.getenv("SUPERMEMORY_API_KEY"),
        container_tag=container_tag,
        custom_id=call_request.call_id,  # Required: Groups all messages
        container_tags=[org_id] if org_id else None,
        config=SupermemoryCartesiaAgent.MemoryConfig(
            mode="full",
            search_limit=15,
            search_threshold=0.15,
        )
    )

    return memory_agent

app = VoiceAgentApp(get_agent=get_agent)

Deployment

To deploy to Cartesia Line, create a main.py file in your project root:

python
import os
import sys

# Add src to path for local imports
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))

from line.llm_agent import LlmAgent, LlmConfig
from line.voice_agent_app import VoiceAgentApp
from supermemory_cartesia import SupermemoryCartesiaAgent

async def get_agent(env, call_request):
    """Create a memory-enabled voice agent."""
    container_tag = call_request.metadata.get("user_id", "default-user")

    base_agent = LlmAgent(
        model="anthropic/claude-haiku-4-5-20251001",
        api_key=os.getenv("ANTHROPIC_API_KEY"),
        config=LlmConfig(
            system_prompt="""You are a helpful voice assistant with memory.
            You remember past conversations and can reference them naturally.
            Keep responses brief and conversational.""",
            introduction="Hello! Great to talk with you again!",
        ),
    )

    memory_agent = SupermemoryCartesiaAgent(
        agent=base_agent,
        api_key=os.getenv("SUPERMEMORY_API_KEY"),
        container_tag=container_tag,
        custom_id=call_request.call_id,  # Required: Groups all messages
    )

    return memory_agent

app = VoiceAgentApp(get_agent=get_agent)

Then deploy with:

bash
cartesia deploy

Make sure to set these environment variables in your Cartesia deployment:

  • SUPERMEMORY_API_KEY - Your Supermemory API key
  • ANTHROPIC_API_KEY - Your Anthropic API key (or the key for your chosen LLM provider)

Apps Docs Integrations Claude Code (`apps/docs/integrations/claude-code.mdx`)


title: "Claude Code" sidebarTitle: "Claude Code" icon: "/images/claude-code-icon.svg"

supermemory is a Claude Code plugin that gives your AI persistent memory across sessions. Your agent remembers what you worked on — across sessions, across projects.

**Prefer to keep everything on your machine?** This plugin works with [self-hosted Supermemory](/self-hosting/overview) — run `npx supermemory local`, then set `baseUrl` in project config (or point your install at your local API) and use the API key printed on first boot.

Install the plugin

Requires Node.js 18+ on your PATH — the memory hooks run as Node scripts.

bash
# Add the plugin marketplace
/plugin marketplace add supermemoryai/claude-supermemory

# Install the plugin
/plugin install supermemory
**Migrating from the old `claude-supermemory` plugin name?** It was renamed to `supermemory` and will not update in place:
bash
/plugin marketplace update supermemory-plugins
/plugin install supermemory@supermemory-plugins
# Only if the old plugin is still installed:
/plugin uninstall claude-supermemory@supermemory-plugins

Authenticate

Create a Supermemory API key from the API Keys page in the console, then add it to your shell profile:

```bash echo 'export SUPERMEMORY_CC_API_KEY="sm_..."' >> ~/.zshrc source ~/.zshrc ``` ```bash echo 'export SUPERMEMORY_CC_API_KEY="sm_..."' >> ~/.bashrc source ~/.bashrc ``` ```powershell [System.Environment]::SetEnvironmentVariable("SUPERMEMORY_CC_API_KEY", "sm_...", "User") ``` Restart your terminal after running this.

How it works

Once installed, the plugin runs automatically:

  • Reasoned recall — Before each turn, Claude decides whether recalling memory would help the current message, and only searches when it is worth it.
  • Auto-capture — Conversations and important tool usage are saved for later sessions.
  • Team memory — Project knowledge is shared separately from personal memories.
  • Explicit skills — Ask Claude to search or save memories when you need control.

Commands

Command Description
/supermemory:index Index codebase architecture and patterns
/supermemory:project-config Configure project-level settings
/supermemory:logout Clear saved credentials
/supermemory:session Show a clickable URL for the current session document
/supermemory:status Show authentication status

Configuration

Environment variables

bash
SUPERMEMORY_CC_API_KEY=sm_...    # Required
SUPERMEMORY_DEBUG=true           # Optional: enable debug logging

Global settings

Create ~/.supermemory-claude/settings.json:

json
{
  "maxProfileItems": 5,
  "signalExtraction": true,
  "signalKeywords": ["remember", "architecture", "decision", "bug", "fix"],
  "signalTurnsBefore": 3,
  "includeTools": ["Edit", "Write"]
}
Option Description
maxProfileItems Max memories in context (default: 5)
recallDirective Override the built-in reasoned-recall instruction
signalExtraction Only capture important turns (default: false)
signalKeywords Keywords that trigger capture
signalTurnsBefore Context turns before a signal (default: 3)
includeTools Tools to explicitly capture

Project config

Per-repo overrides in .claude/.supermemory-claude/config.json. Run /supermemory:project-config or create manually:

json
{
  "apiKey": "sm_...",
  "baseUrl": "https://api.supermemory.ai",
  "repoContainerTag": "my-team-project",
  "signalExtraction": true
}
Option Description
apiKey Project-specific API key
baseUrl Supermemory API URL (use for self-hosted)
personalContainerTag Override personal container
repoContainerTag Override team container tag

Next steps

Source code, issues, and detailed README. Memory for your Cursor chats.

Apps Docs Integrations Claude Memory (`apps/docs/integrations/claude-memory.mdx`)


title: "Claude Memory Tool" sidebarTitle: "Claude Memory Tool" icon: "/images/anthropic-1.svg"

Claude has a native memory tool that allows it to store and retrieve information across conversations. Supermemory provides a backend implementation that maps Claude's memory commands to persistent storage.

This integration works with Claude's built-in memory tool type, introduced in the Anthropic API. It requires the context-management beta flag.

Installation

bash
npm install @supermemory/tools @anthropic-ai/sdk

Quick start

text
/* Detailed source-code truncated for AI context efficiency. */

Configuration

typescript
import { createClaudeMemoryTool } from "@supermemory/tools/claude-memory"

const memoryTool = createClaudeMemoryTool(process.env.SUPERMEMORY_API_KEY!, {
  // Scope memories to a project or user
  namespace: "my-app",

  // Or use container tags for more flexibility
  namespace: "user-123",

  // Custom memory container prefix (default: "claude_memory")
  
  // Custom API endpoint
  baseUrl: "https://custom.api.com",
})

How it works

Claude's memory tool uses a file-system metaphor. Supermemory maps these operations to document storage:

Claude Command Supermemory Action
view Search/retrieve documents
create Add new document
str_replace Update document content
insert Insert content at line
delete Delete document
rename Move document to new path

Memory path structure

All memory paths must start with /memories/:

text
/memories/preferences.txt       # User preferences
/memories/projects/react.txt    # Project-specific notes
/memories/context/current.txt   # Current context
Paths are normalized for storage: `/memories/preferences` is stored as `--memories--preferences`.

Commands reference

View (read/list)

typescript
// List directory contents
{ command: "view", path: "/memories/" }

// Read file contents
{ command: "view", path: "/memories/preferences.txt" }

// Read specific lines
{ command: "view", path: "/memories/notes.txt", view_range: [1, 10] }

Create

typescript
{
  command: "create",
  path: "/memories/preferences.txt",
  file_text: "User prefers dark mode\nFavorite language: TypeScript"
}

String replace

typescript
{
  command: "str_replace",
  path: "/memories/preferences.txt",
  old_str: "dark mode",
  new_str: "light mode"
}

Insert

typescript
{
  command: "insert",
  path: "/memories/notes.txt",
  insert_line: 5,
  insert_text: "New note added here"
}

Delete

typescript
{ command: "delete", path: "/memories/old-notes.txt" }

Rename

typescript
{
  command: "rename",
  old_path: "/memories/old-name.txt",
  new_path: "/memories/new-name.txt"
}

Complete example

text
/* Detailed source-code truncated for AI context efficiency. */

Environment variables

bash
SUPERMEMORY_API_KEY=your_supermemory_key
ANTHROPIC_API_KEY=your_anthropic_key

Comparison with other approaches

Feature Claude Memory Tool OpenAI SDK Tools AI SDK Tools
Automatic memory ✅ Claude decides ❌ Manual control ❌ Manual control
Filesystem metaphor ✅ Files/directories ❌ Flat storage ❌ Flat storage
Path organization ✅ Hierarchical ❌ Tags only ❌ Tags only
Integration Anthropic SDK only OpenAI SDK only Vercel AI SDK

Next steps

Use with Vercel AI SDK for streamlined development Memory tools for OpenAI function calling

Apps Docs Integrations Codex (`apps/docs/integrations/codex.mdx`)


title: "Codex" sidebarTitle: "Codex" icon: "/images/codex.svg"

codex-supermemory wires Supermemory into the OpenAI Codex CLI via hooks and skills. Your agent gets two layers of memory:

  • Implicit (hooks) — automatically recalls context before each prompt and captures conversations incrementally during the session.
  • Explicit (skills) — lets you or the agent save, search, and manage memories on demand.
**Prefer to keep everything on your machine?** This plugin works with [self-hosted Supermemory](/self-hosting/overview) — run `npx supermemory local`, then `export SUPERMEMORY_API_URL="http://localhost:6767"` (or set `baseUrl` in `~/.codex/supermemory.json`) and use the API key printed on first boot.

Install the plugin

bash
npx codex-supermemory@latest install

This command:

  • Copies hook and skill scripts to ~/.codex/supermemory/
  • Enables codex_hooks in ~/.codex/config.toml
  • Registers UserPromptSubmit (recall) and Stop (flush) hooks in ~/.codex/hooks.json
  • Installs explicit memory skills under ~/.codex/skills/

Restart Codex CLI after installing.

Authenticate

Browser auth is preferred. Start Codex CLI — on your first prompt a browser window opens to authenticate with Supermemory.

Alternatively:

  • Use $supermemory-login / /supermemory-login inside Codex
  • Or set an API key from API Keys:
```bash echo 'export SUPERMEMORY_CODEX_API_KEY="sm_..."' >> ~/.zshrc source ~/.zshrc ``` ```bash echo 'export SUPERMEMORY_CODEX_API_KEY="sm_..."' >> ~/.bashrc source ~/.bashrc ``` ```powershell [System.Environment]::SetEnvironmentVariable("SUPERMEMORY_CODEX_API_KEY", "sm_...", "User") ``` Restart your terminal after running this.

How it works

Once installed, the plugin runs on every Codex session:

Hook Event What it does
recall UserPromptSubmit Captures new turns (every N prompts), searches Supermemory, injects memories + profile as additionalContext
flush Stop Captures any remaining turns at session end so nothing is lost
  • Incremental capture — Memories are saved every N turns (default: 3) so mid-session context is available for later prompts in the same session.
  • Privacy — Content wrapped in ... is redacted before storage.

Memory scopes

Tag Derived from Description
User git config user.email (hashed) Preferences and workflows across projects
Project Git common directory (hashed) Repo-scoped knowledge (worktrees share by default)

Override tags in ~/.codex/supermemory.json if needed:

json
{
  "userContainerTag": "my-custom-user-tag",
  "projectContainerTag": "my-custom-project-tag"
}

Set SUPERMEMORY_ISOLATE_WORKTREES=true to keep each worktree isolated.

Explicit memory skills

Skill Description
supermemory-search Search memories by natural-language query
supermemory-save Save important project knowledge
supermemory-forget Remove outdated or incorrect memories
supermemory-profile Show remembered profile facts
supermemory-status Check connection, hooks, config, and skills
supermemory-login Re-authenticate with Supermemory
supermemory-logout Remove saved local credentials

Example prompts:

text
> Remember that this project uses Vitest for unit tests and Playwright for E2E.
> What do you remember about our database schema?
> Forget the memory about the old API endpoint.
> Is Supermemory connected?

Verify installation

bash
npx codex-supermemory status

Uninstall

bash
npx codex-supermemory uninstall

This removes hook registrations and skill scripts. Your existing memories in Supermemory are preserved.

Configuration

Create ~/.codex/supermemory.json to override defaults:

json
{
  "apiKey": "sm_...",
  "baseUrl": "https://api.supermemory.ai",
  "similarityThreshold": 0.6,
  "maxMemories": 5,
  "maxProfileItems": 5,
  "injectProfile": true,
  "containerTagPrefix": "codex",
  "autoSaveEveryTurns": 3,
  "signalExtraction": false,
  "debug": false
}
Option Default Description
apiKey — API key (env / browser auth preferred)
baseUrl https://api.supermemory.ai API base URL (SUPERMEMORY_API_URL overrides)
similarityThreshold 0.6 Minimum match score for recall (0–1)
maxMemories 5 Max memories injected per prompt
maxProfileItems 5 Max profile facts injected per prompt
injectProfile true Include user profile in context
containerTagPrefix "codex" Prefix for auto-generated container tags
autoSaveEveryTurns 3 Save memories every N turns
signalExtraction false Only capture turns with signal keywords
debug false Write debug logs to ~/.codex-supermemory.log

Logging

bash
export SUPERMEMORY_DEBUG=true
tail -f ~/.codex-supermemory.log

Next steps

Source code, issues, and detailed README. Memory for your Cursor chats.

Apps Docs Integrations Convex (`apps/docs/integrations/convex.mdx`)


title: "Convex" sidebarTitle: "Convex" icon: "/icons/hugeicons/database-01.svg"

Convex apps don't have built-in memory for AI. Supermemory fixes that. You get a memory layer that stores conversations, builds user profiles, and gives your AI context about who it's talking to.

What you can do

  • Store user interactions and retrieve them in future sessions
  • Build automatic user profiles from conversations
  • Search memories to give your AI relevant context
  • Keep everything in your Convex database for full visibility

Setup

Install the packages:

bash
npm install supermemory convex

For the AI chat example, also install the AI SDK packages:

bash
npm install @supermemory/tools @ai-sdk/openai ai

Set up your environment variable in Convex:

bash
npx convex env set SUPERMEMORY_API_KEY your-supermemory-api-key

Get your Supermemory API key from console.supermemory.ai.

Basic integration

Create simple helper functions for each Supermemory operation:

typescript
// convex/memory.ts
import { action } from "./_generated/server";
import { v } from "convex/values";
import { Supermemory } from "supermemory";

const memory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });

// Get user profile
export const getProfile = action({
  args: { userId: v.string() },
  handler: async (ctx, { userId }) => {
    return await memory.profile(userId);
  },
});

// Add a memory
export const addMemory = action({
  args: { userId: v.string(), content: v.string() },
  handler: async (ctx, { userId, content }) => {
    return await memory.add(userId, {
      content,
      dreaming: "instant", // memories are ready as soon as status is done
    });
  },
});

// Search memories
export const searchMemories = action({
  args: { userId: v.string(), query: v.string(), limit: v.optional(v.number()) },
  handler: async (ctx, { userId, query, limit }) => {
    return await memory.search(userId, {
      query,
      searchMode: "hybrid",
      limit: limit ?? 10,
    });
  },
});

Example: AI chat with memory

A chat endpoint using the Supermemory AI SDK middleware. It automatically injects context and saves memories.

typescript
// convex/chat.ts
import { action } from "./_generated/server";
import { v } from "convex/values";
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { withSupermemory } from "@supermemory/tools/ai-sdk";

export const chat = action({
  args: { userId: v.string(), message: v.string() },
  handler: async (ctx, { userId, message }) => {
    // Wrap the model - automatically injects context and saves memories
    const model = withSupermemory(openai("gpt-4o-mini"), {
      namespace: userId,
      id: `convex-chat-${userId}`,
      mode: "full",
      addMemory: "always",
    });

    const { text } = await generateText({
      model,
      system: "You are a helpful assistant.",
      prompt: message,
    });

    return text;
  },
});

Storing memories in Convex tables

Keep a local copy of memories in your Convex database for full visibility:

typescript
// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  memories: defineTable({
    userId: v.string(),
    content: v.string(),
    createdAt: v.number(),
  }).index("by_user", ["userId"]),
});
typescript
// convex/memory.ts
import { action, mutation, query } from "./_generated/server";
import { api } from "./_generated/api";
import { v } from "convex/values";
import { Supermemory } from "supermemory";

const memory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });

// Store in Convex
export const storeMemory = mutation({
  args: { userId: v.string(), content: v.string() },
  handler: async (ctx, { userId, content }) => {
    return await ctx.db.insert("memories", {
      userId,
      content,
      createdAt: Date.now(),
    });
  },
});

// Add memory to both Supermemory and Convex
export const addMemory = action({
  args: { userId: v.string(), content: v.string() },
  handler: async (ctx, { userId, content }) => {
    // Add to Supermemory
    await memory.add(userId, { content });
    
    // Store in Convex
    // Note: in production, handle partial failures — if the Convex mutation
    // fails after the Supermemory write succeeds, the two stores will be out of sync.
    await ctx.runMutation(api.memory.storeMemory, { userId, content });
  },
});

// List memories from Convex
export const listMemories = query({
  args: { userId: v.string() },
  handler: async (ctx, { userId }) => {
    return await ctx.db
      .query("memories")
      .withIndex("by_user", q => q.eq("userId", userId))
      .order("desc")
      .take(50);
  },
});

How automatic profiling works Filtering and search modes Memory middleware for Next.js Memory for LangChain apps

Apps Docs Integrations Crewai (`apps/docs/integrations/crewai.mdx`)


title: "CrewAI" sidebarTitle: "CrewAI" icon: "/icons/hugeicons/user-multiple.svg"

CrewAI agents don't remember anything between runs by default. Supermemory fixes that. You get a memory layer that stores what happened, who the user is, and what they care about. Your crews can pick up where they left off.

What you can do

  • Give agents access to user preferences and past interactions
  • Store crew outputs so future runs can reference them
  • Search memories to give agents relevant context before they start

Setup

Install the required packages:

bash
pip install crewai supermemory python-dotenv

Configure your environment:

bash
# .env
SUPERMEMORY_API_KEY=your-supermemory-api-key
OPENAI_API_KEY=your-openai-api-key

Get your Supermemory API key from console.supermemory.ai.

Basic integration

Initialize Supermemory and inject user context into your agent's backstory:

python
import os
from crewai import Agent, Task, Crew, Process
from supermemory import Supermemory
from dotenv import load_dotenv

load_dotenv()

memory = Supermemory()

def build_context(user_id: str, query: str) -> str:
    """Fetch user profile and relevant memories."""
    profile = memory.profile(user_id).profile
    memories = memory.search(user_id, query=query, limit=5).results

    static = [m.memory for m in profile.static]
    dynamic = [m.memory for m in profile.dynamic]

    return f"""
User Profile:
{chr(10).join(static) if static else 'No profile data.'}

Current Context:
{chr(10).join(dynamic) if dynamic else 'No recent activity.'}

Relevant History:
{chr(10).join([m.memory or m.chunk for m in memories[:5]]) if memories else 'None.'}
"""

def create_agent_with_memory(user_id: str, role: str, goal: str, query: str) -> Agent:
    """Create an agent with user context baked into its backstory."""
    context = build_context(user_id, query)

    return Agent(
        role=role,
        goal=goal,
        backstory=f"""You have access to the following information about the user:
{context}

Use this context to personalize your work.""",
        verbose=True
    )

Core concepts

User profiles

Supermemory tracks two kinds of user data:

  • Static facts: Things that don't change often (preferences, job title, tech stack)
  • Dynamic context: What the user is working on right now
python
result = memory.profile("user_abc")

print([m.memory for m in result.profile.static])   # ["Prefers Agile methodology", "Senior engineer"]
print([m.memory for m in result.profile.dynamic])  # ["Working on Q2 roadmap", "Focused on API design"]

Storing memories

Save crew outputs so future runs can reference them:

python
def store_crew_result(user_id: str, task_description: str, result: str):
    """Save crew output as a memory."""
    memory.add(
        user_id,
        content=f"Task: {task_description}\nResult: {result}",
        metadata={"type": "crew_execution"},
        dreaming="instant"
    )

Searching memories

Pull up past interactions before running a crew:

python
results = memory.search(
    "user_abc",
    query="previous project recommendations",
    search_mode="hybrid",  # Searches memories + document chunks
    limit=10
)

for r in results.results:
    print(r.memory or r.chunk)

Example: research crew with memory

This crew has two agents: a researcher and a writer. The researcher adjusts its technical depth based on the user's background. The writer remembers formatting preferences. Both can see what the user has asked about before.

text
/* Detailed source-code truncated for AI context efficiency. */

More patterns

Crews with multiple users

Sometimes you need context from several users at once:

python
def create_collaborative_context(user_ids: list[str], topic: str) -> str:
    """Aggregate context from multiple users."""
    combined = []

    for user_id in user_ids:
        static = [m.memory for m in memory.profile(user_id).profile.static]
        if static:
            combined.append(f"{user_id}: {', '.join(static[:3])}")

    return "\n".join(combined) if combined else "No shared context available."

Only storing successful runs

You might not want to save every crew output:

python
def store_if_successful(user_id: str, task: str, result: str, success: bool):
    """Only store successful task completions."""
    if not success:
        return

    memory.add(
        user_id,
        content=f"Completed: {task}\nOutcome: {result}",
        metadata={"type": "success", "task": task},
        dreaming="instant"
    )

Using metadata to organize memories

Metadata lets you filter memories by project, agent, or whatever else makes sense:

python
# Store with metadata
memory.add(
    "user_123",
    content="Research findings on distributed systems",
    dreaming="instant",
    metadata={
        "project": "infrastructure-review",
        "agents": ["researcher", "writer"],
        "confidence": "high"
    }
)

# Search with filters
results = memory.search(
    "user_123",
    query="distributed systems",
    filter={
        "operator": "and",
        "operands": [
            {"field": "project", "operator": "eq", "value": "infrastructure-review"},
            {"field": "confidence", "operator": "eq", "value": "high"}
        ]
    }
)

How automatic profiling works Filtering and search modes Memory for LangChain apps Memory middleware for Next.js

Apps Docs Integrations Cursor (`apps/docs/integrations/cursor.mdx`)


title: "Cursor" sidebarTitle: "Cursor" icon: "/images/cursor-logo.svg"

Your agent remembers the decisions, bugs, and conventions from earlier chats instead of starting cold every time.

Install

Requires [Node.js](https://nodejs.org) on your `PATH`. Installing Cursor does not put one there.

Run this in Cursor:

text
/add-plugin cursor-supermemory

Or install it from the Cursor Marketplace: open Customize, find Supermemory, select Install, and choose project or user scope.

Restart Cursor or run Developer: Reload Window afterwards.

Authenticate

Open a new chat in Cursor and run:

text
/supermemory-setup

A browser window opens. Sign in to Supermemory and you are done.

As a fallback, set an API key from API Keys:

```bash echo 'export SUPERMEMORY_API_KEY="sm_..."' >> ~/.zshrc source ~/.zshrc ``` ```bash echo 'export SUPERMEMORY_API_KEY="sm_..."' >> ~/.bashrc source ~/.bashrc ``` ```powershell [System.Environment]::SetEnvironmentVariable("SUPERMEMORY_API_KEY", "sm_...", "User") ``` Restart your terminal after running this.

Restart Cursor after installing the plugin or changing credentials.

Check the connection any time with /supermemory-status.

The slash commands just run the plugin's CLI for you. To drive it yourself:
bash
node "${CURSOR_PLUGIN_ROOT}/dist/cli.js" login
node "${CURSOR_PLUGIN_ROOT}/dist/cli.js" status
node "${CURSOR_PLUGIN_ROOT}/dist/cli.js" logout

CURSOR_PLUGIN_ROOT is set for plugin hooks. If it is empty in your shell, run node dist/cli.js <command> from the installed plugin directory.

Credentials are stored in ~/.supermemory-cursor/credentials.json.

How it works

Layer What it does
Session profile Loads your persistent profile when a Cursor conversation starts
Automatic recall Searches on substantive prompts, deduplicates results, and injects them after the first supported tool result
Incremental capture Saves each completed turn, and retries unsaved transcript deltas at session end
MCP tools Explicit memory control from any Cursor AI session
Context gatherer Fans out targeted searches before substantial work
Always-on rule Makes the agent recall relevant history proactively

Skills and commands

Name Type Description
memory-init Skill Explore the codebase and initialize project memory
memory-save Skill Save an insight, decision, or solution worth keeping
memory-search Skill Search memory for past work, bugs, and decisions
supermemory-context-gatherer Agent Gather deep background before substantial work
supermemory-setup Command Connect Supermemory to Cursor
supermemory-status Command Check authentication and live connectivity
supermemory-config Command Create or edit the project config file
supermemory-logout Command Disconnect Supermemory from Cursor

MCP tools

Tool Description
supermemory_get_config Show current config, resolved container tags, and config file paths
supermemory_set_config Update config at project or global scope
supermemory_containers Show what user and project container tags resolve to
supermemory_search Search memories by query
supermemory_add Save new information to memory
supermemory_list List stored memories
supermemory_forget Delete a memory by id or content
supermemory_profile Get your user profile summary

Every tool that takes a container argument accepts:

  • "user" (default): personal memories for the current repository
  • "project": project knowledge for the current repository
  • "both": both scopes plus compatible legacy memories
  • any custom string: used as a raw container tag

user and project write to the same repository container. The sm_scope metadata field is what keeps personal and session memories separate from explicit project knowledge when an agent asks for one scope.

Container tags

Cursor shares one repository tag with the Claude Code, Muse Code, OpenAI Codex, and OpenCode plugins, so agents working on the same repo read and write the same memory:

text
repo_<project_name>__<project_id>

The project ID is a stable hash of the normalized Git remote. Repositories without a remote fall back to their resolved local path. Two repos with the same directory name never collide, and different agents on the same repository share memory.

The plugin still reads the former cursor_user_* and cursor_project_* tags, along with legacy tags from the other agents. New writes only use the unified repository tag. Set repoContainerTag only when you need an explicit shared override.

Configuration

**Prefer to keep everything on your machine?** This plugin works with [self-hosted Supermemory](/self-hosting/overview): run `npx supermemory local`, then set `SUPERMEMORY_API_URL="http://localhost:6767"` (or `baseUrl` in your config file) and use the API key printed on first boot.

Environment variables

Variable Description
SUPERMEMORY_API_KEY API key (overrides all other sources)
SUPERMEMORY_API_URL Override the Supermemory API base URL
SUPERMEMORY_REPO_TAG Override the unified repository container tag
SUPERMEMORY_USER_TAG Legacy Cursor personal container to continue reading
SUPERMEMORY_PROJECT_TAG Legacy Cursor project container to continue reading
CURSOR_USER_EMAIL Used only to find legacy Cursor personal memories

Global config

~/.config/cursor/supermemory.json holds user-wide defaults and applies to all projects.

json
{
  "repoContainerTag": "repo_my_project__0123456789abcdef",
  "similarityThreshold": 0.55,
  "maxMemories": 10,
  "injectProfile": true,
  "signalExtraction": false,
  "signalKeywords": ["remember", "architecture", "decision", "bug", "fix"],
  "signalTurnsBefore": 3
}

Project config

.cursor/.supermemory/config.json holds per-workspace overrides and wins over global config. Add it to .gitignore if it contains an API key.

json
{
  "apiKey": "sm_...",
  "repoContainerTag": "repo_my_project__0123456789abcdef",
  "similarityThreshold": 0.55,
  "maxMemories": 10,
  "injectProfile": true
}
Option Default Description
apiKey — Project-specific API key
baseUrl Supermemory API Override the Supermemory API base URL
repoContainerTag derived from normalized Git remote or project path Override the unified repository container
userContainerTag — Legacy Cursor personal container to continue reading
projectContainerTag — Legacy Cursor project container to continue reading
similarityThreshold 0.55 Minimum similarity for prompt recall. Values below 0.55 are floored.
maxMemories 10 Max profile facts injected at session start
injectProfile true Whether to inject the user profile at session start
signalExtraction false Capture only turns containing durable-signal keywords
signalKeywords remember, architecture, decision, bug, fix Keywords that trigger signal-based capture
signalTurnsBefore 3 Number of nearby turns retained around a signal

You can also set these from the agent with supermemory_set_config, or edit the file by hand.

Log out

Run /supermemory-logout in Cursor.

This removes the stored credentials. Your memories in Supermemory are preserved.

Next steps

Source code, issues, and detailed README.

Apps Docs Integrations Eve (`apps/docs/integrations/eve.mdx`)


title: "Eve" sidebarTitle: "Eve" icon: "/images/eve.svg"

Your next conversation can start where the last one left off. Supermemory gives your Eve agent the context to continue a project: what was decided, what changed, and the source material behind it.

You don't need to build your own parsing, embedding, indexing or memory-retrieval pipeline. Supermemory handles that infrastructure. Eve's memory scope and namespace let you keep context separated by user or workspace.

Add Supermemory

Start with an existing Eve project, Node.js 24 or newer, and an Eve version compatible with eve@^0.47.3.

Run this from your Eve project:
text
```bash
eve add memory/supermemory
```

Eve installs `@supermemory/eve` and creates `agent/memory/supermemory.ts`, the file that connects your agent to Supermemory.
Create an [API key in the Supermemory console](https://console.supermemory.ai/keys) and add it to your project's environment:
text
```bash .env.local
SUPERMEMORY_API_KEY="your-supermemory-api-key"
```
Start or restart Eve to load the provider and API key:
text
```bash
eve dev
```

Automatic recall and conversation capture are enabled by default. The memory tools are available to your agent alongside its existing tools.

You can also install the provider manually.

How memory works

Two conversations about the same launch show the three parts working together: session history, indexed sources and memories.

<video
className="eve-memory-video"
controls
playsInline
preload="metadata"
poster="/images/eve-memory-poster.png"
aria-label="How Eve saves context and retrieves it in a new session"

Watch the memory explainer.

After a successful turn, the provider saves the user and assistant messages as session history. Supermemory processes that conversation into durable memories, such as the launch date or the decision to keep the first release web-only. Capture is automatic; processing happens asynchronously.

Files take a separate path. When Eve works with an attachment, URL, or text, SuperRAG parses or transcribes it into an indexed source the agent can read. Eve can then save a useful conclusion as a memory, retaining the source document's ID. In the example, the budget stays connected to the launch brief.

Before a later turn, the provider loads the user profile and searches for relevant saved context. Eve can read an earlier session or follow a source-backed memory to the original document when it needs more detail. The new conversation can then use both the earlier decision and the evidence behind it. Recall also runs after context compaction.

Memory tools

The provider gives Eve seven tools alongside automatic recall and capture. The generated file is named supermemory.ts, so Eve prefixes their names with supermemory__.

Tool Purpose
search Find relevant memories and session history in the current memory scope.
read_session Read a stored conversation found through search.
read_document Read sections of a processed source, fetching more as needed.
remember Save a standalone fact, preference, decision, or correction; optionally retain its sourceDocumentId.
extract Parse, transcribe, and index an Eve attachment, URL, or text with SuperRAG.
forget Remove one memory by ID.
forget_matching Preview memories within a requested boundary, then remove selected IDs.
Eve can find a specific memory with `search` and remove it with `forget`. For a broader request, `forget_matching` supports a dry run that returns candidate memories. Its tool instructions tell Eve to show that preview, ask you to confirm, and remove the exact IDs you approved.

Forgetting removes derived memories. The original session history and source documents remain stored.

Choose whose memory Eve uses

The generated memory file uses Eve's byPrincipal scope. In an authenticated application, memory follows the caller's identity. During eve dev, it uses the shared local-dev scope. Outside local development, anonymous and runtime principals have memory disabled.

For a team or multi-tenant application, derive the scope from verified user and tenant identity. Eve resolves that scope before the provider runs, so the agent's tools cannot choose another user's memory. See Eve's multi-tenant memory guide.

Eve also supplies a default namespace for the memory slot. You only need to set namespace yourself when you want a custom memory domain. Keep it stable: changing it selects a different memory space. Eve's namespace documentation explains the defaults and how to share a domain intentionally.

Configure the provider

Edit the options passed to supermemory(...) in agent/memory/supermemory.ts. The generated file only needs your API key; the defaults enable automatic search and capture.

You can adjust those behaviors independently. Set autoSearch.enabled to false to disable automatic search while keeping profile recall. Set capture.enabled to false to stop saving completed turns automatically. Both settings leave the explicit memory tools available.

| Option | Default | Purpose | | --- | --- | --- | | `apiKey` | Required | Supermemory API key, or a function returning the key synchronously or asynchronously. | | `containerTagPrefix` | `"eve_agent"` | Prefix for storage tags derived from Eve's scope. Keep it stable to access the same data. | | `autoSearch.enabled` | `true` | Search for context relevant to the current request during recall. | | `profileContext.timeZone` | `"UTC"` | IANA time zone used to format profile context. | | `capture.enabled` | `true` | Save completed turns automatically. | | `capture.taskType` | `"memory"` | Generate memories from conversations. Use `"superrag"` to index them for document retrieval only. | | `capture.dreaming` | `"dynamic"` | Memory generation mode: `"dynamic"` or `"instant"`. | | `capture.entityContext` | Built-in capture policy | Instructions for what durable information to learn from conversations. | | `capture.metadata` | `{}` | Additional metadata attached to captured conversation documents. |

Namespace, description, and scope are Eve memory-slot settings. Configure them on defineMemory(...), outside the provider options.

Manual installation

If you prefer to create the memory file yourself, install the package in your Eve project:

bash
npm install @supermemory/eve

Then create the same memory slot that eve add generates:

typescript agent/memory/supermemory.ts
import supermemory from "@supermemory/eve";
import { defineMemory } from "eve/memory";
import { byPrincipal } from "eve/memory/scope";

export default defineMemory({
  description: "Recall and manage durable context for the current user.",
  provider: supermemory({
    apiKey: process.env.SUPERMEMORY_API_KEY!,
  }),
  scope: byPrincipal,
});

Set SUPERMEMORY_API_KEY in .env.local and start your agent as described in Add Supermemory.

For implementation details, see the provider repository and Eve's Supermemory integration.


Apps Docs Integrations Grok Bot (`apps/docs/integrations/grok-bot.mdx`)


title: "Grok Bot" sidebarTitle: "Grok Bot" icon: "/images/grok-logo.png"

Grok Bots are cloud agents. A new task can mean a new machine and a blank context. Supermemory is the memory they keep between those tasks — findings, decisions, and preferences — so the next Bot does not start cold.

How it helps

Without memory, a Bot redoes investigation you already paid for. With Supermemory:

  • Later Bots know what earlier ones already figured out
  • You can save a decision or finding once and have it stick
  • You can ask what it already remembers instead of starting over

Install once, sign in, and it is available to your Grok Bots.

Install

Open the Supermemory for Grok Bot plugin page and select Add to Grok Bot.

Restart Grok Bot afterwards so the plugin loads.

Authenticate

Just ask Grok Bot:

text
Sign me in to Supermemory

It shows a connect card. Follow it to link your Supermemory account.

To check later, ask Is Supermemory connected?

Skills

Grok Bot picks these up from what you ask. There are no slash commands.

Skill Ask for it like this
memory-init "Learn this codebase and remember it"
memory-save "Remember that we use Vitest for unit tests"
memory-search "What do you remember about our database schema?"

Log out

Ask Grok Bot to Disconnect Supermemory.

This removes the stored credentials. Your memories in Supermemory are preserved.

Next steps

Source code, issues, and detailed README. The same plugin, installed through Cursor.

Apps Docs Integrations Hermes (`apps/docs/integrations/hermes.mdx`)


title: "Hermes" sidebarTitle: "Hermes" icon: "/images/hermes.svg"

Hermes agent ships a native supermemory memory provider: semantic long-term memory, profile recall, search, explicit memory tools, and session-aware ingest — not a bolt-on script the model might skip. Hermes runs across Telegram, Discord, Slack, WhatsApp, Signal, and the CLI from one gateway.

Hermes also ships with built-in MEMORY.md and USER.md files. Supermemory adds structure and isolation (profile-scoped and optional multi-container tags), plus retrieval that goes beyond a single flat file.

Get your API key

Create a supermemory API key from the API Keys page in the console. During hermes memory setup you can paste it when prompted, or persist it in your environment:

Install the memory provider

bash
pip install supermemory
bash
hermes memory setup

Select supermemory when prompted and paste your API key.

Or set the provider and key manually:

bash
hermes config set memory.provider supermemory
echo 'SUPERMEMORY_API_KEY=sm_...' >> ~/.hermes/.env

(Adjust the path if your Hermes home directory differs.)

How it works

Once configured, the provider runs through Hermes’s normal memory lifecycle:

  • Prefetch — Relevant memory context can be loaded before each turn.
  • Turn capture — Cleaned user/assistant turns can be stored after each completed response.
  • Session ingest — The full session can be ingested at session end for richer graph updates.
  • Explicit tools — Search, store, forget, and profile tools are available to the model when appropriate.
  • Built-in file memory — This does not replace MEMORY.md / USER.md; mirroring behavior depends on Hermes version and config (see upstream README).

Tools

Kebab-case names are registered for the agent; snake_case aliases remain supported.

Tool Alias Description
supermemory-save supermemory_store Store an explicit memory.
supermemory-search supermemory_search Search by semantic similarity.
supermemory-forget supermemory_forget Forget a memory by ID or best-match query.
supermemory-profile supermemory_profile Retrieve persistent profile and recent context.

Commands

Interactive setup: pick supermemory and enter your API key.

text
hermes memory setup

Environment variables

These variables configure the supermemory provider (for example in your shell or Hermes env file):

bash
SUPERMEMORY_API_KEY=sm_...              # Required
SUPERMEMORY_CONTAINER_TAG=hermes-work # Optional: overrides container tag from config

Multi-container tags

By default, recall and capture use a single primary container_tag (optionally profile-scoped with {identity}). Multi-container mode adds extra named tags so the model can read and write specific namespaces — for example work vs personal, or one bucket per project.

How to enable — In $HERMES_HOME/supermemory.json, set:

  • enable_custom_container_tags to true
  • custom_containers to an array of allowed tag strings (e.g. work, personal, project-alpha)
  • custom_container_instructions (recommended) — short guidance the provider injects into the system prompt so Hermes knows when to use which tag

Your primary container_tag stays the default namespace; listed custom tags are additional allowlisted namespaces.

How it works

  • supermemory_search, supermemory_store, supermemory_forget, and supermemory_profile accept an optional container_tag argument. The tag must be either the primary container_tag (after template resolution) or one of custom_containers.
  • Automatic behavior (turn sync, prefetch, mirroring built-in memory writes, session-end ingest) always uses the primary container only — it does not pick a custom tag for you.
  • Instructions in custom_container_instructions steer the model toward passing the right container_tag on tool calls when the user’s intent matches a namespace (e.g. “check my personal notes” → personal).

Example:

json
{
  "container_tag": "hermes",
  "enable_custom_container_tags": true,
  "custom_containers": ["work", "personal", "shared-knowledge"],
  "custom_container_instructions": "Use work for job and coding context, personal for life and hobbies, shared-knowledge for facts that apply across both."
}

See the upstream plugin README for the exact schema and any newer options.

Config file

Create or edit $HERMES_HOME/supermemory.json. Common keys:

Key Default Description
container_tag hermes Tag for search/writes; use {identity} for profile-scoped tags (e.g. hermes-{identity} → hermes-coder).
auto_recall true Inject memory context before turns.
auto_capture true Store turns after each response.
max_recall_results 10 Max items merged into context.
profile_frequency 50 Profile on first turn and every N turns.
capture_mode all How aggressively turns are captured.
search_mode hybrid hybrid, memories, or documents.
api_timeout 5.0 SDK / ingest timeout (seconds).
enable_custom_container_tags false Set true to allow extra namespaces; see Multi-container tags above.
custom_containers — Allowlisted tags beyond the primary container_tag.
custom_container_instructions — Prompt text that explains when to pass container_tag on tools.

Example profile-scoped container:

json
{
  "container_tag": "hermes-{identity}",
  "search_mode": "hybrid",
  "auto_recall": true,
  "auto_capture": true
}

Self-hosted API

If you run your own supermemory API, set base_url (and any other host-specific options) in supermemory.json or via env as documented in the upstream plugin README — alongside your key and container settings.

Next steps

Full config table, env vars, and multi-container details. Multi-platform memory for Telegram, WhatsApp, Discord, and more.

Questions about the API or product? Discord · [email protected] · Developer docs


Apps Docs Integrations Langchain (`apps/docs/integrations/langchain.mdx`)


Build AI applications with LangChain that remember context across conversations. Supermemory handles memory storage, retrieval, and user profiling while LangChain manages your conversation flow.

Overview

This guide shows how to integrate Supermemory with LangChain to create AI agents that:

  • Maintain user context through automatic profiling
  • Store and retrieve relevant memories semantically
  • Personalize responses based on conversation history

Setup

Install the required packages:

bash
pip install langchain langchain-openai supermemory python-dotenv

Configure your environment:

bash
# .env
SUPERMEMORY_API_KEY=your-supermemory-api-key
OPENAI_API_KEY=your-openai-api-key

Get your Supermemory API key from console.supermemory.ai.

Basic integration

Initialize both clients and set up a simple chat function with memory:

text
/* Detailed source-code truncated for AI context efficiency. */

Core concepts

User profiles

Supermemory automatically maintains user profiles with two types of information:

  • Static facts: Long-term information about the user (preferences, expertise, background)
  • Dynamic context: Recent activity and current focus areas
python
# Fetch the profile for a namespace
result = memory.profile("user_123")

print([m.memory for m in result.profile.static])   # ["User is a Python developer", "Prefers dark mode"]
print([m.memory for m in result.profile.dynamic])  # ["Currently working on API integration", "Debugging auth issues"]

Memory storage

Content you add is automatically processed into searchable memories:

python
# Store a conversation
memory.add(
    "user_123",
    content="User asked about async Python patterns. Explained asyncio basics.",
    metadata={"topic": "python", "type": "conversation"},
    dreaming="instant"
)

# Store a document
memory.add(
    "user_123",
    content="https://docs.python.org/3/library/asyncio.html",
    dreaming="instant"
)

Search returns both extracted memories and document chunks:

python
results = memory.search(
    "user_123",
    query="async programming",
    search_mode="hybrid",  # Searches memories + document chunks
    limit=5
)

for r in results.results:
    print(r.memory or r.chunk, r.similarity)

Complete example: Code review assistant

Here's a full example of a code review assistant that learns from past reviews and adapts to the user's coding style:

text
/* Detailed source-code truncated for AI context efficiency. */
```\n{code}\n```")
        ])

        chain = prompt | self.llm
        response = chain.invoke({})

        # Store the review for future context
        self.memory.add(
            user_id,
            content=f"Code review feedback: {response.content[:500]}",
            metadata={"type": "code_review", "language": language},
            dreaming="instant"
        )

        return response.content

    def learn_preference(self, user_id: str, preference: str):
        """Store a coding preference or style guideline."""
        self.memory.add(
            user_id,
            content=f"Developer preference: {preference}",
            metadata={"type": "preference"},
            dreaming="instant"
        )


# Usage
if __name__ == "__main__":
    assistant = CodeReviewAssistant()
    user_id = "dev_alice"

    # Teach the assistant about preferences
    assistant.learn_preference(user_id, "Prefers functional programming patterns")
    assistant.learn_preference(user_id, "Values descriptive variable names over comments")

    # Review some code
    code = """
def calc(x, y):
    r = []
    for i in x:
        if i in y:
            r.append(i)
    return r
    """

    review = assistant.review(user_id, code, language="python")
    print(review)

Advanced patterns

Conversation history with memory

Maintain multi-turn conversations while building long-term memory:

text
/* Detailed source-code truncated for AI context efficiency. */

Metadata filtering

Use metadata to organize and filter memories:

python
# Store with metadata
memory.add(
    "user_123",
    content="Discussed React hooks and state management",
    dreaming="instant",
    metadata={
        "topic": "react",
        "type": "discussion",
        "project": "frontend-redesign"
    }
)

# Search with filters
results = memory.search(
    "user_123",
    query="state management",
    filter={
        "operator": "and",
        "operands": [
            {"field": "topic", "operator": "eq", "value": "react"},
            {"field": "project", "operator": "eq", "value": "frontend-redesign"}
        ]
    }
)

Batch memory operations

Efficiently store multiple memories:

python
# Store meeting notes as separate memories
notes = [
    "Decided to use PostgreSQL for the new service",
    "Timeline: MVP ready by end of Q2",
    "Alice will lead the database migration"
]

for note in notes:
    memory.add(
        "team_standup",
        content=note,
        metadata={"date": "2024-01-15", "type": "decision"},
        dreaming="instant"
    )

Next steps

Deep dive into automatic user profiling Advanced search patterns and filtering Native OpenAI integration with memory tools Memory middleware for Next.js apps

Apps Docs Integrations Langgraph (`apps/docs/integrations/langgraph.mdx`)


title: "LangGraph" sidebarTitle: "LangGraph" icon: "/images/langgraph.svg"

Build stateful agents with LangGraph that remember context across sessions. Supermemory handles memory storage and retrieval while LangGraph manages your graph-based conversation flow.

Overview

This guide shows how to integrate Supermemory with LangGraph to create agents that:

  • Maintain user context through automatic profiling
  • Store and retrieve relevant memories at each node
  • Use conditional logic to decide what's worth remembering
  • Combine short-term (session) and long-term (cross-session) memory

Setup

Install the required packages:

bash
pip install langgraph langchain-openai supermemory python-dotenv

Configure your environment:

bash
# .env
SUPERMEMORY_API_KEY=your-supermemory-api-key
OPENAI_API_KEY=your-openai-api-key

Get your Supermemory API key from console.supermemory.ai.

Basic integration

A minimal agent that fetches user context before responding and stores the conversation after:

text
/* Detailed source-code truncated for AI context efficiency. */

Core concepts

User profiles

Supermemory automatically builds user profiles from stored memories:

  • Static facts: Long-term information (preferences, expertise, background)
  • Dynamic context: Recent activity and current focus
python
result = memory.profile("user_123")

print([m.memory for m in result.profile.static])   # ["User is a Python developer", "Prefers functional style"]
print([m.memory for m in result.profile.dynamic])  # ["Working on async patterns", "Debugging rate limiting"]

Memory storage

Content you add gets processed into searchable memories:

python
# Store a conversation
memory.add(
    "user_123",
    content="User asked about graph traversal. Explained BFS vs DFS.",
    metadata={"topic": "algorithms", "type": "conversation"},
    dreaming="instant"
)

# Store a document
memory.add(
    "user_123",
    content="https://langchain-ai.github.io/langgraph/",
    dreaming="instant"
)

Search returns both extracted memories and document chunks:

python
results = memory.search(
    "user_123",
    query="graph algorithms",
    search_mode="hybrid",  # Searches memories + document chunks
    limit=5
)

for r in results.results:
    print(r.memory or r.chunk, r.similarity)

Complete example: support agent

A support agent that learns from past tickets and adapts to each user's technical level:

text
/* Detailed source-code truncated for AI context efficiency. */

Advanced patterns

Conditional memory storage

Not everything is worth remembering. Use conditional edges to filter:

python
def should_store(state: State) -> str:
    """Skip storing trivial messages."""
    last_msg = state["messages"][-1].content.lower()

    skip_phrases = ["thanks", "ok", "got it", "bye"]
    if len(last_msg) < 20 or any(p in last_msg for p in skip_phrases):
        return "skip"
    return "store"

graph.add_conditional_edges("respond", should_store, {
    "store": "store",
    "skip": END
})

Parallel memory operations

Fetch memories and categorize at the same time:

python
from langgraph.graph import StateGraph, START, END

graph = StateGraph(State)
graph.add_node("retrieve", retrieve_context)
graph.add_node("categorize", categorize)
graph.add_node("respond", respond)

# Both run in parallel after START
graph.add_edge(START, "retrieve")
graph.add_edge(START, "categorize")

# Both must complete before respond
graph.add_edge("retrieve", "respond")
graph.add_edge("categorize", "respond")
graph.add_edge("respond", END)

Metadata filtering

Organize memories by project, topic, or any custom field:

python
# Store with metadata
memory.add(
    "user_123",
    content="User prefers detailed error messages with stack traces",
    dreaming="instant",
    metadata={
        "type": "preference",
        "project": "api-v2",
        "priority": "high"
    }
)

# Search with filters
results = memory.search(
    "user_123",
    query="error handling preferences",
    filter={
        "operator": "and",
        "operands": [
            {"field": "type", "operator": "eq", "value": "preference"},
            {"field": "project", "operator": "eq", "value": "api-v2"}
        ]
    }
)

Combining session and long-term memory

LangGraph's checkpointer handles within-session state. Supermemory handles cross-session memory. Use both:

python
from langgraph.checkpoint.memory import MemorySaver

# Session memory (cleared when thread ends)
checkpointer = MemorySaver()
app = graph.compile(checkpointer=checkpointer)

# Long-term memory (persists across sessions)
# Handled by Supermemory in your nodes

Next steps

Deep dive into automatic user profiling Advanced search patterns and filtering Native OpenAI integration with memory tools Memory middleware for Next.js apps

Apps Docs Integrations Livekit (`apps/docs/integrations/livekit.mdx`)


title: "LiveKit" sidebarTitle: "LiveKit (Voice)" icon: "/images/livekit.svg"

Supermemory integrates with LiveKit Agents, so a voice agent can remember a caller across sessions and use that context on the next turn. Recall runs before the model replies. Completed turns are stored automatically. The model can also search, remember, and forget explicitly.

A Supermemory timeout or outage is skipped. It does not end the call.

Installation

bash
pip install supermemory-livekit
bash
export SUPERMEMORY_API_KEY=your_supermemory_api_key

Create a key at console.supermemory.ai. For a self-hosted API, pass base_url to SupermemoryLiveKit.

Scope

Memory is isolated by container tag. Use a stable caller id, the same one you use in the rest of your app, so a LiveKit call and a chat session share one profile.

Container tags may only contain letters, numbers, _, -, and :, and must be 100 characters or fewer. Two ways to set it:

  • Pass container_tag yourself, usually from job metadata.
  • Let the plugin read the participant attribute supermemory_container_tag, or fall back to the participant identity.

Identities that are not valid container tags are sanitized to a stable tag. Set the attribute when the identity is an email, phone number, or SIP address and you already have memories under a different id.

Set the attribute in the access token your backend issues. Do not grant the caller canUpdateOwnMetadata, or they could change the attribute and read another caller's memory.

One call is stored as a single document. Pass the LiveKit room name as session_id. The session id is the document's custom id, so a later update with the same id appends to that call instead of creating another.

Use one SupermemoryLiveKit per call. If you do rebind it, turns already captured stay with the caller who said them, and memory from the earlier caller is never shown to the new one.

Quick start

Create one SupermemoryLiveKit inside the job. A worker handles many calls, and each call needs its own scope.

python
import json
import os

from livekit import agents
from livekit.agents import AgentServer, AgentSession, ChatContext, JobContext
from supermemory_livekit import SupermemoryAgent, SupermemoryLiveKit

server = AgentServer()


@server.rtc_session(agent_name="memory-agent")
async def entrypoint(ctx: JobContext):
    memory = SupermemoryLiveKit(api_key=os.environ["SUPERMEMORY_API_KEY"])
    metadata = json.loads(ctx.job.metadata or "{}")
    container_tag = metadata.get("container_tag")
    if container_tag:
        memory.bind(container_tag=container_tag, session_id=ctx.room.name)

    chat_ctx = ChatContext()
    if container_tag:
        await memory.preload(chat_ctx)

    await ctx.connect()
    if not container_tag:
        participant = await ctx.wait_for_participant()
        memory.bind(participant=participant, session_id=ctx.room.name)

    session = AgentSession(
        stt="deepgram/nova-3:en",
        llm="openai/gpt-4.1-mini",
        tts="cartesia/sonic-3",
    )
    memory.attach(session)
    await session.start(
        room=ctx.room,
        agent=SupermemoryAgent(
            memory,
            chat_ctx=chat_ctx,
            instructions=(
                "You are a helpful voice assistant. You remember this caller across calls. "
                "Use that naturally, and do not mention the memory system."
            ),
        ),
    )


if __name__ == "__main__":
    agents.cli.run_app(server)

preload puts the caller's profile into the first turn, so the greeting can use it. attach stores the conversation after each agent reply, and stores anything left when the session closes, including a caller who hangs up mid-turn. SupermemoryAgent recalls inside llm_node before each reply and adds the memory tools. That covers voice turns, text turns from generate_reply or session.run, and LiveKit's preemptive generation. Each user message is recalled once, and tool follow-ups reuse it.

A runnable version, with a greeting that uses the caller's memory, is in the example agent.

agent_name turns on explicit dispatch, which is how job metadata reaches the agent. Remove it to join every new room automatically, for example when testing in the Agents Playground.

Your own agent

If you already subclass Agent, keep that class. Pass the tools in, and recall from llm_node.

python
from livekit.agents import Agent
from supermemory_livekit import SupermemoryLiveKit


class Assistant(Agent):
    def __init__(self, memory: SupermemoryLiveKit) -> None:
        super().__init__(
            instructions="You are a helpful voice assistant.",
            tools=memory.tools(),
        )
        self.memory = memory

    async def llm_node(self, chat_ctx, tools, model_settings):
        await self.memory.enrich(chat_ctx)
        return Agent.default.llm_node(self, chat_ctx, tools, model_settings)

Call memory.attach(session) before session.start.

Recall in llm_node rather than on_user_turn_completed. Changing the turn context in that hook makes LiveKit discard its preemptive generation, which adds latency to every turn.

Realtime models skip llm_node. With a realtime model, call await self.memory.on_user_turn_completed(turn_ctx, new_message) from on_user_turn_completed instead. That hook only runs when turn detection runs in your agent, not inside the model. See LiveKit's external data guide. SupermemoryAgent picks the right hook for you. Turn capture still listens for conversation_item_added.

What gets recalled

Mode Static profile Dynamic profile Search Use when
profile Yes Yes No Durable facts are enough
query No No Yes Only this turn's related memories
full Yes Yes Yes Default
python
from supermemory_livekit import InputParams, SupermemoryLiveKit

memory = SupermemoryLiveKit(
    container_tag="user_123",
    params=InputParams(
        mode="full",
        search_limit=10,
        search_threshold=0.1,
        recall_timeout=2.0,
        capture="always",
        capture_dreaming="instant",
    ),
)

Recall waits at most recall_timeout seconds (default 2). If the profile call is slower than that, or fails, the turn falls back to the profile loaded by preload at the start of the call, or proceeds without memory if there was none. In our tests most recalls took 0.4 to 1.4 seconds, with occasional calls near 3 seconds. Raise recall_timeout if you would rather wait than miss memory on a slow turn. Retrieved text is inserted immediately before the user message and is not stored back as something the agent said.

Parameter Default Description
search_limit 10 Maximum search results merged into the turn
search_threshold 0.1 Minimum similarity, from 0 to 1
mode "full" profile, query, or full
recall_timeout 2.0 Seconds to wait before skipping recall
capture "always" never disables storing the call
capture_dreaming "instant" How the call transcript becomes memories. See below.

Tools

SupermemoryAgent adds these tools. They are scoped to the bound container tag, so the model cannot read or write another caller.

Tool Used for
search_memories Look up facts and past calls when the automatic recall is not enough
remember Save one explicit fact, preference, or correction
forget Forget one fact by id from search_memories, or by exact text

remember stores a standalone fact and processes it immediately (dreaming="instant"), so it is usually recallable within a minute. If your organization has no balance for instant processing, it is saved on the default schedule instead. It is not appended to the call transcript. Automatic capture is what records the conversation.

When a call becomes recallable

Captured turns are written after each agent reply, but they become memories on Supermemory's processing schedule:

  • With the default capture_dreaming="instant", each write is processed on its own and is usually recallable within a minute, so a caller who rings back is recalled from the last call. Each write bills one extra operation, and the call is written after every agent reply. If your organization has no balance for instant processing, the call is saved on the dynamic schedule instead.
  • With capture_dreaming="dynamic", related documents are processed together at no extra cost. In our tests this took 10 to 20 minutes, so a caller who rings back right away is not recalled from the last call yet.
  • In both modes, search_memories finds the raw call text as soon as it is stored, so the model can still look up a recent call.

Self-hosting

python
memory = SupermemoryLiveKit(
    api_key=os.environ["SUPERMEMORY_API_KEY"],
    base_url="http://localhost:6767",
    container_tag="user_123",
)

Apps Docs Integrations Mastra (`apps/docs/integrations/mastra.mdx`)


title: "Mastra" sidebarTitle: "Mastra" icon: "/images/mastra-icon.svg"

import MigrateCli from "/snippets/migrate-cli.mdx";

Integrate Supermemory with Mastra to give your AI agents persistent memory. Use the withSupermemory wrapper for zero-config setup or processors for fine-grained control.

Check out the NPM page for more details

Installation

bash
npm install @supermemory/tools @mastra/core

Quick start

Wrap your agent config with withSupermemory to add memory capabilities:

typescript
import { Agent } from "@mastra/core/agent"
import { withSupermemory } from "@supermemory/tools/mastra"
import { openai } from "@ai-sdk/openai"

// Create agent with memory-enhanced config
const agent = new Agent(withSupermemory(
  {
    id: "my-assistant",
    name: "My Assistant",
    model: openai("gpt-4o"),
    instructions: "You are a helpful assistant.",
  },
  {
    namespace: "user-123",  // Required: scopes memories to this user
    id: "conv-456",      // Required: groups messages for contextual memory
    mode: "full",
  }
))

const response = await agent.generate("What do you know about me?")
**Memory saving is enabled by default.** Conversations are automatically saved to Supermemory. To disable saving:
typescript
const agent = new Agent(withSupermemory(
  { id: "my-assistant", model: openai("gpt-4o"), ... },
  {
    namespace: "user-123",
    id: "conv-456",
    addMemory: "never",  // Disable automatic conversation saving
  }
))

How it works

The Mastra integration uses Mastra's native Processor interface:

  1. Input Processor - Fetches relevant memories from Supermemory and injects them into the system prompt before the LLM call
  2. Output Processor - Optionally saves the conversation to Supermemory after generation completes
mermaid
sequenceDiagram
    participant User
    participant Agent
    participant InputProcessor
    participant LLM
    participant OutputProcessor
    participant Supermemory

    User->>Agent: Send message
    Agent->>InputProcessor: Process input
    InputProcessor->>Supermemory: Fetch memories
    Supermemory-->>InputProcessor: Return memories
    InputProcessor->>Agent: Inject into system prompt
    Agent->>LLM: Generate response
    LLM-->>Agent: Return response
    Agent->>OutputProcessor: Process output
    OutputProcessor->>Supermemory: Save conversation (if enabled)
    Agent-->>User: Return response

Configuration options

Option Type Default Description
namespace string Required User/container tag for scoping memories
id string Required Groups messages into a single document for contextual memory
apiKey string SUPERMEMORY_API_KEY env Your Supermemory API key
baseUrl string https://api.supermemory.ai Custom API endpoint
mode "profile" | "query" | "full" "profile" Memory search mode
addMemory "always" | "never" "always" Auto-save conversations
verbose boolean false Enable debug logging
promptTemplate function - Custom memory formatting

Memory search modes

Profile Mode (Default) - Retrieves the user's complete profile without query-based filtering:

typescript
const agent = new Agent(withSupermemory(config, {
  namespace: "user-123",
  id: "conv-456",
  mode: "profile",
}))

Query Mode - Searches memories based on the user's message:

typescript
const agent = new Agent(withSupermemory(config, {
  namespace: "user-123",
  id: "conv-456",
  mode: "query",
}))

Full Mode - Combines profile AND query-based search for maximum context:

typescript
const agent = new Agent(withSupermemory(config, {
  namespace: "user-123",
  id: "conv-456",
  mode: "full",
}))

### Mode Comparison

| Mode | Description | Use Case |
|------|-------------|----------|
| `profile` | Static + dynamic user facts | General personalization |
| `query` | Semantic search on user message | Specific Q&A |
| `full` | Both profile and search | Chatbots, assistants |

---

## Saving Conversations

Conversation saving is enabled by default (`addMemory: "always"`). Messages are grouped using the required `id`:

```typescript
const agent = new Agent(withSupermemory(
  { id: "my-assistant", model: openai("gpt-4o"), instructions: "..." },
  {
    namespace: "user-123",
    id: "conv-456",  // Required: groups messages for contextual memory
  }
))

// All messages in this conversation are saved automatically
await agent.generate("I prefer TypeScript over JavaScript")
await agent.generate("My favorite framework is Next.js")

To disable automatic saving:

typescript
const agent = new Agent(withSupermemory(
  { id: "my-assistant", model: openai("gpt-4o"), instructions: "..." },
  {
    namespace: "user-123",
    id: "conv-456",
    addMemory: "never",  // Only retrieve memories, don't save
  }
))

Custom Prompt Templates

Customize how memories are formatted and injected. The template receives userMemories, generalSearchMemories, and searchResults (raw array for filtering by metadata):

typescript
import { Agent } from "@mastra/core/agent"
import { withSupermemory } from "@supermemory/tools/mastra"
import type { MemoryPromptData } from "@supermemory/tools/mastra"

const claudePrompt = (data: MemoryPromptData) => `
<context>
  
    ${data.userMemories}
  
  <relevant_memories>
    ${data.generalSearchMemories}
  </relevant_memories>
</context>
`.trim()

const agent = new Agent(withSupermemory(
  { id: "my-assistant", model: openai("gpt-4o"), instructions: "..." },
  {
    namespace: "user-123",
    id: "conv-456",
    mode: "full",
    promptTemplate: claudePrompt,
  }
))

Direct Processor Usage

For advanced use cases, use processors directly instead of the wrapper:

Input Processor Only

Inject memories without saving conversations:

typescript
import { Agent } from "@mastra/core/agent"
import { createSupermemoryProcessor } from "@supermemory/tools/mastra"
import { openai } from "@ai-sdk/openai"

const agent = new Agent({
  id: "my-assistant",
  name: "My Assistant",
  model: openai("gpt-4o"),
  inputProcessors: [
    createSupermemoryProcessor({
      namespace: "user-123",
      id: "conv-456",
      mode: "full",
      addMemory: "never",
      verbose: true,
    }),
  ],
})

Output Processor Only

Save conversations without memory injection:

typescript
import { Agent } from "@mastra/core/agent"
import { createSupermemoryOutputProcessor } from "@supermemory/tools/mastra"
import { openai } from "@ai-sdk/openai"

const agent = new Agent({
  id: "my-assistant",
  name: "My Assistant",
  model: openai("gpt-4o"),
  outputProcessors: [
    createSupermemoryOutputProcessor({
      namespace: "user-123",
      id: "conv-456",
    }),
  ],
})

Both Processors

Use the factory function for shared configuration:

typescript
import { Agent } from "@mastra/core/agent"
import { createSupermemoryProcessors } from "@supermemory/tools/mastra"
import { openai } from "@ai-sdk/openai"

const { input, output } = createSupermemoryProcessors({
  namespace: "user-123",
  id: "conv-456",
  mode: "full",
  verbose: true,
})

const agent = new Agent({
  id: "my-assistant",
  name: "My Assistant",
  model: openai("gpt-4o"),
  inputProcessors: [input],
  outputProcessors: [output],
})

Using RequestContext for Dynamic Thread IDs

For server setups where one agent instance handles multiple concurrent conversations, use Mastra's RequestContext to provide per-request thread IDs. RequestContext takes precedence over the construction-time id:

typescript
import { Agent } from "@mastra/core/agent"
import { RequestContext, MASTRA_THREAD_ID_KEY } from "@mastra/core/request-context"
import { withSupermemory } from "@supermemory/tools/mastra"
import { openai } from "@ai-sdk/openai"

const agent = new Agent(withSupermemory(
  { id: "my-assistant", model: openai("gpt-4o"), instructions: "..." },
  {
    namespace: "user-123",
    id: "fallback-conv",  // Used only when RequestContext doesn't provide a threadId
    mode: "full",
  }
))

// Per-request threadId takes precedence over id
const ctx = new RequestContext()
ctx.set(MASTRA_THREAD_ID_KEY, "user-456-session-789")

await agent.generate("Hello!", { requestContext: ctx })
// This conversation is stored under "user-456-session-789", not "fallback-conv"
**Server-side usage**: Always use `RequestContext` to pass unique conversation IDs per request. Using a fixed `id` for all requests will merge conversations from different users.

Verbose Logging

Enable detailed logging for debugging:

typescript
const agent = new Agent(withSupermemory(
  { id: "my-assistant", model: openai("gpt-4o"), instructions: "..." },
  {
    namespace: "user-123",
    id: "conv-456",
    verbose: true,
  }
))

// Console output:
// [supermemory] Starting memory search { namespace: "user-123", mode: "profile" }
// [supermemory] Found 5 memories
// [supermemory] Injected memories into system prompt { length: 1523 }

Working with Existing Processors

The wrapper correctly merges with existing processors in the config:

typescript
// Supermemory processors are merged correctly:
// - Input: [supermemory, myLogging] (supermemory runs first)
// - Output: [myAnalytics, supermemory] (supermemory runs last)
const agent = new Agent(withSupermemory(
  {
    id: "my-assistant",
    model: openai("gpt-4o"),
    inputProcessors: [myLoggingProcessor],
    outputProcessors: [myAnalyticsProcessor],
  },
  {
    namespace: "user-123",
    id: "conv-456",
  }
))

API Reference

`withSupermemory`

Enhances a Mastra agent config with memory capabilities.

typescript
function withSupermemory<T extends AgentConfig>(
  config: T,
  options: SupermemoryMastraOptions
): T

Parameters:

  • config - The Mastra agent configuration object
  • options - Configuration options (includes required namespace and id)

Returns: Enhanced config with Supermemory processors injected

`createSupermemoryProcessor`

Creates an input processor for memory injection.

typescript
function createSupermemoryProcessor(
  options: SupermemoryMastraOptions
): SupermemoryInputProcessor

`createSupermemoryOutputProcessor`

Creates an output processor for conversation saving.

typescript
function createSupermemoryOutputProcessor(
  options: SupermemoryMastraOptions
): SupermemoryOutputProcessor

`createSupermemoryProcessors`

Creates both processors with shared configuration.

typescript
function createSupermemoryProcessors(
  options: SupermemoryMastraOptions
): {
  input: SupermemoryInputProcessor
  output: SupermemoryOutputProcessor
}

`SupermemoryMastraOptions`

typescript
interface SupermemoryMastraOptions {
  namespace: string         // Required: User/container tag for scoping memories
  id: string             // Required: Groups messages for contextual memory generation
  apiKey?: string
  baseUrl?: string
  mode?: "profile" | "query" | "full"
  addMemory?: "always" | "never"  // Default: "always"
  verbose?: boolean
  promptTemplate?: (data: MemoryPromptData) => string
}

Environment Variables

bash
SUPERMEMORY_API_KEY=your_supermemory_key

Error Handling

Processors gracefully handle errors without breaking the agent:

  • API errors - Logged and skipped; agent continues without memories
  • Missing API key - Throws immediately with helpful error message
typescript
// Missing API key throws immediately
const agent = new Agent(withSupermemory(
  { id: "my-assistant", model: openai("gpt-4o"), instructions: "..." },
  {
    namespace: "user-123",
    id: "conv-456",
    apiKey: undefined,  // Will check SUPERMEMORY_API_KEY env
  }
))
// Error: SUPERMEMORY_API_KEY is not set

Next Steps

Use with Vercel AI SDK for streamlined development Learn about user profile management

Apps Docs Integrations Memory Graph (`apps/docs/integrations/memory-graph.mdx`)


title: 'Memory Graph' sidebarTitle: "Memory Graph" description: 'Interactive visualization for documents, memories and connections' icon: "/icons/hugeicons/hierarchy-square-01.svg"

Memory Graph is a React component that visualizes your Supermemory documents and memories as an interactive network. Documents appear as rectangular nodes, memories as hexagonal nodes, and connections between them show relationships and similarity.

Check out the NPM page for more details

Installation

bash
npm install @supermemory/memory-graph

Requirements: React 18.0.0 or higher

Quick start

tsx
'use client'; // For Next.js App Router

import { MemoryGraph } from '@supermemory/memory-graph';
import type { DocumentWithMemories } from '@supermemory/memory-graph';
import { useEffect, useState } from 'react';

export default function GraphPage() {
  const [documents, setDocuments] = useState<DocumentWithMemories[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    fetch('/api/graph')
      .then(res => res.json())
      .then(data => {
        setDocuments(data.documents);
        setIsLoading(false);
      })
      .catch(err => {
        setError(err);
        setIsLoading(false);
      });
  }, []);

  return (
    
      <MemoryGraph
        documents={documents}
        isLoading={isLoading}
        error={error}
        variant="console"
      />
    
  );
}

Optional rendering and document navigation

Existing icon-based document nodes and connection styling remain the defaults. Opt in to stacked squares and stronger semantic connections with appearance:

tsx
<MemoryGraph
  documents={documents}
  appearance={{ documentNodes: 'stacked', connections: 'emphasized' }}
  openDocumentOnEnter
  onOpenDocument={openPersistedDocument}
/>

documentNodes accepts 'icons' or 'stacked'; connections accepts 'default' or 'emphasized'. Both fields are optional and also work on GraphCanvas. Stacks stay inside existing node bounds and simplify at low zoom. Use colors.edgeDocument to configure document-source ink; older custom GraphCanvas palettes without that field use textSecondary in emphasized mode.

openDocumentOnEnter is off by default. With an onOpenDocument callback, MemoryGraph exposes a focused Enter action for the selected persisted document or one confirmed memory source. Typing, modifier shortcuts, repeats, composition and keys outside the graph do not open documents. Your callback retains responsibility for routing and access control; consumers without a callback receive no new opening behavior.

Backend API route

Create an API route to fetch documents from Supermemory:

typescript next.js app router
// app/api/graph/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
  const response = await fetch('https://api.supermemory.ai/v3/documents/documents', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`,
    },
    body: JSON.stringify({
      page: 1,
      limit: 500,
      sort: 'createdAt',
      order: 'desc',
    }),
  });

  const data = await response.json();
  return NextResponse.json(data);
}
typescript next.js pages router
// pages/api/graph.ts
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const response = await fetch('https://api.supermemory.ai/v3/documents/documents', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`,
    },
    body: JSON.stringify({ page: 1, limit: 500, sort: 'createdAt', order: 'desc' }),
  });

  const data = await response.json();
  res.json(data);
}
javascript express
app.get('/api/graph', async (req, res) => {
  const response = await fetch('https://api.supermemory.ai/v3/documents/documents', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`,
    },
    body: JSON.stringify({ page: 1, limit: 500, sort: 'createdAt', order: 'desc' }),
  });

  const data = await response.json();
  res.json(data);
});
Never expose your Supermemory API key to the client. Always fetch data through your backend.

Variants

Console Variant - Full-featured dashboard view (0.8x zoom, space selector visible):

tsx
<MemoryGraph documents={documents} variant="console" />

Consumer Variant - Embedded widget view (0.5x zoom, space selector hidden):

tsx
<MemoryGraph documents={documents} variant="consumer" />

Examples

With pagination

tsx
'use client';

import { MemoryGraph } from '@supermemory/memory-graph';
import { useCallback, useEffect, useState } from 'react';

export default function PaginatedGraph() {
  const [documents, setDocuments] = useState([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const [isLoading, setIsLoading] = useState(true);
  const [isLoadingMore, setIsLoadingMore] = useState(false);

  useEffect(() => { fetchPage(1, false); }, []);

  const fetchPage = async (pageNum, append) => {
    pageNum === 1 ? setIsLoading(true) : setIsLoadingMore(true);

    const res = await fetch(`/api/graph?page=${pageNum}&limit=100`);
    const data = await res.json();

    append ? setDocuments(prev => [...prev, ...data.documents]) : setDocuments(data.documents);
    setHasMore(data.pagination.currentPage < data.pagination.totalPages);
    setIsLoading(false);
    setIsLoadingMore(false);
  };

  const loadMore = useCallback(async () => {
    if (!isLoadingMore && hasMore) {
      const nextPage = page + 1;
      setPage(nextPage);
      await fetchPage(nextPage, true);
    }
  }, [page, hasMore, isLoadingMore]);

  return (
    <MemoryGraph
      documents={documents}
      isLoading={isLoading}
      isLoadingMore={isLoadingMore}
      hasMore={hasMore}
      totalLoaded={documents.length}
      loadMoreDocuments={loadMore}
    />
  );
}

Highlighting search results

tsx
<MemoryGraph
  documents={documents}
  highlightDocumentIds={searchResults}
  highlightsVisible={searchResults.length > 0}
/>

Controlled space selection

tsx
<MemoryGraph
  documents={documents}
  selectedSpace={selectedSpace}
  onSpaceChange={setSelectedSpace}
  showSpacesSelector={false}
/>

Custom empty state

tsx
<MemoryGraph documents={documents} isLoading={isLoading}>
  
    <h2>No memories yet</h2>
    Add content to see your knowledge graph
  
</MemoryGraph>

Props reference

Core props

Prop Type Default Description
documents DocumentWithMemories[] required Array of documents to display
isLoading boolean false Shows loading indicator
error Error | null null Error to display
variant "console" | "consumer" "console" Visual variant
children ReactNode - Custom empty state content

Pagination props

Prop Type Default Description
isLoadingMore boolean false Shows indicator when loading more
hasMore boolean false Whether more documents available
totalLoaded number - Total documents currently loaded
loadMoreDocuments () => Promise<void> - Callback to load more
autoLoadOnViewport boolean true Auto-load when 80% visible

Display props

Prop Type Default Description
showSpacesSelector boolean variant-based Show space filter dropdown
highlightDocumentIds string[] [] Document IDs to highlight
highlightsVisible boolean true Whether highlights shown
occludedRightPx number 0 Pixels occluded on right

Controlled state props

Prop Type Description
selectedSpace string Currently selected space (use "all" for all)
onSpaceChange (spaceId: string) => void Callback when space changes
memoryLimit number Max memories per document when space selected

Data types

DocumentWithMemories

typescript
interface DocumentWithMemories {
  id: string;
  customId?: string | null;
  title?: string | null;
  content?: string | null;
  summary?: string | null;
  url?: string | null;
  source?: string | null;
  type?: string | null;
  status: 'pending' | 'processing' | 'done' | 'failed';
  metadata?: Record<string, string | number | boolean> | null;
  createdAt: string | Date;
  updatedAt: string | Date;
  memoryEntries: MemoryEntry[];
}

MemoryEntry

typescript
interface MemoryEntry {
  id: string;
  documentId: string;
  content: string | null;
  summary?: string | null;
  title?: string | null;
  type?: string | null;
  metadata?: Record<string, string | number | boolean> | null;
  createdAt: string | Date;
  updatedAt: string | Date;
  spaceContainerTag?: string | null;
  relation?: 'updates' | 'extends' | 'derives' | null;
  isLatest?: boolean;
  spaceId?: string | null;
}

Exports

Components

typescript
import {
  MemoryGraph,
  GraphCanvas,
  Legend,
  LoadingIndicator,
  NodeDetailPanel,
  SpacesDropdown
} from '@supermemory/memory-graph';

Hooks

typescript
import { useGraphData, useGraphInteractions } from '@supermemory/memory-graph';

Constants

typescript
import { colors, GRAPH_SETTINGS, LAYOUT_CONSTANTS } from '@supermemory/memory-graph';

Performance

The graph handles hundreds of nodes efficiently through:

  • Canvas-based rendering (not DOM elements)
  • Viewport culling (only draws visible nodes)
  • Level-of-detail optimization (simplifies when zoomed out)
  • Change-based rendering (only redraws when state changes)

For very large datasets (1000+ documents), use pagination to load data in chunks.

Browser support

Works in all modern browsers supporting Canvas 2D API, ES2020, and CSS custom properties. Tested on Chrome, Firefox, Safari, and Edge.


Apps Docs Integrations Muse Code (`apps/docs/integrations/muse-code.mdx`)


title: "Muse Code" sidebarTitle: "Muse Code" description: "Persistent memory for Meta Muse Code with the muse-supermemory plugin." icon: "/images/muse-code-icon.svg"

muse-supermemory is a native Muse Code plugin that gives your agent persistent memory across sessions. Your agent remembers what you worked on — across sessions, across projects.

**Prefer to keep everything on your machine?** This plugin works with [self-hosted Supermemory](/self-hosting/overview) — run `npx supermemory local`, then set `baseUrl` in `.muse/supermemory.json` (Muse clears hook env, so `SUPERMEMORY_API_URL` may not reach the plugin) and use the API key printed on first boot.

Install the plugin

Requires Node.js 18+ on your PATH — the memory hooks and MCP proxy run as Node scripts.

In a Muse session, open /plugins and install from the marketplace. Or from the CLI:

bash
export MUSE_EXPERIMENTAL_PLUGINS=1
muse plugins marketplace add supermemory https://github.com/supermemoryai/muse-supermemory
muse plugins install supermemory@supermemory
muse plugins approve supermemory

approve trusts the runtime capabilities (hooks + MCP). Without it, the plugin is installed but inert.

Local bundle (a clone of the repo):

bash
export MUSE_EXPERIMENTAL_PLUGINS=1
muse plugins install . --scope user
muse plugins approve supermemory

Authenticate

Muse runs hooks with a cleared environment, so an exported API key in your shell does not reach them. Auth is file-based.

The first session opens a browser login and writes ~/.supermemory-muse/credentials.json.

If you already logged in through the Claude Code plugin, that key is reused from ~/.supermemory-claude/credentials.json.

Or write the file yourself with a key from API Keys:

json
{ "apiKey": "sm_..." }

Check the connection any time with /supermemory:status.

How it works

Two layers, same as the other coding plugins:

Layer When What it does
Session profile SessionStart Injects up to 5 persistent profile facts and 5 recent memories for this repo
Prompt recall UserPromptSubmit Searches supermemory with the prompt, injects up to 5 new hits (similarity ≥ 0.55)
Auto-approve PreToolUse Allows read-only supermemory MCP tools without a permission prompt
Auto-capture Stop Saves new user prompts and assistant messages from Muse's session.jsonl
MCP tools on demand Search, save, list, and manage memories explicitly

Recalled lines are marked ◪. When the model uses one, it keeps that mark and says "from supermemory" — never "from memory".

Slash commands and very short prompts skip recall. Timeouts fail open so a slow network never blocks the turn.

Memory scope

Container tags match Claude Code:

text
repo___<pro