# Technical Documentation: supermemoryai/supermemory

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/supermemoryai/supermemory)

### Overview (README.md)

<p align="center">
  <picture>
    <source srcset="apps/web/public/logo-fullmark.svg" media="(prefers-color-scheme: dark)">
    <source srcset="apps/web/public/logo-light-fullmark.svg" media="(prefers-color-scheme: light)">
    <img src="apps/web/public/logo-fullmark.svg" alt="Supermemory" width="400" />
  </picture>
</p>

<p align="center">
  <strong>State-of-the-art memory and context engine for AI.</strong>
</p>

<p align="center">
  <a href="https://supermemory.ai/docs">Docs</a> ·
  <a href="https://supermemory.ai/docs/quickstart">Quickstart</a> ·
  <a href="https://supermemory.ai/docs/self-hosting/overview">Self-host</a> ·
  <a href="https://console.supermemory.ai">Dashboard</a> ·
  <a href="https://supermemory.link/discord">Discord</a>
</p>

<p align="center">
  <a href="https://www.npmjs.com/package/supermemory"><img src="https://img.shields.io/npm/v/supermemory?style=flat-square&color=blue" alt="npm" /></a>
  <a href="https://pypi.org/project/supermemory/"><img src="https://img.shields.io/pypi/v/supermemory?style=flat-square&color=blue" alt="pypi" /></a>
  <a href="https://supermemory.ai/docs"><img src="https://img.shields.io/badge/docs-supermemory.ai-blue?style=flat-square" alt="docs" /></a>
</p>

<p align="center">
  <strong>English</strong> · <a href="README.zh-CN.md">简体中文</a>
</p>

<p align="center">
  <strong>#1 on every major AI memory benchmark — <a href="https://github.com/xiaowu0162/LongMemEval">LongMemEval</a>, <a href="https://github.com/snap-research/locomo">LoCoMo</a>, and <a href="https://github.com/Salesforce/ConvoMem">ConvoMem</a>.</strong><br/>
  <strong>95% Recall@15 with a 99.4% context reduction · ~50ms user profiles.</strong><br/>
  <a href="https://supermemory.ai/research">Read the research →</a>
</p>

---

Supermemory is the memory and context layer for AI. **#1 on [LongMemEval](https://github.com/xiaowu0162/LongMemEval), [LoCoMo](https://github.com/snap-research/locomo), and [ConvoMem](https://github.com/Salesforce/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. 

<img width="1414" height="937" alt="image" src="https://github.com/user-attachments/assets/8863b6d9-c043-4c75-b200-4f1759e7edaf" />


---

## Use Supermemory

<table>
<tr>
<td width="50%" valign="top">

<h3>🧑‍💻 I use AI tools</h3>

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](#give-your-ai-memory)**

</td>
<td width="50%" valign="top">

<h3>🔧 I'm building AI products</h3>

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](#build-with-supermemory-api)**

</td>
</tr>
<tr>
<td colspan="2" valign="top">

<h3>🖥️ I want to run it myself</h3>

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](#supermemory-local--run-it-yourself)**

</td>
</tr>
</table>

---

## 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.

<img width="844" height="484" alt="image" src="https://github.com/user-attachments/assets/ecb879a2-8652-495d-9228-f305a97ba603" />

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

You can find them here: 

- Claude Code plugin: https://github.com/supermemoryai/claude-supermemory
- Muse Code plugin: https://github.com/supermemoryai/muse-supermemory
- Cursor plugin: https://github.com/supermemoryai/cursor-supermemory
- Codex plugin: https://github.com/supermemoryai/codex-supermemory
- OpenClaw plugin: https://github.com/supermemoryai/openclaw-supermemory
- OpenCode plugin: https://github.com/supermemoryai/opencode-supermemory
- Hermes agent (Supermemory memory provider): https://github.com/NousResearch/hermes-agent

### 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](https://supermemory.ai/docs/supermemory-mcp/mcp).

### 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](https://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](apps/docs/self-hosting/docker.mdx) 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](https://supermemory.ai/docs/self-hosting/overview) — [quickstart](https://supermemory.ai/docs/self-hosting/quickstart), [configuration](https://supermemory.ai/docs/self-hosting/configuration), [embeddings](https://supermemory.ai/docs/self-hosting/embeddings), and [local vs. Enterprise](https://supermemory.ai/docs/self-hosting/local-vs-enterprise).

---

## Benchmarks

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

| Benchmark | What it measures | Result |
|---|---|---|
| **[LongMemEval](https://github.com/xiaowu0162/LongMemEval)** | Long-term memory across sessions with knowledge updates | **#1** |
| **[LoCoMo](https://github.com/snap-research/locomo)** | Fact recall across extended conversations (single-hop, multi-hop, temporal, adversarial) | **#1** |
| **[ConvoMem](https://github.com/Salesforce/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](https://supermemory.ai/research).

We also built **[MemoryBench](https://supermemory.ai/docs/memorybench/overview)** — 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.

```
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

```
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.

---

## Links

- 📖 [Documentation](https://supermemory.ai/docs)
- 🚀 [Quickstart](https://supermemory.ai/docs/quickstart)
- 🖥️ [Self-hosting (Supermemory local)](https://supermemory.ai/docs/self-hosting/overview)
- 🧪 [MemoryBench](https://supermemory.ai/docs/memorybench/overview)
- 🔌 [Integrations](https://supermemory.ai/docs/integrations)
- 💬 [Discord](https://supermemory.link/discord)
- 𝕏 [Twitter](https://twitter.com/supermemory)

---

<p align="center">
  <strong>Give your AI a memory. It's about time..</strong>
</p>

---

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

---
title: "Introduction"
description: "Context infrastructure for AI agents"
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 }) => (
  <a className="sm-home-link" href={href}>
    <span className="sm-home-link-icon"><Icon icon={icon} size={12} /></span>
    <span>{children}</span>
  </a>
)

export const PathCard = ({ title, description, href, image, children }) => (
  <div className="sm-home-card">
    <a className="sm-home-card-media" href={href} aria-label={title}>
      <img src={image} alt="" />
    </a>
    <div className="sm-home-card-body">
      <a className="sm-home-card-title" href={href}>{title}</a>
      <p className="sm-home-card-desc">{description}</p>
      <div className="sm-home-links">{children}</div>
    </div>
  </div>
)

export const ListCard = ({ title, children }) => (
  <div className="sm-home-card sm-home-list-card">
    <div className="sm-home-card-body">
      <div className="sm-home-card-title">{title}</div>
      <div className="sm-home-links">{children}</div>
    </div>
  </div>
)

<div className="sm-home">

  <section className="sm-home-hero">
    <div className="sm-home-hero-copy">
      <h1 className="sm-home-title sm-home-title-split">
        <span>Memory and continual learning</span> <span className="sm-home-title-muted">for the world’s best agents.</span>
      </h1>
      <p className="sm-home-lede">
        Supermemory gives your agent long-term memory of every user. Paste one prompt into your coding agent and it wires memory into your project.
      </p>
      <div className="sm-home-actions">
        <CopyPromptButton prompt={AGENT_PROMPT} label="Copy prompt" copiedLabel="Copied" className="sm-home-btn sm-home-btn-primary" />
        <a href="/quickstart" className="sm-home-btn sm-home-btn-secondary">Read the quickstart</a>
      </div>
      <AgentMarks />
    </div>
    <div className="sm-home-sample not-prose">
      <PromptPanel
        tabs={[
          { label: "setup-prompt.md", text: AGENT_PROMPT, kind: "prompt" },
          { label: "TypeScript", text: HERO_TS, kind: "code" },
          { label: "Python", text: HERO_PY, kind: "code" },
          { label: "curl", text: HERO_CURL, kind: "code" },
        ]}
      />
    </div>
  </section>

  <section className="sm-home-section">
    <p className="sm-home-label">Platform</p>
    <h2 className="sm-home-heading">Choose how you use supermemory</h2>
    <p className="sm-home-sub">Start with the path that fits your app and your team.</p>
    <div className="sm-home-grid sm-home-grid-3">
      <PathCard title="Developer platform" description="API reference, concepts, and SDKs" href="/overview/what-is-supermemory" image="/images/intro-developer-platform.jpg">
        <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>
      </PathCard>
      <PathCard title="Plugins and MCP" description="Drop memory into Claude and coding agents" href="/supermemory-mcp/mcp" image="/images/intro-plugins.jpg">
        <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>
      </PathCard>
      <PathCard title="Self-hosting" description="Run it locally with zero config" href="/self-hosting/overview" image="/images/intro-developer-platform.png">
        <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>
      </PathCard>
    </div>
  </section>

  <section className="sm-home-section">
    <p className="sm-home-label">Products</p>
    <h2 className="sm-home-heading">Explore features</h2>
    <p className="sm-home-sub">Browse memory, retrieval, profiles, and connectors.</p>
    <div className="sm-home-grid sm-home-grid-2">
      <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>
    </div>
  </section>

</div>

---

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

---
title: "Quickstart"
description: "Ingest a conversation and a document, retrieve them three ways, then wire it into a chat harness."
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](https://console.supermemory.ai) — **API Keys → Create API Key**.

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

<CodeGroup>
```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_..."
```
</CodeGroup>

## 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.

<CodeGroup>
```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"
```

```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"
  }'
```
</CodeGroup>

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

<Note>
**`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).
</Note>

## 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.

<CodeGroup>
```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);
```

```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"
  }'
```
</CodeGroup>

## 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.

<CodeGroup>
```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");
```

```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" }
```
</CodeGroup>

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**.

<CodeGroup>
```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);
}
```

```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
  }'
```
</CodeGroup>

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.

<CodeGroup>
```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));
```

```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
  }'
```
</CodeGroup>

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](/concepts/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.

<CodeGroup>
```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));
```

```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"
```
</CodeGroup>

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](/user-profiles/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](/concepts/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

<CodeGroup>
```
/* 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,
    )
```
</CodeGroup>

### Example: Vercel AI SDK

Same pattern, wrapped: `withSupermemory` injects context and can save the conversation for you. Details: [AI SDK integration](/integrations/ai-sdk).

```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:

```
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:

```
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

```
  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

<CardGroup cols={2}>
  <Card title="Add context" icon="/icons/hugeicons/plus-sign.svg" href="/ingestion/add-memories">
    Conversations, files, URLs, `id` updates, and status.
  </Card>
  <Card title="Search" icon="/icons/hugeicons/search-01.svg" href="/recall/search">
    Hybrid vs memories, filters, thresholds, rerank.
  </Card>
  <Card title="Graph memory" icon="/icons/hugeicons/route-02.svg" href="/concepts/graph-memory">
    How relations and entity chains are produced.
  </Card>
  <Card title="User profiles" icon="/icons/hugeicons/user.svg" href="/concepts/user-profiles">
    Static vs dynamic, and when to inject a profile every turn.
  </Card>
  <Card title="AI SDK" icon="/icons/hugeicons/triangle.svg" href="/integrations/ai-sdk">
    withSupermemory modes, id, addMemory.
  </Card>
  <Card title="Namespaces" icon="/icons/hugeicons/lock.svg" href="/concepts/container-tags">
    Isolation for multi-tenant products.
  </Card>
</CardGroup>

---

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

---
title: "API reference"
description: "Interactive reference for the Supermemory HTTP API: ingest, search, profiles, memories, connectors and settings."
icon: "/icons/hugeicons/plug-socket.svg"
---

<Warning>
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.
</Warning>

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](/quickstart) and [Using supermemory](/ingestion/add-memories).

## Base URL

```
https://api.supermemory.ai
```

Self-hosted: use your instance URL (for example `http://localhost:6767`). See [Self-hosting](/self-hosting/overview).

## Authentication

All endpoints use a Bearer API key. Create one in the [developer console](https://console.supermemory.ai).

```bash
Authorization: Bearer sm_...
```

Details: [API keys & auth](/authentication).

## 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](/quickstart).

## SDKs

Official clients wrap this API:

- TypeScript: `npm install supermemory`
- Python: `pip install supermemory`

See [Supermemory SDK](/integrations/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](https://api.supermemory.ai/v3/openapi)

---

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

---
title: "Connectors overview"
description: "Integrate Google Drive, Gmail, Notion, OneDrive, GitHub, Granola and Web Crawler to automatically sync documents into your knowledge base"
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

<CardGroup cols={2}>
  <Card title="Google Drive" icon="/images/google-drive-icon.svg" href="/connectors/google-drive">
    **Google Docs, Slides, Sheets**

    Real-time sync via webhooks. Supports shared drives, nested folders, and collaborative documents.
  </Card>

  <Card title="Gmail" icon="/icons/hugeicons/mail-01.svg" href="/connectors/gmail">
    **Email Threads**

    Real-time sync via Pub/Sub webhooks. Syncs threads with full conversation history and metadata.
  </Card>

  <Card title="Notion" icon="/images/notion-icon.svg" href="/connectors/notion">
    **Pages, Databases, Blocks**

    Instant sync of workspace content. Handles rich formatting, embeds, and database properties.
  </Card>

  <Card title="OneDrive" icon="/images/microsoft-icon.svg" href="/connectors/onedrive">
    **Word, Excel, PowerPoint**

    Scheduled sync every 4 hours. Supports personal and business accounts with file versioning.
  </Card>
  
  
  <Card title="GitHub" icon="/images/github-icon.svg" href="/connectors/github">
    **GitHub Repositories**

    Real-time incremental sync via webhooks. Supports documentation files in repositories.
  </Card>

  <Card title="Granola" icon="/images/granola.svg" href="/connectors/granola">
    **Meeting notes and transcripts**

    Syncs AI meeting notes, summaries, attendees, and transcripts from your Granola workspace.
  </Card>

  <Card title="Web Crawler" icon="/icons/hugeicons/globe-02.svg" href="/connectors/web-crawler">
    **Web Pages, Documentation**

    Crawl websites automatically with robots.txt compliance. Scheduled recrawling keeps content up to date.
  </Card>
</CardGroup>

## Quick start

### 1. Create a Connector

<CodeGroup>

```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"
#   }
# }
```

</CodeGroup>

### 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

<CodeGroup>

```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": "user@example.com",
#   "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, ...}}
```

</CodeGroup>

## 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

<CodeGroup>

```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": "user@company.com",
#       "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"
```

</CodeGroup>

### 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.

<CodeGroup>

```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"}
```

</CodeGroup>

### 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](/connectors/managing-resources).

<CodeGroup>

```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
```

</CodeGroup>

### 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. |

<Note>
Setting `deleteDocuments=false` is useful when you want to disconnect an integration without losing the memories that were already imported.
</Note>

<CodeGroup>

```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"
```

</CodeGroup>

<Note>
There is no delete-by-provider route in v5. Find the connector with `connectors.list(namespace)` and delete it by `id`.
</Note>

## 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:
   - Google: [console.developers.google.com/apis/credentials/oauthclient](https://console.developers.google.com/apis/credentials/oauthclient)
   - Notion: [notion.so/my-integrations](https://www.notion.so/my-integrations)
   - OneDrive: [Azure Portal → App registrations](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsMenu)
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`).

<Warning>
Enabling custom keys for a provider applies to all new connectors for that provider. Existing connectors will need to be re-authorized.
</Warning>

---

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

---
title: "MemoryBench"
description: "Open-source framework for benchmarking memory providers, including your own."
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`](https://github.com/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.

<Card title="git.new/membench" icon="/icons/hugeicons/github.svg" href="https://github.com/supermemoryai/memorybench">
  Clone it, run it against your own data, or read the source for exactly how each provider is scored.
</Card>

---

## 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](/memorybench/extend-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:

```
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.

<Columns cols={3}>
  <Card title="Supermemory" icon="/icons/hugeicons/brain-01.svg" href="https://supermemory.ai">
    Chunk-based semantic search
  </Card>
  <Card title="Mem0" icon="/icons/hugeicons/database-01.svg" href="https://mem0.ai">
    LLM-powered memory extraction
  </Card>
  <Card title="Zep" icon="/icons/hugeicons/flash.svg" href="https://getzep.com">
    Knowledge graph construction
  </Card>
</Columns>

| Benchmark | Tests | Source |
|---|---|---|
| **LoCoMo** | Fact recall across extended, multi-session conversations — single-hop, multi-hop, temporal, adversarial | [snap-research/locomo](https://github.com/snap-research/locomo) |
| **LongMemEval** | Long-term memory across sessions, including knowledge that gets updated mid-conversation | [xiaowu0162/longmemeval](https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned) |
| **ConvoMem** | Personalization, preference learning, and reference resolution within a conversation | [Salesforce/ConvoMem](https://huggingface.co/datasets/Salesforce/ConvoMem) |

None of these fit your use case? [Build your own benchmark](/memorybench/extend-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.

---

## Read next

<CardGroup cols={2}>
  <Card title="Building a benchmark" icon="/icons/hugeicons/test-tube-01.svg" href="/memorybench/extend-benchmark">
    Test on scenarios that actually match your product, not just the built-in datasets.
  </Card>
  <Card title="Adding a provider" icon="/icons/hugeicons/plug-01.svg" href="/memorybench/extend-provider">
    Register your own memory system so it can be scored and compared.
  </Card>
  <Card title="Measuring results" icon="/icons/hugeicons/dashboard-speed-01.svg" href="/memorybench/memscore">
    MemScore, judge scoring, and how to read a report.
  </Card>
  <Card title="Report an issue" icon="/icons/hugeicons/bug-01.svg" href="https://github.com/supermemoryai/memorybench/issues">
    Found a bug, or want a provider/benchmark we don't support yet?
  </Card>
</CardGroup>

---

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

---
title: "Supermemory local"
sidebarTitle: "Overview"
description: "State-of-the-art memory, running on your machine. One binary, zero config."
icon: "/icons/hugeicons/server-stack-01.svg"
---

Supermemory runs on your own hardware. It's the same memory engine behind the [hosted platform](https://console.supermemory.ai) — ingestion, memory extraction, hybrid semantic search, and the full API — as a single self-contained binary.

<CodeGroup>
```bash curl
curl -fsSL https://supermemory.ai/install | bash
```

```bash npx
npx supermemory local
```
</CodeGroup>

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](https://git.new/memory) 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](/self-hosting/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

<Note>
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.
</Note>

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](/quickstart) works the same way. The coding plugins do too — [Claude Code](/integrations/claude-code), [Muse Code](/integrations/muse-code), [Codex](/integrations/codex), and [OpenCode](/integrations/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](/connectors/overview) (Google Drive, Notion, Gmail, OneDrive) | — | ✅ |
| [Supermemory MCP](/supermemory-mcp/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](https://console.supermemory.ai) 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](/self-hosting/local-vs-enterprise).

## Next steps

<CardGroup cols={3}>
  <Card title="Quickstart" icon="/icons/hugeicons/play.svg" href="/self-hosting/quickstart">
    Install, run, and store your first memory in under two minutes
  </Card>
  <Card title="Configuration" icon="/icons/hugeicons/settings-01.svg" href="/self-hosting/configuration">
    Every environment variable: LLM providers, storage, auth, tuning
  </Card>
  <Card title="Embeddings" icon="/icons/hugeicons/route-02.svg" href="/self-hosting/embeddings">
    Local default, remote providers, multilingual, dimension lock
  </Card>
</CardGroup>

---

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

---
title: "Self-hosting quickstart"
sidebarTitle: "Quickstart"
description: "From zero to your first memory in under two minutes."
icon: "/icons/hugeicons/play.svg"
---

## Install

<Tabs>
<Tab title="curl">
```bash
curl -fsSL https://supermemory.ai/install | bash
```
</Tab>
<Tab title="npx">
```bash
npx supermemory local
```
</Tab>
<Tab title="bunx">
```bash
bunx supermemory local
```
</Tab>
</Tabs>

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
```

<Warning>
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.
</Warning>

Release tags are `server-v<version>` on [GitHub Releases](https://github.com/supermemoryai/supermemory/releases) (for example [`server-v0.0.3`](https://github.com/supermemoryai/supermemory/releases/tag/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:

```
╭───────────────────────────────────────────────────────────────╮
│  → 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.

<Note>
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).
</Note>

<Tip>
**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.
</Tip>

For the prebuilt Docker image, follow [Run Supermemory with Docker](/self-hosting/docker) for installation, authentication, backups, and upgrades.

## Add your first memory

<Note>
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.
</Note>

<Tabs>
<Tab title="TypeScript">
```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",
})
```
</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",
)
```
</Tab>
<Tab title="curl">
```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"
  }'
```
</Tab>
</Tabs>

## Search it

<Tabs>
<Tab title="TypeScript">
```typescript
const { results } = await client.search("user_dhravya", {
  query: "what food should I avoid?",
})
```
</Tab>
<Tab title="Python">
```python
results = client.search("user_dhravya", query="what food should I avoid?")
```
</Tab>
<Tab title="curl">
```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?"
  }'
```
</Tab>
</Tabs>

That's it. Everything in the [Memory API](/quickstart) — 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

<CardGroup cols={3}>
  <Card title="Configuration" icon="/icons/hugeicons/settings-01.svg" href="/self-hosting/configuration">
    LLM providers, local models, performance tuning
  </Card>
  <Card title="Embeddings" icon="/icons/hugeicons/route-02.svg" href="/self-hosting/embeddings">
    Local default, OpenAI / Gemini / Ollama, multilingual
  </Card>
  <Card title="Memory API" icon="/icons/hugeicons/book-open-01.svg" href="/quickstart">
    The full API — it all works against your local server
  </Card>
</CardGroup>

---

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

---
title: "SMFS"
sidebarTitle: "Overview"
description: "Memory your agent can grep."
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.

<CardGroup cols={2}>
  <Card title="Mount (smfs binary)" icon="/icons/hugeicons/hard-drive.svg" href="/smfs/install">
    For agents and tools with a real filesystem. Claude Code, Cursor, devcontainers, Docker, Codespaces. NFSv3 on macOS, FUSE on Linux.
  </Card>
  <Card title="Bash tool (TypeScript & Python)" icon="/icons/hugeicons/command-line.svg" href="/smfs/bash-tool">
    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.
  </Card>
</CardGroup>

## Use SMFS with your sandbox provider

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

<CardGroup cols={2}>
  <Card title="Daytona" icon="/icons/hugeicons/server-stack-01.svg" href="/smfs/providers/daytona">
    Isolated Linux sandboxes with millisecond boot times. Mount SMFS inside or use the bash tool from your orchestrating code.
  </Card>
  <Card title="E2B" icon="/icons/hugeicons/cube.svg" href="/smfs/providers/e2b">
    Firecracker microVMs for AI code execution. Install SMFS directly or use a custom template with it pre-installed.
  </Card>
  <Card title="Vercel AI SDK" icon="/icons/hugeicons/triangle.svg" href="/smfs/providers/vercel">
    The most popular TypeScript agent framework. Add memory as a tool with one function call.
  </Card>
  <Card title="Cloudflare Workers" icon="/icons/hugeicons/cloud.svg" href="/smfs/providers/cloudflare">
    Edge-first agents. Use the bash tool in Workers, or mount SMFS in Cloudflare Containers.
  </Card>
</CardGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Install SMFS" icon="/icons/hugeicons/download-01.svg" href="/smfs/install">
    One curl, one mount, you're done.
  </Card>
  <Card title="Use the Bash tool" icon="/icons/hugeicons/command-line.svg" href="/smfs/bash-tool">
    Drop SMFS into a TypeScript or Python agent without mounting anything.
  </Card>
  <Card title="Examples" icon="/icons/hugeicons/source-code.svg" href="/smfs/examples">
    Full working apps you can clone and run — legal docs, support agents, and more.
  </Card>
</CardGroup>

---

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

## Migrate by hand

<Warning>
  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.
</Warning>

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`](https://api.supermemory.ai/v5/reference) is the interactive v5 reference, not an API path prefix.

## Recommended migration process

<Steps>
  <Step title="Inventory every legacy call">
    Search for `/v3/`, `/v4/`, `containerTag`, `containerTags`, `customId`, `entityContext`, `filterByMetadata`, `filters`, and legacy SDK methods such as `client.search.execute`, `client.profile`, and `client.connections.*`.
  </Step>
  <Step title="Resolve one namespace per request">
    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.
  </Step>
  <Step title="Translate requests by domain">
    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.
  </Step>
  <Step title="Update response readers">
    Migrate envelopes, includes, pagination, profile buckets, system fields, and partial-error handling before switching traffic.
  </Step>
  <Step title="Verify legacy and v5 side by side">
    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.
  </Step>
  <Step title="Cut over one domain at a time">
    Switch traffic, monitor failures and semantic drift, then remove legacy compatibility code only after that domain passes verification.
  </Step>
</Steps>

## Upgrade the SDK

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

```bash
npm i supermemory
```

<CodeGroup>
```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" })
```

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

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

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` |

<CodeGroup>
```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"
})
```
</CodeGroup>

`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"
description: "Documents, search, profiles, lists, memories, namespaces, and organization settings in the latest Supermemory API"
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
```

<MigrateCli legacy="v3 or v4" guideHref="/migration/api-v5" guideLabel="v5 migration guide" />

Use the [v5 reference](https://api.supermemory.ai/v5/reference) for the stable v5 API. [`/reference`](https://api.supermemory.ai/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="...")`.

<CardGroup cols={2}>
  <Card title="Migrate to v5" icon="arrow-up-right" href="/migration/api-v5">
    Translate every v3/v4 request and response deterministically.
  </Card>
  <Card title="Authentication" icon="key" href="/authentication">
    Authenticate with the same bearer API keys used by legacy endpoints.
  </Card>
</CardGroup>

---

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

---
title: "Agents, skills and MCP"
description: "Set up coding agents to integrate Supermemory with the CLI, the skill and the docs 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](/supermemory-mcp/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](https://github.com/supermemoryai/skills).

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

## 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

<Tabs>
  <Tab title="Cursor">
    Add to `~/.cursor/mcp.json`:

    ```json
    {
      "mcpServers": {
        "supermemory-docs": {
          "url": "https://supermemory.ai/docs/mcp"
        }
      }
    }
    ```
  </Tab>

  <Tab title="Claude Code">
    ```bash
    claude mcp add --transport http supermemory-docs https://supermemory.ai/docs/mcp
    ```

    Or project `.mcp.json`:

    ```json
    {
      "mcpServers": {
        "supermemory-docs": {
          "type": "http",
          "url": "https://supermemory.ai/docs/mcp"
        }
      }
    }
    ```
  </Tab>

  <Tab title="Codex">
    ```bash
    codex mcp add supermemory-docs --url https://supermemory.ai/docs/mcp
    ```

    Or `~/.codex/config.toml`:

    ```toml
    [mcp_servers.supermemory-docs]
    url = "https://supermemory.ai/docs/mcp"
    ```
  </Tab>

  <Tab title="OpenCode">
    ```json
    {
      "mcp": {
        "supermemory-docs": {
          "type": "remote",
          "url": "https://supermemory.ai/docs/mcp",
          "enabled": true
        }
      }
    }
    ```
  </Tab>

  <Tab title="VS Code">
    Add to `.vscode/mcp.json`:

    ```json
    {
      "servers": {
        "supermemory-docs": {
          "type": "http",
          "url": "https://supermemory.ai/docs/mcp"
        }
      }
    }
    ```
  </Tab>

  <Tab title="Other">
    ```json
    {
      "mcpServers": {
        "supermemory-docs": {
          "url": "https://supermemory.ai/docs/mcp"
        }
      }
    }
    ```

    Stdio-only clients can proxy:

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

### 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:

<Accordion title="Copy full integration prompt" icon="/icons/hugeicons/copy-01.svg">
```
/* Detailed source-code truncated for AI context efficiency. */
````
</Accordion>

## 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](/supermemory-mcp/mcp)

## Next steps

<CardGroup cols={2}>
  <Card title="Quickstart" icon="/icons/hugeicons/play.svg" href="/quickstart">
    Conversation + document ingest, RAG, graph, profile, harness.
  </Card>
  <Card title="Memory MCP" icon="/icons/hugeicons/ai-brain-01.svg" href="/supermemory-mcp/mcp">
    Persistent memory for assistants — separate from docs setup.
  </Card>
  <Card title="Plugins" icon="/icons/hugeicons/puzzle.svg" href="/integrations/openclaw">
    Claude Code, OpenClaw, Codex, Hermes, and more.
  </Card>
  <Card title="AI SDK" icon="/icons/hugeicons/triangle.svg" href="/integrations/ai-sdk">
    withSupermemory and memory tools in app code.
  </Card>
</CardGroup>

---

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

---
title: "API keys & auth"
description: "Org API keys, namespace-scoped keys, and connector branding."
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](https://console.supermemory.ai).

<Snippet file="getting-api-key.mdx" />

Include your key in all requests:

<CodeGroup>

```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")
```

</CodeGroup>

---

## 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](/concepts/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.

<Warning>
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.
</Warning>

Pairs with [namespaces](/concepts/container-tags) 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"
description: "Invite a colleague to your Supermemory organization, choose their access, and share the invitation link."
contentType: "How-to"
icon: "/icons/hugeicons/user-multiple.svg"
---

Invite colleagues from the [Supermemory console](https://console.supermemory.ai) 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:

<Steps>
  <Step title="Open your organization’s Team settings">
    Open the [console](https://console.supermemory.ai) and select your organization from the top-left organization menu. Open **Settings** → **Team**.

    <Frame caption="Team settings lists members and pending invitations. This screenshot uses a local Acme demo organization.">
      <img src="/images/team/team-settings.png" alt="Acme Team settings with Invite member above Members and a Pending invitations table containing an invite link." />
    </Frame>
  </Step>
  <Step title="Enter your colleague’s email">
    Click **Invite member**. Enter the email address your colleague uses to sign in.
  </Step>
  <Step title="Choose their role and access">
    Select a role:

    - **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.">
      <img src="/images/team/invite-member.png" alt="Invite member dialog with jordan@example.com entered, Member and Full access selected, and the Send invite button enabled." width="512" />
    </Frame>
  </Step>
  <Step title="Create the invitation and share its link">
    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.

    The invitation expires after seven days. To cancel it, click the **×** at the end of its row.
  </Step>
</Steps>

## 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"
description: "The full loop: authenticate, put context in, get it back out, keep it correct."
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`.

<Journey>
  <JourneyStep number="1" title="Get started">
    <JourneyItem icon="/icons/hugeicons/play.svg" title="Quickstart" href="/quickstart" />
    <JourneyItem icon="/icons/hugeicons/key-01.svg" title="Get an API key" href="/authentication" />
    <JourneyItem icon="/icons/hugeicons/book-open-01.svg" title="API Reference" href="/api-reference/overview" />
  </JourneyStep>

  <JourneyStep number="2" title="Ingest — get content in">
    <JourneyItem icon="/icons/hugeicons/plus-sign.svg" title="Add memories & documents" href="/ingestion/add-memories" />
    <JourneyItem icon="/icons/hugeicons/clock-01.svg" title="Backfill historical data" href="/ingestion/batch-ingest-historical-data" />
    <JourneyItem icon="/icons/hugeicons/files-02.svg" title="Document operations" href="/ingestion/document-operations" />
    <JourneyItem icon="/icons/hugeicons/plug-01.svg" title="Connectors" href="/connectors/overview" />
    <JourneyItem icon="/icons/hugeicons/files-01.svg" title="Content types" href="/concepts/content-types" />
    <JourneyItem icon="/icons/hugeicons/command-line.svg" title="Ingest API reference" href="/api-reference/ingest" />
  </JourneyStep>

  <JourneyStep number="3" title="Retrieve — get context back out">
    <JourneyItem icon="/icons/hugeicons/search-01.svg" title="Search" href="/recall/search" />
    <JourneyItem icon="/icons/hugeicons/user.svg" title="User profiles" href="/recall/user-profiles" />
    <JourneyItem icon="/icons/hugeicons/database-01.svg" title="SMFS" href="/smfs/overview" />
    <JourneyItem icon="/icons/hugeicons/command-line.svg" title="Recall API reference" href="/api-reference/search" />
  </JourneyStep>

  <JourneyStep number="4" title="Manage">
    <JourneyItem icon="/icons/hugeicons/eraser-01.svg" title="CRUD & forgetting" href="/recall/memory-operations" />
    <JourneyItem icon="/icons/hugeicons/check-list.svg" title="Review inferred memories" href="/recall/memory-review" />
    <JourneyItem icon="/icons/hugeicons/settings-02.svg" title="Customizing behavior" href="/concepts/customization" />
    <JourneyItem icon="/icons/hugeicons/justice-scale-01.svg" title="Rules of thumb" href="/concepts/rules" />
    <JourneyItem icon="/icons/hugeicons/command-line.svg" title="Memories API reference" href="/api-reference/memories" />
    <JourneyItem icon="/icons/hugeicons/test-tube-01.svg" title="Benchmark it yourself" href="/memorybench/overview" />
  </JourneyStep>
</Journey>

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](/concepts/memory-vs-rag).

## Where next

<CardGroup cols={2}>
  <Card title="Quickstart" icon="/icons/hugeicons/play.svg" href="/quickstart">
    Walk the whole loop end to end with one working example.
  </Card>
  <Card title="How it works" icon="/icons/hugeicons/cpu.svg" href="/concepts/how-it-works">
    What happens between `add()` and a memory showing up in search.
  </Card>
  <Card title="Namespaces" icon="/icons/hugeicons/folder-01.svg" href="/concepts/container-tags">
    The isolation boundary every ingest and retrieve call is scoped to (v3/v4 called it a container tag).
  </Card>
  <Card title="Memory vs RAG" icon="/icons/hugeicons/balance-scale.svg" href="/concepts/memory-vs-rag">
    When to reach for search, profiles, or both.
  </Card>
  <Card title="Benchmarking" icon="/icons/hugeicons/test-tube-01.svg" href="/memorybench/overview">
    Run standardized, reproducible evals against Supermemory and other providers — including your own.
  </Card>
</CardGroup>

---

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

---
title: "Connections"
sidebarTitle: "Overview"
description: "Create, configure, sync and manage external connectors."
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](/connectors/overview) · provider pages under Connectors

---

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

---
title: "Container tags"
sidebarTitle: "Overview"
description: "Settings, merge and delete for multi-tenant containers."
icon: "/icons/hugeicons/book-open-01.svg"
---

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

`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](/concepts/container-tags) · [Filtering](/concepts/filtering)

---

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

---
title: "Documents"
sidebarTitle: "Overview"
description: "List, get status, update, delete, and inspect ingested documents."
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](/ingestion/document-operations)

---

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

---
title: "Ingest"
sidebarTitle: "Overview"
description: "Add documents, files, batches, and conversations to Supermemory."
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](/ingestion/add-memories) · [Quickstart](/quickstart)

<Tip>
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.
</Tip>

---

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

---
title: "Memories"
sidebarTitle: "Overview"
description: "Create, list, update, and forget extracted memory entries (v4)."
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](/api-reference/documents). For pipeline ingest, use [Ingest](/api-reference/ingest).

**Guide:** [Memory operations](/recall/memory-operations)

---

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

---
title: "Profiles"
sidebarTitle: "Profiles overview"
description: "Static and dynamic facts about an entity, scoped to a container."
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](/recall/user-profiles) · [Concepts](/concepts/user-profiles) · [Buckets](/user-profiles/buckets)

---

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

---
title: "Recall"
sidebarTitle: "Overview"
description: "Semantic search over memories, document chunks or both, plus user profiles."
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](/recall/search) · [User profiles](/recall/user-profiles) · [SuperRAG](/concepts/super-rag) · [Memory vs RAG](/concepts/memory-vs-rag)

---

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

---
title: "Settings"
sidebarTitle: "Overview"
description: "Organization settings, profile buckets, and data reset."
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](/concepts/customization)

---

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

---
title: "Namespaces"
sidebarTitle: "Namespaces"
description: "Namespaces, formerly container tags, are the isolation boundary that partitions memories by user, project, or any logical scope"
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.

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

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.

<CardGroup cols={2}>
  <Card title="Group" icon="/icons/hugeicons/layers-01.svg">
    Bucket memories by user, project, agent, workspace, or any boundary that makes sense for your app.
  </Card>
  <Card title="Isolate" icon="/icons/hugeicons/shield-01.svg">
    Each namespace maps to its own vector index, so search and retrieval never leak across boundaries.
  </Card>
</CardGroup>

---

## 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.

<CodeGroup>

```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?" }'
```

</CodeGroup>

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.

<Note>
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.
</Note>

---

## 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. |

<Warning>
A request can only touch one namespace. To read across several namespaces, make one request per namespace and merge the results in your application.
</Warning>

---

## 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](/overview/security).

<CodeGroup>

```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"
```

</CodeGroup>

---

## 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 |

<Tip>
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.
</Tip>

---

## Next steps

<CardGroup cols={2}>
  <Card title="Organizing & Filtering" icon="/icons/hugeicons/filter.svg" href="/concepts/filtering">
    Combine namespaces with metadata filters for precise retrieval.
  </Card>
  <Card title="Scoped API keys" icon="/icons/hugeicons/key-01.svg" href="/authentication#scoped-api-keys">
    Mint keys that can only touch one namespace — multi-tenant clients without the org master key.
  </Card>
  <Card title="Adding Memories" icon="/icons/hugeicons/plus-sign.svg" href="/ingestion/add-memories">
    See namespaces in action across the add API.
  </Card>
</CardGroup>

---

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

---
title: "Supported content types"
sidebarTitle: "Multi-modal ingestion"
description: "All the content formats Supermemory can ingest and process"
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](/ingestion/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](/overview/billing#feature-availability) 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](/connectors/google-drive):
- 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](/concepts/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.

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

---

## Next steps

<CardGroup cols={2}>
  <Card title="Add memories" icon="/icons/hugeicons/plus-sign.svg" href="/ingestion/add-memories">
    Upload content via the API
  </Card>
  <Card title="Super RAG" icon="/icons/hugeicons/flash.svg" href="/concepts/super-rag">
    How content is chunked and indexed
  </Card>
</CardGroup>

---

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

---
title: "Customizing for your use case"
sidebarTitle: "Customizing"
description: "Configure Supermemory's behavior for your specific application"
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`,
});
```

<AccordionGroup>
  <Accordion title="Personal assistant">
    ```typescript
    organizationalContext: `Personal AI assistant. Prioritize recent content, action items,
    and personal context. Exclude spam and duplicates.`
    ```
  </Accordion>
  <Accordion title="Customer support">
    ```typescript
    organizationalContext: `Customer support agent. Prioritize verified solutions, official docs,
    and resolved tickets. Exclude internal discussions and PII.`
    ```
  </Accordion>
  <Accordion title="Legal assistant">
    ```typescript
    organizationalContext: `Legal research assistant. Prioritize precedents, current regulations,
    and approved contract language. Exclude privileged communications.`
    ```
  </Accordion>
  <Accordion title="Finance agent">
    ```typescript
    organizationalContext: `Financial analysis assistant. Prioritize latest reports, verified data,
    and regulatory filings. Exclude speculative data and MNPI.`
    ```
  </Accordion>
  <Accordion title="Healthcare">
    ```typescript
    organizationalContext: `Healthcare information assistant. Prioritize evidence-based guidelines
    and FDA-approved info. Exclude PHI and outdated recommendations.`
    ```
  </Accordion>
  <Accordion title="Developer docs">
    ```typescript
    organizationalContext: `Developer documentation assistant. Prioritize current APIs, working
    examples, and best practices. Exclude deprecated APIs and test fixtures.`
    ```
  </Accordion>
</AccordionGroup>

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 john@acme.com and Brand.ai assistant.
    Focus on John's design preferences and brand requirements.`,
});
```

<Accordion title="Update supporting context only">
  Set the supporting context for a namespace without uploading content.

  ```typescript
  await supermemory.namespaces.update("session_abc123", {
    supportingContext: `Design exploration conversation between john@acme.com and Brand.ai assistant.
      Focus on John's design preferences and brand requirements.`,
  });
  ```
</Accordion>

<Note>
Supporting context persists on the namespace and combines with the org-level organizational context.
</Note>

---

<Note>
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.
</Note>

## 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 |

<Note>
Smaller chunks generate more memories per document. Larger chunks provide more context but may reduce precision.
</Note>

---

## Connector branding

Show "Log in to **YourApp**" instead of "Log in to Supermemory" when users connect external services. See [Connectors Overview](/connectors/overview) for the full list of supported integrations.

<AccordionGroup>
  <Accordion title="Google Drive">
    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`

    ```typescript
    await client.settings.update({
      googleDriveCustomKeyEnabled: true,
      googleDriveClientId: "your-client-id.apps.googleusercontent.com",
      googleDriveClientSecret: "your-client-secret"
    });
    ```
  </Accordion>
  <Accordion title="Notion">
    1. Create integration at [Notion Developers](https://developers.notion.com/)
    2. Redirect URI: `https://api.supermemory.ai/v3/connections/auth/callback/notion`

    ```typescript
    await client.settings.update({
      notionCustomKeyEnabled: true,
      notionClientId: "your-notion-client-id",
      notionClientSecret: "your-notion-client-secret"
    });
    ```
  </Accordion>
  <Accordion title="OneDrive">
    1. Register app in [Azure Portal](https://portal.azure.com/)
    2. Redirect URI: `https://api.supermemory.ai/v3/connections/auth/callback/onedrive`

    ```typescript
    await client.settings.update({
      onedriveCustomKeyEnabled: true,
      onedriveClientId: "your-azure-app-id",
      onedriveClientSecret: "your-azure-client-secret"
    });
    ```
  </Accordion>
</AccordionGroup>

---

## 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: "...",
});
```

<Note>
Organization settings are organization-wide and require an organization administrator. Changes apply to new content only—existing memories aren't reprocessed.
</Note>

---

## Next steps

<CardGroup cols={2}>
  <Card title="Add memories" icon="/icons/hugeicons/plus-sign.svg" href="/ingestion/add-memories">
    See your custom settings in action
  </Card>
  <Card title="Connectors" icon="/icons/hugeicons/plug-01.svg" href="/connectors/overview">
    Set up automatic syncing from external platforms
  </Card>
</CardGroup>

---

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

---
title: "Organizing & filtering memories"
sidebarTitle: "Metadata filtering"
description: "Use namespaces and metadata to organize and retrieve memories"
icon: "/icons/hugeicons/filter.svg"
---

Supermemory provides two ways to organize your memories:

<CardGroup cols={2}>
  <Card title="Namespaces" icon="/icons/hugeicons/folder-01.svg">
    **Organize memories** into isolated spaces by user, project, or workspace
  </Card>
  <Card title="Metadata filtering" icon="/icons/hugeicons/database-01.svg">
    **Query memories** by custom properties like category, status, or date
  </Card>
</CardGroup>

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",
});
```

<Note>
Each search is scoped to a single namespace. Passing `"user_123"` as the namespace restricts results to memories stored in that namespace.
</Note>

### Recommended patterns

| 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 |

<AccordionGroup>
  <Accordion title="More Namespace Examples">
    ```typescript
    // Multi-tenant SaaS - isolate by organization and user
    await supermemory.add("org_acme_user_john", {
      content: "Company policy document",
    });

    // 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.
  </Accordion>
</AccordionGroup>

---

## 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",
});
```

<AccordionGroup>
  <Accordion title="More Metadata Filter Examples">
    **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",
    });
    ```

    **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: "alice@company.com" },
      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",
    });
    ```
  </Accordion>

  <Accordion title="Real-world patterns">
    **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",
    });
    ```

    **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",
    });
    ```
  </Accordion>
</AccordionGroup>

### Filters Beyond Search

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

<Note>
If you need more conditions than these limits allow, break your query into multiple requests or use broader search terms with post-processing.
</Note>

### 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

<CardGroup cols={2}>
  <Card title="Search" icon="/icons/hugeicons/search-01.svg" href="/recall/search">
    Apply filters in search queries
  </Card>
  <Card title="Add Memories" icon="/icons/hugeicons/plus-sign.svg" href="/ingestion/add-memories">
    Add content with namespaces and metadata
  </Card>
</CardGroup>

---

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

---
title: "Graph memory"
sidebarTitle: "Graph memory"
description: "How facts connect, update and stay true: relationships, temporal truth and automatic forgetting."
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](/concepts/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](https://console.supermemory.ai) — **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](/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.

<Note>
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.
</Note>

## 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](/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](/recall/search).

## 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](/ingestion/add-memories); 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](/concepts/how-it-works#dreaming-how-memories-enter-the-graph) and [Processing modes](/ingestion/add-memories#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](/recall/memory-operations) and [Memory review](/recall/memory-review).

## What you don’t do

You do **not** hand-maintain the graph. You:

1. Ingest under a [namespace](/concepts/container-tags)
2. Wait for the [pipeline](/concepts/how-it-works) when needed
3. [Search](/recall/search) or load a [profile](/recall/user-profiles)

```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
```

## Related in the docs

| If you need… | Go to |
| --- | --- |
| Pipeline statuses, dreaming, documents in | [How it works](/concepts/how-it-works) |
| Memory vs document retrieval | [Memory vs RAG](/concepts/memory-vs-rag) · [SuperRAG](/concepts/super-rag) |
| Always-on summary of a user | [Profiles](/concepts/user-profiles) |
| Isolation / tenants | [Multi-tenancy](/concepts/container-tags) |
| API: add / search / forget | [Ingestion](/ingestion/add-memories) · [Search](/recall/search) · [Forget & update](/recall/memory-operations) |

<CardGroup cols={2}>
  <Card title="How it works" icon="/icons/hugeicons/cpu.svg" href="/concepts/how-it-works">
    Ingest pipeline, statuses, and outputs.
  </Card>
  <Card title="Memory vs RAG" icon="/icons/hugeicons/balance-scale.svg" href="/concepts/memory-vs-rag">
    When to use memory vs document retrieval.
  </Card>
  <Card title="Profiles" icon="/icons/hugeicons/user.svg" href="/concepts/user-profiles">
    Static + dynamic context built from the graph.
  </Card>
  <Card title="Quickstart" icon="/icons/hugeicons/play.svg" href="/quickstart">
    See entity chains in a full conversation + document flow.
  </Card>
</CardGroup>

---

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

---
title: "How Supermemory works"
sidebarTitle: "How it works"
description: "How a file or chat turn becomes something you can search: the ingest pipeline, statuses and outputs."
icon: "/icons/hugeicons/cpu.svg"
---

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

<CardGroup cols={2}>
  <Card title="Learning model">
    Decides what and how to learn, what is important, when to forget, creating relations, etc.
  </Card>
  <Card title="Temporal vector-graph engine">
    Where the learnings are actually stored, optimized for search. Fact-based temporal graph that has Vector, FTS, and graph built in.
  </Card>
</CardGroup>

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

<CardGroup cols={2}>
  <Card title="1. Get an API key" icon="/icons/hugeicons/key-01.svg" href="https://console.supermemory.ai">
    From the [developer console](https://console.supermemory.ai) — **API Keys → Create API Key**. `console.supermemory.ai` is where keys and usage live.
  </Card>
  <Card title="2. Use it" icon="/icons/hugeicons/command-line.svg" href="/using-supermemory">
    Install the SDK, drop in your key, add a memory, and search it — right below, or the full [ingest → retrieve loop](/using-supermemory).
  </Card>
</CardGroup>

<CodeGroup>
```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?",
});
```

```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?" }'
```
</CodeGroup>

## 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](/concepts/content-types) for formats, and [Add context](/ingestion/add-memories) 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](/overview/billing#full-discount-on-already-seen-tokens-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](/ingestion/add-memories) (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](/overview/billing) 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](/quickstart).

How those memories connect and stay true over time is [Graph memory](/concepts/graph-memory). API detail: [Processing modes](/ingestion/add-memories#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](/concepts/super-rag), [Search API](/recall/search) |
| **Memories** | Extracted facts in a living graph — updates, links, time | [Graph memory](/concepts/graph-memory) |
| **Profile** | A sample of memories, static + dynamic summary for always-on context | [Profiles](/concepts/user-profiles), [Profile API](/recall/user-profiles) |

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](/concepts/memory-vs-rag).

## Isolation and identity

- **`namespace`** — hard isolation boundary (user, tenant, project), carried in the URL. See [Namespaces](/concepts/container-tags).
- **Metadata** — soft dimensions *inside* a namespace for filtering. See [Metadata filtering](/concepts/filtering).
- **Scoped API keys** — credentials that cannot cross a namespace. See [API keys](/authentication#scoped-api-keys).

## Next steps

<CardGroup cols={2}>
  <Card title="Graph memory" icon="/icons/hugeicons/share-08.svg" href="/concepts/graph-memory">
    How facts connect, update, and stay true over time.
  </Card>
  <Card title="Multi-modal ingestion" icon="/icons/hugeicons/files-01.svg" href="/concepts/content-types">
    Formats, extractors, and what you can send.
  </Card>
  <Card title="Add context" icon="/icons/hugeicons/plus-sign.svg" href="/ingestion/add-memories">
    API: add, id, files, dreaming, status.
  </Card>
  <Card title="Search API" icon="/icons/hugeicons/search-01.svg" href="/recall/search">
    Query documents and memories after the pipeline finishes.
  </Card>
</CardGroup>

---

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

---
title: "Memory vs RAG: Understanding the difference"
description: "Learn why agent memory and RAG are fundamentally different, and when to use each approach"
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:

<Tabs>
  <Tab title="The scenario">
    ```
    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?"
    ```
  </Tab>

  <Tab title="RAG approach (wrong)">
    ```python
    # RAG sees these as isolated embeddings
    query = "What sneakers should I buy?"

    # 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.
  </Tab>

  <Tab title="Memory approach (right)">
    ```python
    # Supermemory understands temporal context
    query = "What sneakers should I buy?"

    # 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.
  </Tab>
</Tabs>

## The technical difference

### RAG: Semantic similarity
```
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
```
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

<CardGroup cols={2}>
  <Card title="Use RAG For" icon="/icons/hugeicons/search-01.svg">
    - Static documentation
    - Knowledge bases
    - Research queries
    - General Q&A
    - Content that doesn't change per user
  </Card>

  <Card title="Use memory for" icon="/icons/hugeicons/brain-01.svg">
    - User preferences
    - Conversation history
    - Personal facts
    - Behavioral patterns
    - Anything that evolves over time
  </Card>
</CardGroup>

## Real-world examples

### E-commerce assistant

<Tabs>
  <Tab title="RAG component">
    Stores product catalogs, specifications, reviews

    ```python
    # Good for RAG
    "What are the specs of iPhone 15?"
    "Compare Nike and Adidas running shoes"
    "Show me waterproof jackets"
    ```
  </Tab>

  <Tab title="Memory component">
    Tracks user preferences, purchase history, interactions

    ```python
    # Needs Memory
    "What size do I usually wear?"
    "Did I like my last purchase?"
    "What's my budget preference?"
    ```
  </Tab>
</Tabs>

### Customer support bot

<Tabs>
  <Tab title="RAG component">
    FAQ documents, troubleshooting guides, policies

    ```python
    # Good for RAG
    "How do I reset my password?"
    "What's your return policy?"
    "Troubleshooting WiFi issues"
    ```
  </Tab>

  <Tab title="Memory component">
    Previous issues, user account details, conversation context

    ```python
    # Needs Memory
    "Is my issue from last week resolved?"
    "What plan am I on?"
    "You were helping me with..."
    ```
  </Tab>
</Tabs>

## 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

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

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

<CardGroup cols={2}>
  <Card title="Graph memory" icon="/icons/hugeicons/hierarchy-square-01.svg" href="/concepts/graph-memory">
    How memory relationships work
  </Card>
  <Card title="Super RAG" icon="/icons/hugeicons/flash.svg" href="/concepts/super-rag">
    Our managed RAG solution
  </Card>
  <Card title="Add memories" icon="/icons/hugeicons/plus-sign.svg" href="/ingestion/add-memories">
    Start ingesting content
  </Card>
  <Card title="Search" icon="/icons/hugeicons/search-01.svg" href="/recall/search">
    Query your memories and documents
  </Card>
</CardGroup>

---

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

---
title: "Multi-tenancy examples"
sidebarTitle: "Examples"
description: "Common namespace and metadata patterns for personal agents, company agents, email assistants, and support platforms"
icon: "/icons/hugeicons/check-list.svg"
---

A few common shapes multi-tenancy takes in practice, combining [namespaces](/concepts/container-tags) for isolation with [metadata filters](/concepts/filtering) 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: "no-reply@spotify.com",
    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

<CardGroup cols={2}>
  <Card title="Multi-tenancy Overview" icon="/icons/hugeicons/user-multiple.svg" href="/concepts/multi-tenancy">
    Why namespaces and metadata are separate mechanisms.
  </Card>
  <Card title="Namespaces" icon="/icons/hugeicons/folder-01.svg" href="/concepts/container-tags">
    How isolation works, naming rules, and access control.
  </Card>
  <Card title="Organizing & Filtering" icon="/icons/hugeicons/filter.svg" href="/concepts/filtering">
    Metadata filter operators, combining `and`/`or`, and query limits.
  </Card>
</CardGroup>

---

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

---
title: "Multi-tenancy overview"
sidebarTitle: "Overview"
description: "How Supermemory isolates and organizes memories across users, tenants, and projects"
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:

<CardGroup cols={2}>
  <Card title="Namespaces" icon="/icons/hugeicons/folder-01.svg" href="/concepts/container-tags">
    **Isolation.** A namespace is a hard boundary. Memories in one namespace are never returned by a search scoped to another namespace.
  </Card>
  <Card title="Metadata filtering" icon="/icons/hugeicons/database-01.svg" href="/concepts/filtering">
    **Organization.** Metadata is a set of custom key/value properties on a memory that you filter by — category, priority, date, participants, anything you define.
  </Card>
</CardGroup>

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",
});
```

<Note>
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.
</Note>

---

## 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](/concepts/container-tags#access-control) for the details.

---

## Next steps

<CardGroup cols={2}>
  <Card title="Examples" icon="/icons/hugeicons/check-list.svg" href="/concepts/multi-tenancy-examples">
    Personal agents, company agents, email assistants, and support platforms.
  </Card>
  <Card title="Namespaces" icon="/icons/hugeicons/folder-01.svg" href="/concepts/container-tags">
    How isolation works, naming rules, and access control.
  </Card>
  <Card title="Organizing & Filtering" icon="/icons/hugeicons/filter.svg" href="/concepts/filtering">
    Metadata filter operators, combining `and`/`or`, and query limits.
  </Card>
  <Card title="Scoped API keys" icon="/icons/hugeicons/key-01.svg" href="/authentication#scoped-api-keys">
    Mint keys that can only touch one namespace.
  </Card>
</CardGroup>

---

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

---
title: "Rules of supermemory"
description: "Best practices and things to consider when using supermemory in your system"
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](/recall/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](/ingestion/add-memories); the pipeline derives memories from them and maintains a profile per [namespace](/concepts/container-tags). `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.

#### Ingest with SuperRag when you just need search

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

<CodeGroup>

```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" }'
```

</CodeGroup>

#### 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.

<CodeGroup>

```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" }'
```

</CodeGroup>

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`.

```
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.

<CodeGroup>

```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"
  }'
```

</CodeGroup>

#### 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:

<CodeGroup>

```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" }
  }'
```

</CodeGroup>

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

<CodeGroup>

```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" }
  }'
```

</CodeGroup>

## 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](https://console.supermemory.ai/playground), and read more in [this post on memory at the harness level](https://dhravya.dev/writing/memory-on-the-harness-level/).

---

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

---
title: "SuperRAG (managed RAG as a service)"
sidebarTitle: "SuperRAG"
description: "Supermemory provides a managed RAG solution - extraction, indexing, storing, and retrieval."
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.

<CodeGroup>

```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" }'
```

</CodeGroup>

| | `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** |

<Tip>
`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.
</Tip>

<Warning>
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"`.
</Warning>

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](/concepts/rules#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.

```
├── 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](/concepts/content-types) for the full list of supported formats.

---

## Hybrid memory + RAG

Supermemory combines the best of both approaches in every search:

<CardGroup cols={2}>
  <Card title="Traditional RAG" icon="/icons/hugeicons/search-01.svg">
    - Finds similar document chunks
    - Great for knowledge retrieval
    - Stateless — same results for everyone
  </Card>

  <Card title="Memory system" icon="/icons/hugeicons/brain-01.svg">
    - Extracts and tracks user facts
    - Understands temporal context
    - Personalizes results per user
  </Card>
</CardGroup>

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

<CardGroup cols={2}>
  <Card title="Content types" icon="/icons/hugeicons/files-01.svg" href="/concepts/content-types">
    All supported formats and how they're processed
  </Card>
  <Card title="How it works" icon="/icons/hugeicons/cpu.svg" href="/concepts/how-it-works">
    The full processing pipeline
  </Card>
  <Card title="Memory vs RAG" icon="/icons/hugeicons/balance-scale.svg" href="/concepts/memory-vs-rag">
    When to use each approach
  </Card>
  <Card title="Search" icon="/icons/hugeicons/search-01.svg" href="/recall/search">
    Search parameters and optimization
  </Card>
  <Card title="Billing" icon="/icons/hugeicons/invoice-01.svg" href="/overview/billing#memory-vs-superrag-tokens">
    Exact meter rates for memory vs SuperRAG tokens
  </Card>
  <Card title="Adding memories" icon="/icons/hugeicons/plus-sign.svg" href="/ingestion/add-memories">
    `taskType` and other ingestion parameters
  </Card>
</CardGroup>

---

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

---
title: "User profiles"
sidebarTitle: "Profiles"
description: "Automatically maintained context about your users"
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.

<CardGroup cols={2}>
  <Card title="Instant context" icon="/icons/hugeicons/flash.svg">
    No search needed — comprehensive user info always ready
  </Card>
  <Card title="Auto-Updated" icon="/icons/hugeicons/rotate-clockwise.svg">
    Profiles update as users interact with your system
  </Card>
</CardGroup>

## 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`](/concepts/customization#organizational-context) (called `filterPrompt` in v3/v4), which steers what gets extracted at all — buckets only organize facts that already made it into the profile.

<Card title="Profile Buckets reference" icon="/icons/hugeicons/tag-01.svg" href="/user-profiles/buckets">
  Request bucketed profiles, create buckets at the org or namespace level, and see validation limits.
</Card>

---

## How it works

Profiles are built automatically through ingestion:

1. **Ingest content** — Users [add documents](/ingestion/add-memories), 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

<Note>
You don't manually manage profiles — they build themselves as users interact. Start by [adding content](/ingestion/add-memories) to see profiles in action.
</Note>

---

## Profiles + search

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](/concepts/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](/recall/user-profiles#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

<CardGroup cols={2}>
  <Card title="User profiles API" icon="/icons/hugeicons/source-code.svg" href="/recall/user-profiles">
    Fetch and use profiles via the API
  </Card>
  <Card title="Profile buckets" icon="/icons/hugeicons/tag-01.svg" href="/user-profiles/buckets">
    Create and configure topical buckets
  </Card>
  <Card title="Graph memory" icon="/icons/hugeicons/hierarchy-square-01.svg" href="/concepts/graph-memory">
    How the underlying knowledge graph works
  </Card>
  <Card title="AI SDK integration" icon="/icons/hugeicons/triangle.svg" href="/integrations/ai-sdk">
    Automatic profile injection with AI SDK
  </Card>
  <Card title="Add memories" icon="/icons/hugeicons/plus-sign.svg" href="/ingestion/add-memories">
    Build profiles by adding content
  </Card>
</CardGroup>

---

### Apps Docs Connectors Github (`apps/docs/connectors/github.mdx`)

---
title: "GitHub connector"
sidebarTitle: "GitHub"
description: "Connect GitHub repositories to sync documentation files into your Supermemory knowledge base"
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.

<Warning>
The GitHub connector requires a **Scale Plan** or **Enterprise Plan**.
</Warning>

## Quick setup

### 1. Create GitHub Connector

<Tabs>
  <Tab title="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: "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)
    ```
  </Tab>
  <Tab title="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": "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}")
    ```
  </Tab>
  <Tab title="cURL">
    ```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
      }'

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

<Note>
**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
</Note>

### 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.

<Note>
See [Managing Connector Selection](/connectors/managing-resources) for the generic selection API that GitHub, Gmail and Google Drive share.
</Note>

<Tabs>
  <Tab title="TypeScript">
    ```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"
    })

    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")
    ```
  </Tab>
  <Tab title="Python">
    ```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",
    )

    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")
    ```
  </Tab>
  <Tab title="cURL">
    ```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"

    # 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"]
        }
      }'
    ```
  </Tab>
</Tabs>

<Note>
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.
</Note>

## 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.

<Note>
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.
</Note>

## 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.

<Note>
**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.
</Note>

### 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.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // Check sync status and selection
    const connector = await supermemory.connectors.get("user-123", connectorId, {
      include: "syncs",
    })

    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)
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # Check sync status and selection
    connector = client.connectors.get("user-123", connector_id, include=["syncs"])

    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}")
    ```
  </Tab>
  <Tab title="cURL">
    ```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"
    ```
  </Tab>
</Tabs>

## Connector Management

### List All Connectors

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // List GitHub connectors in a namespace
    const { connectors } = await supermemory.connectors.list("user-123")

    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("---")
      })
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # List GitHub connectors in a namespace
    connectors = client.connectors.list("user-123", provider="github").connectors

    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("---")
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    # List connectors in a namespace
    curl "https://api.supermemory.ai/ns/user-123/connectors" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
    ```
  </Tab>
</Tabs>

### Update Repository Selection

You can update which repositories are synced at any time. The new selection replaces the old one and starts a sync.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // Add or remove repositories
    await supermemory.connectors.update("user-123", connectorId, {
      selection: {
        repos: ["123456789", "987654321"],
      },
    })

    console.log("Repository selection updated")
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # Add or remove repositories
    client.connectors.update(
        "user-123",
        connector_id,
        selection={"repos": ["123456789", "987654321"]},
    )

    print("Repository selection updated")
    ```
  </Tab>
  <Tab title="cURL">
    ```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"]
        }
      }'
    ```
  </Tab>
</Tabs>

<Note>
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
</Note>

### Delete Connector

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // Delete the connector and its imported documents (default)
    await supermemory.connectors.delete("user-123", connectorId)

    // Delete the connector but keep the imported documents
    await supermemory.connectors.delete("user-123", connectorId, {
      deleteDocuments: false,
    })
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # Delete the connector and its imported documents (default)
    client.connectors.delete("user-123", connector_id)

    # Delete the connector but keep the imported documents
    client.connectors.delete("user-123", connector_id, delete_documents=False)
    ```
  </Tab>
  <Tab title="cURL">
    ```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"

    # 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"
    ```
  </Tab>
</Tabs>

<Warning>
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)
</Warning>

### Manual sync

Trigger a manual synchronization for all selected repositories. The call returns `409` while a sync for that connector is already running.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    const run = await supermemory.connectors.sync("user-123", connectorId)

    console.log(run.status)
    // Output: queued
    ```
  </Tab>
  <Tab title="Python">
    ```python
    run = client.connectors.sync("user-123", connector_id)

    print(run.status)
    # Output: queued
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}/sync" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

    # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
    ```
  </Tab>
</Tabs>

## 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](/connectors/overview#custom-oauth-applications).

<Note>
**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.
</Note>

---

### Apps Docs Connectors Gmail (`apps/docs/connectors/gmail.mdx`)

---
title: "Gmail connector"
sidebarTitle: "Gmail"
description: "Sync email threads from Gmail with real-time Pub/Sub webhooks and incremental sync"
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.

<Note>
**Max Plan Required:** The Gmail connector is available on Max plan and above.
</Note>

## Quick setup

### 1. Create Gmail Connector

<Tabs>
  <Tab title="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: "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)
    ```
  </Tab>
  <Tab title="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": "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}")
    ```
  </Tab>
  <Tab title="cURL">
    ```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
      }'

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

### 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](/connectors/managing-resources).

### 3. Check Connector Status

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // Get connector details with recent sync runs
    const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
      include: "syncs",
    })

    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`)
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # Get connector details with recent sync runs
    connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])

    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")
    ```
  </Tab>
  <Tab title="cURL">
    ```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"

    # 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 '{}'
    ```
  </Tab>
</Tabs>

<Note>
There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.
</Note>

## 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 <team@company.com>`) |
| `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: "team@company.com" },
    ],
  },
  searchMode: "chunks",
})
```

## Connector Management

### List All Connectors

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // List all connectors in a namespace
    const { connectors } = await supermemory.connectors.list("user-123")

    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("---")
    })
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # List all connectors in a namespace
    connectors = client.connectors.list("user-123").connectors

    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("---")
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    # List all connectors in a namespace
    curl "https://api.supermemory.ai/ns/user-123/connectors" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
    ```
  </Tab>
</Tabs>

### Delete Connector

<Tabs>
  <Tab title="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,
    })
    ```
  </Tab>
  <Tab title="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)
    ```
  </Tab>
  <Tab title="cURL">
    ```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"

    # 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"
    ```
  </Tab>
</Tabs>

<Note>
Deleting a connector will:
- Stop all future syncs from Gmail
- Remove the OAuth authorization
- Delete the synced documents unless you pass `deleteDocuments: false`
</Note>

### Manual sync

Trigger a manual synchronization. The call returns `409` while a sync for that connector is already running.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")

    console.log(run.status)
    // Output: queued
    ```
  </Tab>
  <Tab title="Python">
    ```python
    run = client.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")

    print(run.status)
    # Output: queued
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

    # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
    ```
  </Tab>
</Tabs>

## 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

<Note>
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.
</Note>

## 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 |

<Warning>
**Read-only Access:** The Gmail connector only reads emails. It cannot send, delete, or modify any emails in the user's account.
</Warning>

## Limitations

<Warning>
**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
</Warning>

## 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"
description: "Connect Google Drive to sync documents into your Supermemory knowledge base"
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.

<Note>
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.
</Note>

## Quick setup

### 1. Create Google Drive Connector

<Tabs>
  <Tab title="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: "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)
    ```
  </Tab>
  <Tab title="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": "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}")
    ```
  </Tab>
  <Tab title="cURL">
    ```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" }
      }'

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

<Note>
For **whole Drive** sync, send `"config": { "syncScope": "full" }` on the same `POST /ns/{namespace}/connectors` request instead of `"selected"`.
</Note>

### 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.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
      include: "picker",
      returnUrl: "https://yourapp.com/settings/integrations"
    })

    if (connector.picker) window.location.href = connector.picker.url
    ```
  </Tab>
  <Tab title="cURL">
    ```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"

    # Response includes: "picker": { "url": "https://...", "expiresAt": "2024-01-15T11:30:00.000Z" }
    ```
  </Tab>
</Tabs>

If you already know the Drive file and folder ids, set them directly. A new selection replaces the old one and starts a sync.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    await supermemory.connectors.update("user-123", "PTzGiUYei7pgzg5buzZHgA", {
      selection: {
        files: ["1AbCdEfGhIjKlMnOpQrStUvWxYz"],
        folders: ["0BxYzAbCdEfGhIjKlMnOpQrStU"],
      },
    })
    ```
  </Tab>
  <Tab title="cURL">
    ```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"]
        }
      }'
    ```
  </Tab>
</Tabs>

### 3. Check Connector Status

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // Get connector details with recent sync runs
    const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
      include: "syncs",
    })

    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")
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # Get connector details with recent sync runs
    connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])

    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
    ```
  </Tab>
  <Tab title="cURL">
    ```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"

    # 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 '{}'
    ```
  </Tab>
</Tabs>

<Note>
There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.
</Note>

## 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.

<Note>
Drive documents are converted to markdown before ingestion. This conversion is lossy — some formatting may not be preserved.
</Note>

## Connector Management

### List All Connectors

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // List all connectors in a namespace
    const { connectors } = await supermemory.connectors.list("user-123")

    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("---")
    })
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # List all connectors in a namespace
    connectors = client.connectors.list("user-123").connectors

    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("---")
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    # List all connectors in a namespace
    curl "https://api.supermemory.ai/ns/user-123/connectors" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

    # Response example:
    # {
    #   "connectors": [
    #     {
    #       "id": "PTzGiUYei7pgzg5buzZHgA",
    #       "provider": "google-drive",
    #       "namespace": "user-123",
    #       "account": "user@example.com",
    #       "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 }
    # }
    ```
  </Tab>
</Tabs>

### Delete Connector

<Tabs>
  <Tab title="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,
    })
    ```
  </Tab>
  <Tab title="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)
    ```
  </Tab>
  <Tab title="cURL">
    ```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"

    # 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"
    ```
  </Tab>
</Tabs>

<Note>
Deleting a connector will:
- Stop all future syncs from Google Drive
- Remove the OAuth authorization
- Delete the synced documents unless you pass `deleteDocuments: false`
</Note>

### Manual sync

Trigger a manual synchronization. The call returns `409` while a sync for that connector is already running.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")

    console.log(run.status)
    // Output: queued
    ```
  </Tab>
  <Tab title="Python">
    ```python
    run = client.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")

    print(run.status)
    # Output: queued
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

    # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
    ```
  </Tab>
</Tabs>



## 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](/connectors/overview#custom-oauth-applications) for the setup steps and callback URL.

<Warning>
**Important Notes:**
- Document processing happens asynchronously
- Use one namespace per user or tenant
- Check `latestRun.system.status` and `latestRun.error` for failed syncs
</Warning>

---

### Apps Docs Connectors Granola (`apps/docs/connectors/granola.mdx`)

---
title: "Granola connector"
sidebarTitle: "Granola"
description: "Sync AI meeting notes and transcripts from Granola into your Supermemory knowledge base"
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.

<Note>
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**.
</Note>

## Quick setup

### From the console

1. Open the [Supermemory Console](https://console.supermemory.ai).
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.

<Note>
The console limits connector setup to 500 documents. Use the API setup below for higher `documentLimit` values, up to 10,000.
</Note>

### With the API

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    import { Supermemory } from "supermemory"

    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)
    ```
  </Tab>
  <Tab title="Python">
    ```python
    from supermemory import Supermemory
    import os

    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}")
    ```
  </Tab>
  <Tab title="cURL">
    ```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
      }'

    # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "authorization": null}
    ```
  </Tab>
</Tabs>

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) |

<Note>
The API key is stored encrypted and is never returned by `connectors.get` or `connectors.list`.
</Note>

## 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: "alex@company.com" },
    ],
  },
  searchMode: "chunks",
})
```

## Connector Management

### Check Sync Status

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    const connector = await supermemory.connectors.get("org-123", "PTzGiUYei7pgzg5buzZHgA", {
      include: "syncs",
    })

    console.log("Last sync:", connector.latestRun?.system.status, connector.latestRun?.error)
    console.log("Notes synced:", connector.documentCount)
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
    ```
  </Tab>
</Tabs>

### Delete Connector

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    await supermemory.connectors.delete("org-123", "PTzGiUYei7pgzg5buzZHgA")
    ```
  </Tab>
  <Tab title="Python">
    ```python
    client.connectors.delete("org-123", "PTzGiUYei7pgzg5buzZHgA")
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl -X DELETE "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
    ```
  </Tab>
</Tabs>

<Warning>
By default, deleting a connector removes all synced documents from Supermemory. To keep documents, pass `deleteDocuments: false` (`DELETE /ns/{namespace}/connectors/{id}?deleteDocuments=false`).
</Warning>

### Manual sync

The call returns `409` while a sync for that connector is already running.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    await supermemory.connectors.sync("org-123", "PTzGiUYei7pgzg5buzZHgA")
    ```
  </Tab>
  <Tab title="Python">
    ```python
    client.connectors.sync("org-123", "PTzGiUYei7pgzg5buzZHgA")
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl -X POST "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

    # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
    ```
  </Tab>
</Tabs>

## 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'
description: 'Choose what a connector syncs with the hosted picker or a direct selection update'
icon: 'folder-sync'
---

<Note>
**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`.
</Note>

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.

<CodeGroup>
```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)
```

```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"
```
</CodeGroup>

### 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"
  }
}
```

<Note>
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`.
</Note>

## 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.

<CodeGroup>
```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)
```

```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"]
    }
  }'
```
</CodeGroup>

### 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

<Note>
**Automatic Sync:** After a selection is saved, a sync starts for the connector. You don't need to call `connectors.sync` afterwards.
</Note>

## Example: GitHub repository selection

Here's a complete example for GitHub:

<CodeGroup>
```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 },
})
```

```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"]
    }
  }'
```
</CodeGroup>

---

### Apps Docs Connectors Notion (`apps/docs/connectors/notion.mdx`)

---
title: "Notion connector"
sidebarTitle: "Notion"
description: "Sync Notion pages, databases, and blocks with real-time webhooks and workspace integration"
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

<Tabs>
  <Tab title="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/auth/notion/callback",
      documentLimit: 2000,
    })

    // Send the user to Notion to authorize
    if (connector.authorization) window.location.href = connector.authorization.url
    ```
  </Tab>
  <Tab title="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/auth/notion/callback",
            "document_limit": 2000,
        },
    )

    # Send the user to Notion to authorize
    print(f"Redirect to: {connector.authorization.url}")
    ```
  </Tab>
  <Tab title="cURL">
    ```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
      }'

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

### 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

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // Check connector details and recent sync runs
    const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
      include: "syncs",
    })

    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")
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # Check connector details and recent sync runs
    connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])

    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
    ```
  </Tab>
  <Tab title="cURL">
    ```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"

    # Response includes connector details:
    # {
    #   "id": "PTzGiUYei7pgzg5buzZHgA",
    #   "provider": "notion",
    #   "namespace": "user-123",
    #   "account": "workspace@example.com",
    #   "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": {...}}
    ```
  </Tab>
</Tabs>

<Note>
There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.
</Note>

## 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

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")

    console.log(run.status)
    // Output: queued
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

    # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
    # 409 if a sync is already running
    ```
  </Tab>
</Tabs>

## 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 | `| Col 1 | Col 2 |\n|-------|-------|` |
| **Image** | Referenced with metadata | `` |
| **Embed** | Link with context | `[Embedded Content](url)` |

## Delete Connector

Remove a Notion connector when no longer needed:

<Tabs>
  <Tab title="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,
    })
    ```
  </Tab>
  <Tab title="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)
    ```
  </Tab>
  <Tab title="cURL">
    ```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"

    # 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"
    ```
  </Tab>
</Tabs>

<Note>
Deleting a connector will:
- Stop all future syncs from Notion
- Remove the OAuth authorization
- Delete the synced documents unless you pass `deleteDocuments: false`
</Note>

## 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](/connectors/overview#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_<property name>`, 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](#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

<Callout type="info">
**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
</Callout>

<Warning>
**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
</Warning>

---

### Apps Docs Connectors Onedrive (`apps/docs/connectors/onedrive.mdx`)

---
title: "OneDrive connector"
sidebarTitle: "OneDrive"
description: "Sync Microsoft Office documents from OneDrive with scheduled synchronization and business account support"
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

<CodeGroup>

```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"
#   }
# }
```

</CodeGroup>

### 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

<CodeGroup>
  ```typescript Typescript
    // Check connector details with recent sync runs
    const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
      include: "syncs",
    })

    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"
```

</CodeGroup>

<Note>
Deleting a connector will:
- Stop all future syncs from OneDrive
- Remove the OAuth authorization
- Delete the synced documents unless you pass `deleteDocuments: false`
</Note>

## 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](/connectors/overview#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

<Callout type="info">
**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
</Callout>

<Warning>
**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
</Warning>

---

### Apps Docs Connectors S3 (`apps/docs/connectors/s3.mdx`)

---
title: "S3 Connector"
sidebarTitle: "S3"
description: "Connect Amazon S3 or S3-compatible storage to sync files into your Supermemory knowledge base"
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.

<Note>
The S3 connector requires a **Scale Plan** or higher. You can also create S3 connectors directly from the [Supermemory Console](https://console.supermemory.ai).
</Note>

## Quick setup

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    import { Supermemory } from "supermemory"

    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)
    ```
  </Tab>
  <Tab title="Python">
    ```python
    from supermemory import Supermemory
    import os

    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}")
    ```
  </Tab>
  <Tab title="cURL">
    ```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"
        }
      }'

    # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "authorization": null}
    ```
  </Tab>
</Tabs>

## 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) |

<Note>
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`.
</Note>

## 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",
  },
})
```

<Note>
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`.
</Note>

## 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

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    const connector = await supermemory.connectors.get("org-123", "PTzGiUYei7pgzg5buzZHgA", {
      include: "syncs",
    })

    console.log("Bucket:", connector.config?.bucket)
    console.log("Last sync:", connector.latestRun?.system.status, connector.latestRun?.error)
    console.log("Files synced:", connector.documentCount)
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
    ```
  </Tab>
</Tabs>

### Delete Connector

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    await supermemory.connectors.delete("org-123", "PTzGiUYei7pgzg5buzZHgA")
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl -X DELETE "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
    ```
  </Tab>
</Tabs>

<Warning>
By default, deleting a connector removes all synced documents from Supermemory. To keep documents, pass `deleteDocuments: false` (`DELETE /ns/{namespace}/connectors/{id}?deleteDocuments=false`).
</Warning>

### Manual sync

The call returns `409` while a sync for that connector is already running.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    await supermemory.connectors.sync("org-123", "PTzGiUYei7pgzg5buzZHgA")
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl -X POST "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

    # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
    ```
  </Tab>
</Tabs>

## 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"
description: "Diagnose and resolve common issues with Google Drive, Gmail, Notion, and OneDrive connectors"
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:

<CodeGroup>

```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"}}'
```

</CodeGroup>

<Note>
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`.
</Note>

## 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](/connectors/overview#custom-oauth-applications), 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"
description: "Crawl and sync websites automatically with scheduled recrawling and robots.txt compliance"
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.

<Warning>
The web crawler connector requires a **Scale Plan** or **Enterprise Plan**.
</Warning>

## Quick setup

### 1. Create Web Crawler Connector

<Tabs>
  <Tab title="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: "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
    ```
  </Tab>
  <Tab title="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": "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
    ```
  </Tab>
  <Tab title="cURL">
    ```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
      }'

    # Response: {
    #   "id": "PTzGiUYei7pgzg5buzZHgA",
    #   "authorization": null
    # }
    ```
  </Tab>
</Tabs>

### 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

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // Check connector details with recent sync runs
    const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
      include: "syncs",
    })

    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`)
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # Check connector details with recent sync runs
    connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])

    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")
    ```
  </Tab>
  <Tab title="cURL">
    ```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"

    # 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": {...}}
    ```
  </Tab>
</Tabs>

<Note>
There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.
</Note>

## 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

<Note>
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.
</Note>

### Manual Recrawl

Start a crawl now. The call returns `409` while a sync for that connector is already running.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")

    console.log(run.status)
    // Output: queued
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

    # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
    ```
  </Tab>
</Tabs>

## Connector Management

### List All Connectors

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // List all web crawler connectors in a namespace
    const { connectors } = await supermemory.connectors.list("user-123")

    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}`)
    })
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # List all web crawler connectors in a namespace
    connectors = client.connectors.list("user-123", provider="web-crawler").connectors

    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}")
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    # List all connectors in a namespace
    curl "https://api.supermemory.ai/ns/user-123/connectors" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"

    # 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 }
    # }
    ```
  </Tab>
</Tabs>

### Delete Connector

Remove a web crawler connector when no longer needed:

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // Delete the connector and its crawled pages (default)
    await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")

    // Delete the connector but keep the crawled pages
    await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
      deleteDocuments: false,
    })
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # Delete the connector and its crawled pages (default)
    client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")

    # Delete the connector but keep the crawled pages
    client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
    ```
  </Tab>
  <Tab title="cURL">
    ```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"

    # 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"
    ```
  </Tab>
</Tabs>

<Note>
Deleting a connector will:
- Stop all future crawls of the website
- Remove the connector configuration
- Delete the crawled pages unless you pass `deleteDocuments: false`
</Note>

## 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


<Warning>
**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)
</Warning>

---

### Apps Docs Ingestion Add Memories (`apps/docs/ingestion/add-memories.mdx`)

---
title: "Ingesting context to supermemory"
sidebarTitle: "API"
description: "Add text, files, and URLs to Supermemory"
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](#memory-vs-superrag-ingestion) 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

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    import { Supermemory } from "supermemory";

    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"
    });
    ```
  </Tab>
  <Tab title="Python">
    ```python
    from supermemory import Supermemory

    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")
    ```
  </Tab>
  <Tab title="cURL">
    ```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"}
      }'
    ```
  </Tab>
</Tabs>

**Response:**
```json
{ "id": "abc123", "status": "queued" }
```

<Warning>
If an irrecoverable processing error occurs, the document's status becomes `failed`. Send the content again with the same `id` to reprocess it.
</Warning>

---

## 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.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    import { openAsBlob } from "node:fs";

    await supermemory.documents.uploadFile("user_123", {
      file: await openAsBlob("document.pdf"),
      metadata: JSON.stringify({ source: "upload" }),
    });
    ```
  </Tab>
  <Tab title="Python">
    ```python
    import json

    with open("document.pdf", "rb") as file:
        client.documents.upload_file(
            "user_123",
            file=file,
            metadata=json.dumps({"source": "upload"}),
        )
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl -X POST "https://api.supermemory.ai/ns/user_123/document/file" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
      -F "file=@document.pdf" \
      -F 'metadata={"source":"upload"}'
    ```
  </Tab>
</Tabs>

### 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

<Note>
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.
</Note>

---

## 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](#filtered-writes) |
| `supportingContext` | string | Context for memory extraction on this namespace. Max 1500 chars. See [Customization](/concepts/customization#supporting-context) |
| `date` | string | ISO 8601 date the content was originally created. Use when backfilling. See [Backfill historical data](/ingestion/batch-ingest-historical-data) |
| `dreaming` | `"dynamic" \| "instant"` | Processing mode. Default `"dynamic"`. `"instant"` processes each document on its own and bills one extra operation. See [Processing Modes](#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](/concepts/super-rag#ingesting-as-pure-superrag-tasktype-superrag) |

<AccordionGroup>
  <Accordion title="Parameter details & examples">
    **Content Types:**
    ```typescript
    // Any text — conversations, notes, documents
    { content: "Meeting notes from today's standup" }
    { content: JSON.stringify(messages) }

    // 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 john@acme.com 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
  </Accordion>
</AccordionGroup>

---

## 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](/concepts/super-rag#ingesting-as-pure-superrag-tasktype-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.

<Note>
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.
</Note>

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    await supermemory.add("user_123", {
      content: "New research findings on transformer architectures...",
      metadata: { category: "ml", source: "arxiv" },
      group: { category: "ml" },
    });
    ```
  </Tab>
  <Tab title="Python">
    ```python
    client.add(
        "user_123",
        content="New research findings on transformer architectures...",
        metadata={"category": "ml", "source": "arxiv"},
        group={"category": "ml"},
    )
    ```
  </Tab>
  <Tab title="cURL">
    ```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"}
      }'
    ```
  </Tab>
</Tabs>

### 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"
```

<AccordionGroup>
  <Accordion title="Batch upload">
    Process multiple documents with rate limiting:

    ```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)
  </Accordion>

  <Accordion title="Error handling">
    | 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 |

    ```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);
    }
    ```
  </Accordion>

  <Accordion title="Delete Content">
    **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"],
    });
    ```

    **Delete everything in a namespace:**
    ```typescript
    // Delete all content for a user
    await supermemory.namespaces.delete("user_123");
    ```

    Deletes are permanent — no recovery.
  </Accordion>
</AccordionGroup>

---

## Next steps

- [How to backfill historical data](/ingestion/batch-ingest-historical-data) — Import dated content with the batch API
- [Search Memories](/recall/search) — Query your content
- [User Profiles](/recall/user-profiles) — Get user context
- [Organizing & Filtering](/concepts/filtering) — Namespaces and metadata

---

### 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"
description: "Backfill historical documents into Supermemory with date, stable IDs, and the batch ingestion API."
icon: "/icons/hugeicons/clock-01.svg"
---

Use `POST /ns/{namespace}/document/batch` to backfill exports, emails, messages, or other dated records.

<Warning>
  Sort the source data oldest to newest, add `date` to every document.
</Warning>

## 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`](/api-reference/ingest/batch-add-documents)

<CodeGroup>

```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" }
    ]
  }'
```

</CodeGroup>

`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}`](/api-reference/documents/get-document)

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.

<CodeGroup>

```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" }
```

</CodeGroup>

---

### Apps Docs Ingestion Document Operations (`apps/docs/ingestion/document-operations.mdx`)

---
title: "Document operations"
sidebarTitle: "Documents"
description: "List, get, update, and delete your ingested 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`.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    const { documents } = await supermemory.list("user_123", "documents", {
      limit: 10,
    });

    documents.forEach(d => {
      console.log(d.id, d.title, d.system.status);
    });
    ```
  </Tab>
  <Tab title="Python">
    ```python
    page = client.list("user_123", "documents", limit=10)

    for doc in page.documents:
        print(doc.id, doc.title, doc.system.status)
    ```
  </Tab>
  <Tab title="cURL">
    ```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 '{}'
    ```
  </Tab>
</Tabs>

**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](/concepts/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.

<Accordion title="Pagination example">
  ```typescript
  async function getAllDocuments(namespace: string) {
    const all = [];
    let page = 1;

    while (true) {
      const { documents, pagination } = await supermemory.list(namespace, "documents", {
        limit: 100,
        page,
      });

      all.push(...documents);
      if (page >= pagination.totalPages) break;
      page++;
    }

    return all;
  }
  ```
</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" },
      ],
    },
  });
  ```
</Accordion>

---

## Get document

Get a specific document with its processing status. Pass `include` to include its chunks and memories.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    const doc = await supermemory.documents.get("user_123", "doc_abc123", {
      include: ["chunks", "memories"], // optional
    });

    console.log(doc.system.status);  // "queued" | "extracting" | ... | "done" | "failed"
    console.log(doc.content);
    ```
  </Tab>
  <Tab title="Python">
    ```python
    doc = client.documents.get(
        "user_123",
        "doc_abc123",
        include=["chunks", "memories"],  # optional
    )

    print(doc.system.status)  # "queued" | "extracting" | ... | "done" | "failed"
    print(doc.content)
    ```
  </Tab>
  <Tab title="cURL">
    ```bash
    curl "https://api.supermemory.ai/ns/user_123/document/doc_abc123?include=chunks&include=memories" \
      -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
    ```
  </Tab>
</Tabs>

The `id` may be the Supermemory document ID or the `id` you supplied at ingestion. It resolves only inside the namespace in the path.

<Note>
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.
</Note>

### 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.

<Accordion title="Poll for completion">
  ```typescript
  async function waitForProcessing(namespace: string, id: string) {
    while (true) {
      const doc = await supermemory.documents.get(namespace, id);

      if (doc.system.status === "done") return doc;
      if (doc.system.status === "failed") throw new Error("Processing failed");

      await new Promise(r => setTimeout(r, 2000));
    }
  }
  ```
</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.

<Tabs>
  <Tab title="TypeScript">
    ```typescript
    // One or many
    const { count, errors } = await supermemory.documents.delete("user_123", {
      ids: ["doc_1", "doc_2", "doc_3"],
    });

    // Delete a whole namespace (all content for a user)
    await supermemory.namespaces.delete("user_123");
    ```
  </Tab>
  <Tab title="Python">
    ```python
    # One or many
    result = client.documents.delete("user_123", ids=["doc_1", "doc_2", "doc_3"])
    print(result.count, result.errors)

    # Delete a whole namespace (all content for a user)
    client.namespaces.delete("user_123")
    ```
  </Tab>
  <Tab title="cURL">
    ```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"]}'

    # 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 '{}'
    ```
  </Tab>
</Tabs>

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.

<Warning>
Deletes are permanent — no recovery.
</Warning>

---

## 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.

<Tabs>
  <Tab title="TypeScript">
    ```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`);
    ```
  </Tab>
  <Tab title="Python">
    ```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")
    ```
  </Tab>
  <Tab title="cURL">
    ```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 '{}'
    ```
  </Tab>
</Tabs>

---

## Next steps

- [Memory Operations](/recall/memory-operations) — Forget memories
- [Search](/recall/search) — Query your memories
- [Ingesting Content](/ingestion/add-memories) — Add new content

---

### Apps Docs Integrations Agent Framework (`apps/docs/integrations/agent-framework.mdx`)

---
title: "Microsoft Agent Framework"
sidebarTitle: "MS Agent Framework"
description: "Add persistent memory to Microsoft Agent Framework agents with Supermemory"
icon: "/images/microsoft-icon.svg"
---

Microsoft's [Agent Framework](https://github.com/microsoft/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
```

<Note>Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai).</Note>

---

## 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](/migration/api-v5); the adapter does not delete or rewrite legacy data.

---

## Context provider (recommended)

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:

```
/* 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 |

---

## Related docs

<CardGroup cols={2}>
  <Card title="User profiles" icon="/icons/hugeicons/user.svg" href="/recall/user-profiles">
    How automatic profiling works
  </Card>
  <Card title="Search" icon="/icons/hugeicons/search-01.svg" href="/recall/search">
    Filtering and search modes
  </Card>
  <Card title="OpenAI Agents SDK" icon="/icons/hugeicons/chat-bot.svg" href="/integrations/openai-agents-sdk">
    Memory for OpenAI Agents SDK
  </Card>
  <Card title="LangChain" icon="/icons/hugeicons/link-01.svg" href="/integrations/langchain">
    Memory for LangChain apps
  </Card>
</CardGroup>

---

### Apps Docs Integrations Agno (`apps/docs/integrations/agno.mdx`)

---
title: "Agno"
sidebarTitle: "Agno"
description: "Add persistent memory to Agno agents with Supermemory"
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
```

<Note>Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai).</Note>

## Basic integration

Fetch user context before running an agent, then store the interaction after.

```
/* 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.

```
/* 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"}
        ]
    }
)
```

---

## Related docs

<CardGroup cols={2}>
  <Card title="User profiles" icon="/icons/hugeicons/user.svg" href="/recall/user-profiles">
    How automatic profiling works
  </Card>
  <Card title="Search" icon="/icons/hugeicons/search-01.svg" href="/recall/search">
    Filtering and search modes
  </Card>
  <Card title="LangChain" icon="/icons/hugeicons/link-01.svg" href="/integrations/langchain">
    Memory for LangChain apps
  </Card>
  <Card title="CrewAI" icon="/icons/hugeicons/user-multiple.svg" href="/integrations/crewai">
    Multi-agent systems with memory
  </Card>
</CardGroup>

---

### Apps Docs Integrations Ai Sdk (`apps/docs/integrations/ai-sdk.mdx`)

---
title: "Vercel AI SDK"
sidebarTitle: "Vercel AI SDK"
description: "Use Supermemory with Vercel AI SDK for seamless memory management"
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.

<MigrateCli legacy="@supermemory/tools 2.x" guideHref="/migration/tools-v3-upgrade" guideLabel="3.0 upgrade guide" />

<Card title="@supermemory/tools on npm" icon="/icons/hugeicons/package.svg" href="https://www.npmjs.com/package/@supermemory/tools">
    Check out the NPM page for more details
</Card>

## 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).

<Note>
  **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",
  })
  ```
</Note>

### 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)"
description: "Integrate Supermemory with Cartesia for conversational memory in voice AI agents"
icon: "/images/cartesia.svg"
---

Supermemory integrates with [Cartesia](https://cartesia.ai/agents), 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](https://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"
description: "Supermemory plugin for Claude Code. Keeps memory across coding sessions."
icon: "/images/claude-code-icon.svg"
---

<img
  src="/images/claude-code-supermemory.png"
  alt="Claude Code + Supermemory"
  className="rounded-lg"
/>

[supermemory](https://github.com/supermemoryai/claude-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.

<Tip>
**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.
</Tip>

## 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
```

<Note>
**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
```
</Note>

## Authenticate

Create a Supermemory API key from the [API Keys](https://console.supermemory.ai/keys) page in the console, then add it to your shell profile:

<Tabs>
  <Tab title="macOS / Linux (zsh)">
    ```bash
    echo 'export SUPERMEMORY_CC_API_KEY="sm_..."' >> ~/.zshrc
    source ~/.zshrc
    ```
  </Tab>
  <Tab title="macOS / Linux (bash)">
    ```bash
    echo 'export SUPERMEMORY_CC_API_KEY="sm_..."' >> ~/.bashrc
    source ~/.bashrc
    ```
  </Tab>
  <Tab title="Windows (PowerShell)">
    ```powershell
    [System.Environment]::SetEnvironmentVariable("SUPERMEMORY_CC_API_KEY", "sm_...", "User")
    ```
    Restart your terminal after running this.
  </Tab>
</Tabs>

## 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

<CardGroup cols={2}>
  <Card title="GitHub repository" icon="/images/github-icon.svg" href="https://github.com/supermemoryai/claude-supermemory">
    Source code, issues, and detailed README.
  </Card>

  <Card title="Cursor plugin" icon="/images/cursor-logo.svg" href="/integrations/cursor">
    Memory for your Cursor chats.
  </Card>
</CardGroup>

---

### Apps Docs Integrations Claude Memory (`apps/docs/integrations/claude-memory.mdx`)

---
title: "Claude Memory Tool"
sidebarTitle: "Claude Memory Tool"
description: "Use Claude's native memory tool with Supermemory as the backend"
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.

<Info>
This integration works with Claude's built-in `memory` tool type, introduced in the Anthropic API. It requires the `context-management` beta flag.
</Info>

## Installation

```bash
npm install @supermemory/tools @anthropic-ai/sdk
```

## Quick start

```
/* 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/`:

```
/memories/preferences.txt       # User preferences
/memories/projects/react.txt    # Project-specific notes
/memories/context/current.txt   # Current context
```

<Note>
Paths are normalized for storage: `/memories/preferences` is stored as `--memories--preferences`.
</Note>

## 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

```
/* 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

<CardGroup cols={2}>
  <Card title="AI SDK integration" icon="/icons/hugeicons/triangle.svg" href="/integrations/ai-sdk">
    Use with Vercel AI SDK for streamlined development
  </Card>

  <Card title="OpenAI SDK" icon="/icons/hugeicons/flash.svg" href="/integrations/openai">
    Memory tools for OpenAI function calling
  </Card>
</CardGroup>

---

### Apps Docs Integrations Codex (`apps/docs/integrations/codex.mdx`)

---
title: "Codex"
sidebarTitle: "Codex"
description: "Persistent memory for the OpenAI Codex CLI with the codex-supermemory plugin."
icon: "/images/codex.svg"
---

[codex-supermemory](https://github.com/supermemoryai/codex-supermemory) wires Supermemory into the [OpenAI Codex CLI](https://github.com/openai/codex) 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.

<Tip>
**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.
</Tip>

## 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](https://console.supermemory.ai/keys):

<Tabs>
  <Tab title="macOS / Linux (zsh)">
    ```bash
    echo 'export SUPERMEMORY_CODEX_API_KEY="sm_..."' >> ~/.zshrc
    source ~/.zshrc
    ```
  </Tab>
  <Tab title="macOS / Linux (bash)">
    ```bash
    echo 'export SUPERMEMORY_CODEX_API_KEY="sm_..."' >> ~/.bashrc
    source ~/.bashrc
    ```
  </Tab>
  <Tab title="Windows (PowerShell)">
    ```powershell
    [System.Environment]::SetEnvironmentVariable("SUPERMEMORY_CODEX_API_KEY", "sm_...", "User")
    ```
    Restart your terminal after running this.
  </Tab>
</Tabs>

## 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 `<private>...</private>` 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:

```
> 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

<CardGroup cols={2}>
  <Card title="GitHub repository" icon="/images/github-icon.svg" href="https://github.com/supermemoryai/codex-supermemory">
    Source code, issues, and detailed README.
  </Card>

  <Card title="Cursor plugin" icon="/images/cursor-logo.svg" href="/integrations/cursor">
    Memory for your Cursor chats.
  </Card>
</CardGroup>

---

### Apps Docs Integrations Convex (`apps/docs/integrations/convex.mdx`)

---
title: "Convex"
sidebarTitle: "Convex"
description: "Add persistent memory to Convex apps with Supermemory"
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
```

<Note>Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai).</Note>

## 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);
  },
});
```

---

## Related docs

<CardGroup cols={2}>
  <Card title="User profiles" icon="/icons/hugeicons/user.svg" href="/recall/user-profiles">
    How automatic profiling works
  </Card>
  <Card title="Search" icon="/icons/hugeicons/search-01.svg" href="/recall/search">
    Filtering and search modes
  </Card>
  <Card title="Vercel AI SDK" icon="/icons/hugeicons/triangle.svg" href="/integrations/ai-sdk">
    Memory middleware for Next.js
  </Card>
  <Card title="LangChain" icon="/icons/hugeicons/link-01.svg" href="/integrations/langchain">
    Memory for LangChain apps
  </Card>
</CardGroup>

---

### Apps Docs Integrations Crewai (`apps/docs/integrations/crewai.mdx`)

---
title: "CrewAI"
sidebarTitle: "CrewAI"
description: "Add persistent memory to CrewAI agents with Supermemory"
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
```

<Note>Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai).</Note>

## 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.

```
/* 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"}
        ]
    }
)
```

---

## Related docs

<CardGroup cols={2}>
  <Card title="User profiles" icon="/icons/hugeicons/user.svg" href="/recall/user-profiles">
    How automatic profiling works
  </Card>
  <Card title="Search" icon="/icons/hugeicons/search-01.svg" href="/recall/search">
    Filtering and search modes
  </Card>
  <Card title="LangChain" icon="/icons/hugeicons/link-01.svg" href="/integrations/langchain">
    Memory for LangChain apps
  </Card>
  <Card title="Vercel AI SDK" icon="/icons/hugeicons/triangle.svg" href="/integrations/ai-sdk">
    Memory middleware for Next.js
  </Card>
</CardGroup>

---

### Apps Docs Integrations Cursor (`apps/docs/integrations/cursor.mdx`)

---
title: "Cursor"
sidebarTitle: "Cursor"
description: "cursor-supermemory: persistent memory across your Cursor chats"
icon: "/images/cursor-logo.svg"
---

Your agent remembers the decisions, bugs, and conventions from earlier chats instead of starting cold every time.

## Install

<Note>
Requires [Node.js](https://nodejs.org) on your `PATH`. Installing Cursor does not put one there.
</Note>

Run this in Cursor:

```
/add-plugin cursor-supermemory
```

Or install it from the [Cursor Marketplace](https://cursor.com/marketplace/supermemory): 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:

```
/supermemory-setup
```

A browser window opens. Sign in to Supermemory and you are done.

As a fallback, set an API key from [API Keys](https://console.supermemory.ai/keys):

<Tabs>
  <Tab title="macOS / Linux (zsh)">
    ```bash
    echo 'export SUPERMEMORY_API_KEY="sm_..."' >> ~/.zshrc
    source ~/.zshrc
    ```
  </Tab>
  <Tab title="macOS / Linux (bash)">
    ```bash
    echo 'export SUPERMEMORY_API_KEY="sm_..."' >> ~/.bashrc
    source ~/.bashrc
    ```
  </Tab>
  <Tab title="Windows (PowerShell)">
    ```powershell
    [System.Environment]::SetEnvironmentVariable("SUPERMEMORY_API_KEY", "sm_...", "User")
    ```
    Restart your terminal after running this.
  </Tab>
</Tabs>

Restart Cursor after installing the plugin or changing credentials.

Check the connection any time with `/supermemory-status`.

<Accordion title="Prefer the terminal?" icon="/icons/hugeicons/command-line.svg">
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`.
</Accordion>

## 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](/integrations/claude-code), [Muse Code](/integrations/muse-code), [OpenAI Codex](/integrations/codex), and [OpenCode](/integrations/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

<Tip>
**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.
</Tip>

### 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

<Card title="GitHub repository" icon="/images/github-icon.svg" href="https://github.com/supermemoryai/cursor-supermemory">
  Source code, issues, and detailed README.
</Card>

---

### Apps Docs Integrations Eve (`apps/docs/integrations/eve.mdx`)

---
title: "Eve"
sidebarTitle: "Eve"
description: "Give your Eve agent memory across conversations with Supermemory."
icon: "/images/eve.svg"
---

Your next conversation can start where the last one left off. Supermemory gives your [Eve](https://eve.dev) 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`.

<Steps>
  <Step title="Add the provider">
    Run this from your Eve project:

    ```bash
    eve add memory/supermemory
    ```

    Eve installs `@supermemory/eve` and creates `agent/memory/supermemory.ts`, the file that connects your agent to Supermemory.
  </Step>

  <Step title="Connect your Supermemory account">
    Create an [API key in the Supermemory console](https://console.supermemory.ai/keys) and add it to your project's environment:

    ```bash .env.local
    SUPERMEMORY_API_KEY="your-supermemory-api-key"
    ```
  </Step>

  <Step title="Start your agent">
    Start or restart Eve to load the provider and API key:

    ```bash
    eve dev
    ```

    Automatic recall and conversation capture are enabled by default. The memory tools are available to your agent alongside its existing tools.
  </Step>
</Steps>

You can also [install the provider manually](#manual-installation).

## 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"
>
  <source src="/videos/eve-memory.mp4" type="video/mp4" />
  <a href="/videos/eve-memory.mp4">Watch the memory explainer</a>.
</video>

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](/concepts/super-rag) 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](/concepts/user-profiles) 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__`.

<Accordion title="Tool reference">

  | 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. |
</Accordion>

<Accordion title="Forgetting saved memories">
  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.
</Accordion>

## 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](https://eve.dev/docs/patterns/multi-tenant-memory).

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](https://eve.dev/docs/memory#namespace) 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.

<Accordion title="Provider options and defaults">
  | 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.
</Accordion>

## 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](#add-supermemory).

For implementation details, see the [provider repository](https://github.com/supermemoryai/eve-supermemory) and [Eve's Supermemory integration](https://eve.dev/integrations/supermemory).

---

### Apps Docs Integrations Grok Bot (`apps/docs/integrations/grok-bot.mdx`)

---
title: "Grok Bot"
sidebarTitle: "Grok Bot"
description: "Supermemory for Grok Bot: persistent memory for your Grok Bots"
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](https://x.ai/bot/plugin/58578698) and select **Add to Grok Bot**.

Restart Grok Bot afterwards so the plugin loads.

## Authenticate

Just ask Grok Bot:

```
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

<CardGroup cols={2}>
  <Card title="GitHub repository" icon="/images/github-icon.svg" href="https://github.com/supermemoryai/cursor-supermemory">
    Source code, issues, and detailed README.
  </Card>

  <Card title="Cursor plugin" icon="/images/cursor-logo.svg" href="/integrations/cursor">
    The same plugin, installed through Cursor.
  </Card>
</CardGroup>

---

### Apps Docs Integrations Hermes (`apps/docs/integrations/hermes.mdx`)

---
title: "Hermes"
sidebarTitle: "Hermes"
description: "Supermemory plugin for Hermes. Semantic memory, profiles and search across Telegram, Discord, Slack, the CLI and more."
icon: "/images/hermes.svg"
---


[Hermes agent](https://github.com/NousResearch/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](https://console.supermemory.ai/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.

```
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](https://github.com/NousResearch/hermes-agent/tree/main/plugins/memory/supermemory) 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](#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](https://github.com/NousResearch/hermes-agent/tree/main/plugins/memory/supermemory) — alongside your key and container settings.

## Next steps

<CardGroup cols={2}>
  <Card title="Hermes + plugin README" icon="/images/github-icon.svg" href="https://github.com/NousResearch/hermes-agent/tree/main/plugins/memory/supermemory">
    Full config table, env vars, and multi-container details.
  </Card>

  <Card title="OpenClaw plugin" icon="/icons/hugeicons/message-02.svg" href="/integrations/openclaw">
    Multi-platform memory for Telegram, WhatsApp, Discord, and more.
  </Card>
</CardGroup>

Questions about the API or product? [Discord](https://supermemory.link/discord) · [support@supermemory.com](mailto:support@supermemory.com) · [Developer docs](/overview/what-is-supermemory)

---

### Apps Docs Integrations Langchain (`apps/docs/integrations/langchain.mdx`)

---
title: "LangChain"
sidebarTitle: "LangChain"
description: "Build AI agents with persistent memory using LangChain and Supermemory"
icon: "/icons/hugeicons/link-01.svg"
---

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
```

<Note>Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai).</Note>

## Basic integration

Initialize both clients and set up a simple chat function with memory:

```
/* 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"
)
```

### Memory search

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:

```
/* 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:

```
/* 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

<CardGroup cols={2}>
  <Card title="User profiles" icon="/icons/hugeicons/user.svg" href="/recall/user-profiles">
    Deep dive into automatic user profiling
  </Card>
  <Card title="Search API" icon="/icons/hugeicons/search-01.svg" href="/recall/search">
    Advanced search patterns and filtering
  </Card>
  <Card title="OpenAI SDK" icon="/icons/hugeicons/robotic.svg" href="/integrations/openai">
    Native OpenAI integration with memory tools
  </Card>
  <Card title="Vercel AI SDK" icon="/icons/hugeicons/triangle.svg" href="/integrations/ai-sdk">
    Memory middleware for Next.js apps
  </Card>
</CardGroup>

---

### Apps Docs Integrations Langgraph (`apps/docs/integrations/langgraph.mdx`)

---
title: "LangGraph"
sidebarTitle: "LangGraph"
description: "Add persistent memory to LangGraph agents with Supermemory"
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
```

<Note>Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai).</Note>

## Basic integration

A minimal agent that fetches user context before responding and stores the conversation after:

```
/* 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"
)
```

### Memory search

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:

```
/* 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

<CardGroup cols={2}>
  <Card title="User profiles" icon="/icons/hugeicons/user.svg" href="/recall/user-profiles">
    Deep dive into automatic user profiling
  </Card>

  <Card title="Search API" icon="/icons/hugeicons/search-01.svg" href="/recall/search">
    Advanced search patterns and filtering
  </Card>

  <Card title="OpenAI SDK" icon="/icons/hugeicons/chat-bot.svg" href="/integrations/openai">
    Native OpenAI integration with memory tools
  </Card>

  <Card title="AI SDK" icon="/icons/hugeicons/triangle.svg" href="/integrations/ai-sdk">
    Memory middleware for Next.js apps
  </Card>
</CardGroup>

---

### Apps Docs Integrations Livekit (`apps/docs/integrations/livekit.mdx`)

---
title: "LiveKit"
sidebarTitle: "LiveKit (Voice)"
description: "Add persistent memory to LiveKit Agents voice sessions"
icon: "/images/livekit.svg"
---

Supermemory integrates with [LiveKit Agents](https://docs.livekit.io/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](https://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](https://docs.livekit.io/agents/build/external-data/).
- 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](https://github.com/supermemoryai/supermemory/tree/main/packages/livekit-sdk-python/examples).

`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](https://agents-playground.livekit.io).

## 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](https://docs.livekit.io/agents/build/external-data/). `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"
description: "Add persistent memory to Mastra AI agents with Supermemory processors"
icon: "/images/mastra-icon.svg"
---

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

Integrate Supermemory with [Mastra](https://mastra.ai) to give your AI agents persistent memory. Use the `withSupermemory` wrapper for zero-config setup or processors for fine-grained control.

<MigrateCli legacy="@supermemory/tools 2.x" guideHref="/migration/tools-v3-upgrade" guideLabel="3.0 upgrade guide" />

<Card title="@supermemory/tools on npm" icon="/icons/hugeicons/package.svg" href="https://www.npmjs.com/package/@supermemory/tools">
    Check out the NPM page for more details
</Card>

## 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?")
```

<Note>
  **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
    }
  ))
  ```
</Note>

---

## How it works

The Mastra integration uses Mastra's native [Processor](https://mastra.ai/docs/agents/processors) 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>
  <user_profile>
    ${data.userMemories}
  </user_profile>
  <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"
```

<Note>
  **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.
</Note>

---

## 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

<CardGroup cols={2}>
  <Card title="Vercel AI SDK" icon="/icons/hugeicons/triangle.svg" href="/integrations/ai-sdk">
    Use with Vercel AI SDK for streamlined development
  </Card>

  <Card title="User Profiles" icon="/icons/hugeicons/user.svg" href="/recall/user-profiles">
    Learn about user profile management
  </Card>
</CardGroup>

---

### 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.

<Card title="@supermemory/memory-graph on npm" icon="/icons/hugeicons/package.svg" href="https://www.npmjs.com/package/@supermemory/memory-graph">
    Check out the NPM page for more details
</Card>

## 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 (
    <div style={{ height: '100vh' }}>
      <MemoryGraph
        documents={documents}
        isLoading={isLoading}
        error={error}
        variant="console"
      />
    </div>
  );
}
```

## 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:

<CodeGroup>

```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);
});
```

</CodeGroup>

<Warning>
  Never expose your Supermemory API key to the client. Always fetch data through your backend.
</Warning>

---

## 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}>
  <div style={{ textAlign: 'center', padding: '2rem' }}>
    <h2>No memories yet</h2>
    <p>Add content to see your knowledge graph</p>
  </div>
</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](https://github.com/supermemoryai/muse-supermemory) is a native [Muse Code](https://dev.meta.ai/docs/muse-code) plugin that gives your agent persistent memory across sessions. Your agent remembers what you worked on — across sessions, across projects.

<Tip>
**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.
</Tip>

## 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](/integrations/claude-code), that key is reused from `~/.supermemory-claude/credentials.json`.

Or write the file yourself with a key from [API Keys](https://console.supermemory.ai/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](/integrations/claude-code):

```text
repo_<project_name>__<project_id>
```

The project ID is a hash of the git remote (or the resolved local path if there isn't one). The same repo in Muse and Claude Code shares memory. Override with `repoContainerTag` in project config.

## Commands

| Command | Description |
| --- | --- |
| `/supermemory:index` | Index codebase architecture and patterns |
| `/supermemory:status` | Auth, container tag, API and MCP reachability |
| `/supermemory:logout` | Clear saved Muse credentials |

A bundled skill also tells the agent when to search vs save on its own.

## Configuration

### Global settings

`~/.supermemory-muse/settings.json`

```json
{
  "maxProfileItems": 5,
  "debug": false
}
```

| Option | Description |
| --- | --- |
| `maxProfileItems` | Max profile facts injected at session start (default: 5) |
| `debug` | Log hook activity to stderr |
| `recallDirective` | Replace prompt-recall injection with a fixed instruction |

### Project config

Per-repo overrides in `.muse/supermemory.json`. A Claude Code config at `.claude/.supermemory-claude/config.json` is also read, so one repo keeps one container tag across tools.

```json
{
  "apiKey": "sm_...",
  "baseUrl": "https://api.supermemory.ai",
  "repoContainerTag": "my-team-project"
}
```

| Option | Description |
| --- | --- |
| `apiKey` | Project-specific API key |
| `baseUrl` | Supermemory API URL (use for self-hosted) |
| `repoContainerTag` | Override the auto-derived project container |

Add the project file to `.gitignore` if it contains an API key.

## Next steps

<CardGroup cols={2}>
  <Card title="GitHub repository" icon="/images/github-icon.svg" href="https://github.com/supermemoryai/muse-supermemory">
    Source code, issues, and detailed README.
  </Card>

  <Card title="Claude Code plugin" icon="/images/claude-code-icon.svg" href="/integrations/claude-code">
    The same memory in Claude Code.
  </Card>
</CardGroup>

---

### Apps Docs Integrations N8n (`apps/docs/integrations/n8n.mdx`)

---
title: "n8n"
sidebarTitle: "n8n"
description: "Automate knowledge management with Supermemory in n8n workflows"
icon: "/icons/hugeicons/workflow-square-01.svg"
---

Connect Supermemory to your n8n workflows to build intelligent automation workflows and agents that leverage your full knowledge base.

## Quick start

### Prerequisites

- n8n instance (self-hosted or cloud)
- Supermemory API key ([get one here](https://console.supermemory.ai/settings))
- Basic understanding of n8n workflows

### Setting up the HTTP request node

The Supermemory integration in n8n uses the HTTP Request node to interact with the Supermemory API. Here's how to configure it:

1. Add an **HTTP Request** node to your workflow (Core > HTTP Request)

2. Set the **Method** to `POST`
3. Set the **URL** to the appropriate Supermemory API endpoint:
   - Add memory: `https://api.supermemory.ai/ns/{namespace}/document`
   - Search memories: `https://api.supermemory.ai/ns/{namespace}/search`

   Replace `{namespace}` with the namespace that scopes these memories, for example a user id or `gmail`.
4. For authentication, select **Generic Credential Type** and then **Bearer Auth**
5. Click on **Create New Credential** and paste the Supermemory API Key in the Bearer Token field.

6. Check **Send Body** and select **JSON** as the Body Content Type. The fields depend on what API endpoint you're sending the request to. You can find detailed step-by-step examples below.

## Step-by-step tutorial

In this tutorial, we'll create a workflow that automatically adds every email from Gmail to your Supermemory knowledge base. We'll use the HTTP Request node to send email data to Supermemory's API, creating a searchable archive of all your communications.

### Adding Gmail emails to Supermemory

Follow these steps to build a workflow that captures and stores your Gmail messages:

#### Step 1: Set up Gmail trigger



1. **Add a Gmail Trigger node** to your workflow
2. Configure your Gmail credentials (OAuth2 recommended)
3. Set the trigger to **Message Received**
4. Optional: Add labels or filters to process specific emails only

#### Step 2: Configure HTTP request node

1. **Add an HTTP Request node** after the Gmail Trigger
2. **Method**: `POST`
3. **URL**: `https://api.supermemory.ai/ns/gmail/document` (the namespace `gmail` is part of the URL)
4. Select your auth credentials you created with the Supermemory API Key.

#### Step 3: Format email data for Supermemory

In the HTTP Request node's **Body**, select **JSON** and **Using Fields Below**

And create 1 field:

1. name: `content`, value: `{{ $json.snippet }}`




#### Step 4: Handle attachments (optional)

If you want to process attachments:

1. **Add a Loop node** after the Gmail Trigger
2. Loop through `{{$json.attachments}}`
3. **Add a Gmail node** to download each attachment
4. **Add another HTTP Request node** to store attachment metadata


#### Step 5: Add error handling

1. **Add an Error Trigger node** connected to your workflow
2. Configure it to catch errors from the HTTP Request node
3. **Add a notification node** (Email, Slack, etc.) to alert you of failures
4. Optional: Add a **Wait node** with retry logic

#### Step 6: Test your workflow

1. **Activate the workflow** in test mode
2. Send a test email to your Gmail account
3. Check the execution to ensure the email was captured
4. Verify in Supermemory that the email appears in search results

Refer to the API Reference tab to learn more about other supermemory API endpoints.

---

### Apps Docs Integrations Openai Agents Sdk (`apps/docs/integrations/openai-agents-sdk.mdx`)

---
title: "OpenAI Agents SDK"
sidebarTitle: "OpenAI Agents SDK"
description: "Add persistent memory to OpenAI agents with Supermemory"
icon: "/images/openai.svg"
---

OpenAI's Agents SDK gives you a straightforward way to build agents with tools, handoffs, and guardrails. But agents don't remember users between sessions. Supermemory adds that missing piece: your agents can store what they learn and recall it later.

## What you can do

- Pull user profiles and relevant memories before an agent runs
- Store agent outputs and decisions for future sessions
- Give agents tools to search and add memories on their own

## Setup

Install the packages:

```bash
pip install openai-agents supermemory python-dotenv
```

Set up your environment:

```bash
# .env
SUPERMEMORY_API_KEY=your-supermemory-api-key
OPENAI_API_KEY=your-openai-api-key
```

<Note>Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai).</Note>

## Basic integration

The simplest approach: fetch user context and pass it in the agent's instructions.

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

---

## Core concepts

### User profiles

Supermemory keeps two buckets of user info:

- **Static facts**: Stuff that doesn't change much (preferences, job, expertise)
- **Dynamic context**: What they're working on right now

```python
result = memory.profile("user_123")

print([m.memory for m in result.profile.static])   # ["Prefers window seats", "Vegetarian"]
print([m.memory for m in result.profile.dynamic])  # ["Planning trip to Japan", "Traveling in March"]
```

### Storing memories

Save agent interactions so future sessions have context:

```python
def store_interaction(user_id: str, task: str, result: str):
    memory.add(
        user_id,
        content=f"Task: {task}\nOutcome: {result}",
        metadata={"type": "agent_run"},
        dreaming="instant"
    )
```

### Searching memories

Look up past interactions before running an agent:

```python
results = memory.search(
    "user_123",
    query="previous travel recommendations",
    search_mode="hybrid",  # Searches memories + document chunks
    limit=5
)

for r in results.results:
    print(r.memory or r.chunk)
```

---

## Adding memory tools to agents

You can give agents direct access to memory operations. They'll decide when to search or store information.

```python
from agents import Agent, Runner, function_tool
from supermemory import Supermemory

memory = Supermemory()

@function_tool
def search_memories(query: str, user_id: str) -> str:
    """Search the user's memories for relevant information.

    Args:
        query: What to search for
        user_id: The user's identifier
    """
    results = memory.search(user_id, query=query, limit=5)

    if not results.results:
        return "No relevant memories found."

    return "\n".join([
        r.memory or r.chunk
        for r in results.results
    ])

@function_tool
def save_memory(content: str, user_id: str) -> str:
    """Store something important about the user for later.

    Args:
        content: The information to remember
        user_id: The user's identifier
    """
    memory.add(user_id, content=content, dreaming="instant")
    return f"Saved: {content}"

agent = Agent(
    name="assistant",
    instructions="""You are a helpful assistant with memory.

When users share preferences or important information, save it.
When they ask questions, search your memories first.""",
    tools=[search_memories, save_memory],
    model="gpt-4o"
)
```

---

## Example: support agent with memory

A support agent that knows who it's talking to. Past tickets, account info, communication preferences - all available without the customer repeating themselves.

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

---

## Multi-agent handoffs with shared memory

Agents handing off to each other usually lose context. Not if they're sharing a memory store.

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

---

## Metadata for filtering

Tags let you narrow down searches later:

```python
# Store with metadata
memory.add(
    "user_123",
    content="User prefers detailed technical explanations",
    dreaming="instant",
    metadata={
        "type": "preference",
        "category": "communication_style",
        "source": "support_chat"
    }
)

# Search with filters
results = memory.search(
    "user_123",
    query="communication preferences",
    filter={
        "operator": "and",
        "operands": [
            {"field": "type", "operator": "eq", "value": "preference"},
            {"field": "category", "operator": "eq", "value": "communication_style"}
        ]
    }
)
```

---

## Related docs

<CardGroup cols={2}>
  <Card title="User profiles" icon="/icons/hugeicons/user.svg" href="/recall/user-profiles">
    How automatic profiling works
  </Card>
  <Card title="Search" icon="/icons/hugeicons/search-01.svg" href="/recall/search">
    Filtering and search modes
  </Card>
  <Card title="OpenAI SDK" icon="/icons/hugeicons/chat-bot.svg" href="/integrations/openai">
    Function calling with the regular OpenAI SDK
  </Card>
  <Card title="LangChain" icon="/icons/hugeicons/link-01.svg" href="/integrations/langchain">
    Memory for LangChain apps
  </Card>
</CardGroup>

---

### Apps Docs Integrations Openai (`apps/docs/integrations/openai.mdx`)

---
title: "OpenAI SDK"
sidebarTitle: "OpenAI SDK"
description: "Memory tools for OpenAI function calling with Supermemory integration"
icon: "/images/openai.svg"
---

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

Add memory capabilities to the official OpenAI SDKs using Supermemory. Two approaches available:

1. **`withSupermemory` wrapper** - Automatic memory injection into system prompts (zero-config)
2. **Function calling tools** - Explicit tool calls for search/add memory operations

<MigrateCli legacy="@supermemory/tools 2.x" guideHref="/migration/tools-v3-upgrade" guideLabel="3.0 upgrade guide" />

<Tip>
**New to Supermemory?** Start with `withSupermemory` for the simplest integration. It automatically injects relevant memories into your prompts.
</Tip>

<CardGroup>
<Card title="Supermemory tools on npm" icon="/icons/hugeicons/package.svg" href="https://www.npmjs.com/package/@supermemory/tools">
    Check out the NPM page for more details
</Card>
<Card title="Supermemory AI SDK" icon="/icons/hugeicons/python.svg" href="https://pypi.org/project/supermemory-openai-sdk/">
    Check out the PyPI page for more details
</Card>
</CardGroup>

---

## withSupermemory wrapper

The simplest way to add memory to your OpenAI client. Wraps your client to automatically inject relevant memories into system prompts.

### Installation

```bash
npm install @supermemory/tools openai
```

### Quick start

```typescript
import OpenAI from "openai"
import { withSupermemory } from "@supermemory/tools/openai"

const openai = new OpenAI()

// Wrap client with memory - memories auto-injected into system prompts
const client = withSupermemory(openai, {
  namespace: "user-123",     // Required: identifies the user/container
  id: "conversation-456", // Required: groups messages into the same document 
  mode: "full",                 // "profile" | "query" | "full"
  addMemory: "always",          // "always" (default) | "never"
})

// Use normally - memories are automatically included
const response = await client.chat.completions.create({
  model: "gpt-5",
  messages: [
    { role: "system", content: "You are a helpful assistant." },
    { role: "user", content: "What's my favorite programming language?" }
  ]
})
```

### Configuration options

```typescript
const client = withSupermemory(openai, {
  // Required: identifies the user/container
  namespace: "user-123",

  // Required: Group messages into the same document
  id: "conv-456",

  // Memory search mode
  mode: "full",  // "profile" (user profile only), "query" (search only), "full" (both)

  // Auto-save conversations as memories (default: "always")
  addMemory: "always",  // "always" | "never"

  // Enable debug logging
  verbose: true,

  // Custom API endpoint
  baseUrl: "https://custom.api.com"
})
```

### Modes explained

| Mode | Description | Use Case |
|------|-------------|----------|
| `profile` | Injects user profile (static + dynamic facts) | General personalization |
| `query` | Searches memories based on user message | Question answering |
| `full` | Both profile and query-based search | Best for chatbots |

### Works with Responses API too

```typescript
const client = withSupermemory(openai, { namespace: "user-123", id: "conv-456", mode: "full" })

// Memories injected into instructions
const response = await client.responses.create({
  model: "gpt-5",
  instructions: "You are a helpful assistant.",
  input: "What do you know about me?"
})
```

### Environment variables

```bash
SUPERMEMORY_API_KEY=your_supermemory_key
OPENAI_API_KEY=your_openai_key
```

---

## Function calling tools

For explicit control over memory operations, use function calling tools. The model decides when to search or add memories.

## Installation

<CodeGroup>

```bash Python
# Using uv (recommended)
uv add supermemory-openai-sdk

# Or with pip
pip install supermemory-openai-sdk
```

```bash JavaScript/TypeScript
npm install @supermemory/tools
```

</CodeGroup>

## Quick start

<CodeGroup>

```python Python SDK
import asyncio
import openai
from supermemory_openai import SupermemoryTools, execute_memory_tool_calls

async def main():
    # Initialize OpenAI client
    client = openai.AsyncOpenAI(api_key="your-openai-api-key")

    # Initialize Supermemory tools
    tools = SupermemoryTools(
        api_key="your-supermemory-api-key",
        config={"project_id": "my-project"}
    )

    # Chat with memory tools
    response = await client.chat.completions.create(
        model="gpt-5",
        messages=[
            {
                "role": "system",
                "content": "You are a helpful assistant with access to user memories."
            },
            {
                "role": "user",
                "content": "Remember that I prefer tea over coffee"
            }
        ],
        tools=tools.get_tool_definitions()
    )

    # Handle tool calls if present
    if response.choices[0].message.tool_calls:
        tool_results = await execute_memory_tool_calls(
            api_key="your-supermemory-api-key",
            tool_calls=response.choices[0].message.tool_calls,
            config={"project_id": "my-project"}
        )
        print("Tool results:", tool_results)

    print(response.choices[0].message.content)

asyncio.run(main())
```

```typescript JavaScript/TypeScript SDK
import { supermemoryTools, getToolDefinitions, createToolCallExecutor } from "@supermemory/tools/openai"
import OpenAI from "openai"

const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY!,
})

// Get tool definitions for OpenAI
const toolDefinitions = getToolDefinitions()

// Create tool executor
const executeToolCall = createToolCallExecutor(process.env.SUPERMEMORY_API_KEY!, {
  namespace: "your-project-id",
})

// Use with OpenAI Chat Completions
const completion = await client.chat.completions.create({
  model: "gpt-5",
  messages: [
    {
      role: "user",
      content: "What do you remember about my preferences?",
    },
  ],
  tools: toolDefinitions,
})

// Execute tool calls if any
if (completion.choices[0]?.message.tool_calls) {
  for (const toolCall of completion.choices[0].message.tool_calls) {
    const result = await executeToolCall(toolCall)
    console.log(result)
  }
}
```

</CodeGroup>

## Configuration

### Memory tools configuration

<CodeGroup>

```python Python Configuration
from supermemory_openai import SupermemoryTools

tools = SupermemoryTools(
    api_key="your-supermemory-api-key",
    config={
        "project_id": "my-project",  # or use container_tags
        "base_url": "https://custom-endpoint.com",  # optional
    }
)
```

```typescript JavaScript Configuration
import { supermemoryTools } from "@supermemory/tools/openai"

const tools = supermemoryTools(process.env.SUPERMEMORY_API_KEY!, {
  namespace: "your-user-id",
  baseUrl: "https://custom-endpoint.com", // optional
})
```

</CodeGroup>

## Available tools

### Search memories

Search through user memories using semantic search:

<CodeGroup>

```python Python
# Search memories
result = await tools.search_memories(
    information_to_get="user preferences",
    limit=10,
    include_full_docs=True
)
print(f"Found {len(result.memories)} memories")
```

```typescript JavaScript
// Search memories
const searchResult = await tools.searchMemories({
  informationToGet: "user preferences",
  limit: 10,
})
console.log(`Found ${searchResult.memories.length} memories`)
```

</CodeGroup>

### Add memory

Store new information in memory:

<CodeGroup>

```python Python
# Add memory
result = await tools.add_memory(
    memory="User prefers tea over coffee"
)
print(f"Added memory with ID: {result.memory.id}")
```

```typescript JavaScript
// Add memory
const addResult = await tools.addMemory({
  memory: "User prefers dark roast coffee",
})
console.log(`Added memory with ID: ${addResult.memory.id}`)
```

</CodeGroup>

## Individual tools

Use tools separately for more granular control:

<CodeGroup>

```python Python Individual Tools
from supermemory_openai import (
    create_search_memories_tool,
    create_add_memory_tool
)

search_tool = create_search_memories_tool("your-api-key")
add_tool = create_add_memory_tool("your-api-key")

# Use individual tools in OpenAI function calling
tools_list = [search_tool, add_tool]
```

```typescript JavaScript Individual Tools
import {
  createSearchMemoriesTool,
  createAddMemoryTool
} from "@supermemory/tools/openai"

const searchTool = createSearchMemoriesTool(process.env.SUPERMEMORY_API_KEY!)
const addTool = createAddMemoryTool(process.env.SUPERMEMORY_API_KEY!)

// Use individual tools
const toolDefinitions = [searchTool.definition, addTool.definition]
```

</CodeGroup>

## Complete chat example

Here's a complete example showing a multi-turn conversation with memory:

<CodeGroup>

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

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

</CodeGroup>

## Error handling

Handle errors gracefully in your applications:

<CodeGroup>

```python Python Error Handling
from supermemory_openai import SupermemoryTools
import openai

async def safe_chat():
    try:
        client = openai.AsyncOpenAI()
        tools = SupermemoryTools(api_key="your-api-key")

        response = await client.chat.completions.create(
            model="gpt-5",
            messages=[{"role": "user", "content": "Hello"}],
            tools=tools.get_tool_definitions()
        )

    except openai.APIError as e:
        print(f"OpenAI API error: {e}")
    except Exception as e:
        print(f"Unexpected error: {e}")
```

```typescript JavaScript Error Handling
import OpenAI from "openai"
import { getToolDefinitions } from "@supermemory/tools/openai"

async function safeChat() {
  try {
    const client = new OpenAI()

    const response = await client.chat.completions.create({
      model: "gpt-5",
      messages: [{ role: "user", content: "Hello" }],
      tools: getToolDefinitions(),
    })

  } catch (error) {
    if (error instanceof OpenAI.APIError) {
      console.error("OpenAI API error:", error.message)
    } else {
      console.error("Unexpected error:", error)
    }
  }
}
```

</CodeGroup>

## API reference

### Python SDK

#### `SupermemoryTools`

**Constructor**
```python
SupermemoryTools(
    api_key: str,
    config: Optional[SupermemoryToolsConfig] = None
)
```

**Methods**
- `get_tool_definitions()` - Get OpenAI function definitions
- `search_memories(information_to_get, limit, include_full_docs)` - Search user memories
- `add_memory(memory)` - Add new memory
- `execute_tool_call(tool_call)` - Execute individual tool call

#### `execute_memory_tool_calls`

```python
execute_memory_tool_calls(
    api_key: str,
    tool_calls: List[ToolCall],
    config: Optional[SupermemoryToolsConfig] = None
) -> List[dict]
```

### JavaScript SDK

#### `supermemoryTools`

```typescript
supermemoryTools(
  apiKey: string,
  config?: { namespace?: string; baseUrl?: string }
)
```

#### `createToolCallExecutor`

```typescript
createToolCallExecutor(
  apiKey: string,
  config?: { namespace?: string; baseUrl?: string }
) -> (toolCall: OpenAI.Chat.ChatCompletionMessageToolCall) => Promise<any>
```

## Environment variables

Set these environment variables:

```bash
SUPERMEMORY_API_KEY=your_supermemory_key
OPENAI_API_KEY=your_openai_key
SUPERMEMORY_BASE_URL=https://custom-endpoint.com  # optional
```

## Development

### Python setup

```bash
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# Setup project
git clone <repository-url>
cd packages/openai-sdk-python
uv sync --dev

# Run tests
uv run pytest

# Type checking
uv run mypy src/supermemory_openai

# Formatting
uv run black src/ tests/
uv run isort src/ tests/
```

### JavaScript setup

```bash
# Install dependencies
npm install

# Run tests
npm test

# Type checking
npm run type-check

# Linting
npm run lint
```

## Next steps

<CardGroup cols={2}>
  <Card title="AI SDK integration" icon="/icons/hugeicons/triangle.svg" href="/integrations/ai-sdk">
    Use with Vercel AI SDK for streamlined development
  </Card>

  <Card title="Add memories" icon="/icons/hugeicons/database-01.svg" href="/ingestion/add-memories">
    Direct API access for advanced memory management
  </Card>
</CardGroup>

---

### Apps Docs Integrations Openclaw (`apps/docs/integrations/openclaw.mdx`)

---
title: "OpenClaw"
sidebarTitle: "OpenClaw"
description: "Supermemory plugin for OpenClaw. Works across Telegram, WhatsApp, Discord, Slack and more."
icon: "/images/openclaw-logo.jpg"
---

[OpenClaw](https://github.com/supermemoryai/openclaw-supermemory) is a multi-platform AI messaging gateway that connects to WhatsApp, Telegram, Discord, Slack, iMessage, and other messaging channels. The Supermemory plugin gives OpenClaw memory across every channel.

<Tip>
**Prefer to keep everything on your machine?** This plugin works with [self-hosted Supermemory](/self-hosting/overview) — run `npx supermemory local`, then `export SUPERMEMORY_BASE_URL="http://localhost:6767"` (or set `baseUrl` in the plugin config) and use the API key printed on first boot.
</Tip>

## Install the plugin

```bash
openclaw plugins install @supermemory/openclaw-supermemory
```

Restart OpenClaw after installing.

## Setup

Run the setup command and enter your API key when prompted.

```bash
openclaw supermemory setup
openclaw gateway restart
```

Enter your API key from the [API Keys](https://console.supermemory.ai/keys) page in the console. That's it.

<AccordionGroup>
  <Accordion title="Advanced setup">
    Configure all options interactively with the advanced setup command:

    ```bash
    openclaw supermemory setup-advanced
    openclaw gateway restart
    ```

    This lets you configure: container tag, auto-recall, auto-capture, capture mode, custom container tags, and more.

    See [Configuration Options](#configuration-options) for all available settings.
  </Accordion>
</AccordionGroup>

## How it works

Once installed, the plugin runs automatically with zero interaction.

- **Auto-Recall** — Before every AI turn, Supermemory is queried for relevant memories and the user's profile. These are injected as context so the AI sees preferences, facts, and semantically similar past conversations.
- **Auto-Capture** — After every AI turn, the conversation exchange is sent to Supermemory for extraction and long-term storage. Supermemory handles deduplication and profile building.
- **Custom Container Tags** — When enabled via advanced setup, define custom memory containers (e.g., `work`, `personal`, `bookmarks`). The AI automatically picks the right container based on your instructions.

## Features

### AI tools

The AI can use these tools autonomously during conversations. With custom container tags enabled, all tools support a `containerTag` parameter.

| Tool | Description |
|------|-------------|
| `supermemory_store` | Save information to long-term memory. |
| `supermemory_search` | Search memories by query with similarity scores. |
| `supermemory_forget` | Delete a memory by query or ID. |
| `supermemory_profile` | View the user profile — persistent facts and recent context. |

### Slash commands

Users can interact with memory directly in chat.

| Command | Description |
|---------|-------------|
| `/remember [text]` | Manually save something to memory. |
| `/recall [query]` | Search memories and see results with similarity scores. |

### CLI commands

Manage your memory from the terminal.

```bash
openclaw supermemory setup              # Configure API key
openclaw supermemory setup-advanced     # Configure all options
openclaw supermemory status             # View current configuration
openclaw supermemory search <query>     # Search memories
openclaw supermemory profile            # View user profile
openclaw supermemory wipe               # Delete all memories (requires confirmation)
```

<a id="configuration-options" />

<AccordionGroup>
  <Accordion title="Manual configuration">
    ### Configuration options

    Set API key (and, for self-hosted instances, the base URL) via environment variables:

    ```bash
    export SUPERMEMORY_OPENCLAW_API_KEY="sm_..."
    export SUPERMEMORY_BASE_URL="http://localhost:6767"   # optional
    ```

    Or configure in `~/.openclaw/openclaw.json`:

    | Key | Type | Default | Description |
    |-----|------|---------|-------------|
    | `apiKey` | `string` | — | Supermemory API key. |
    | `baseUrl` | `string` | `https://api.supermemory.ai` | API endpoint (self-hosted / local). |
    | `containerTag` | `string` | `openclaw_{hostname}` | Root memory namespace. |
    | `autoRecall` | `boolean` | `true` | Inject relevant memories before every AI turn. |
    | `autoCapture` | `boolean` | `true` | Store conversation content after every turn. |
    | `maxRecallResults` | `number` | `10` | Max memories injected into context per turn. |
    | `profileFrequency` | `number` | `50` | Inject full user profile every N turns. |
    | `captureMode` | `string` | `"all"` | `"all"` filters noise. `"everything"` captures all messages. |
    | `debug` | `boolean` | `false` | Verbose debug logs. |
    | `enableCustomContainerTags` | `boolean` | `false` | Enable custom container routing. |
    | `customContainers` | `array` | `[]` | Custom containers with `tag` and `description`. |
    | `customContainerInstructions` | `string` | `""` | Instructions for AI on container routing. |

    ### Full example

    ```json
    {
      "plugins": {
        "entries": {
          "openclaw-supermemory": {
            "enabled": true,
            "config": {
              "apiKey": "${SUPERMEMORY_OPENCLAW_API_KEY}",
              "containerTag": "my_memory",
              "autoRecall": true,
              "autoCapture": true,
              "maxRecallResults": 10,
              "profileFrequency": 50,
              "captureMode": "all",
              "debug": false,
              "enableCustomContainerTags": true,
              "customContainers": [
                { "tag": "work", "description": "Work-related memories" },
                { "tag": "personal", "description": "Personal notes" }
              ],
              "customContainerInstructions": "Store work tasks in 'work', personal stuff in 'personal'"
            }
          }
        }
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## FAQ

<AccordionGroup>
  <Accordion title="How do I install OpenClaw with Supermemory?">
    1. Install the plugin:

    ```bash
    openclaw plugins install @supermemory/openclaw-supermemory
    ```

    2. Run the setup wizard:

    ```bash
    openclaw supermemory setup
    ```

    3. When prompted, paste your API key from the [API Keys](https://console.supermemory.ai/keys) page in the console. The key starts with `sm_`.

    4. Restart OpenClaw to activate the plugin:

    ```bash
    openclaw gateway restart
    ```

    5. Verify the connection:

    ```bash
    openclaw supermemory status
    ```

    That's it — Supermemory is now connected and will automatically recall and capture memories across all your channels.
  </Accordion>

  <Accordion title="I have Twitter bookmarks in a separate container. How do I use them with Supermemory?">
    Use the advanced setup to enable custom container tags and add a read-only Twitter bookmarks container.

    ```bash
    openclaw supermemory setup-advanced
    ```

    When prompted, enter:

    - **API key**: paste your `sm_` key
    - **Container tag**: keep the default (or set your preferred root tag)
    - **Auto recall**: `true` (recommended)
    - **Auto capture**: `true`
    - **Enable custom container tags**: `true`
    - **Custom container tags**: `twitter-bookmarks:Twitter bookmarks saved from Twitter`

    <Tip>
    Custom container tags use the format `tag:description`, separated by a colon. For example: `twitter-bookmarks:Twitter bookmarks saved from Twitter`.
    </Tip>

    When asked for **custom container instructions**, enter something like:

    ```
    Whenever the user asks about Twitter bookmarks, search the twitter-bookmarks container.
    Never save anything to the twitter-bookmarks container — it is read-only and populated directly from Twitter.
    ```

    This ensures the AI references your Twitter bookmarks when relevant but never writes to that container.
  </Accordion>

  <Accordion title="How do I separate work (Slack + Gmail) and personal (Telegram + WhatsApp) memories?">
    Use custom container tags to route memories by channel. Run the advanced setup:

    ```bash
    openclaw supermemory setup-advanced
    ```

    When prompted, enter:

    - **API key**: paste your `sm_` key
    - **Container tag**: keep the default
    - **Auto recall**: `true`
    - **Auto capture**: `true`
    - **Enable custom container tags**: `true`
    - **Custom container tags**:
      - `work:Work-related memories from Slack and Gmail`
      - `personal:Personal memories from Telegram and WhatsApp`

    When asked for **custom container instructions**, enter:

    ```
    When the active channel is slack or gmail, always use the work container for storing and recalling memories. Never mix personal memories into work context.

    When the active channel is telegram or whatsapp, always use the personal container. Do not recall or store work memories in personal conversations.
    ```

    This keeps your work and personal memories completely separated — Slack and Gmail conversations only see `work` memories, while Telegram and WhatsApp only see `personal` memories.
  </Accordion>

  <Accordion title="I'm not technical. How can I set up advanced mode?">
    Ask the OpenClaw agent directly in any chat. No terminal needed.

    ```
    What are all the available configurations for the Supermemory plugin advanced mode?
    ```

    The agent will list every option. Then tell it what you want:

    ```
    Set up my Supermemory plugin with these settings:
    - Auto recall: true
    - Auto capture: true
    - Enable custom container tags: true
    - Custom containers:
      - work: Work-related memories from Slack and Gmail
      - personal: Personal memories from Telegram and WhatsApp
    - Custom instructions: When on Slack or Gmail, use the work container.
      When on Telegram or WhatsApp, use the personal container.
    ```

    Each custom container needs a **tag** and a **description** — e.g. `work: Work-related memories from Slack and Gmail`. The description helps the AI understand what belongs in that container.

    <Tip>
    You can change settings anytime by just telling the agent. For example: "Add a new container called `twitter-bookmarks` with description `Twitter bookmarks saved from Twitter` and make it read-only".
    </Tip>
  </Accordion>

  <Accordion title="All my session memories go into one container. How can I separate them by channel?">
    By default, all session memories across every channel are stored under a single root-level container tag.

    To store specific memories separately, use **custom container tags** — see the FAQ above on separating work and personal memories.

    Automatic per-channel separation is not supported yet. If you need this, let us know — with enough requests, we'll implement it right away.

    <Card title="Request this feature" icon="/icons/hugeicons/mail-01.svg" href="mailto:support@supermemory.com?subject=Feature%20Request%3A%20Per-Channel%20Container%20Tags&body=Hey%2C%0A%0AMy%20name%20is%20%5Byour%20name%5D.%20I%20just%20saw%20that%20you%20offer%20custom%20plugins.%0A%0AThis%20is%20my%20use%20case%3A%20%5Bdescribe%20your%20use%20case%5D%0A%0AI%20would%20love%20to%20have%20separate%20container%20tags%20for%20each%20channel%20so%20my%20session%20memories%20are%20automatically%20isolated.%0A%0AThanks!">
      Email us with your use case.
    </Card>
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="GitHub repository" icon="/images/github-icon.svg" href="https://github.com/supermemoryai/openclaw-supermemory">
    Source code, issues, and detailed README.
  </Card>

  <Card title="Claude Memory Tool" icon="/icons/hugeicons/brain-01.svg" href="/integrations/claude-memory">
    Use Claude's native memory tool with Supermemory as backend.
  </Card>
</CardGroup>

---

### Apps Docs Integrations Opencode (`apps/docs/integrations/opencode.mdx`)

---
title: "OpenCode"
sidebarTitle: "OpenCode"
description: "Supermemory plugin for OpenCode. Keeps memory across coding sessions."
icon: "/images/opencode-logo.svg"
---

[OpenCode-Supermemory](https://github.com/supermemoryai/opencode-supermemory) is an OpenCode plugin that gives your AI persistent memory across sessions. Your agent remembers what you worked on — across sessions, across projects.

<Tip>
**Prefer to keep everything on your machine?** This plugin works with [self-hosted Supermemory](/self-hosting/overview) — run `npx supermemory local`, then set `apiKey` / base URL in `~/.config/opencode/supermemory.jsonc` and use the API key printed on first boot.
</Tip>

## Install the plugin

```bash
bunx opencode-supermemory@latest install
```

For LLM agents (non-interactive):

```bash
bunx opencode-supermemory@latest install --no-tui
```

## Authenticate

Browser login (recommended):

```bash
bunx opencode-supermemory@latest login
```

Check the connection any time:

```bash
bunx opencode-supermemory@latest status
```

Or set an API key manually from the [API Keys](https://console.supermemory.ai/keys) page in the console:

<Tabs>
  <Tab title="macOS / Linux (zsh)">
    ```bash
    echo 'export SUPERMEMORY_API_KEY="sm_..."' >> ~/.zshrc
    source ~/.zshrc
    ```
  </Tab>
  <Tab title="macOS / Linux (bash)">
    ```bash
    echo 'export SUPERMEMORY_API_KEY="sm_..."' >> ~/.bashrc
    source ~/.bashrc
    ```
  </Tab>
  <Tab title="Windows (PowerShell)">
    ```powershell
    [System.Environment]::SetEnvironmentVariable("SUPERMEMORY_API_KEY", "sm_...", "User")
    ```
    Restart your terminal after running this.
  </Tab>
</Tabs>

Ensure your `~/.config/opencode/opencode.jsonc` contains:

```json
{
  "plugin": ["opencode-supermemory"]
}
```

## How it works

Once installed, the plugin runs automatically:

- **Context Injection** — On session start, relevant memories are fetched and injected into the agent's context (user profile, project knowledge, semantic matches).
- **Keyword Detection** — Phrases like "remember" or "save this" trigger automatic storage.
- **Smart Compaction** — At 80% context capacity, sessions are summarized and saved as memories.
- **Privacy Protection** — Content within `<private>` tags never persists.

### Memory scopes

| Scope | Description |
|-------|-------------|
| `user` | Memories that persist across all projects |
| `project` | Memories isolated to the current project (default) |

### Memory types

| Type | Description |
|------|-------------|
| `project-config` | Project configuration and setup details |
| `architecture` | Codebase structure and design patterns |
| `error-solution` | Problems encountered and their fixes |
| `preference` | User preferences and coding style |
| `learned-pattern` | Patterns discovered during sessions |
| `conversation` | Important conversation context |

## Commands

### /supermemory-init

Explore and index your codebase structure into memory:

```
/supermemory-init
```

## Tools

The agent has access to a `supermemory` tool with these modes:

| Mode | Parameters | Function |
|------|------------|----------|
| `add` | content, type?, scope? | Store information |
| `search` | query, scope? | Find relevant memories |
| `profile` | query? | View user preferences |
| `list` | scope?, limit? | Display stored items |
| `forget` | memoryId, scope? | Remove memory |

## Configuration

Create `~/.config/opencode/supermemory.jsonc`:

```jsonc
{
  "apiKey": "sm_...",              // Or use SUPERMEMORY_API_KEY / browser login
  "similarityThreshold": 0.6,      // Minimum match score (0-1)
  "maxMemories": 5,                // Memories per injection
  "maxProjectMemories": 10,        // Project memory listings
  "maxProfileItems": 5,            // Profile facts injected
  "injectProfile": true,           // Include user preferences in context
  "containerTagPrefix": "opencode", // Tag prefix for scoping
  "userContainerTag": "my-user-tag",       // Optional override
  "projectContainerTag": "my-project-tag", // Optional override
  "keywordPatterns": ["log\\s+this"],      // Extra auto-save triggers
  "compactionThreshold": 0.80      // Context usage ratio for summarization
}
```

## Logging

View plugin activity:

```bash
tail -f ~/.opencode-supermemory.log
```

## Next steps

<CardGroup cols={2}>
  <Card title="GitHub repository" icon="/images/github-icon.svg" href="https://github.com/supermemoryai/opencode-supermemory">
    Source code, issues, and detailed README.
  </Card>

  <Card title="Claude Code plugin" icon="/icons/hugeicons/source-code.svg" href="/integrations/claude-code">
    Memory plugin for Claude Code.
  </Card>
</CardGroup>

---

### Apps Docs Integrations Pipecat (`apps/docs/integrations/pipecat.mdx`)

---
title: "Pipecat"
sidebarTitle: "Pipecat (voice)"
description: "Integrate Supermemory with Pipecat for conversational memory in voice AI agents"
icon: "/images/pipecat.svg"
---

Supermemory integrates with [Pipecat](https://github.com/pipecat-ai/pipecat), providing long-term memory capabilities for voice AI agents. Your Pipecat applications will remember past conversations and provide personalized responses based on user history.

## Installation

To use Supermemory with Pipecat, install the required dependencies:

```bash
pip install supermemory-pipecat
```

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](https://console.supermemory.ai).

## Configuration

Supermemory integration is provided through the `SupermemoryPipecatService` class in Pipecat:

```python
from supermemory_pipecat import InputParams, SupermemoryPipecatService

memory = SupermemoryPipecatService(
    api_key=os.getenv("SUPERMEMORY_API_KEY"),
    user_id="unique_user_id",
    session_id="session_123",
    params=InputParams(
        mode="full",            # "profile" | "query" | "full"
        search_limit=10,        # Max memories to retrieve
        search_threshold=0.1,   # Relevance threshold (0.0-1.0)
        system_prompt="Based on previous conversations:\n\n",
    ),
)
```

## Pipeline integration

The `SupermemoryPipecatService` should be positioned between your context aggregator and LLM service in the Pipecat pipeline:

```python
pipeline = Pipeline([
    transport.input(),
    stt,                           # Speech-to-text
    context_aggregator.user(),
    memory,                        # <- Supermemory memory service
    llm,
    tts,                           # Text-to-speech
    transport.output(),
    context_aggregator.assistant(),
])
```

## How it works

When integrated with Pipecat, Supermemory provides two key functionalities:

### 1. Memory retrieval

When a user message 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 LLM context before generation, giving the model awareness of past conversations.

## 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:

### InputParams

```python
InputParams(
    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",
    inject_mode="auto",        # "auto" | "system" | "user"
)
```

| 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                               |
| `inject_mode`      | str   | "auto"                                 | How memories are injected: `"auto"`, `"system"`, or `"user"` |

## Injection modes

The `inject_mode` parameter controls how memories are added to the LLM context:

| Mode       | Behavior                                                                                                                                                                |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"auto"`   | **Auto-detects** based on frame types. If audio frames detected → injects to system prompt (speech-to-speech). If only text frames → injects as user message (STT/TTS). |
| `"system"` | Always injects memories into the system prompt                                                                                                                          |
| `"user"`   | Always injects memories as a user message                                                                                                                               |

## Speech-to-speech models (Gemini Live, etc.)

For speech-to-speech models like Gemini Live, the SDK **automatically detects** audio frames and injects memories into the system prompt. No configuration needed:

```python
from supermemory_pipecat import SupermemoryPipecatService

# Auto-detection works out of the box
memory = SupermemoryPipecatService(
    api_key=os.getenv("SUPERMEMORY_API_KEY"),
    user_id="unique_user_id",
)
```

## Example: Voice agent with memory

Here's a complete example of a Pipecat voice agent with Supermemory integration:

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

## Example: Gemini Live with memory

For a complete example using Gemini Live speech-to-speech with Supermemory, check out the reference implementation:

<Card
  title="Pipecat Memory Example"
  icon="/images/github-icon.svg"
  href="https://github.com/supermemoryai/pipecat-memory"
>
  Full working example with Gemini Live, including frontend and backend code.
</Card>

---

### Apps Docs Integrations Supermemory Sdk (`apps/docs/integrations/supermemory-sdk.mdx`)

---
title: 'Supermemory SDK'
sidebarTitle: "Supermemory SDK"
description: 'Official Python and JavaScript SDKs for Supermemory'
icon: "/images/supermemory.svg"
---

<CardGroup cols={2}>
  <Card title="Python SDK" icon="/icons/hugeicons/python.svg" href="https://pypi.org/project/supermemory/">
    pip install supermemory
  </Card>
  <Card title="JavaScript SDK" icon="/icons/hugeicons/java-script.svg" href="https://www.npmjs.com/package/supermemory">
    npm install supermemory
  </Card>
</CardGroup>

<Tip>
Both SDKs also work against [self-hosted Supermemory](/self-hosting/overview) running `supermemory-server` v0.0.9 or later. Pass `baseUrl: "http://localhost:6767"` (TypeScript) or `base_url="http://localhost:6767"` (Python) when creating the client.
</Tip>

<Tabs>
  <Tab title="TypeScript">
    ## Install the TypeScript SDK

    ```bash
    npm i supermemory
    ```

    ## Start a TypeScript client

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

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

    One rule for every call: URL values first as positional arguments (`namespace`, then `id` where there is one), then a single object with everything else. A namespace scopes memories to one user, workspace, or tenant, and you pass it on every call.

    ## Add

    ```typescript
    const { id, status } = await supermemory.add("user_123", {
      content: "Meeting notes from Q1 planning",
      id: "meeting-q1", // optional, your own id; the response echoes it
      metadata: { category: "engineering", priority: "high" },
      dreaming: "instant",
    });
    ```

    `dreaming` defaults to `"dynamic"`, which batches memory extraction. A fresh namespace can show zero memories and an empty profile for minutes. Pass `dreaming: "instant"` in quick-start flows and anywhere the next step is a memory search or a profile read.

    ## Search

    ```typescript
    const { results, searchTime } = await supermemory.search("user_123", {
      query: "planning notes",
      limit: 10,
    });

    for (const result of results) {
      console.log(result.memory ?? result.chunk, result.similarity);
    }
    ```

    `searchMode` is `"hybrid"` (default), `"memories"`, or `"chunks"`. Filter on metadata with a typed expression:

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

    Two defaults changed from the legacy SDK: `threshold` is now `0.3` (was `0.6`) and `searchMode` is now `hybrid` (was `memories`). Set both explicitly if you compare results against old code.

    ## Profile

    ```typescript
    const { profile } = await supermemory.profile("user_123");
    console.log(profile.static);
    console.log(profile.dynamic);
    console.log(profile.buckets);
    ```

    Buckets group profile facts under names you define:

    ```typescript
    await supermemory.profiles.setBuckets("user_123", {
      buckets: { work: "Job, team, and current projects" },
    });

    const buckets = await supermemory.profiles.getBuckets("user_123");

    await supermemory.profiles.deleteBuckets("user_123", {
      buckets: ["work"],
    });
    ```

    ## List

    ```typescript
    const { documents, pagination } = await supermemory.list("user_123", "documents", {
      page: 1,
      limit: 20,
      sort: "createdAt",
      order: "desc",
    });

    console.log(documents[0]?.system.status, pagination.totalPages);
    ```

    `type` is required: `"documents"`, `"chunks"`, or `"memories"`. The response always has `documents`, `chunks`, `memories`, and `pagination`, and only the requested array is filled. Pass `{ filter }` to narrow the list.

    ## Documents

    ```typescript
    const doc = await supermemory.documents.get("user_123", "meeting-q1", {
      include: ["chunks", "memories"],
    });
    console.log(doc.title, doc.summary, doc.system.status);

    await supermemory.documents.update("user_123", "meeting-q1", {
      metadata: { priority: "low" },
    });

    await supermemory.documents.batchAdd("user_123", {
      documents: [{ content: "First note" }, { content: "Second note" }],
    });

    await supermemory.documents.uploadFile("user_123", { // file is a File or Blob
      file,
      metadata: JSON.stringify({ source: "upload" }),
    });

    const { count, errors } = await supermemory.documents.delete("user_123", {
      ids: ["meeting-q1"],
    });
    ```

    `uploadFile` is multipart, so `metadata` is a JSON string there. `delete` can return partial failures, so check `errors` as well as `count`.

    ## Memories

    ```typescript
    const preview = await supermemory.memories.forgetMatching("user_123", {
      query: "old office address",
      dryRun: true,
    });

    const { count } = await supermemory.memories.forget("user_123", {
      ids: preview.matches.map((match) => match.id),
    });
    ```

    `dryRun` is required on `forgetMatching`. Preview with `dryRun: true`, review `matches`, then forget by id. There is no direct memory create or update: ingest or update the source document instead.

    ## Namespaces

    ```typescript
    const { namespaces, pagination } = await supermemory.namespaces.list({ limit: 50 });

    const ns = await supermemory.namespaces.get("user_123");
    console.log(ns.supportingContext, namespaces[0]?.documentCount, pagination.totalPages);

    await supermemory.namespaces.update("user_123", {
      supportingContext: "A paying customer on the Pro plan",
    });

    await supermemory.namespaces.delete("user_123", { // optional, moves content instead of deleting it
      moveTo: "archive",
    });
    ```

    ## Organization

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

    await supermemory.organization.update({
      organizationalContext: "Acme sells billing software to dental clinics",
    });
    ```

    ## Connectors

    ```typescript
    const { id, authorization } = await supermemory.connectors.create("user_123", {
      provider: "notion",
      redirectUrl: "https://app.example.com/connected",
    });
    // send the user to authorization.url before authorization.expiresAt

    const connectors = await supermemory.connectors.list("user_123");
    const everyConnector = await supermemory.connectors.listAll();

    const connector = await supermemory.connectors.get("user_123", id, {
      include: ["syncs", "picker"],
    });
    console.log(connector.latestRun?.system.status, connector.documentCount);

    await supermemory.connectors.update("user_123", id, {
      documentLimit: 500,
    });

    await supermemory.connectors.sync("user_123", id);

    await supermemory.connectors.delete("user_123", id, { deleteDocuments: true });
    ```

    OAuth providers (`notion`, `google-drive`, `onedrive`, `gmail`, `github`) return an `authorization` link. Config providers start syncing right away and return `authorization: null`:

    ```typescript
    await supermemory.connectors.create("user_123", {
      provider: "web-crawler",
      config: { startUrl: "https://docs.example.com", crawlDepth: 2 },
    });
    ```

    `sync` returns `{ id, status: "queued" }` and responds with 409 if a sync is already running.
  
### Error handling

Every HTTP error throws a `SupermemoryError` with `statusCode`, `body`, and `rawResponse`. Common statuses have their own subclasses, all exported from the package root: `BadRequestError` (400), `UnauthorizedError` (401), `PaymentRequiredError` (402), `ForbiddenError` (403), `NotFoundError` (404), `ConflictError` (409), `InternalServerError` (500), and `ServiceUnavailableError` (503). A timeout throws `SupermemoryTimeoutError`; a network failure is a `SupermemoryError` without a `statusCode`.

```typescript
import { Supermemory, NotFoundError, SupermemoryError } from "supermemory";

try {
  await supermemory.documents.get("user_123", "missing");
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log("gone");
  } else if (error instanceof SupermemoryError) {
    console.error(error.statusCode, error.body);
  } else {
    throw error;
  }
}
```

### Timeouts and retries

The client retries connection errors, 408, 429, and 5xx responses twice with backoff. Tune it on the client or per call; the per-call options object is always the optional last argument.

```typescript
const supermemory = new Supermemory({
  apiKey: process.env.SUPERMEMORY_API_KEY,
  timeoutInSeconds: 30, // default 60
  maxRetries: 0, // default 2
});

await supermemory.search("user_123", { query: "planning notes" }, {
  timeoutInSeconds: 5,
  maxRetries: 3,
  abortSignal: controller.signal,
});
```

</Tab>

  <Tab title="Python">
    ## Install the Python SDK

    ```bash
    pip install supermemory
    ```

    ## Start a Python client

    ```python
    import os
    from supermemory import Supermemory

    client = Supermemory(
        api_key=os.environ.get("SUPERMEMORY_API_KEY"),  # Default, can be omitted
    )

    # Add a document
    result = client.add(
        "user_123",
        content="Meeting notes from Q1 planning",
        id="meeting-q1",  # optional, your own id; the response echoes it
        metadata={"category": "engineering", "priority": "high"},
        dreaming="instant",
    )
    print(result.id, result.status)

    # Search (hybrid by default: memories plus document chunks)
    response = client.search("user_123", query="planning notes", limit=10)
    for hit in response.results:
        print(hit.memory or hit.chunk, hit.similarity)

    # Get user profile
    profile = client.profile("user_123").profile
    print(profile.static)
    print(profile.dynamic)
    print(profile.buckets)
    ```

    ## Run common Python operations

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

    ## Handle Python SDK errors

    Failed requests raise `supermemory.APIStatusError` (`.status_code`, `.body`) or a subclass such as `NotFoundError` or `RateLimitError`. Network failures raise `supermemory.APIConnectionError`. Set `max_retries=` and `timeout=` on the client, or per call. `AsyncSupermemory` has the same methods, awaited.

    Requires Python 3.10+.
  </Tab>
</Tabs>

---

### Apps Docs Integrations Viasocket (`apps/docs/integrations/viasocket.mdx`)

---
title: "viaSocket"
sidebarTitle: "viaSocket"
description: "Connect Supermemory with viaSocket to build automation flows using triggers, API tokens, and actions like Gmail."
icon: "/images/viasocket-icon-ico.ico"
---

Connect Supermemory to viaSocket to build powerful automation flows — search your memory, trigger actions, and wire up services like Gmail, all without writing code.

## Prerequisites

- A Supermemory API key ([get one here](https://console.supermemory.ai/settings))
- A viaSocket account

## Step-by-step tutorial

<Steps>
    <Step title="Generate Supermemory API token">
        - Log in to your [Supermemory account](https://console.supermemory.ai).
        - Go to **Settings → API Key**.
        - Copy your Personal API key.
        - Keep the key secure — treat it like a password.
        

    </Step>

    <Step title="Create a new flow in viaSocket">
        - Click **Create New Flow** in your viaSocket dashboard.
        - In the **Trigger** section, search for and select **Supermemory**.
        - Choose a trigger — **Search Memory** or **Search User Profile**.
        

    </Step>

    <Step title="Connect your Supermemory account">
        - Click **Connect to Supermemory**.
        - Paste your Supermemory API key.
        - Click **Save** to store the connection.
        - Confirm the connection is successfully added before proceeding.
        
    </Step>

    <Step title="Configure the trigger">
        - Provide a **Query** — either a static value or a dynamic input from a previous step.
        - Click **TEST** to run a sample and verify the output.
        - Save the trigger once the test returns expected data.
        
    </Step>

    <Step title="Add an action (example: Gmail send email)">
        - Click **Add Step**.
        - Select **Gmail → Send Email**.
        - Choose an existing Gmail connection or create a new one.
        - Map the required fields:
            - **To** — recipient email address
            - **Subject** — email subject line
            - **Message Body** — use the trigger's `body` object as dynamic input
        - Click **Test** to send a test email.
        - Confirm a **200** response status before saving.
        
    </Step>

    <Step title="Go live & monitor">
        - Click **GO LIVE** to activate your flow.
        - Confirm the activation prompt.
        - Use **Flow View** to inspect the flow structure and **Log View** to monitor executions in real time.
        - If needed, re-run any execution from **Run History**.
        
    </Step>

</Steps>

<Note>
  Make sure your Supermemory API key has the correct permissions before
  connecting. If the TEST step returns no data, double-check the query and
  ensure your Supermemory account has indexed content.
</Note>

You can extend this flow with other actions and services supported by viaSocket.

---

### Apps Docs Integrations Voltagent (`apps/docs/integrations/voltagent.mdx`)

---
title: "VoltAgent"
sidebarTitle: "VoltAgent"
description: "Integrate Supermemory with VoltAgent for long-term memory in AI agents"
icon: "/icons/hugeicons/flash.svg"
---

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

Supermemory integrates with [VoltAgent](https://github.com/VoltAgent/voltagent), providing long-term memory capabilities for AI agents. Your VoltAgent applications will remember past conversations and provide personalized responses based on user history.

<MigrateCli legacy="@supermemory/tools 2.x" guideHref="/migration/tools-v3-upgrade" guideLabel="3.0 upgrade guide" />

<Card title="@supermemory/tools on npm" icon="/icons/hugeicons/package.svg" href="https://www.npmjs.com/package/@supermemory/tools">
    Check out the NPM page for more details
</Card>

## Installation

```bash
npm install @supermemory/tools @voltagent/core ai@^6 @ai-sdk/openai@^3
```

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](https://console.supermemory.ai).

## Quick start

Supermemory provides a `withSupermemory` wrapper that enhances any VoltAgent agent config with automatic memory retrieval and storage:

```typescript
import { withSupermemory } from "@supermemory/tools/voltagent"
import { Agent } from "@voltagent/core"
import { openai } from "@ai-sdk/openai"

// Create an agent with Supermemory memory capabilities
const configWithMemory = withSupermemory({
  agentConfig: {
    name: "my-agent",
    instructions: "You are a helpful assistant.",
    model: openai("gpt-4o"),
  },
  namespace: "user-123",
  id: "conversation-123",
})

const agent = new Agent(configWithMemory)

// Memories are automatically injected and saved
const result = await agent.generateText("What's my name?")
```

<Note>
  **Memory saving is enabled by default** in the VoltAgent integration. To disable it:

  ```typescript
  const configWithMemory = withSupermemory({
    agentConfig: {
      name: "my-agent",
      instructions: "You are a helpful assistant.",
      model: openai("gpt-4o"),
    },
    namespace: "user-123",
    id: "conversation-123",
    addMemory: "never",
  })
  ```
</Note>

## How it works

When integrated with VoltAgent, Supermemory hooks into two lifecycle events:

### 1. Memory retrieval (onPrepareMessages)

Before each LLM call, Supermemory automatically:
- Extracts the user's latest message
- Searches for relevant memories scoped to the `namespace`
- Injects retrieved memories into the system prompt

### 2. Conversation saving (onEnd)

After each agent response, the conversation is saved to Supermemory for future retrieval. This requires a `id` to be set.

## Memory modes

| Mode        | Description                                 | Use Case                       |
| ----------- | ------------------------------------------- | ------------------------------ |
| `"profile"` | Retrieves the user's complete profile       | Personalization without search |
| `"query"`   | Searches memories based on the user's message | Finding relevant past context |
| `"full"`    | Combines profile AND query-based search     | Complete memory (recommended)  |

```typescript
const configWithMemory = withSupermemory({
  agentConfig: {
    name: "my-agent",
    instructions: "You are a helpful assistant.",
    model: openai("gpt-4o"),
  },
  namespace: "user-123",
  id: "conversation-123",
  mode: "full",
})
```

## Configuration options

```typescript
const configWithMemory = withSupermemory({
  // Agent configuration
  agentConfig: {
    name: "my-agent",
    instructions: "You are a helpful assistant.",
    model: openai("gpt-4o"),
  },

  // Required
  namespace: "user-123",        // User/project ID for scoping memories

  // Memory behavior
  mode: "full",                    // "profile" | "query" | "full"
  addMemory: "always",             // "always" | "never"
  id: "conv-456",            // Groups messages into a conversation

  // Search tuning
  searchMode: "hybrid",            // "memories" | "documents" | "hybrid"
  threshold: 0.6,                  // 0.0-1.0 (higher = more accurate)
  limit: 10,                       // Integer from 1 to 100
  rerank: true,                    // Rerank for best relevance
  rewriteQuery: false,             // AI-rewrite query (+400ms latency)

  // Context
  metadata: { source: "voltagent" },  // Attached to saved conversations

  // API
  apiKey: "sk-...",                // Falls back to SUPERMEMORY_API_KEY env var
  baseUrl: "https://api.supermemory.ai",
})
```

| Parameter         | Type     | Default      | Description                                              |
| ----------------- | -------- | ------------ | -------------------------------------------------------- |
| `agentConfig`     | object   | **required** | VoltAgent agent configuration object                     |
| `namespace`    | string   | **required** | User/project ID for scoping memories                     |
| `mode`            | string   | `"profile"`  | Memory retrieval mode                                    |
| `addMemory`       | string   | `"always"`   | Whether to save conversations after each response        |
| `id`        | string   | **required** | Custom ID to group messages into a conversation          |
| `searchMode`      | string   | —            | `"memories"`, `"documents"`, or `"hybrid"`               |
| `threshold`       | number   | —            | Similarity threshold (0 = more results, 1 = more accurate) |
| `limit`           | number   | —            | Maximum number of memory results (integer from 1 to 100) |
| `rerank`          | boolean  | `false`      | Rerank results for relevance                             |
| `rewriteQuery`    | boolean  | `false`      | AI-rewrite query for better results (+400ms)             |
| `entityContext`   | string   | —            | Deprecated and ignored. [Configure it on the container tag instead](/concepts/customization#entity-context). |
| `metadata`        | object   | —            | Custom metadata attached to saved conversations          |
| `promptTemplate`  | function | —            | Custom function to format memory data into prompt        |

When `threshold` or `limit` is omitted, the selected Supermemory backend route applies its own default. Set them explicitly when you need consistent search tuning across modes.

## Search modes

The `searchMode` option controls what type of results are searched:

| Mode          | Description                                            |
| ------------- | ------------------------------------------------------ |
| `"memories"`  | Search only memory entries (atomic facts about the user) |
| `"documents"` | Search only document chunks                            |
| `"hybrid"`    | Search both memories AND document chunks (recommended) |

---

### Apps Docs Integrations Zapier (`apps/docs/integrations/zapier.mdx`)

---
title: "Zapier"
sidebarTitle: "Zapier"
description: "Automate memory management with Supermemory in Zapier workflows"
icon: "/icons/hugeicons/flash.svg"
---

With Supermemory you can now easily add memory to your Zapier workflow steps. Here's how:

## Prerequisites
- A Supermemory API Key. Get yours [here](https://console.supermemory.ai)

## Step-by-step tutorial

For this tutorial, we're building a simple flow that adds incoming emails in Gmail to Supermemory.

<Steps>
    <Step title="Make a flow">
        Open your Zapier account and click on 'Zap' to make a new automation.
        
    </Step>
    <Step title="Add Gmail node">
        Add a new Gmail node that gets triggered on every new email. Connect to your Google account.
        
    </Step>
    <Step title="Add code block">
        Now, add a new 'Code by Zapier' block. Set it up to run Python.

        In the **Input Data** section, map the content field to the Gmail raw snippet.

        
    </Step>
    <Step title="Integrate Supermemory">
        Since we're ingesting data here, we'll use the add document endpoint. The namespace (`gmail` here) is part of the URL.

        Add the following code block:
        
        ```python 
            import requests

            url = "https://api.supermemory.ai/ns/gmail/document"

            payload = { "content": inputData['content'] }
            headers = {
                "Authorization": "Bearer YOUR_SM_API_KEY",
                "Content-Type": "application/json"
            }

            response = requests.post(url, json=payload, headers=headers)

            print(response.json())
        ```

        The `inputData['content']` field maps to the Gmail content fetched from Zapier. 

        
    </Step>
</Steps>

<Note>
    Sometimes Zapier might show an error on the first test run. It usually works right after. Weird bug, we know.
</Note>


You can perform other operations like search, filtering, user profiles, etc., by using other Supermemory API endpoints which can be found in our API Reference tab.

---

### Apps Docs Memorybench Extend Benchmark (`apps/docs/memorybench/extend-benchmark.mdx`)

---
title: "Building a benchmark"
description: "Add a custom benchmark dataset when the built-in ones don't match your use case"
sidebarTitle: "Building a benchmark"
icon: "/icons/hugeicons/test-tube-01.svg"
---

MemoryBench ships with three datasets — [LoCoMo](https://github.com/snap-research/locomo), [LongMemEval](https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned), and [ConvoMem](https://huggingface.co/datasets/Salesforce/ConvoMem) — but they won't cover every product. If your agent has a workflow the built-in benchmarks don't exercise (a specific domain, a scenario your users actually hit, a regression you want to guard against), you can add your own benchmark dataset and every provider — including Supermemory — runs against it the same way.

## Choosing between the built-in datasets

Before building your own, check whether one of the existing datasets already covers what you need:

| Benchmark | Best for | Primary challenge |
|---|---|---|
| **LoCoMo** | Personal assistants, support bots — anything with recurring sessions over days/weeks | Temporal context, cross-session recall |
| **LongMemEval** | RAG / document search, knowledge bases | Information density, precise retrieval, synthesis |
| **ConvoMem** | Dialogue systems, interview bots, meeting assistants | Reference resolution within a single conversation |

You can also run all three to get a full picture of where a memory system is strong or weak — a system that's great at LoCoMo but weak at LongMemEval is good at temporal recall but struggles with dense information retrieval, for example.

## The benchmark interface

Every dataset — built-in or custom — implements the same interface (`src/types/benchmark.ts`):

```typescript
interface Benchmark {
  name: string
  load(config?: BenchmarkConfig): Promise<void>
  getQuestions(filter?: QuestionFilter): UnifiedQuestion[]
  getHaystackSessions(questionId: string): UnifiedSession[]
  getGroundTruth(questionId: string): string
  getQuestionTypes(): QuestionTypeRegistry
}
```

Sessions — the raw conversational data a provider ingests before being asked a question — are normalized to a single shape regardless of benchmark:

```typescript
interface UnifiedSession {
  sessionId: string
  messages: Array<{ role: "user" | "assistant"; content: string }>
  metadata?: {
    date?: string           // ISO format
    formattedDate?: string  // human readable
    [key: string]: any
  }
}
```

## Adding your own benchmark

1. Create `src/benchmarks/mybenchmark/index.ts` implementing the `Benchmark` interface above
2. `load()` parses your dataset (JSON, CSV, whatever you have) into `UnifiedSession`s and questions
3. `getQuestions()` returns your question set, each with a ground-truth answer and a question type
4. Register it in `src/benchmarks/index.ts` and add the name to `BenchmarkName` in `src/types/benchmark.ts`
5. Run it exactly like a built-in benchmark:

```bash
bun run src/index.ts run -p supermemory -b mybenchmark
bun run src/index.ts compare -p supermemory,mem0,zep -b mybenchmark
```

Question types are your own vocabulary — group questions however matters to your product (`billing_history`, `escalation_context`, `preference_drift`, etc.), and the final report breaks accuracy down per type so you can see exactly where a provider is strong or weak on *your* scenarios, not a generic academic one.

<Note>
Full interface details and a working template: [`src/benchmarks/README.md`](https://github.com/supermemoryai/memorybench/blob/main/src/benchmarks/README.md) in the repo.
</Note>

---

## Next

<CardGroup cols={2}>
  <Card title="Adding a provider" icon="/icons/hugeicons/plug-01.svg" href="/memorybench/extend-provider">
    Register a memory system to run against your new benchmark.
  </Card>
  <Card title="Measuring results" icon="/icons/hugeicons/dashboard-speed-01.svg" href="/memorybench/memscore">
    How accuracy, latency, and MemScore are computed.
  </Card>
</CardGroup>

---

### Apps Docs Memorybench Extend Provider (`apps/docs/memorybench/extend-provider.mdx`)

---
title: "Adding a provider"
description: "Register your own memory implementation so MemoryBench can score and compare it"
sidebarTitle: "Adding a provider"
icon: "/icons/hugeicons/plug-01.svg"
---

A "provider" in MemoryBench is any memory or RAG system that can ingest sessions and answer a search query — Supermemory, Mem0, and Zep ship as built-in providers, and **your own memory implementation is just another provider**. Once it's registered, it runs through the exact same pipeline and gets scored on the exact same footing as everything else.

There are two ways to do this: let the skill generate the provider for you, or write it by hand.

## The fast path: the MemoryBench skill

MemoryBench ships a Claude Code skill (`benchmark-context`) that automates the whole flow — from reading your code to a finished comparison report — without you writing any MemoryBench-specific code yourself.

```bash
# Run from your project root, not from inside memorybench
/memorybench
```

It walks through 7 phases:

| Phase | What happens |
|---|---|
| **1. Setup** | Clones `memorybench` into `./memorybench` and installs dependencies |
| **2. Discovery** | An agent reads your memory code to find its init, ingest, and search methods |
| **3. Code generation** | Generates a provider adapter implementing the `Provider` interface, adapted to your code |
| **4. Registration** | Registers the provider in the framework's types and config |
| **5. Configuration** | Asks for your API keys (your provider, any comparison providers, and a judge model) and writes `.env.local` |
| **6. Validation** | Runs one question end-to-end to confirm ingest/search actually work before committing to a full run |
| **7. Benchmark execution** | Runs the full comparison and reports accuracy, latency, and context-token results side by side |

Before it does anything, it asks 5 quick questions: your provider's name, where your memory code lives, which [benchmark](/memorybench/extend-benchmark) to run, which providers to compare against (Supermemory, Mem0, Zep, or the no-API-key `filesystem`/`rag` baselines), and how many questions to sample (5 for a quick check, 20 for a real signal, or the full set).

<Note>
Run it from **your project's root**, not from inside `memorybench` — the skill clones the framework as a subdirectory and analyzes your code via relative paths.
</Note>

## Doing it by hand

If you'd rather write the adapter yourself, every provider implements the same interface (`src/types/provider.ts`):

```typescript
interface Provider {
  name: string
  prompts?: ProviderPrompts
  initialize(config: ProviderConfig): Promise<void>
  ingest(sessions: UnifiedSession[], options: IngestOptions): Promise<IngestResult>
  awaitIndexing(result: IngestResult, containerTag: string): Promise<void>
  search(query: string, options: SearchOptions): Promise<unknown[]>
  clear(containerTag: string): Promise<void>
}
```

| Method | Responsibility |
|---|---|
| `initialize()` | Set up your client with an API key / config |
| `ingest()` | Push benchmark sessions into your system, return the resulting document IDs |
| `awaitIndexing()` | Block until those documents are actually searchable — a no-op if your system indexes synchronously, a poll loop with backoff if it's async |
| `search()` | Run a query, return results in whatever shape your system returns them |
| `clear()` | Delete everything under a `containerTag`, so runs don't bleed into each other |

Steps:

1. Create `src/providers/myprovider/index.ts` implementing `Provider`
2. Register it in `src/providers/index.ts`
3. Add the name to `ProviderName` in `src/types/provider.ts`
4. Add its config (API key, base URL, etc.) in `src/utils/config.ts`
5. Optionally override `prompts` (`ProviderPrompts`) if your search results need custom formatting before they're handed to the answering LLM or the judge

```bash
bun run src/index.ts test -p myprovider -b locomo -q question_1   # one question, fast sanity check
bun run src/index.ts run -p myprovider -b locomo                  # full run
bun run src/index.ts compare -p myprovider,supermemory,mem0 -b locomo -l 20
```

<Note>
Full interface, including async-indexing and custom-prompt examples: [`src/providers/README.md`](https://github.com/supermemoryai/memorybench/blob/main/src/providers/README.md) in the repo.
</Note>

---

## Next

<CardGroup cols={2}>
  <Card title="Building a benchmark" icon="/icons/hugeicons/test-tube-01.svg" href="/memorybench/extend-benchmark">
    Run your provider against a dataset built for your own use case.
  </Card>
  <Card title="Measuring results" icon="/icons/hugeicons/dashboard-speed-01.svg" href="/memorybench/memscore">
    What the report actually tells you.
  </Card>
</CardGroup>

---

### Apps Docs Memorybench Memscore (`apps/docs/memorybench/memscore.mdx`)

---
title: "Measuring results"
description: "How MemoryBench scores a run with qualitative judging and quantitative metrics."
sidebarTitle: "Measuring"
icon: "/icons/hugeicons/dashboard-speed-01.svg"
---

Every question goes through the same pipeline (ingest → search → answer → evaluate → report) and produces two kinds of signal: a qualitative judgment on whether the answer was right, and quantitative measurements of how fast and how expensive it was to get there. MemoryBench keeps both instead of collapsing everything into a single number.

## Qualitative: was the answer right?

Correctness isn't decided by string matching — a **judge LLM** (GPT-4o, Claude Sonnet, or Gemini Flash, your choice) compares the provider's answer against ground truth and returns a verdict:

```typescript
{ score: 0 | 1, label: "correct" | "incorrect", explanation: string }
```

The judge is judge-agnostic on purpose — score the same run with two different judges if you want to sanity-check that a result isn't an artifact of one model's grading bias. The prompt the judge uses can also vary by question type (temporal questions get graded differently than abstention questions, for example), and providers can supply their own judge prompts if their answers need custom framing.

```bash
# Grade a run with a different judge
bun run src/index.ts run -p supermemory -b locomo -j sonnet-4
```

## Quantitative: how fast, how much

Alongside the correctness verdict, every question also records hard numbers:

| Metric | What it measures |
|---|---|
| **Accuracy** | Judge score (0–1) averaged across all questions, and broken down per question type |
| **Latency** | Search time and answer-generation time per question (p50 / p95 across a run) |
| **Context tokens** | How much context was sent to the answering model — a proxy for retrieval cost |
| **Success rate** | Percentage of questions that completed without erroring (failures are excluded from accuracy, not counted against it) |

## MemScore

The report doesn't reduce these to one score. **MemScore** reports the three that matter for a production decision side by side:

```
MemScore: 86% / 145ms / 1823tok
          ▲     ▲       ▲
          │     │       └─ context tokens sent to the answering model (cost)
          │     └───────── search latency
          └─────────────── answer accuracy vs. ground truth
```

A provider that's 2% more accurate but 5x slower and 3x more expensive in context tokens isn't unambiguously "better" — MemScore leaves that trade-off to you instead of picking weights for you.

## Reading the numbers

Rough bands, from running MemoryBench across the built-in benchmarks:

| Accuracy | Read |
|---|---|
| 80%+ | Excellent, production-ready |
| 70–80% | Good, some room to improve |
| 60–70% | Adequate, likely needs tuning |
| `<60%` | Investigate — retrieval, prompts, or indexing likely need work |

| Search latency | Read |
|---|---|
| `<100ms` | Excellent (vector-search level) |
| 100–300ms | Good, typical API latency |
| 300–500ms | Adequate for most use cases |
| `>500ms` | Slow — worth optimizing |

Per-question-type breakdowns are usually more useful than the headline number: strong on LoCoMo but weak on LongMemEval means good temporal/cross-session recall but weak dense-retrieval; the reverse means the opposite. That's what tells you what to actually go fix.

## Digging into a specific run

```bash
bun run src/index.ts status -r my-run             # progress / summary
bun run src/index.ts show-failures -r my-run       # full context on what got graded wrong
bun run src/index.ts serve                         # web UI at localhost:3000 for visual inspection
```

Results and checkpoints for every run live at `data/runs/{runId}/report.json`, with per-question-type accuracy, latency percentiles, and per-question token counts.

---

## Next

<CardGroup cols={2}>
  <Card title="Adding a provider" icon="/icons/hugeicons/plug-01.svg" href="/memorybench/extend-provider">
    Get your own system into a run that produces these numbers.
  </Card>
  <Card title="Building a benchmark" icon="/icons/hugeicons/test-tube-01.svg" href="/memorybench/extend-benchmark">
    Measure against scenarios specific to your product.
  </Card>
</CardGroup>

---

### Apps Docs Migration Api V5 Document Reads (`apps/docs/migration/api-v5-document-reads.mdx`)

---
title: "Migrate content management to v5"
description: "Upgrade document retrieval, resource lists, and document or memory deletion"
sidebarTitle: "Content management"
---

import MigrateCli from "/snippets/migrate-cli.mdx";
import ContentManagement from "/snippets/api-v5-content-management.mdx";

<MigrateCli legacy="v3 or v4" guideHref="/migration/api-v5" guideLabel="full v5 migration guide" />

## Migration details

<ContentManagement />

---

### Apps Docs Migration Api V5 Document Updates (`apps/docs/migration/api-v5-document-updates.mdx`)

---
title: "Migrate document and file updates to v5"
description: "Choose append, replacement, or metadata-only updates deliberately"
sidebarTitle: "Document updates"
---

import MigrateCli from "/snippets/migrate-cli.mdx";
import DocumentUpdates from "/snippets/api-v5-document-updates.mdx";

<MigrateCli legacy="v3 or v4" guideHref="/migration/api-v5" guideLabel="full v5 migration guide" />

## Migration details

<DocumentUpdates />

---

### Apps Docs Migration Api V5 Document Writes (`apps/docs/migration/api-v5-document-writes.mdx`)

---
title: "Migrate document ingestion to v5"
description: "Upgrade single, batch, and file ingestion without changing append behavior"
sidebarTitle: "Document ingestion"
---

import MigrateCli from "/snippets/migrate-cli.mdx";
import DocumentIngestion from "/snippets/api-v5-document-ingestion.mdx";

<MigrateCli legacy="v3 or v4" guideHref="/migration/api-v5" guideLabel="full v5 migration guide" />

## Migration details

<DocumentIngestion />

---

### Apps Docs Migration Api V5 Filters (`apps/docs/migration/api-v5-filters.mdx`)

---
title: "Migrate metadata filters to v5"
description: "Convert legacy Query filters into strict, type-safe v5 filter expressions"
sidebarTitle: "Typed filters"
---

import MigrateCli from "/snippets/migrate-cli.mdx";
import Filters from "/snippets/api-v5-filters.mdx";

<MigrateCli legacy="v3 or v4" guideHref="/migration/api-v5" guideLabel="full v5 migration guide" />

## Migration details

<Filters />

---

### Apps Docs Migration Api V5 Memory Forgetting (`apps/docs/migration/api-v5-memory-forgetting.mdx`)

---
title: "Migrate memory forgetting to v5"
description: "Replace legacy exact and semantic forgetting with one response contract"
sidebarTitle: "Memory forgetting"
---

import MigrateCli from "/snippets/migrate-cli.mdx";
import MemoryForgetting from "/snippets/api-v5-memory-forgetting.mdx";

<MigrateCli legacy="v3 or v4" guideHref="/migration/api-v5" guideLabel="full v5 migration guide" />

## Migration details

<MemoryForgetting />

---

### Apps Docs Migration Api V5 Organization (`apps/docs/migration/api-v5-organization.mdx`)

---
title: "Migrate organization settings to v5"
description: "Reduce organization settings to shared context and namespace count"
sidebarTitle: "Organization settings"
---

import MigrateCli from "/snippets/migrate-cli.mdx";
import Organization from "/snippets/api-v5-organization.mdx";

<MigrateCli legacy="v3 or v4" guideHref="/migration/api-v5" guideLabel="full v5 migration guide" />

## Migration details

<Organization />

---

### Apps Docs Migration Api V5 Profiles (`apps/docs/migration/api-v5-profiles.mdx`)

---
title: "Migrate profiles and buckets to v5"
description: "Separate profile retrieval from search and manage namespace-owned buckets"
sidebarTitle: "Profiles and buckets"
---

import MigrateCli from "/snippets/migrate-cli.mdx";
import Profiles from "/snippets/api-v5-profiles.mdx";

<MigrateCli legacy="v3 or v4" guideHref="/migration/api-v5" guideLabel="full v5 migration guide" />

## Migration details

<Profiles />

---

### Apps Docs Migration Api V5 Recall (`apps/docs/migration/api-v5-recall.mdx`)

---
title: "Migrate search to v5"
description: "Upgrade search modes, filters, included context, defaults, and response readers"
sidebarTitle: "Search"
---

import MigrateCli from "/snippets/migrate-cli.mdx";
import Search from "/snippets/api-v5-search.mdx";

<MigrateCli legacy="v3 or v4" guideHref="/migration/api-v5" guideLabel="full v5 migration guide" />

## Migration details

<Search />

---

### Apps Docs Migration Api V5 Rollout (`apps/docs/migration/api-v5-rollout.mdx`)

---
title: "Verify and roll out a v5 migration"
description: "Prove behavioral parity, detect intentional differences, and cut over safely"
sidebarTitle: "Verification and rollout"
---

import MigrateCli from "/snippets/migrate-cli.mdx";
import Rollout from "/snippets/api-v5-rollout.mdx";

<MigrateCli legacy="v3 or v4" guideHref="/migration/api-v5" guideLabel="full v5 migration guide" />

## Migration details

<Rollout />

---

### Apps Docs Migration Api V5 Settings (`apps/docs/migration/api-v5-settings.mdx`)

---
title: "Migrate container tags to namespaces"
description: "Upgrade namespace discovery, settings, deletion, and moves"
sidebarTitle: "Namespaces"
---

import MigrateCli from "/snippets/migrate-cli.mdx";
import Namespaces from "/snippets/api-v5-namespaces.mdx";

<MigrateCli legacy="v3 or v4" guideHref="/migration/api-v5" guideLabel="full v5 migration guide" />

## Migration details

<Namespaces />

---

### Apps Docs Migration Api V5 (`apps/docs/migration/api-v5.mdx`)

---
title: "Migrate Supermemory v3/v4 to v5"
description: "Upgrade legacy API calls to the namespace-scoped v5 API"
sidebarTitle: "Legacy API to v5"
icon: "arrow-up-right"
---

import AgentPrompt from "/snippets/api-v5-agent-prompt.mdx";
import Overview from "/snippets/api-v5-overview.mdx";
import DocumentIngestion from "/snippets/api-v5-document-ingestion.mdx";
import DocumentUpdates from "/snippets/api-v5-document-updates.mdx";
import ContentManagement from "/snippets/api-v5-content-management.mdx";
import Search from "/snippets/api-v5-search.mdx";
import Profiles from "/snippets/api-v5-profiles.mdx";
import MemoryForgetting from "/snippets/api-v5-memory-forgetting.mdx";
import Namespaces from "/snippets/api-v5-namespaces.mdx";
import Organization from "/snippets/api-v5-organization.mdx";
import Filters from "/snippets/api-v5-filters.mdx";
import Rollout from "/snippets/api-v5-rollout.mdx";
import Sdks from "/snippets/api-v5-sdks.mdx";
import Completion from "/snippets/api-v5-completion.mdx";

<AgentPrompt />

<Overview />

## SDKs, tools and the CLI

<Sdks />

## Document ingestion

<DocumentIngestion />

## Document updates

<DocumentUpdates />

## Content management

<ContentManagement />

## Search

<Search />

## Profiles and buckets

<Profiles />

## Memory forgetting

<MemoryForgetting />

## Namespaces

<Namespaces />

## Organization settings

<Organization />

## Typed filters

<Filters />

## Verification and rollout

<Rollout />

<Completion />

---

### Apps Docs Migration From Mem0 (`apps/docs/migration/from-mem0.mdx`)

---
title: "Migrating from Mem0 to Supermemory"
description: "Complete guide to migrate your data and applications from Mem0 to Supermemory"
sidebarTitle: "From Mem0"
---

Migrating from Mem0 to Supermemory is straightforward. This guide walks you through exporting your memories from Mem0 and importing them into Supermemory.

## Why migrate to Supermemory?

Supermemory offers enhanced capabilities over Mem0:
- **Knowledge graph** architecture for better context relationships
- **Multiple content types** (URLs, PDFs, images, videos)
- **Generous free tier** (100k tokens) with affordable pricing
- **Multiple integration options** (API, MCP, SDKs)

## Quick migration (all-in-one)

Complete migration in one script:

```python
from mem0 import MemoryClient
from supermemory import Supermemory
import json, time

# Export from Mem0
mem0 = MemoryClient(api_key="your_mem0_api_key")
export = mem0.create_memory_export(
    schema={"type": "object", "properties": {"memories": {"type": "array", "items": {"type": "object"}}}},
    filters={}
)
time.sleep(5)
data = mem0.get_memory_export(memory_export_id=export["id"])

# Import to Supermemory
supermemory = Supermemory(api_key="your_supermemory_api_key")
for memory in data["memories"]:
    if memory.get("content"):
        supermemory.add("imported_from_mem0", content=memory["content"])
        print(f"✅ {memory['content'][:50]}...")

print("Migration complete!")
```

## Step-by-step migration

<Steps>
  <Step title="Export from Mem0">
    Mem0 provides two ways to export your memories:

    ### Option 1: Export via dashboard (recommended)
    1. Log into your [Mem0 dashboard](https://app.mem0.ai)
    2. Navigate to the export section
    3. Download your memories as JSON

    ### Option 2: Export via API

    Simple script to export all your memories from Mem0:

    ```python
    from mem0 import MemoryClient
    import json
    import time

    # Connect to Mem0
    client = MemoryClient(api_key="your_mem0_api_key")

    # Create export job
    schema = {
        "type": "object",
        "properties": {
            "memories": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "id": {"type": "string"},
                        "content": {"type": "string"},
                        "metadata": {"type": "object"},
                        "created_at": {"type": "string"}
                    }
                }
            }
        }
    }

    response = client.create_memory_export(schema=schema, filters={})
    export_id = response["id"]

    # Wait and retrieve
    print("Exporting memories...")
    time.sleep(5)
    export_data = client.get_memory_export(memory_export_id=export_id)

    # Save to file
    with open("mem0_export.json", "w") as f:
        json.dump(export_data, f, indent=2)

    print(f"Exported {len(export_data['memories'])} memories")
    ```
  </Step>

  <Step title="Set up Supermemory">
    Create your Supermemory account and get your API key:

    1. Sign up at [console.supermemory.ai](https://console.supermemory.ai)
    2. Create a new project
    3. Generate an API key from the dashboard

    ```bash
    # Set your environment variable
    export SUPERMEMORY_API_KEY="your_supermemory_api_key"
    ```
  </Step>

  <Step title="Import to Supermemory">
    Simple script to import your Mem0 memories into Supermemory:

    ```python
    import json
    from supermemory import Supermemory

    # Load your Mem0 export
    with open("mem0_export.json", "r") as f:
        mem0_data = json.load(f)

    # Connect to Supermemory
    client = Supermemory(api_key="your_supermemory_api_key")

    # Import memories
    for memory in mem0_data["memories"]:
        content = memory.get("content", "")

        # Skip empty memories
        if not content:
            continue

        # Import to Supermemory
        try:
            result = client.add(
                "imported_from_mem0",
                content=content,
                metadata={
                    "source": "mem0",
                    "created_at": memory.get("created_at"),
                    **(memory.get("metadata") or {})
                }
            )
            print(f"Imported: {content[:50]}...")
        except Exception as e:
            print(f"Failed: {e}")

    print("Migration complete!")
    ```
  </Step>
</Steps>

## API migration reference

Here's how common Mem0 operations map to Supermemory:

### Adding memories

<CodeGroup>

```python mem0
from mem0 import MemoryClient

client = MemoryClient(api_key="...")
client.add(
    messages="User prefers dark mode",
    user_id="alice"
)
```

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

const client = new Supermemory({ apiKey: "..." });
await client.add("user_alice", {
  content: "User prefers dark mode",
});
```

</CodeGroup>

### Searching memories

<CodeGroup>

```python Mem0
results = client.search(
    query="user preferences",
    user_id="alice"
)
```

```typescript Supermemory
const { results } = await client.search("user_alice", {
  query: "user preferences",
  searchMode: "memories",
});
```

</CodeGroup>

### Getting all memories

<CodeGroup>

```python Mem0
memories = client.get_all(
    user_id="alice"
)
```

```typescript Supermemory
const { memories } = await client.list("user_alice", "memories", {
  limit: 100,
});
```

</CodeGroup>

### Deleting memories

<CodeGroup>

```python Mem0
client.delete(memory_id="mem_123")
```

```typescript Supermemory
await client.documents.delete("user_alice", {
  ids: ["mem_123"],
});
```

</CodeGroup>

<Note>
For enterprise migrations, [contact us](mailto:support@supermemory.ai) for assistance.
</Note>

## Next steps

1. [Explore](/concepts/how-it-works) how Supermemory works
2. Read the [quickstart](/quickstart) and add and retrieve your first memories
3. [Connect](/connectors/overview) to Google Drive, Notion, and OneDrive with automatic syncing

---

### Apps Docs Migration From Zep (`apps/docs/migration/from-zep.mdx`)

---
title: "Migrating from Zep to Supermemory"
description: "Quick guide to migrate from Zep to Supermemory"
sidebarTitle: "From Zep"
---

## Key differences

| Zep AI | Supermemory |
|--------|-------------|
| Sessions & Messages | Documents & Namespaces |
| `session.create()` | Use the `namespace` parameter |
| `memory.add(session_id, ...)` | `client.add(namespace, { content })` |
| `memory.search(session_id, {text: ...})` | `client.search(namespace, { query })` |

## Installation

<CodeGroup>

```bash Python
pip install supermemory
```

```bash TypeScript
npm install supermemory
```

</CodeGroup>

<CodeGroup>

```python Python
from supermemory import Supermemory
client = Supermemory(api_key="your-api-key")
```

```typescript TypeScript
import { Supermemory } from "supermemory";
const client = new Supermemory({ apiKey: "your-api-key" });
```

</CodeGroup>

## API mapping

### Session management

<CodeGroup>

```python Zep AI
session = client.session.create(
    session_id="user_123",
    user_id="user_123"
)
```

```typescript Supermemory
// No explicit session creation, use a namespace
const namespace = "user_123";
```

</CodeGroup>

### Adding memories

<CodeGroup>

```python Zep AI
client.memory.add(
    session_id="user_123",
    memory={"content": "User prefers dark mode"}
)
```

```typescript Supermemory
await client.add("user_123", {
  content: "User prefers dark mode",
});
```

</CodeGroup>

### Searching

<CodeGroup>

```python Zep AI
results = client.memory.search(
    session_id="user_123",
    search_payload={"text": "preferences", "limit": 5}
)
```

```typescript Supermemory
const { results } = await client.search("user_123", {
  query: "preferences",
  limit: 5,
});
```

</CodeGroup>

### Getting all memories

<CodeGroup>

```python Zep AI
memories = client.memory.get(session_id="user_123")
```

```typescript Supermemory
const { documents } = await client.list("user_123", "documents", {
  limit: 100,
});
```

</CodeGroup>

## Migration steps

1. **Replace client initialization** - Use Supermemory client instead of Zep
2. **Map sessions to namespaces** - Replace `session_id="user_123"` with `namespace: "user_123"`
3. **Update method calls** - Use `add()` and `search()` instead of `memory.add()` and `memory.search()`
4. **Change search parameter** - Use `query` instead of `text`
5. **Handle async processing** - Documents process asynchronously (status: `queued` → `done`)

## Complete example

<CodeGroup>

```python Zep AI
from zep_python import ZepClient

client = ZepClient(api_key="...")
session = client.session.create(session_id="user_123", user_id="user_123")

client.memory.add("user_123", {
    "content": "I love Python",
    "role": "user"
})

results = client.memory.search("user_123", {
    "text": "programming",
    "limit": 3
})
```

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

const client = new Supermemory({ apiKey: "..." });
const namespace = "user_123";

await client.add(namespace, {
  content: "I love Python",
  metadata: { role: "user" },
});

const { results } = await client.search(namespace, {
  query: "programming",
  limit: 3,
});
```

</CodeGroup>

## Important notes

- **No session creation needed** - Just pass `namespace` on each call
- **Messages are documents** - Store with `metadata.role` and `metadata.type`
- **Async processing** - Documents may take a moment to be searchable. Pass `dreaming: "instant"` on `add` when you need memories right away
- **Response structure** - `search` returns `results`, each with a `memory` or `chunk` and a `similarity` score

## Migrating existing data

### Quick migration (all-in-one)

Complete migration in one script:

<CodeGroup>

```typescript TypeScript
import { ZepClient } from "@getzep/zep-js";
import { Supermemory } from "supermemory";

// Initialize clients
const zep = new ZepClient({ apiKey: "your_zep_api_key" });
const supermemory = new Supermemory({ apiKey: "your_supermemory_api_key" });

// Export from Zep and import to Supermemory
const sessionIds = ["session_1", "session_2"]; // Add your session IDs

for (const sessionId of sessionIds) {
  const memory = await zep.memory.get(sessionId);
  const memories = memory?.memories || [];

  for (const mem of memories) {
    if (mem.content) {
      await supermemory.add(`session:${sessionId}:user:${memory.user_id || "unknown"}`, {
        content: mem.content,
        metadata: {
          role: mem.role,
          type: "message",
          original_uuid: mem.uuid,
          ...mem.metadata
        },
      });
      console.log(`✅ Imported: ${mem.content.substring(0, 50)}...`);
    }
  }
}

console.log("Migration complete!");
```

```python Python
from zep_python import ZepClient
from supermemory import Supermemory

# Initialize clients
zep = ZepClient(api_key="your_zep_api_key")
supermemory = Supermemory(api_key="your_supermemory_api_key")

# Export from Zep and import to Supermemory
session_ids = ["session_1", "session_2"]  # Add your session IDs

for session_id in session_ids:
    memory = zep.memory.get(session_id)
    memories = memory.memories if memory else []

    for mem in memories:
        if mem.content:
            supermemory.add(
                f"session:{session_id}:user:{memory.user_id or 'unknown'}",
                content=mem.content,
                metadata={
                    "role": mem.role,
                    "type": "message",
                    "original_uuid": mem.uuid,
                    **(mem.metadata or {}),
                },
            )
            print(f"✅ Imported: {mem.content[:50]}...")

print("Migration complete!")
```

</CodeGroup>

### Full migration script

For a complete migration script with error handling, verification, and progress tracking, copy this TypeScript script:

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


## Resources

- [Supermemory SDKs](/integrations/supermemory-sdk)
- [API Reference](/api-reference/overview)
- [Search Documentation](/recall/search)

---

### Apps Docs Migration Tools V2 Upgrade (`apps/docs/migration/tools-v2-upgrade.mdx`)

---
title: 'Upgrading @supermemory/tools to v2.0.0'
description: "Migrate from @supermemory/tools 1.4.x to 2.0.0: the config-object signature, customId and new defaults."
sidebarTitle: 'Tools: v1.4 → 2.0'
---

`@supermemory/tools` v2.0.0 unifies the API across all four integrations (Vercel AI SDK, OpenAI, Mastra, VoltAgent) around a single config-object signature and a consistent conversation-grouping concept. This guide walks you through the breaking changes.

<Note>
  This release is **breaking**. Update calls and re-test before bumping in
  production.
</Note>

## What changed at a glance

| Area                  | v1.4.x                                                | v2.0.0                                                      |
| --------------------- | ----------------------------------------------------- | ----------------------------------------------------------- |
| Signature             | `withSupermemory(model, "user-123", { ... })`         | `withSupermemory(model, { containerTag: "user-123", ... })` |
| Conversation grouping | `conversationId` (Vercel/OpenAI), `threadId` (Mastra) | **`customId`** everywhere                                   |
| `customId`            | Optional                                              | **Required** — throws if missing or empty                   |
| `containerTag`        | Positional argument                                   | **Required** field on options object                        |
| `addMemory` default   | `"never"`                                             | `"always"`                                                  |
| VoltAgent `verbose`   | Hardcoded to `false`                                  | Honored from options                                        |

## Install

```bash
npm install @supermemory/tools@^2.0.0
```

## 1. Vercel AI SDK

```typescript
// v1.4.x
import { withSupermemory } from '@supermemory/tools/ai-sdk';

const model = withSupermemory(openai('gpt-4'), 'user-123', {
  conversationId: 'conv-456',
  mode: 'full',
});
```

```typescript
// v2.0.0
import { withSupermemory } from '@supermemory/tools/ai-sdk';

const model = withSupermemory(openai('gpt-4'), {
  containerTag: 'user-123',
  customId: 'conv-456',
  mode: 'full',
});
```

<Warning>
  `customId` is now **required**. Passing an empty string or omitting it throws
  at construction time.
</Warning>

## 2. OpenAI SDK

```typescript
// v1.4.x
import { withSupermemory } from '@supermemory/tools/openai';

const client = withSupermemory(openai, 'user-123', {
  conversationId: 'conv-456',
});
```

```typescript
// v2.0.0
import { withSupermemory } from '@supermemory/tools/openai';

const client = withSupermemory(openai, {
  containerTag: 'user-123',
  customId: 'conv-456',
});
```

Both `containerTag` and `customId` are validated and throw with explicit error messages if missing.

## 3. Mastra

Processor constructors and factory functions both moved to a single options argument. `threadId` is gone — use `customId` instead.

```typescript
// v1.4.x
import {
  SupermemoryInputProcessor,
  createSupermemoryOutputProcessor,
} from '@supermemory/tools/mastra';

const input = new SupermemoryInputProcessor('user-123', {
  mode: 'full',
});

const output = createSupermemoryOutputProcessor('user-123', {
  threadId: 'conv-456',
  addMemory: 'always',
});
```

```typescript
// v2.0.0
import {
  SupermemoryInputProcessor,
  createSupermemoryOutputProcessor,
} from '@supermemory/tools/mastra';

const input = new SupermemoryInputProcessor({
  containerTag: 'user-123',
  customId: 'conv-456',
  mode: 'full',
});

const output = createSupermemoryOutputProcessor({
  containerTag: 'user-123',
  customId: 'conv-456',
});
```

<Note>
  In server setups, Mastra's `RequestContext` thread ID still takes precedence
  over the construction-time `customId` — the option now acts as the fallback
  when no per-request thread ID is provided.
</Note>

## 4. VoltAgent

VoltAgent already used a config-object signature, so the call shape is unchanged. Two behavior fixes ship in v2.0.0:

- `verbose: true` is now honored (was hardcoded to `false` in v1.4.x).
- A runtime warning is logged when advanced search params (`threshold`, `limit`, `rerank`, `rewriteQuery`, `filters`, `include`, `searchMode`) are set while `mode: "profile"` — those parameters are ignored in profile mode.

If you were relying on `verbose: false` implicitly while passing `verbose: true`, you will now see logs. Adjust as needed.

## 5. New default: `addMemory: "always"`

Across all four integrations, `addMemory` now defaults to `"always"`. If your v1.4.x code relied on the old default of `"never"`, set it explicitly:

```typescript
const model = withSupermemory(openai('gpt-4'), {
  containerTag: 'user-123',
  customId: 'conv-456',
  addMemory: 'never', // preserve v1.4.x behavior
});
```

## Conversation persistence

In v1.4.x the Vercel middleware fell back to `client.add` with a synthesized `customId` when no `conversationId` was passed. In v2.0.0, because `customId` is required, all conversation persistence goes through the `/v4/conversations` endpoint via `addConversation`. There is no fallback path.

## Migration checklist

<Steps>
  <Step title="Bump the dependency">
    `npm install @supermemory/tools@^2.0.0`
  </Step>
  <Step title="Find every withSupermemory / processor call">
    Grep your codebase for `withSupermemory(`, `SupermemoryInputProcessor`,
    `SupermemoryOutputProcessor`, `createSupermemoryProcessor`,
    `createSupermemoryOutputProcessor`.
  </Step>
  <Step title="Move containerTag into the options object">
    Drop the positional `containerTag` argument and add it to the options
    object.
  </Step>
  <Step title="Rename conversationId / threadId to customId">
    Make sure every call site provides a non-empty `customId`.
  </Step>
  <Step title="Audit addMemory">
    If you depended on the old `"never"` default, pass `addMemory: "never"`
    explicitly.
  </Step>
  <Step title="Run your test suite">
    Validation throws happen at construction time, so missing fields surface
    immediately.
  </Step>
</Steps>

## Need help?

- [Vercel AI SDK integration](/integrations/ai-sdk)
- [OpenAI integration](/integrations/openai)
- [Mastra integration](/integrations/mastra)
- [VoltAgent integration](/integrations/voltagent)

If you hit something this guide does not cover, open an issue on [GitHub](https://github.com/supermemoryai/supermemory).

---

### Apps Docs Migration Tools V3 Upgrade (`apps/docs/migration/tools-v3-upgrade.mdx`)

---
title: "Upgrading @supermemory/tools to 3.0"
description: "Migrate from @supermemory/tools 2.x to 3.0: one namespace per config, v5 option names, conversations stored as documents."
sidebarTitle: "Tools: 2.x → 3.0"
keywords: ["containerTags", "projectId", "customId", "namespace", "tools"]
---

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

<MigrateCli legacy="@supermemory/tools 2.x" guideHref="/migration/api-v5" guideLabel="v5 migration guide" />

`@supermemory/tools` 3.0 moves every integration (Vercel AI SDK, OpenAI, Mastra, VoltAgent, Claude memory) to the [v5 API](/migration/api-v5) through `supermemory@5`. The names changed to match: a **namespace** is what 2.x called a container tag.

```bash
npm install @supermemory/tools@3 supermemory@5
```

## One namespace per config

`containerTags` and `projectId` are gone. Pass one `namespace`, and every tool reads and writes only that namespace.

```ts
// 2.x
supermemoryTools(apiKey, { containerTags: ["user_123"] })
supermemoryTools(apiKey, { projectId: "personal" })

// 3.0
supermemoryTools(apiKey, { namespace: "user_123" })
supermemoryTools(apiKey, { namespace: "sm_project_personal" }) // projectId "x" lived at sm_project_x
```

With no config, tools still use `sm_project_default`, so existing data is where it was. If you passed several tags, pick one: a v5 document lives in exactly one namespace.

## v5 option names in `withSupermemory`

| 2.x | 3.0 |
| --- | --- |
| `containerTag` | `namespace` |
| `customId` | `id` |
| `memoryContainerTag` (Claude memory) | removed; files are marked with `metadata.source = "claude-memory"` |

```ts
// 2.x
withSupermemory(model, { containerTag: "user_123", customId: "conv_456" })

// 3.0
withSupermemory(model, { namespace: "user_123", id: "conv_456" })
```

Mastra, VoltAgent and the OpenAI middleware take the same two names. There are no aliases for the old ones.

## Behaviour changes

- **Conversations are documents.** The middlewares no longer call `/v4/conversations`. Each conversation is one document whose `id` is the `id` you pass, so the same conversation keeps updating the same document.
- **`memoryForget`** drops `reason`. Forgetting by `memoryContent` previews matching memories with a dry run, then forgets only exact text matches.
- **`getProfile`** returns v5 entries, `{ id, memory }` instead of strings. Those ids work with `memoryForget`.
- **`documentList`** returns v5 documents; the status is `system.status`.
- **No per-call scope.** `getProfile`, `documentList`, `documentDelete` and `memoryForget` no longer accept a `containerTag` argument.
- **VoltAgent** search options use v5 shapes: `filters` becomes typed `filter`, `rerank` is `"none" | "order" | "aggregate"`, `searchMode: "documents"` is `"chunks"`, and `entityContext` is `supportingContext`.
- **Claude memory** files written by 2.x under two tags are not migrated.

## `@supermemory/ai-sdk` is retired

It was a re-export of `@supermemory/tools/ai-sdk`. Import from there instead.

```ts
import { supermemoryTools } from "@supermemory/tools/ai-sdk"
```

---

### Apps Docs Overview Analytics (`apps/docs/overview/analytics.mdx`)

---
title: "Analytics & monitoring"
description: "Observe usage, errors, and logs to monitor your Supermemory integration"
icon: "/icons/hugeicons/chart-line-data-01.svg"
---

Monitor your Supermemory usage with detailed analytics on API calls, errors, and performance metrics.

## Overview

The Analytics API provides comprehensive insights into your Supermemory usage:

- **Usage Statistics**: Track API calls by type, hourly trends, and per-API key breakdown
- **Error Monitoring**: Identify top error types and patterns
- **Detailed Logs**: Inspect stored request logs and compacted response data for debugging
- **Performance Metrics**: Monitor average response times and processing duration

<Note>
Analytics data is available for your entire organization and can be filtered by time period.
</Note>

## Usage statistics

Get comprehensive usage statistics including hourly breakdowns and per-key metrics.

### Endpoint

`GET /v3/analytics/usage`

### Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `from` | string (ISO 8601) | Start date/time for the period |
| `to` | string (ISO 8601) | End date/time for the period |
| `period` | string | Alternative to `from`: `24h`, `7d`, `30d`, `all`. Use `from` and `to` for other windows |
| `page` | integer | Page number for pagination (default: 1) |
| `limit` | integer | Items per page (default: 20, max: 100) |

### Example request

<CodeGroup>

```typescript TypeScript
// Get usage for the last 24 hours
const usage = await fetch('https://api.supermemory.ai/v3/analytics/usage?period=24h', {
  headers: {
    'Authorization': `Bearer ${SUPERMEMORY_API_KEY}`
  }
});

const data = await usage.json();
```

```python Python
import requests
from datetime import datetime, timedelta

# Get usage for the last 7 days
response = requests.get(
    'https://api.supermemory.ai/v3/analytics/usage',
    params={'period': '7d'},
    headers={'Authorization': f'Bearer {SUPERMEMORY_API_KEY}'}
)

data = response.json()
```

```bash cURL
# Get usage for a specific date range
curl -X GET "https://api.supermemory.ai/v3/analytics/usage?from=2024-01-01T00:00:00Z&to=2024-01-31T23:59:59Z" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```

</CodeGroup>

### Response schema

```json
{
  "usage": [
    {
      "type": "add",
      "count": 1523,
      "avgDuration": 245.5,
      "lastUsed": "2024-01-15T14:30:00Z"
    },
    {
      "type": "search",
      "count": 3421,
      "avgDuration": 89.2,
      "lastUsed": "2024-01-15T14:35:00Z"
    }
  ],
  "hourly": [
    {
      "hour": "2024-01-15T14:00:00Z",
      "count": 156,
      "avgDuration": 125.3
    }
  ],
  "byKey": [
    {
      "keyId": "key_abc123",
      "keyName": "Production API",
      "count": 2341,
      "avgDuration": 98.7,
      "lastUsed": "2024-01-15T14:35:00Z",
      "tokensUsed": 182340
    }
  ],
  "totalMemories": 45678,
  "pagination": {
    "currentPage": 1,
    "limit": 20,
    "totalItems": 150,
    "totalPages": 8
  }
}
```

`tokensUsed` sums recorded `finalTokens` for the listed keys, including chat usage. It is not the billable ingestion-token meter, and ordinary memory request logs do not necessarily populate it.

## Error monitoring

Track and analyze errors to identify issues and improve reliability.

### Endpoint

`GET /v3/analytics/errors`

### Parameters

Same as usage endpoint - supports `from`, `to`, `period`, `page`, and `limit`.

### Example request

<CodeGroup>

```typescript TypeScript
// Get errors from the last 24 hours
const errors = await fetch('https://api.supermemory.ai/v3/analytics/errors?period=24h', {
  headers: {
    'Authorization': `Bearer ${SUPERMEMORY_API_KEY}`
  }
});

const data = await errors.json();
```

```python Python
# Monitor errors and alert on spikes
response = requests.get(
    'https://api.supermemory.ai/v3/analytics/errors?period=24h',
    headers={'Authorization': f'Bearer {SUPERMEMORY_API_KEY}'}
)

data = response.json()
```

```bash cURL
# Get errors for the last 7 days
curl -X GET "https://api.supermemory.ai/v3/analytics/errors?period=7d" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```

</CodeGroup>

### Response schema

```json
{
  "summary": [
    {
      "type": "search_v4",
      "totalRequests": 3421,
      "successRate": 97.4,
      "errorRate": 2.6,
      "lastRequest": "2024-01-15T14:35:00Z"
    }
  ],
  "errors": [
    {
      "type": "search_v4",
      "statusCode": 400,
      "count": 89,
      "percentage": 100
    }
  ],
  "hourly": [
    {
      "hour": "2024-01-15T14:00:00Z",
      "count": 156,
      "errorCount": 4,
      "errorRate": 2.56
    }
  ],
  "byKey": [
    {
      "keyId": "key_abc123",
      "keyName": "Production API",
      "count": 2341,
      "errorCount": 61,
      "errorRate": 2.61,
      "lastUsed": "2024-01-15T14:35:00Z"
    }
  ],
  "pagination": {
    "currentPage": 1,
    "limit": 20,
    "totalItems": 3421,
    "totalPages": 172
  }
}
```

## Detailed logs

Inspect stored request logs for debugging. Response data is compacted: strings, arrays, fields, and nesting can be truncated. The ordinary request tracker skips HTTP `402`, `404`, and `500` responses, so this is not a complete audit trail.

### Endpoint

`GET /v3/analytics/logs`

### Parameters

Use `from`, `to`, `period`, `page`, and `limit` as above. The current `/logs` handler filters by organization and time only; it does not apply `type`, `statusCode`, or `keyId` filters.

### Example request

<CodeGroup>

```typescript TypeScript
const logs = await fetch('https://api.supermemory.ai/v3/analytics/logs?period=24h', {
  headers: {
    'Authorization': `Bearer ${SUPERMEMORY_API_KEY}`
  }
});

const data = await logs.json();
```

```python Python
response = requests.get(
    'https://api.supermemory.ai/v3/analytics/logs',
    params={
        'period': '24h'
    },
    headers={'Authorization': f'Bearer {SUPERMEMORY_API_KEY}'}
)

logs = response.json()['logs']

```

```bash cURL
# Get all logs for debugging
curl -X GET "https://api.supermemory.ai/v3/analytics/logs?period=24h&limit=50" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```

</CodeGroup>

### Response schema

```json
{
  "logs": [
    {
      "id": "req_xyz789",
      "createdAt": "2024-01-15T14:30:00Z",
      "type": "search",
      "statusCode": 200,
      "duration": 89,
      "input": "{\"q\":\"user query\",\"limit\":10}",
      "output": {
        "type": "search",
        "response": { "results": [], "timing": 85, "total": 0 }
      }
    }
  ],
  "pagination": {
    "currentPage": 1,
    "limit": 20,
    "totalItems": 500,
    "totalPages": 25
  }
}
```

## Limits and retention

The maximum page size is 100 items. Normal API request rate limits also apply; this page does not establish a separate analytics-specific allowance.

<Warning>
An accepted date range does not guarantee that historical logs are still retained. Confirm the applicable service retention policy with Supermemory, and export data to your own systems when you need longer retention.
</Warning>

---

### Apps Docs Overview Billing (`apps/docs/overview/billing.mdx`)

---
title: "Billing & usage"
description: "How Supermemory metering works: SM tokens, operations, search, SuperRAG, plans, diff billing and the usage APIs."
sidebarTitle: "Billing & usage"
icon: "/icons/hugeicons/credit-card.svg"
---

Supermemory billing is **usage-based USD credits**. Plans include a monthly credit balance; metered product usage draws that balance down. You manage plan, invoices, top-ups, and auto top-up in the [Developer Console](https://console.supermemory.ai).

List prices and the public rate card also live on [supermemory.ai/pricing](https://supermemory.ai/pricing). This page is the **contract-level model**: what we meter, when tokens are free (already seen), what an operation is, plan feature gates, and how to read usage programmatically.

<Note>
Console charts are **estimates**. Invoice / customer-portal line items are authoritative.
</Note>

## Mental model

```text
PLAN (Free · Pro · Max · Scale · Enterprise)
     │  includes monthly USD credits
     ▼
USD CREDIT BALANCE  ──draw──►  METERS
                                 ├── sm_tokens_text / sm_tokens_rich     (Memory ingest)
                                 ├── sm_superrag_text / sm_superrag_rich (SuperRAG task)
                                 ├── sm_search_queries                  (Search / profile)
                                 └── sm_operations                      (platform ops)
```

1. You hold a **USD credit balance** (`usd_credits`).
2. Product activity increments **meters** (tokens, queries, operations).
3. Each meter unit multiplies by a **USD-per-unit** rate and debits the balance.
4. **Re-ingesting a document under the same `id` can reuse unchanged content** — token charges follow added or changed text, not simply growth in the document's total length.

---

## Meters (what we count)

| Feature ID | UI label | What counts | Unit |
|---|---|---|---|
| `sm_tokens_text` | Memory tokens (text) | Billable **new** tokens on Memory-path ingest for text-like content (`text`, `tweet`, `github_markdown`) | tokens |
| `sm_tokens_rich` | Memory tokens (rich) | Billable **new** tokens on Memory-path ingest for multimodal / extraction-heavy types (PDF, media, etc.) | tokens |
| `sm_superrag_text` | SuperRAG tokens (text) | Billable **new** tokens when the task type is SuperRAG (text) | tokens |
| `sm_superrag_rich` | SuperRAG tokens (rich) | Billable **new** tokens when the task type is SuperRAG (rich) | tokens |
| `sm_search_queries` | Search queries | Gated v5 namespace search, v3/v4 search, and v4 profile calls that include a non-blank `q` query | queries |
| `sm_operations` | Operations | Counted platform operations not attributed to token or query meters: **instant dreaming**, and search add-ons such as query rewriting, reranking and aggregation | operations |

### List rates (USD)

These match the console meter table (USD **per unit**; ×1000 ≈ price per 1K units):

| Meter | USD per unit | Approx per 1K units |
|---|---|---|
| `sm_tokens_text` | 0.000005 | **0.005 USD** / 1K tokens |
| `sm_tokens_rich` | 0.00001 | **0.010 USD** / 1K tokens |
| `sm_superrag_text` | 0.000001 | **0.001 USD** / 1K tokens |
| `sm_superrag_rich` | 0.000002 | **0.002 USD** / 1K tokens |
| `sm_search_queries` | 0.000005 | **0.005 USD** / 1K queries |
| `sm_operations` | 0.0001 | **0.10 USD** / 1K operations |

Rates can change; treat [pricing](https://supermemory.ai/pricing) + your invoice as source of truth.

### Memory vs SuperRAG tokens

Ingestion is attributed by **task type**:

- **Memory** (`taskType: "memory"`) → `sm_tokens_text` / `sm_tokens_rich`
- **SuperRAG** (`taskType: "superrag"`) → `sm_superrag_text` / `sm_superrag_rich`

Text-like document types bill on the **text** meters; everything else bills as **rich** (OCR, video/audio, heavy extractors).

### What is an operation?

**`sm_operations`** is the meter for discrete platform work that is not “how many tokens did we embed.” Examples include:

- `dreaming: "instant"` extracts a document's memories immediately instead of waiting for the next dynamic batch. It costs **one extra operation per document** on top of normal token metering. See [processing modes](/ingestion/add-memories#processing-modes).
- Search add-ons: query rewriting (`rewriteQuery`), reranking and aggregation (v5 `rerank: "order"` / `"aggregate"`, v3/v4 `rerank`, v4 `aggregate`) each add **one operation** when they run successfully. Failed or skipped add-ons do not necessarily charge an operation.
- Query-based `POST /v4/memories/forget-matching` adds **one operation per search round**, up to five, including `dryRun` previews. Applying an explicit `ids` list bypasses that query-operation charge.

Search is primarily metered as **`sm_search_queries`** (one unit per gated search/profile request). Prefer thinking of operations as **extra platform work**, not as a synonym for “API call.”

### Search and profile

| Call | Typical meter |
|---|---|
| `POST /ns/{namespace}/search`, `supermemory.search()` | `sm_search_queries` (+1), plus `sm_operations` for any add-ons that run |
| `POST /ns/{namespace}/profile`, `supermemory.profile()` | No search-query meter in the current API implementation |
| Legacy `POST /v3/search`, `POST /v4/search` | `sm_search_queries` (+1), plus `sm_operations` for any add-ons that run |
| Legacy `POST /v4/profile` with a non-empty `q` | `sm_search_queries` (+1) |

Blocking metered cloud calls can return **402** (payment required) when the applicable balance or quota is exhausted. Billing exclusions, unlimited contracts, disabled billing, and gate configuration can differ. The current v5 profile route does not install a search-query billing gate; this is not a guarantee of future pricing.

---

<span id="full-discount-on-already-seen-tokens-diff-billing" />

## Reusing unchanged content (diff billing)

This is the most important cost control in production agents.

### How it works

When you re-add content under the **same `id`** (same org / document identity), the pipeline:

1. Loads the document's active chunks and plans the updated content.
2. Reuses unchanged chunks where possible.
3. Meters newly appended text on an append path, or added replacement chunks on a replace path.

This is not a subtraction of the old total token count from the new one. A same-length replacement can still incur charges if its content changed; an identical replacement can reuse its chunks without new token charges. Chunk boundaries and overlap can also affect the billable delta.

Stable IDs let long-lived conversations and repeated document syncs reuse unchanged work rather than billing the whole history every time. Operation surcharges can still apply.

### Requirements

| Requirement | Why |
|---|---|
| **Stable `id`** | Identity for “this is the same document.” Max length 255. |
| **Same org / key** | Documents are org-scoped. |
| **Choose append or replace deliberately** | Append meters the newly appended text; replace compares chunks and can reuse unchanged ones. Changed replacement chunks can bill even without net document growth. |

### Practical patterns

```typescript
// Session stays one document — only new turns bill tokens
await supermemory.add(userId, {
  content: "user: " + msg + "\nassistant: " + reply,
  id: "chat_" + sessionId, // stable for the session
  dreaming: "instant", // optional: +1 operation, faster memory
});
```

Connectors use stable IDs (e.g. Drive file id, Gmail thread id, `s3://bucket/key`), so re-syncs can reuse unchanged content. Changed replacement chunks can still incur token charges.

### What is not free

- **First** ingest of content still bills full token count
- **Gated searches** still bill the query meter every call; legacy v4 profile does so only with a non-blank `q`. The current v5 profile implementation does not charge this meter.
- **Instant dreaming** still bills the **operation** surcharge when used
- **Brand-new `id`** = brand-new document = full tokens

---

## Credits and balance behavior

| Concept | Behavior |
|---|---|
| **Included plan credits** | Monthly USD allowance tied to the plan (resets each billing period; **no rollover**) |
| **Top-up credits** | Purchased USD credits (console presets e.g. 10 / 25 / 50 / 100 USD). Persist until used (do not expire with the calendar month the way subscription inclusion does) |
| **Spend order** | Plan inclusion is consumed before top-up balance (Autumn merges both into `usd_credits`) |
| **Auto top-up** | Configurable in console for paid plans — adds credits when balance is low |
| **Auto top-up purchase limits** | Paid plans can limit automatic credit purchases per interval. This is not a hard cap on total usage or the full invoice. |
| **Depleted balance** | Metered APIs may block (e.g. **402**) until top-up or period reset |

### Plan credit inclusion (approximate)

| Plan | Product ID | Included credits / month |
|---|---|---|
| Free | `api_free` | **5 USD** |
| Pro | `api_pro` | **20 USD** |
| Max | `api_max` | **130 USD** |
| Scale | `api_scale` | **600 USD** |
| Enterprise | `api_enterprise` | Custom / unlimited by contract |

Legacy product IDs (`memory_free`, `memory_starter`, `memory_growth`, `memory_enterprise`) still resolve for existing customers.

---

## Plans and feature access

Usage meters are shared; **feature gates** differ by tier.

### Plan summary

| Plan | Price (list) | Included credits | Best for |
|---|---|---|---|
| **Free** | 0 USD | 5 USD | Plugins, API evaluation, prototypes |
| **Pro** | 19 USD/mo | 20 USD | Developers, core connectors |
| **Max** | 100 USD/mo | 130 USD | More usage headroom and Gmail |
| **Scale** | 399 USD/mo | 600 USD | Production, full connectors, teams |
| **Enterprise** | Custom | Contract | SSO, custom metering, FDE, air-gap |

### Feature availability

| Feature | Free | Pro | Max | Scale | Enterprise |
|---|---|---|---|---|---|
| Memory API, search, profiles | Yes | Yes | Yes | Yes | Yes |
| Standard PDF extraction, including OCR for scans | Yes | Yes | Yes | Yes | Yes |
| [Advanced document extraction for PDFs](/concepts/content-types#advanced-document-extraction) (figure and diagram descriptions) | — | — | — | Yes | Yes |
| Diff / delta token billing | Yes | Yes | Yes | Yes | Yes |
| Coding plugins (Claude Code, Codex, Cursor, OpenCode, OpenClaw, Hermes) | Yes | Yes | Yes | Yes | Yes |
| Team management | — | Yes | Yes | Yes | Yes |
| Google Drive, OneDrive, Notion, Granola connectors | — | Yes | Yes | Yes | Yes |
| Gmail connector | — | — | Yes | Yes | Yes |
| GitHub, S3, Web Crawler connectors | — | — | — | Yes | Yes |
| User Insights | — | — | — | Yes | Yes |
| Two-factor authentication | — | — | — | Yes | Yes |
| Org seat limit (members) | 1 | 3 | 3 | Unlimited | Unlimited |
| Auto top-up in the console | — | Yes | Yes | Yes | Contract |
| Auto top-up purchase limits | — | Yes | Yes | Yes | Contract |
| Custom metering / SSO / dedicated deploy | — | — | — | — | Yes |

Coding plugins are available on every plan, including Free. Their API usage still consumes your plan's credits at the applicable rates.

PDF uploads use the extraction tier available to your organization automatically; no extra API parameter is required. On plans without advanced extraction, uploads still use standard extraction rather than returning a feature-gate error. Advanced extraction can also fall back to standard extraction if needed. This tier distinction applies to PDFs, not all supported file types.

Overrides can be applied per org in metadata (`featureOverrides`) for enterprise deals.

### HTTP behavior when gated

- **403** — plan too low for a **feature** (e.g. connector not on Free)
- **402** — **quota / credits** exhausted for a metered call (search, tokens, etc.)

---

## Programmatic access

**Scoped API keys cannot read billing** (403). `GET /v3/auth/billing` and `POST /v3/auth/billing/auto-topups` also require an org **admin or owner**.

Base: `https://api.supermemory.ai`  
Auth: `Authorization: Bearer YOUR_API_KEY`

### Summary — plan + high-level usage

`GET /v3/auth/billing`

```bash
curl -s https://api.supermemory.ai/v3/auth/billing \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```

Typical fields:

```json
{
  "plan": "pro",
  "usage": {
    "tokens": { "used": 120000, "limit": 0 },
    "queries": { "used": 4200, "limit": 0 }
  },
  "credits": { "balance": 14.62, "label": "USD" },
  "resetDate": "2026-08-01T00:00:00.000Z",
  "orgName": "Acme",
  "billingEmail": "billing@acme.com"
}
```

CLI:

```bash
npx supermemory billing show
npx supermemory billing usage
```

### Feature breakdown (Autumn features)

`GET /v3/auth/billing/usage`

Returns per-feature `used` / `limit` / `unit` for the org’s Autumn customer features (including `usd_credits`, token meters, search, operations), plus period bounds when available.

```bash
curl -s https://api.supermemory.ai/v3/auth/billing/usage \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```

### Meter events (day × feature)

`GET /v3/auth/billing/usage-events`

Optional query: `?start=<ISO>&end=<ISO>` to override the billing period.

```bash
curl -s "https://api.supermemory.ai/v3/auth/billing/usage-events" \
  -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```

Response includes `byFeature`, `byDay`, `memoryTotal`, `superragTotal`, period start/end.

### Auto top-up config

```http
GET  /v3/auth/billing/auto-topups
POST  /v3/auth/billing/auto-topups
```

### Invoices

`GET /v3/auth/billing/invoices`

### Product / request analytics (not the same as USD meters)

For **request volume**, errors, and per-key traffic (ops monitoring, not USD ledger):

`GET /v3/analytics/usage?period=24h|7d|30d`

See [Analytics](/overview/analytics).

### TypeScript sketch

```typescript
const headers = {
  Authorization: "Bearer " + process.env.SUPERMEMORY_API_KEY,
};

const billing = await fetch("https://api.supermemory.ai/v3/auth/billing", {
  headers,
}).then((r) => r.json());

const usage = await fetch("https://api.supermemory.ai/v3/auth/billing/usage", {
  headers,
}).then((r) => r.json());

const events = await fetch(
  "https://api.supermemory.ai/v3/auth/billing/usage-events",
  { headers },
).then((r) => r.json());

console.log({
  plan: billing.plan,
  resetDate: billing.resetDate,
  usage,
  events,
});
```

---

## Cost control checklist (production)

1. **Always set a stable `id`** on conversations and docs so re-sync is free on old tokens.
2. Prefer **session-level** conversation documents over one-line micro-adds (better memory **and** less wasted processing).
3. Use **`dreaming: "instant"`** only when you need immediate memory (extra operation); default `"dynamic"` batches extraction.
4. Cache **profiles** short-TTL in your app if you call them every turn.
5. Scope with a **`namespace`** so you can delete/export a tenant without scanning the org.
6. Configure **auto top-up purchase limits** on paid plans and monitor usage separately. These limits do not cap the entire invoice.
7. Pull **`/v3/auth/billing/usage-events`** into your own FinOps dashboard for per-day Memory vs SuperRAG spend.

---

## Related

- [Pricing page](https://supermemory.ai/pricing) — live plan marketing rates
- [Console billing](https://console.supermemory.ai) — plan, invoices, top-ups
- [Add context](/ingestion/add-memories) — `id`, `dreaming`
- [Analytics](/overview/analytics) — request-level observability
- [Security & compliance](/overview/security) — trust posture for enterprise review

---

### Apps Docs Overview Comparison (`apps/docs/overview/comparison.mdx`)

---
title: "Comparison - should I use supermemory?"
description: "How Supermemory compares with a DIY vector stack, thin memory layers, pure RAG and building it yourself."
sidebarTitle: "Comparison"
icon: "/icons/hugeicons/balance-scale.svg"
---

<Visibility for="humans">

> "Comparison is the thief of joy," but the wrong abstraction costs more than a page like this.

Non-detailed reasons to pick supermemory over the alternatives. If you're migrating from a specific tool, the [migration guides](/migration/from-mem0) name names and map APIs.

## vs rolling your own (vector DB + embeddings + glue)

- **Skip the vendor pile-up.** No stitching together a vector DB, an embedding model, chunking scripts, and a fact-extraction prompt. One engine does it.
- **Get temporal truth for free.** "Loved Adidas, switched to Puma" resolves correctly instead of both facts looking equally relevant forever.
- **Ship identity, not just similar text.** Entities and profiles come built in, not bolted on later.
- **Stay portable.** Use our cloud, or [self-host](/self-hosting/overview) the same engine on your own infra.

## vs "memory layers" that are thin wrappers

- **Facts actually update.** When a user changes their mind, the old fact doesn't sit next to the new one forever.
- **Real relationships, not blobs.** A graph connects people, projects, and events across sessions, not a pile of stored chat turns.
- **Profiles included.** No extra work to get a standing summary of who a user is.
- **Configurable where it matters.** Extractors, retrieval, and isolation are primitives you compose, not a black box.

## vs pure RAG / document search products

- **Both layers, one engine.** SuperRAG for corpus grounding, memory and profiles for people, sharing the same namespaces.
- **Personal state isn't just another document.** "What the policy says" and "what this customer decided last quarter" stay distinct but connected.
- **No second vendor for personalization.** You're not stitching a memory product onto your RAG stack.

## vs building a full context engine in-house

- **Skip years of infra work.** Extraction models, temporal updates, conflict resolution, and connector maintenance, already built.
- **Compliance comes standard.** SOC 2, GDPR, and HIPAA paths, plus scoped keys, without a dedicated platform team.
- **Still yours if you want it.** [Self-host](/self-hosting/overview) the same binary when you need it on your own metal.

Most AI apps are better off using supermemory than building this themselves. Run the [quickstart](/quickstart) and judge for yourself.

## Supermemory is never overkill

In most cases, supermemory is the *lighter* choice (not heavier), and cheaper too. It bundles all the blocks while staying fully composable, so it fits everything from side projects and internal tools to production infra millions of people rely on. Rolling your own means signing up for a dozen separate vendors for the database, hosting, embeddings, graph, vector store, and ingestion pipeline instead.

Data ownership isn't a tradeoff either: [self-host](/self-hosting/overview) supermemory and nothing leaves your servers. And if you're worried about lock-in, you can always export your data, delete it, or run your own instance, **supermemory is an architecture** to build with, not an opinionated service.

</Visibility>

<Visibility for="agents">

This page is intentionally **not** a vendor scorecard. We know things change and improve over time. Instead: the categories people actually evaluate, what each is good at, and when Supermemory is the better fit.

If you are migrating from a specific tool, use the [migration guides](/migration/from-mem0), that is where we name names and map APIs.

<h2 noAnchor>vs Rolling your own (vector DB + embeddings + glue)</h2>

**What this path is:** Pinecone/Weaviate/pgvector + an embedding model + chunking scripts + a prompt that says “here is relevant context.” and running it through a text model, extracting facts.

**When it is enough**

- Static knowledge base, low update rate
- You already run retrieval infra and only need nearest-neighbor chunks
- Latency, ops, and embedding quality are already solved problems for your team

**Where it breaks for agent memory**

- No temporal truth (“loved Adidas” then “switched to Puma”, both stay equally “relevant”)
- You have to use 6+ vendors for all the different stuff
- No entity identity or profile, just similar text
- Multimodal extraction, connectors, forgetting, and multi-tenant isolation become a second product
- You spend lots of time on just figuring out the right plumbing and combinations of vendors
- Not very scalable for memory. New facts need knowledge of all previous facts

**Supermemory instead:** one engine that derives memories with our own model, stores them in a temporal vector-graph engine and builds profiles, with hybrid retrieval and isolation built in. You can still [self-host](/self-hosting/overview) when you want the stack on your own machines.

<h2 noAnchor>vs “Memory layers” that are thin wrappers</h2>

**What this path is:** an API that stores chat turns or summaries in a vector store, sometimes with a light extract-facts prompt. Marketed as memory; architected as RAG with better branding.

**When it is enough**

- Demo chatbots and hackathon agents
- You only need “remember the last few sessions” as blobs of text
- Quality bar is “sometimes recalls a preference”

**Where it breaks in production**

- Facts do not **update** cleanly when users change their mind
- Some of them have no real graph of people, projects, and relations across separately ingested events
- There's no concept of Profiles since they don't have the same underlying learning model and store engine
- You still have to build the entire thing around it, on their abstraction. Low configurability and doesn't support many use cases (Extractors, retrieval, etc.)

**Supermemory instead:** memory is a first-class data model (documents → derived memories → graph → profiles), not a convenience wrapper. The API, MCP and plugins share one store, and every primitive is there for you to compose. Read [how it works](/concepts/how-it-works).

<h2 noAnchor>vs Pure RAG / document search products</h2>

**What this path is:** excellent document ingestion and semantic search over a corpus, wikis, PDFs, tickets. No (or weak) per-user long-horizon memory.

**When it is enough**

- Internal knowledge base Q&A only, no change in text
- “Chat with these PDFs” with a fixed corpus
- Content is universal, not personal. The files are structured and short
- Content does not update enough over long horizons

**Where it breaks**

- Personalized agents that must know *this user* over months
- Mixing “what is in the policy doc” with “what did this customer decide last quarter”
- You still have to build in the contextualization and bear the cost of it. Also need to sign up for many vendors for the same.
- Treating user state as another document collection

**Supermemory instead:** **both** layers in one engine, SuperRAG for corpus grounding, memory + profiles for people and entities. They share namespaces so isolation stays coherent. Deep dive: [Memory vs RAG](/concepts/memory-vs-rag).

<h2 noAnchor>vs Building a full context engine in-house</h2>

**What this path is:** custom extraction models, graph store, profile assembly, hybrid search, connector fleet, multi-tenant keys, compliance pack.

**When it is justified**

- Memory *is* the product and differentiation lives in proprietary models/data
- You have a dedicated platform team and years of runway
- Regulatory constraints force a greenfield design with no external dependency (even then, [self-host](/self-hosting/overview) is often enough)

**What you are actually signing up for**

- Extraction quality and eval harnesses
- Temporal updates, conflict resolution, forgetting
- Multimodal pipelines and connector maintenance
- Authz (scoped keys, namespace boundaries), billing metering, SOC 2 / GDPR / HIPAA paths
- Sub-300ms retrieval under agent-loop load, deployability, maintaining it forever as the industry changes

**Supermemory instead:** that platform as a product, managed cloud or
self-hosted binary, so your team ships agents and apps, not a second infrastructure company. Benchmarks and research: [supermemory.ai/research](https://supermemory.ai/research).

<h2 noAnchor>Decision cheat sheet</h2>

| If your job is… | Prefer |
|---|---|
| Q&A over a mostly static doc set | RAG product or SuperRAG-only usage |
| Remember users across sessions with updates over time | Supermemory memory + profiles |
| Both personalization *and* company docs | Supermemory (memory + SuperRAG, same namespaces) |
| Full control, data never leaves your network | Supermemory [self-host](/self-hosting/overview) / Enterprise |
| Maximum control of every model weight and storage engine | Build in-house (or fork open pieces and accept the ops) |

<h2 noAnchor>Prove it yourself</h2>

We would rather you verify than trust a comparison page:

1. Run the [quickstart](/quickstart), scatter facts across “sessions,” ask a question that requires linking them
2. Reproduce long-horizon results with [MemoryBench](https://supermemory.ai/research) / the MemoryBench docs when you care about evals
3. If you already store memories elsewhere, use a [migration guide](/migration/from-mem0)

<h2 noAnchor>Supermemory is never overkill</h2>

In most cases, supermemory will be the _lighter_ choice (not heavier), and it is cheaper too! Because supermemory involves all the blocks while being fully composable, but we also build the infrastructure ourselves (a post-trained model, etc.), it's perfect for everything from internal tools, side projects, and hobby projects to production-grade infrastructure that millions of people rely on.

Building your own, however, will mean that you have to sign up for 20 different vendors to do your database, hosting, embedding, graph, vector store, learning model, ingestion pipeline, etc.

Why is it cheaper? Because we (the team) come from an infrastructure background, we built some of the best base for memory out there. Owning the database and the model layer gives us a lot of advantages!

And if data ownership is a concern, it shouldn't be :) You can always [self-host](/self-hosting/overview) supermemory which ensures that nothing leaves your servers and you have full control and access.

Concerned about lock-in? You can always export your data, delete it, or switch to running your own instance of supermemory. **Supermemory is an architecture** to build with, not an _opinionated service_. It comes with the right defaults and some easy ways to use it, but you can go as deep as you want to make it perfect for your case.
Finally, we truly believe every use case can make advantage of supermemory, or a base of it's components.

Most AI applications should use supermemory.

</Visibility>

## Related

<CardGroup cols={2}>
  <Card title="What is Supermemory?" icon="/icons/hugeicons/book-open-01.svg" href="/overview/what-is-supermemory">
    Product overview and one-engine mental model.
  </Card>
  <Card title="Memory vs RAG" icon="/icons/hugeicons/balance-scale.svg" href="/concepts/memory-vs-rag">
    Why nearest-neighbor text is not memory.
  </Card>
  <Card title="Billing & plans" icon="/icons/hugeicons/credit-card.svg" href="/overview/billing">
    Usage model if cost is part of the evaluation.
  </Card>
  <Card title="Security & compliance" icon="/icons/hugeicons/shield-01.svg" href="/overview/security">
    Trust posture for production and enterprise buyers.
  </Card>
</CardGroup>

---

### Apps Docs Overview Security (`apps/docs/overview/security.mdx`)

---
title: "Security & compliance"
description: "How Supermemory protects data: encryption, isolation, SOC 2, GDPR, HIPAA BAA and deletion."
sidebarTitle: "Security & compliance"
icon: "/icons/hugeicons/shield-01.svg"
---

Supermemory stores long-horizon context about people and organizations. Security and compliance are part of the product surface, not a footer claim.

This page is the product-level trust overview. For multi-tenant design details, see [Namespaces](/concepts/container-tags) and authentication docs in the Developer Platform.

## Compliance posture

| Framework | Status | Notes |
|---|---|---|
| **SOC 2 Type II** | Certified | Independent audit of security controls. Available on production plans that advertise it (see [pricing](https://supermemory.ai/pricing); typically Scale and above for formal enterprise packaging). |
| **GDPR** | Compliant | EU personal data handled with care; support for access and erasure workflows. |
| **HIPAA** | BAA available | Business Associate Agreement available for eligible cloud plans (Scale / Enterprise). Cloud-only unless you self-host under your own controls. |

Need a report, DPA, or BAA? Contact [support@supermemory.com](mailto:support@supermemory.com) or your enterprise contact.

## Security controls

### Encryption

- **In transit:** TLS for API and console traffic
- **At rest:** Industry-standard encryption for stored data (AES-256 class controls in the managed cloud)

### Isolation and access

- **Namespaces** (what v3/v4 called container tags) enforce hard boundaries between users, tenants, or projects — the primary multi-tenancy primitive.
- **API keys** authenticate every request. Prefer **scoped keys** when a client or session must only touch one namespace.
- **Organizations** in the console manage members, keys, and billing separation.

A malicious or buggy client with a correctly scoped key cannot read another namespace’s memories.

### Data use

Supermemory is infrastructure for *your* agents. Your customer content is never used to train models — this applies to every plan, free or paid, with no difference between them. For contractual wording (DPA, subprocessors, training policies), request the latest legal pack from support.

### Data residency and deployment options

- **Managed cloud** — default multi-tenant SaaS.
- **Self-hosted binary** — the engine, storage, and default local embeddings run on your machine or VPC. If you configure remote LLM or embedding providers, their processing inputs are sent to those providers. See [Self-hosting](/self-hosting/overview).
- **Enterprise / dedicated** — for stricter residency, air-gap, or custom deployment requirements. See [Local vs Enterprise](/self-hosting/local-vs-enterprise).

## Privacy operations you should design for

### Deleting a user (right to erasure)

The practical GDPR-style path for app builders:

1. Scope each end-user (or tenant) to a **namespace**.
2. When the user requests deletion, use `DELETE /ns/{namespace}` with an org admin or owner credential that has write access. It deletes the namespace, its namespace-owned memory records, and documents exclusive to it; documents shared with other namespaces are retained. The default `sm_project_default` namespace cannot be deleted.
3. Revoke any **scoped keys** issued for that user.

Designing isolation up front still makes identifying the user's content easier.

### Connectors and third-party sources

OAuth connectors (Drive, Notion, Gmail, and others) pull content your users authorize. Disconnecting a connector stops future sync; you still control whether already-ingested documents remain in the memory store. Treat connector scope and retention as part of your product privacy policy.

### Self-host when cloud is not enough

If policy requires data never leave your network, run the [self-hosted engine](/self-hosting/overview). You bring the model endpoint (including fully offline OpenAI-compatible local models). Enterprise adds managed on-prem / dedicated options with organizational controls.

## Reliability and support

- Status and incidents are communicated through Supermemory’s status and support channels.
- Support depth scales with plan (community → email → priority → dedicated enterprise).
- Latency targets for retrieval are in the ~sub-300ms p50 range on the managed platform for typical search workloads; always validate on your traffic shape.

## Related

<CardGroup cols={2}>
  <Card title="Billing & plans" icon="/icons/hugeicons/credit-card.svg" href="/overview/billing">
    Which tiers include BAAs, seats, and self-host options.
  </Card>
  <Card title="Namespaces" icon="/icons/hugeicons/tag-01.svg" href="/concepts/container-tags">
    How isolation works in the data model.
  </Card>
  <Card title="Authentication" icon="/icons/hugeicons/key-01.svg" href="/authentication">
    API keys and access for the Developer Platform.
  </Card>
  <Card title="Self-hosting" icon="/icons/hugeicons/server-stack-01.svg" href="/self-hosting/overview">
    Keep memory on your infrastructure.
  </Card>
</CardGroup>

---

--- METRICS ---
- Files Extracted: 100
- Estimated Token Budget: ~162558 tokens
- Canonical Reference: https://codewiki.google/github.com/supermemoryai/supermemory
