# Agents, skills and MCP Source: https://supermemory.ai/docs/agents-and-mcp Set up coding agents to integrate Supermemory — CLI, skill, and docs MCP. This page is for **building with Supermemory** using coding agents: scaffolding a project, following the real API, and searching product docs. It is **not** the consumer Memory MCP (give Claude/Cursor long-term memory about *you*). That is a separate product surface — see [Supermemory MCP](/docs/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 theme={null} npx supermemory ``` Useful for coding agents: ```bash theme={null} npx supermemory setup # detect project, launch/print integration flow npx supermemory setup --prompt # print integration prompt only npx supermemory setup --json # machine-readable output npx supermemory help --json # agent-readable command catalog npx supermemory help --all ``` Also available for smoke tests against your key: `add`, `search`, `profile`, `docs`, `tags`, `config`, `whoami`. Auth via first-run credentials or `SUPERMEMORY_API_KEY`. ```bash theme={null} npx supermemory add "User prefers TypeScript" --tag user_123 npx supermemory search "language preference" --tag user_123 npx supermemory profile --tag user_123 ``` ## Skill Install the official skill so the agent uses the real endpoints, auth, and `containerTag` rules instead of hallucinating APIs: ```bash theme={null} npx skills add https://github.com/supermemoryai/skills --skill supermemory ``` Source: [github.com/supermemoryai/skills](https://github.com/supermemoryai/skills). Best combo for coding agents: **skill** + **docs MCP** + **`npx supermemory setup`**. ## Docs MCP Remote MCP that lets the agent **search Supermemory documentation** while it implements an integration. Server URL: ```text theme={null} https://supermemory.ai/docs/mcp ``` ### Setup by client Add to `~/.cursor/mcp.json`: ```json theme={null} { "mcpServers": { "supermemory-docs": { "url": "https://supermemory.ai/docs/mcp" } } } ``` ```bash theme={null} claude mcp add --transport http supermemory-docs https://supermemory.ai/docs/mcp ``` Or project `.mcp.json`: ```json theme={null} { "mcpServers": { "supermemory-docs": { "type": "http", "url": "https://supermemory.ai/docs/mcp" } } } ``` ```bash theme={null} codex mcp add supermemory-docs --url https://supermemory.ai/docs/mcp ``` Or `~/.codex/config.toml`: ```toml theme={null} [mcp_servers.supermemory-docs] url = "https://supermemory.ai/docs/mcp" ``` ```json theme={null} { "mcp": { "supermemory-docs": { "type": "remote", "url": "https://supermemory.ai/docs/mcp", "enabled": true } } } ``` Add to `.vscode/mcp.json`: ```json theme={null} { "servers": { "supermemory-docs": { "type": "http", "url": "https://supermemory.ai/docs/mcp" } } } ``` ```json theme={null} { "mcpServers": { "supermemory-docs": { "url": "https://supermemory.ai/docs/mcp" } } } ``` Stdio-only clients can proxy: ```json theme={null} { "mcpServers": { "supermemory-docs": { "command": "npx", "args": ["-y", "mcp-remote", "https://supermemory.ai/docs/mcp"] } } } ``` ### Starter prompt (docs + setup) ```text theme={null} 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, containerTag, and SDK usage. - Canonical writes: POST /v3/documents · search: POST /v4/search · profile: POST /v4/profile - Auth: Authorization: Bearer $SUPERMEMORY_API_KEY only - Always scope with containerTag (singular) on write and search - For demos use dreaming: "instant" when memories must be ready right after status done ``` ### Integrate prompt (optional) If the skill is not installed, paste a fuller prompt so the agent asks the right product questions: ``` You are integrating Supermemory into my application. Supermemory provides user memory, semantic search, and automatic knowledge extraction for AI applications. Note: You can always reference the documentation by using the **supermemory-docs MCP** or content on **supermemory.ai/docs**. Prefer `npx supermemory setup` / `npx supermemory help --json` when scaffolding. CANONICAL API SURFACE (use these, nothing else): - Auth header: `Authorization: Bearer $SUPERMEMORY_API_KEY` — the only supported auth header - Write content: POST https://api.supermemory.ai/v3/documents - Search: POST https://api.supermemory.ai/v4/search - Profile + search: POST https://api.supermemory.ai/v4/profile - Settings: PATCH https://api.supermemory.ai/v3/settings - Scoping: `containerTag` (singular string) in the JSON body — never in a header - SDK: `client.add()`, `client.search()`, `client.profile()` DO NOT USE — deprecated, undocumented, or fabricated: - Endpoints: /v1/anything, /v3/memories, /v3/search (use /v3/documents and /v4/search) - Headers: x-supermemory-api-key, x-api-key, x-sm-user-id (for API auth) - Body keys: containerTags (plural) on writes as the only scope, userId, spaces - Mixing: `rerank` and `rewriteQuery` on /v4/search only — never on /v3/search SCOPING IS LOAD-BEARING. Every write and every search MUST include `containerTag`. Prefer for tutorials: - Ingest conversations with customId + dreaming: "instant" when you need memories immediately - Wait until document status is done before search - search with searchMode: "documents" for RAG, search (+ relatedMemories) for the graph, profile for always-on context STEP 1: Ask what I'm building, integration style (AI SDK / OpenAI / Direct SDK / API), data model (user/org/both), profiles yes/no. STEP 2: Install supermemory (npm/pip), set SUPERMEMORY_API_KEY from https://console.supermemory.ai STEP 3: Generate complete working code. DOCS: https://supermemory.ai/docs ``` ## 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](/docs/supermemory-mcp/mcp) ## Next steps Conversation + document ingest, RAG, graph, profile, harness. Persistent memory for assistants — separate from docs setup. Claude Code, OpenClaw, Codex, Hermes, and more. withSupermemory and memory tools in app code. # Connections Source: https://supermemory.ai/docs/api-reference/connections External connectors — create, configure, sync, and manage resources. 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](/docs/connectors/overview) · provider pages under Connectors # Configure connection Source: https://supermemory.ai/docs/api-reference/connections/configure-connection https://api.supermemory.ai/v4/openapi post /v3/connections/{connectionId}/configure Configure resources for a connection (supported providers: GitHub for now) # Create connection Source: https://supermemory.ai/docs/api-reference/connections/create-connection https://api.supermemory.ai/v4/openapi post /v3/connections/{provider} Initialize connection and get authorization URL # Delete connection Source: https://supermemory.ai/docs/api-reference/connections/delete-connection https://api.supermemory.ai/v4/openapi delete /v3/connections/{provider} Delete connection for a specific provider and container tags # Delete connection by ID Source: https://supermemory.ai/docs/api-reference/connections/delete-connection-by-id https://api.supermemory.ai/v4/openapi delete /v3/connections/{connectionId} Delete a specific connection by ID # Fetch resources Source: https://supermemory.ai/docs/api-reference/connections/fetch-resources https://api.supermemory.ai/v4/openapi get /v3/connections/{connectionId}/resources Fetch resources for a connection (supported providers: GitHub for now) # Get connection (by id) Source: https://supermemory.ai/docs/api-reference/connections/get-connection-by-id https://api.supermemory.ai/v4/openapi get /v3/connections/{connectionId} Get connection details with id # Get connection (by provider) Source: https://supermemory.ai/docs/api-reference/connections/get-connection-by-provider https://api.supermemory.ai/v4/openapi post /v3/connections/{provider}/connection Get connection details with provider and container tags # List connections Source: https://supermemory.ai/docs/api-reference/connections/list-connections https://api.supermemory.ai/v4/openapi post /v3/connections/list List all connections # List documents Source: https://supermemory.ai/docs/api-reference/connections/list-documents https://api.supermemory.ai/v4/openapi post /v3/connections/{provider}/documents List documents indexed for a provider and container tags # Sync connection Source: https://supermemory.ai/docs/api-reference/connections/sync-connection https://api.supermemory.ai/v4/openapi post /v3/connections/{provider}/import Initiate a manual sync of connections # Container tags Source: https://supermemory.ai/docs/api-reference/container-tags Multi-tenant containers — settings, merge, and delete. `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 one tag into another | | `GET /v3/container-tags/merge/{mergeId}` | Poll merge status | **Guide:** [Container tags](/docs/concepts/container-tags) · [Filtering](/docs/concepts/filtering) # Delete container tag Source: https://supermemory.ai/docs/api-reference/container-tags/delete-container-tag https://api.supermemory.ai/v4/openapi delete /v3/container-tags/{containerTag} Delete a container tag and all its documents and memories. Only organization owners and admins can perform this action. # Get container tag merge status Source: https://supermemory.ai/docs/api-reference/container-tags/get-container-tag-merge-status https://api.supermemory.ai/v4/openapi get /v3/container-tags/merge/{mergeId} Get queued container tag merge status # Get container tag settings Source: https://supermemory.ai/docs/api-reference/container-tags/get-container-tag-settings https://api.supermemory.ai/v4/openapi get /v3/container-tags/{containerTag} Get settings for a container tag # Merge container tags Source: https://supermemory.ai/docs/api-reference/container-tags/merge-container-tags https://api.supermemory.ai/v4/openapi post /v3/container-tags/merge Merge multiple container tags into a target tag. All documents from the source tags will be updated to reference the target tag, and the source tags will be deleted after successful merge. # Update container tag settings Source: https://supermemory.ai/docs/api-reference/container-tags/update-container-tag-settings https://api.supermemory.ai/v4/openapi patch /v3/container-tags/{containerTag} Update settings for a container tag # Create memories directly Source: https://supermemory.ai/docs/api-reference/content-management/create-memories-directly https://api.supermemory.ai/v4/openapi post /v4/memories Create memories directly, bypassing the document ingestion workflow. Generates embeddings and makes them immediately searchable. # Forget a memory Source: https://supermemory.ai/docs/api-reference/content-management/forget-a-memory https://api.supermemory.ai/v4/openapi delete /v4/memories Forget (soft delete) a memory entry. The memory is marked as forgotten but not permanently deleted. # Forget memories matching a prompt/query Source: https://supermemory.ai/docs/api-reference/content-management/forget-memories-matching-a-promptquery https://api.supermemory.ai/v4/openapi post /v4/memories/forget-matching Agentic mass-forget. Given a prompt or query, a tool-calling agent searches the container's memories and soft-deletes everything matching the target. Use dryRun to preview first. # List memory entries with history Source: https://supermemory.ai/docs/api-reference/content-management/list-memory-entries-with-history https://api.supermemory.ai/v4/openapi post /v4/memories/list List all latest memory entries from specified container tags with their update history and source documents # Update a memory (creates new version) Source: https://supermemory.ai/docs/api-reference/content-management/update-a-memory-creates-new-version https://api.supermemory.ai/v4/openapi patch /v4/memories Update a memory by creating a new version. The original memory is preserved with isLatest=false. # Documents Source: https://supermemory.ai/docs/api-reference/documents List, get status, update, delete, and inspect ingested documents. 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](/docs/ingestion/document-operations) # Get document Source: https://supermemory.ai/docs/api-reference/documents/get-document https://api.supermemory.ai/v4/openapi get /v3/documents/{id} Get a document by ID # Get document chunks Source: https://supermemory.ai/docs/api-reference/documents/get-document-chunks https://api.supermemory.ai/v4/openapi get /v3/documents/{id}/chunks Get all chunks for a document, ordered by position # Get presigned file URL Source: https://supermemory.ai/docs/api-reference/documents/get-presigned-file-url https://api.supermemory.ai/v4/openapi get /v3/documents/{id}/file-url Get a fresh presigned URL for a document's file. Returns a time-limited URL (24h) that can be used to download the file. # Get processing documents Source: https://supermemory.ai/docs/api-reference/documents/get-processing-documents https://api.supermemory.ai/v4/openapi get /v3/documents/processing Get documents that are currently being processed. Default `view=active` is the live in-flight queue. `view=pending` is every unfinished document with no time cutoff. `view=all` also includes failed documents, paginated. # List documents Source: https://supermemory.ai/docs/api-reference/documents/list-documents https://api.supermemory.ai/v4/openapi post /v3/documents/list Retrieves a paginated list of documents with their metadata and workflow status # Search documents Source: https://supermemory.ai/docs/api-reference/documents/search-documents https://api.supermemory.ai/v4/openapi post /v3/search Search memories with advanced filtering # Ingest Source: https://supermemory.ai/docs/api-reference/ingest Add documents, files, batches, and conversations to Supermemory. 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](/docs/ingestion/add-memories) · [Quickstart](/docs/quickstart) Use a stable `customId` (conversation id, doc id) so re-sends upsert instead of duplicating. Pass `dreaming: "instant"` when the next step is memory search or profiles. # Add document Source: https://supermemory.ai/docs/api-reference/ingest/add-document https://api.supermemory.ai/v4/openapi post /v3/documents Add a document with any content type (text, url, file, etc.) and metadata # Batch add documents Source: https://supermemory.ai/docs/api-reference/ingest/batch-add-documents https://api.supermemory.ai/v4/openapi post /v3/documents/batch Add multiple documents in a single request. Each document can have any content type (text, url, file, etc.) and metadata # Bulk delete documents Source: https://supermemory.ai/docs/api-reference/ingest/bulk-delete-documents https://api.supermemory.ai/v4/openapi delete /v3/documents/bulk Bulk delete documents by IDs or container tags # Delete document by ID or customId Source: https://supermemory.ai/docs/api-reference/ingest/delete-document-by-id-or-customid https://api.supermemory.ai/v4/openapi delete /v3/documents/{id} Delete a document by ID or customId # Ingest or update conversation Source: https://supermemory.ai/docs/api-reference/ingest/ingest-or-update-conversation https://api.supermemory.ai/v4/openapi post /v4/conversations Ingest or update a conversation # Update document Source: https://supermemory.ai/docs/api-reference/ingest/update-document https://api.supermemory.ai/v4/openapi patch /v3/documents/{id} Update a document with any content type (text, url, file, etc.) and metadata # Upload a file Source: https://supermemory.ai/docs/api-reference/ingest/upload-a-file https://api.supermemory.ai/v4/openapi post /v3/documents/file Upload a file to be processed # Memories Source: https://supermemory.ai/docs/api-reference/memories Create, list, update, and forget extracted memory entries (v4). 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](/docs/api-reference/documents). For pipeline ingest, use [Ingest](/docs/api-reference/ingest). **Guide:** [Memory operations](/docs/recall/memory-operations) # API Reference Source: https://supermemory.ai/docs/api-reference/overview Interactive reference for the Supermemory HTTP API — ingest, search, profiles, memories, connectors, and settings. 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](/docs/quickstart) and [Using supermemory](/docs/ingestion/add-memories). ## Base URL ``` https://api.supermemory.ai ``` Self-hosted: use your instance URL (for example `http://localhost:6767`). See [Self-hosting](/docs/self-hosting/overview). ## Authentication All endpoints use a Bearer API key. Create one in the [developer console](https://console.supermemory.ai). ```bash theme={null} Authorization: Bearer sm_... ``` Details: [API keys & auth](/docs/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](/docs/quickstart). ## SDKs Official clients wrap this API: * TypeScript: `npm install supermemory` * Python: `pip install supermemory` See [Supermemory SDK](/docs/integrations/supermemory-sdk). Playground snippets come from the OpenAPI spec: official **TypeScript / Python SDK** samples via `x-codeSamples`, plus cURL. (After API deploy — until then you may still see generic HTTP snippets.) 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) # Profiles Source: https://supermemory.ai/docs/api-reference/profiles Entity profiles — static and dynamic facts for a container. 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](/docs/recall/user-profiles) · [Concepts](/docs/concepts/user-profiles) · [Buckets](/docs/user-profiles/buckets) # Get profile buckets Source: https://supermemory.ai/docs/api-reference/profiles/get-profile-buckets https://api.supermemory.ai/v4/openapi post /v4/profile/buckets Returns the effective profile bucket definitions for a given container tag — org-level buckets merged with any container-tag-level additions. # Get user profile Source: https://supermemory.ai/docs/api-reference/profiles/get-user-profile https://api.supermemory.ai/v4/openapi post /v4/profile Get user profile with optional search results # Search memory entries Source: https://supermemory.ai/docs/api-reference/recall-search/search-memory-entries https://api.supermemory.ai/v4/openapi post /v4/search Search memory entries - Low latency for conversational # Recall Source: https://supermemory.ai/docs/api-reference/search Semantic search over memories, document chunks, or both — plus user profiles. 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](/docs/recall/search) · [User profiles](/docs/recall/user-profiles) · [SuperRAG](/docs/concepts/super-rag) · [Memory vs RAG](/docs/concepts/memory-vs-rag) # Settings Source: https://supermemory.ai/docs/api-reference/settings Organization settings, profile buckets, and data reset. 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](/docs/concepts/customization) # Get settings Source: https://supermemory.ai/docs/api-reference/settings/get-settings https://api.supermemory.ai/v4/openapi get /v3/settings Get settings for an organization # Reset organization data Source: https://supermemory.ai/docs/api-reference/settings/reset-organization-data https://api.supermemory.ai/v4/openapi post /v3/settings/reset Reset organization content: removes documents, memories, spaces (except default project), connections, and org settings. Preserves the org, members, and billing. # Suggest profile buckets Source: https://supermemory.ai/docs/api-reference/settings/suggest-profile-buckets https://api.supermemory.ai/v4/openapi post /v3/settings/suggest-buckets Suggest profile bucket definitions based on the organization context prompt. Returns 3–6 bucket suggestions tailored to the use-case described in the prompt. # Update settings Source: https://supermemory.ai/docs/api-reference/settings/update-settings https://api.supermemory.ai/v4/openapi patch /v3/settings Update settings for an organization # API keys & auth Source: https://supermemory.ai/docs/authentication Org API keys, container-scoped keys, and connector branding. ## API Keys All API requests require authentication using a Bearer token. Get your API key from the [Developer Platform](https://console.supermemory.ai). Include your key in all requests: ```bash cURL theme={null} curl https://api.supermemory.ai/v3/search \ --header 'Authorization: Bearer YOUR_API_KEY' \ --header 'Content-Type: application/json' \ -d '{"q": "hello"}' ``` ```typescript TypeScript theme={null} import Supermemory from "supermemory"; const client = new Supermemory({ apiKey: "YOUR_API_KEY" }); ``` ```python Python theme={null} from supermemory import Supermemory client = Supermemory(api_key="YOUR_API_KEY") ``` *** ## Connector Branding When users connect external services (Google Drive, Notion, OneDrive), they see a "Log in to **Supermemory**" prompt by default. You can replace this with your own app name by providing your own OAuth credentials via the settings endpoint. ```typescript theme={null} await client.settings.update({ googleDriveCustomKeyEnabled: true, googleDriveClientId: "your-client-id.apps.googleusercontent.com", googleDriveClientSecret: "your-client-secret" }); ``` This works for Google Drive, Notion, and OneDrive. See the full setup in [Customization](/docs/concepts/customization). *** ## Scoped API keys Scoped keys are restricted to one or more `containerTag`s. They can only access documents and search within those containers — use them to give a client, session, or tenant limited access without shipping your org master key. Pairs with [container tags](/docs/concepts/container-tags) for multi-tenant isolation. **Allowed endpoints:** `/v3/documents`, `/v3/memories`, `/v4/memories`, `/v3/search`, `/v4/search`, `/v4/profile` Scoped keys **cannot** read billing, manage org settings, or mint further keys. ### Create a scoped key ```bash theme={null} 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 | — | Alphanumeric, hyphens, underscores, colons, dots | | `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 theme={null} { "key": "sm_orgId_...", "id": "key-id", "name": "scoped_my-project", "containerTag": "my-project", "expiresAt": "2026-03-08T00:00:00.000Z", "allowedEndpoints": ["/v3/documents", "/v3/memories", "/v4/memories", "/v3/search", "/v4/search", "/v4/profile"] } ``` Use the returned key like a normal API key — it just will not work outside its container scope. ### Disable a scoped key Revoke with the `id` from creation. Subsequent requests get `401`. Memories and container tags are **not** deleted. ```bash theme={null} curl https://api.supermemory.ai/v3/auth/scoped-key/KEY_ID \ --request DELETE \ --header 'Authorization: Bearer YOUR_API_KEY' ``` ```json theme={null} { "success": true } ``` # Container Tags Source: https://supermemory.ai/docs/concepts/container-tags The isolation boundary that groups and partitions memories by user, project, or any logical scope A **container tag** is the primary way you organize and isolate memories in Supermemory. It's a simple string identifier you attach to content when you add it — and that you pass back when you search, list, or update it. Think of a container tag as a **namespace**: every memory tagged with `user_alex` lives in its own isolated space, completely separate from memories tagged `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 across both tags. Bucket memories by user, project, agent, workspace, or any boundary that makes sense for your app. Each container tag maps to its own vector namespace, so search and retrieval never leak across boundaries. *** ## How it works When you add a memory with a container tag, Supermemory automatically creates a **space** for that tag (scoped to your organization) if one doesn't already exist. You don't need to provision anything ahead of time — the first write with a new tag creates the container, and subsequent writes reuse it. ```typescript theme={null} // First call auto-creates the "user_alex" container await client.add({ content: "Alex prefers dark mode and concise answers", containerTag: "user_alex", }); // Later, retrieve only Alex's memories const results = await client.search({ q: "what are the user's UI preferences?", containerTag: "user_alex", }); ``` Under the hood, each container tag is hashed into a dedicated vector namespace. Embeddings, chunks, and memory entries for one tag are stored and searched independently of every other tag — there is no shared index to filter through, which is why isolation is strict rather than best-effort. A container tag is an **opaque identifier you choose**. Supermemory does not parse meaning out of it — `user_123`, `project_mobile`, and `org:acme:team:growth` are all equally valid. Pick a convention that mirrors the access boundaries in your own application. *** ## Naming rules Container tags are validated on every request. A tag must: * Be **100 characters or less** * Contain only **alphanumeric characters, hyphens (`-`), underscores (`_`), and colons (`:`)** Matching pattern: `^[a-zA-Z0-9_:-]+$` ```typescript theme={null} // ✅ 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** tags (for example `org:acme:user:john`) that encode several levels of structure in a single identifier. *** ## `containerTag` vs `containerTags` Supermemory's current API uses a **single** `containerTag` string per request. The plural `containerTags` array field is **deprecated**. It still works for backward compatibility on older (`/v3`) endpoints, but new integrations should use the singular `containerTag` string. The `/v4` API only accepts `containerTag`. | API field | Type | Status | | --------------- | ---------- | -------------------- | | `containerTag` | `string` | ✅ Current — use this | | `containerTags` | `string[]` | ⚠️ Deprecated | *** ## Where container tags are used The same tag flows through the entire lifecycle of a memory. Pass it consistently and your data stays neatly partitioned. | Operation | Behavior | | ------------------- | --------------------------------------------------------------------- | | **Add** | Writes the memory into the tag's container (auto-creating the space). | | **Search** | Restricts retrieval to the given tag's namespace. | | **List** | Returns only memories belonging to the tag(s). | | **Update / Delete** | Targets the memory inside the specified tag's container. | ```typescript theme={null} // Add await client.add({ content: "Q1 planning notes", containerTag: "project_q1" }); // Search within the same container await client.search({ q: "planning", containerTag: "project_q1" }); // List everything in the container await client.documents.list({ containerTags: ["project_q1"] }); ``` *** ## Access control Container tags are also an **authorization boundary**, not just an organizational one. Two mechanisms can restrict which tags a given caller may touch: * **API key scopes** — an API key can be limited to a specific set of container tags, with read or write permission per tag. * **Member restrictions** — an organization member can be granted access to only certain container tags. When a request is restricted, Supermemory validates the requested tag against the caller's allowed set: * Requesting a tag outside the allowed set returns `403 Forbidden`. * A write (add/update/delete) to a read-only tag returns `403 Forbidden`. * If no tag is supplied by a restricted caller, the request is automatically scoped to their allowed tag(s). 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. *** ## Per-container settings Each container tag can carry its own configuration, independent of other tags in the same organization: | Setting | Purpose | | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | A human-friendly display name for the container. | | `entityContext` | A custom context prompt applied when processing documents in this container — useful for steering extraction and summarization per project or tenant. | ```typescript theme={null} await client.containerTags.update("project_research", { entityContext: "This project contains research papers about machine learning.", }); ``` Container tags can also be **merged** when you need to consolidate two buckets of memories into one. *** ## Choosing a convention Pick a tagging scheme that maps onto the isolation boundaries your application actually needs. | Pattern | Example | Use case | | ---------------- | --------------------------- | -------------------------------------- | | User isolation | `user_{userId}` | Per-user memory in a consumer app | | Project grouping | `project_{projectId}` | Project- or workspace-scoped content | | Agent scoping | `agent_{agentId}` | Separate long-term memory per AI agent | | Hierarchical | `org:{orgId}:user:{userId}` | Multi-level, multi-tenant SaaS | Keep tags **deterministic** — derive them directly from IDs you already have (a user ID, a tenant ID) so you can always reconstruct the right tag at query time without a lookup. *** ## Next steps Combine container tags with metadata filters for precise retrieval. Mint keys that can only touch one container — multi-tenant clients without the org master key. See container tags in action across the add API. # Supported Content Types Source: https://supermemory.ai/docs/concepts/content-types All the content formats Supermemory can ingest and process Supermemory automatically extracts and indexes content from various formats. There are two entry points: `client.add()` for text and URLs, `client.documents.uploadFile()` for actual files. See [Add Memories](/docs/ingestion/add-memories) to learn how to ingest content via the API. ## Text Content Raw text, conversations, notes, or any string content. ```typescript theme={null} await client.add({ content: "User prefers dark mode and uses vim keybindings", containerTag: "user_123" }); ``` **Best for:** Chat messages, user preferences, notes, logs, transcripts. *** ## URLs & Web Pages Send a URL and Supermemory fetches, extracts, and indexes the content. ```typescript theme={null} await client.add({ content: "https://docs.example.com/api-reference", containerTag: "documentation" }); ``` **Extracts:** Article text, headings, metadata. Strips navigation, ads, boilerplate. URL extraction is powered by [Markdowner](https://md.dhr.wtf). *** ## Documents ### PDF Files are binary, so they go through `uploadFile`, not `add` — pass a stream, not base64: ```typescript theme={null} import fs from 'fs'; await client.documents.uploadFile({ file: fs.createReadStream('report.pdf'), containerTag: "user_123", metadata: JSON.stringify({ title: "Q4 Financial Report" }) }); ``` **Extracts:** Text, tables, headers. OCR for scanned documents. ### Microsoft Office Word, Excel, and PowerPoint files upload the same way — Supermemory detects the type from the file itself: ```typescript theme={null} await client.documents.uploadFile({ file: fs.createReadStream('roadmap.docx'), containerTag: "user_123", metadata: JSON.stringify({ title: "Product Roadmap" }) }); ``` ### Google Workspace Automatically handled via [Google Drive connector](/docs/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 theme={null} // Markdown await client.add({ content: markdownContent, containerTag: "user_123", metadata: { title: "README.md" } }); // Code (language auto-detected) await client.add({ content: codeContent, containerTag: "user_123", metadata: { language: "typescript" } }); ``` **Extracts:** Structure, headings, code blocks with syntax awareness. Code is chunked using [code-chunk](https://github.com/supermemoryai/code-chunk), which understands AST boundaries to keep functions, classes, and logical blocks intact. See [Super RAG](/docs/concepts/super-rag) for how Supermemory optimizes chunking for each content type. *** ## Images `fileType: "image"` and `mimeType` are both required so Supermemory knows exactly how to process it: ```typescript theme={null} await client.documents.uploadFile({ file: fs.createReadStream('diagram.png'), fileType: "image", mimeType: "image/png", containerTag: "user_123", metadata: JSON.stringify({ title: "Architecture Diagram" }) }); ``` **Extracts:** OCR text, visual descriptions, diagram interpretations. **Supported:** PNG, JPG, JPEG, WebP, GIF *** ## Audio & Video Video has a dedicated `fileType`; audio is uploaded the same way and detected from the file itself: ```typescript theme={null} // Video await client.documents.uploadFile({ file: fs.createReadStream('demo.mp4'), fileType: "video", mimeType: "video/mp4", containerTag: "user_123", metadata: JSON.stringify({ title: "Product Demo" }) }); // Audio await client.documents.uploadFile({ file: fs.createReadStream('call-recording.mp3'), mimeType: "audio/mpeg", containerTag: "user_123", metadata: JSON.stringify({ title: "Customer Call Recording" }) }); ``` **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 theme={null} await client.add({ content: JSON.stringify(userData), containerTag: "user_123", metadata: { title: "User Profile Data", format: "json" } }); ``` ### CSV ```typescript theme={null} await client.add({ content: csvContent, containerTag: "user_123", metadata: { title: "Sales Data Q4", format: "csv" } }); ``` *** ## File Upload For any binary file, use `uploadFile` — it accepts a stream, not base64: ```typescript theme={null} import fs from 'fs'; await client.documents.uploadFile({ file: fs.createReadStream('./document.pdf'), containerTag: "user_123", metadata: JSON.stringify({ title: "document.pdf" }) }); ``` No Node `fs` access? `uploadFile` also accepts a web `File`, a `fetch` `Response`, or the SDK's `toFile` helper: ```typescript theme={null} import Supermemory, { toFile } from 'supermemory'; await client.documents.uploadFile({ file: new File(['my bytes'], 'file') }); await client.documents.uploadFile({ file: await fetch('https://somesite/file') }); await client.documents.uploadFile({ file: await toFile(Buffer.from('my bytes'), 'file') }); ``` *** ## Auto-Detection `add()` tells URLs and plain text apart on its own — no extra flag needed: ```typescript theme={null} // URL detected automatically await client.add({ content: "https://example.com/page" }); // Plain text detected automatically await client.add({ content: "User said they prefer email contact" }); ``` For files, `uploadFile` detects type from the file itself in most cases. `fileType` only exists to force specific processing — and it's required (along with `mimeType`) for images and video. *** ## 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 chunked at the sentence level with a 2-sentence overlap between chunks. For large files, consider chunking or using [connectors](/docs/connectors/overview) for automatic sync. *** ## Next Steps Upload content via the API How content is chunked and indexed # Customizing for Your Use Case Source: https://supermemory.ai/docs/concepts/customization Configure Supermemory's behavior for your specific application Configure how Supermemory processes and retrieves content for your specific use case. ## Filter Prompts Tell Supermemory what content matters during ingestion. This helps filter and prioritize what gets indexed. ```typescript theme={null} // Example: Brand guidelines assistant await client.settings.update({ shouldLLMFilter: true, filterPrompt: `You are ingesting content for Brand.ai's brand guidelines system. Index: - Official brand values and mission statements - Approved tone of voice guidelines - Logo usage and visual identity docs - Approved messaging and taglines Skip: - Draft documents and work-in-progress - Outdated brand materials (pre-2024) - Internal discussions about brand changes - Competitor analysis docs` }); ``` ```typescript theme={null} filterPrompt: `Personal AI assistant. Prioritize recent content, action items, and personal context. Exclude spam and duplicates.` ``` ```typescript theme={null} filterPrompt: `Customer support agent. Prioritize verified solutions, official docs, and resolved tickets. Exclude internal discussions and PII.` ``` ```typescript theme={null} filterPrompt: `Legal research assistant. Prioritize precedents, current regulations, and approved contract language. Exclude privileged communications.` ``` ```typescript theme={null} filterPrompt: `Financial analysis assistant. Prioritize latest reports, verified data, and regulatory filings. Exclude speculative data and MNPI.` ``` ```typescript theme={null} filterPrompt: `Healthcare information assistant. Prioritize evidence-based guidelines and FDA-approved info. Exclude PHI and outdated recommendations.` ``` ```typescript theme={null} filterPrompt: `Developer documentation assistant. Prioritize current APIs, working examples, and best practices. Exclude deprecated APIs and test fixtures.` ``` ### Related settings `shouldLLMFilter` must be `true` for any of these to take effect — using them without it returns a 400 error. | Setting | Type | Limits | | ------------------------------- | ---------- | -------------------------------------------------------------- | | `categories` | `string[]` | 1-50 chars each. If omitted, 3-5 categories are auto-generated | | `includeItems` / `excludeItems` | `string[]` | 1-20 chars each item | | `filterPrompt` | `string` | 1-750 characters | *** ## Entity Context Guide memory extraction for a specific container tag. Filter prompts are org-wide; entity context is per container. ```typescript theme={null} await client.add({ content: "User asked about logo variations for dark backgrounds...", containerTag: "session_abc123", entityContext: `Design exploration conversation between john@acme.com and Brand.ai assistant. Focus on John's design preferences and brand requirements.` }); ``` Update entity context for a container tag without uploading content. ```typescript theme={null} await client.containerTags.update("session_abc123", { entityContext: `Design exploration conversation between john@acme.com and Brand.ai assistant. Focus on John's design preferences and brand requirements.` }); ``` Entity context persists on the container tag and combines with org-level filter prompts. *** ## Chunk Size Control how documents are split into searchable pieces. Smaller chunks = more precise retrieval but less context per result. ```typescript theme={null} await client.settings.update({ chunkSize: 512 // -1 for default }); ``` | Use Case | Chunk Size | Why | | ---------------------- | ----------- | ------------------------------- | | Citations & references | `256-512` | Precise source attribution | | Q\&A / Support | `512-1024` | Balanced context | | Long-form analysis | `1024-2048` | More context per chunk | | Default | `-1` | Supermemory's optimized default | Smaller chunks generate more memories per document. Larger chunks provide more context but may reduce precision. *** ## Connector Branding Show "Log in to **YourApp**" instead of "Log in to Supermemory" when users connect external services. See [Connectors Overview](/docs/connectors/overview) for the full list of supported integrations. 1. Create OAuth credentials in [Google Cloud Console](https://console.cloud.google.com/) 2. Redirect URI: `https://api.supermemory.ai/v3/connections/google-drive/callback` ```typescript theme={null} await client.settings.update({ googleDriveCustomKeyEnabled: true, googleDriveClientId: "your-client-id.apps.googleusercontent.com", googleDriveClientSecret: "your-client-secret" }); ``` 1. Create integration at [Notion Developers](https://developers.notion.com/) 2. Redirect URI: `https://api.supermemory.ai/v3/connections/notion/callback` ```typescript theme={null} await client.settings.update({ notionCustomKeyEnabled: true, notionClientId: "your-notion-client-id", notionClientSecret: "your-notion-client-secret" }); ``` 1. Register app in [Azure Portal](https://portal.azure.com/) 2. Redirect URI: `https://api.supermemory.ai/v3/connections/onedrive/callback` ```typescript theme={null} await client.settings.update({ onedriveCustomKeyEnabled: true, onedriveClientId: "your-azure-app-id", onedriveClientSecret: "your-azure-client-secret" }); ``` *** ## API Reference ```typescript theme={null} // Get current settings const settings = await client.settings.get(); // Update settings await client.settings.update({ shouldLLMFilter: true, filterPrompt: "...", chunkSize: 512 }); ``` Settings are organization-wide. Changes apply to new content only—existing memories aren't reprocessed. *** ## Next Steps See your custom settings in action Set up automatic syncing from external platforms # Organizing & Filtering Memories Source: https://supermemory.ai/docs/concepts/filtering Use container tags and metadata to organize and retrieve memories Supermemory provides two ways to organize your memories: **Organize memories** into isolated spaces by user, project, or workspace **Query memories** by custom properties like category, status, or date Both can be used independently or together for precise filtering. *** ## Container Tags Container tags create isolated memory spaces. Use them to separate memories by user, project, or any logical boundary. ### Adding Memories with Tags ```typescript theme={null} await client.add({ content: "Meeting notes from Q1 planning", containerTag: "user_123" }); ``` ### Searching with Tags ```typescript theme={null} const results = await client.search({ q: "planning notes", containerTag: "user_123", searchMode: "documents" }); ``` Each search is scoped to a single container tag. Passing `containerTag: "user_123"` restricts results to memories stored in that container. ### 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 | ```typescript theme={null} // Multi-tenant SaaS - isolate by organization and user await client.add({ content: "Company policy document", containerTag: "org_acme_user_john" }); // Search only within that user's org context const results = await client.search({ q: "vacation policy", containerTag: "org_acme_user_john", searchMode: "documents" }); // Project-based isolation await client.add({ content: "Sprint 5 retrospective notes", containerTag: "project_mobile_app" }); // Time-based segmentation await client.add({ content: "Q1 2024 financial report", containerTag: "user_cfo_2024_q1" }); ``` **API field differences:** | Operation | Field | Type | | -------------- | --------------- | ------ | | Search | `containerTag` | String | | Documents list | `containerTags` | Array | *** ## Metadata Metadata lets you attach custom properties to memories and filter by them later. ### Adding Memories with Metadata ```typescript theme={null} await client.add({ content: "Technical design document for auth system", containerTag: "user_123", metadata: { category: "engineering", priority: "high", year: 2024 } }); ``` ### Searching with Metadata Filters Filters must be wrapped in `AND` or `OR` arrays: ```typescript theme={null} const results = await client.search({ q: "design document", containerTag: "user_123", searchMode: "documents", filters: { AND: [ { key: "category", value: "engineering" }, { key: "priority", value: "high" } ] } }); ``` ### Filter Types | Type | Example | Description | | --------------- | ------------------------------------------------------------------------------- | ---------------------- | | String equality | `{ key: "status", value: "published" }` | Exact match | | String contains | `{ filterType: "string_contains", key: "title", value: "react" }` | Substring match | | Numeric | `{ filterType: "numeric", key: "priority", value: "5", numericOperator: ">=" }` | Number comparison | | Array contains | `{ filterType: "array_contains", key: "tags", value: "important" }` | Check array membership | ### Combining Filters Use `AND` and `OR` for complex queries: ```typescript theme={null} const results = await client.search({ q: "meeting notes", searchMode: "documents", filters: { AND: [ { key: "type", value: "meeting" }, { OR: [ { key: "team", value: "engineering" }, { key: "team", value: "product" } ] } ] } }); ``` ### Excluding Results Use `negate: true` to exclude matches: ```typescript theme={null} const results = await client.search({ q: "documentation", searchMode: "documents", filters: { AND: [ { key: "status", value: "draft", negate: true } ] } }); ``` **String contains (substring search):** ```typescript theme={null} // Find documents with "machine learning" in the description const results = await client.search({ q: "AI research", searchMode: "documents", filters: { AND: [ { filterType: "string_contains", key: "description", value: "machine learning", ignoreCase: true } ] } }); ``` **Numeric comparisons:** ```typescript theme={null} // Find high-priority items created after a specific date const results = await client.search({ q: "tasks", searchMode: "documents", filters: { AND: [ { filterType: "numeric", key: "priority", value: "7", numericOperator: ">=" }, { filterType: "numeric", key: "created_timestamp", value: "1704067200", // Unix timestamp numericOperator: ">=" } ] } }); ``` **Array contains (check array membership):** ```typescript theme={null} // Find documents where a specific user is a participant const results = await client.search({ q: "meeting notes", searchMode: "documents", filters: { AND: [ { filterType: "array_contains", key: "participants", value: "alice@company.com" } ] } }); ``` **Complex nested filters:** ```typescript theme={null} // (category = "tech" OR category = "science") AND status != "archived" const results = await client.search({ q: "research papers", searchMode: "documents", filters: { AND: [ { OR: [ { key: "category", value: "tech" }, { key: "category", value: "science" } ] }, { key: "status", value: "archived", negate: true } ] } }); ``` **Numeric operator negation mapping:** When using `negate: true`, operators flip: * `<` becomes `>=` * `<=` becomes `>` * `>` becomes `<=` * `>=` becomes `<` * `=` becomes `!=` **User's work documents from 2024:** ```typescript theme={null} const results = await client.search({ q: "quarterly report", containerTag: "user_123", searchMode: "documents", filters: { AND: [ { key: "category", value: "work" }, { key: "type", value: "report" }, { filterType: "numeric", key: "year", value: "2024", numericOperator: "=" } ] } }); ``` **Team meeting notes with specific participants:** ```typescript theme={null} const results = await client.search({ q: "sprint planning", containerTag: "project_alpha", searchMode: "documents", filters: { AND: [ { key: "type", value: "meeting" }, { OR: [ { filterType: "array_contains", key: "participants", value: "alice" }, { filterType: "array_contains", key: "participants", value: "bob" } ] } ] } }); ``` **Exclude drafts and deprecated content:** ```typescript theme={null} const results = await client.search({ q: "documentation", searchMode: "documents", filters: { AND: [ { key: "status", value: "draft", negate: true }, { filterType: "string_contains", key: "content", value: "deprecated", negate: true }, { filterType: "array_contains", key: "tags", value: "archived", negate: true } ] } }); ``` *** ## Quick Reference ### When Adding Memories ```typescript theme={null} await client.add({ content: "Your content here", containerTag: "user_123", // Isolation metadata: { key: "value" } // Custom properties }); ``` ### When Searching ```typescript theme={null} const results = await client.search({ q: "search query", containerTag: "user_123", // Scopes results to this container searchMode: "documents", filters: { // Optional metadata filters AND: [{ key: "status", value: "published" }] } }); ``` ### Metadata Key Rules * Allowed characters: `a-z`, `A-Z`, `0-9`, `_`, `-`, `.` * Max length: 64 characters * No spaces or special characters ### Query Complexity Limits * Maximum 200 conditions per query * Maximum 8 levels of nested `AND`/`OR` expressions If you need more conditions than these limits allow, break your query into multiple requests or use broader search terms with post-processing. ### Searching Within a Document Use `docId` to scope a search to chunks within one large document — useful for books, podcasts, or other long-form content: ```typescript theme={null} const results = await client.search({ q: "machine learning", docId: "doc_123" }); ``` *** ## Next Steps Apply filters in search queries Add content with container tags and metadata # Graph memory Source: https://supermemory.ai/docs/concepts/graph-memory How facts connect, update, and stay true — memory relationships, temporal truth, and automatic forgetting. **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](/docs/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 theme={null} import Supermemory from "supermemory"; const client = new Supermemory({ apiKey: "sm_..." }); // from console.supermemory.ai → API Keys await client.add({ content: "Alex mentioned he just started at Stripe", containerTag: "user_123", }); const results = await client.search({ q: "where does Alex work?", containerTag: "user_123", include: { relatedMemories: true }, }); ``` Full walkthrough with a live example: [Quickstart](/docs/quickstart). ## Documents vs memories | | **Documents** | **Memories** | | ------------- | ---------------------------------- | ---------------------------------------- | | **What** | Raw input you send | Facts Supermemory extracts | | **Examples** | PDF, chat log, Drive file, URL | “Alex is a PM at Stripe” | | **Role** | Source of truth for RAG / SuperRAG | Personal and entity state over time | | **Lifecycle** | You add / update / delete | Graph updates, extends, derives, forgets | Think of documents as books you hand the system. Memories are the insights it keeps — connected to each other as new content arrives. Uploading a long PDF does more than store bytes: Supermemory derives many memories and links them to what it already knows about that entity or user. Chunks of the document remain available for [SuperRAG](/docs/concepts/super-rag) grounding. ## Properties and rules of memories 1. Memories are atomic - Each memory has enough information and context about one particular topic 2. They always build on top of each other - with `updates`, the model knows the history, a memory `extends` from other memories, and new facts are derived (`derives` relation) from existing knowledge. ## Memory relationships When content is processed, new facts connect to existing ones through three relationship types. ### Updates — information changes New fact **replaces** what was true before for search purposes; history can remain for audit. ```text theme={null} 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 theme={null} 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 theme={null} 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](/docs/quickstart) (gift → VP of Product → Sarah → Tokyo offsite). Search can expose edges via `include.relatedMemories` — see [Search API](/docs/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](/docs/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 `customId` 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](/docs/concepts/how-it-works#dreaming-how-memories-enter-the-graph) and [Processing modes](/docs/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](/docs/recall/memory-operations) and [Memory review](/docs/recall/memory-review). ## What you don’t do You do **not** hand-maintain the graph. You: 1. Ingest under a [container tag](/docs/concepts/container-tags) 2. Wait for the [pipeline](/docs/concepts/how-it-works) when needed 3. [Search](/docs/recall/search) or load a [profile](/docs/recall/user-profiles) ```typescript theme={null} await client.add({ content: "Alex mentioned he just started at Stripe", containerTag: "user_123", }); const results = await client.search({ q: "where does Alex work?", containerTag: "user_123", include: { relatedMemories: true }, }); // Prefer latest work fact (Stripe); history remains in the graph ``` ## Related in the docs | If you need… | Go to | | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | Pipeline statuses, dreaming, documents in | [How it works](/docs/concepts/how-it-works) | | Memory vs document retrieval | [Memory vs RAG](/docs/concepts/memory-vs-rag) · [SuperRAG](/docs/concepts/super-rag) | | Always-on summary of a user | [Profiles](/docs/concepts/user-profiles) | | Isolation / tenants | [Multi-tenancy](/docs/concepts/container-tags) | | API: add / search / forget | [Ingestion](/docs/ingestion/add-memories) · [Search](/docs/recall/search) · [Forget & update](/docs/recall/memory-operations) | Ingest pipeline, statuses, and outputs. When to use memory vs document retrieval. Static + dynamic context built from the graph. See entity chains in a full conversation + document flow. # How Supermemory Works Source: https://supermemory.ai/docs/concepts/how-it-works From a file or chat turn to something you can search — the ingest pipeline, statuses, and outputs. At it's core, supermemory is powered by a custom learning model and a graph database that we built internally. Decides what and how to learn, what is important, when to forget, creating relations, etc. Where the learnings are actually stored, optimized for search. Fact-based temporal graph that has Vector, FTS, and graph built in. But, you don't have to think about the above. The interface for users is as simple as it gets. ## Get started in under a minute From the [developer console](https://console.supermemory.ai) — **API Keys → Create API Key**. `console.supermemory.ai` is where keys and usage live. Install the SDK, drop in your key, add a memory, and search it — right below, or the full [ingest → retrieve loop](/docs/using-supermemory). ```bash TypeScript theme={null} npm install supermemory ``` ```typescript TypeScript theme={null} import Supermemory from "supermemory"; const client = new Supermemory({ apiKey: "sm_..." }); // from console.supermemory.ai → API Keys await client.add({ content: "The user loves Paris.", containerTag: "user_123" }); const { results } = await client.search({ q: "where does the user want to travel?", containerTag: "user_123", }); ``` ```python Python theme={null} from supermemory import Supermemory client = Supermemory(api_key="sm_...") # from console.supermemory.ai → API Keys client.add(content="The user loves Paris.", container_tag="user_123") results = client.search( q="where does the user want to travel?", container_tag="user_123", ) ``` ```bash curl theme={null} curl -X POST https://api.supermemory.ai/v3/documents \ -H "Authorization: Bearer sm_..." \ -H "Content-Type: application/json" \ -d '{ "content": "The user loves Paris.", "containerTag": "user_123" }' ``` ## 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](/docs/concepts/content-types) for formats, and [Add context](/docs/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 **`customId`** when the same conversation or file will be updated later (sessions, connector syncs). That identity also drives [diff billing](/docs/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 theme={null} const doc = await client.add({ content: conversationText, containerTag: "user_123", customId: "chat_session_1", }); // Poll until ready const status = await client.documents.get(doc.id); // status.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) Document **status `done`** means chunks are indexed for search. **Memories** — the graph facts, updates, and derives — 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](/docs/ingestion/add-memories): | 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 doc. Bills **one extra [operation](/docs/overview/billing)** per document. | Demos, quickstarts, “I need the graph now” | ```typescript theme={null} // Production default — omit or set explicitly await client.add({ content: conversationText, containerTag: "user_123", customId: "chat_session_1", dreaming: "dynamic", }); // Need memories immediately (e.g. tutorial) await client.add({ content: conversationText, containerTag: "user_123", customId: "chat_session_1", dreaming: "instant", }); ``` **Rule of thumb:** prefer **`dynamic`** for quality and cost in real apps, use **`instant`** when the next step is a memory search or profile that must reflect this document immediately (as in the [quickstart](/docs/quickstart)). Keeping it dynamic helps it pair better with other memories and better connections, inferences to be made. How those memories connect and stay true over time is [Graph memory](/docs/concepts/graph-memory). API detail: [Processing modes](/docs/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 `containerTag`): | Output | Role | Go deeper | | ------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | **Document chunks** | Grounding in the raw source (RAG / SuperRAG) | [SuperRAG](/docs/concepts/super-rag), [Search API](/docs/recall/search) | | **Memories** | Extracted facts in a living graph — updates, links, time | [Graph memory](/docs/concepts/graph-memory) | | **Profile** | A sample of memories, static + dynamic summary for always-on context | [Profiles](/docs/concepts/user-profiles), [Profile API](/docs/recall/user-profiles) | Supermemory does **not** only store the file. It derives **memories** (understanding) and keeps **chunks** (the source) so you can personalize *and* ground. That distinction is the core of [Memory vs RAG](/docs/concepts/memory-vs-rag). ## Isolation and identity * **`containerTag`** — hard isolation boundary (user, tenant, project). See [Container tags](/docs/concepts/container-tags). * **Metadata** — soft dimensions *inside* a tag for filtering. See [Metadata filtering](/docs/concepts/filtering). * **Scoped API keys** — credentials that cannot cross a container. See [API keys](/docs/authentication#scoped-api-keys). ## Next steps How facts connect, update, and stay true over time. Formats, extractors, and what you can send. API: add, customId, files, dreaming, status. Query documents and memories after the pipeline finishes. # Memory vs RAG: Understanding the Difference Source: https://supermemory.ai/docs/concepts/memory-vs-rag Learn why agent memory and RAG are fundamentally different, and when to use each approach Most developers confuse RAG (Retrieval-Augmented Generation) with agent memory. They're not the same thing, and using RAG for memory is why your agents keep forgetting important context. Let's understand the fundamental difference. ## The Core Problem When building AI agents, developers often treat memory as just another retrieval problem. They store conversations in a vector database, embed queries, and hope semantic search will surface the right context. **This approach fails because memory isn't about finding similar text—it's about understanding relationships, temporal context, and user state over time.** ## Documents vs Memories in Supermemory Supermemory makes a clear distinction between these two concepts: ### Documents: Raw Knowledge Documents are the raw content you send to Supermemory—PDFs, web pages, text files. They represent static knowledge that doesn't change based on who's accessing it. **Characteristics:** * **Stateless**: A document about Python programming is the same for everyone * **Unversioned**: Content doesn't track changes over time * **Universal**: Not linked to specific users or entities * **Searchable**: Perfect for semantic similarity search **Use Cases:** * Company knowledge bases * Technical documentation * Research papers * General reference material ### Memories: Contextual Understanding Memories are the insights, preferences, and relationships extracted from documents and conversations. They're tied to specific users or entities and evolve over time. **Characteristics:** * **Stateful**: "User prefers dark mode" is specific to that user * **Temporal**: Tracks when facts became true or invalid * **Personal**: Linked to users, sessions, or entities * **Relational**: Understands connections between facts **Use Cases:** * User preferences and history * Conversation context * Personal facts and relationships * Behavioral patterns ## Why RAG Fails as Memory Let's look at a real scenario that illustrates the problem: ``` Day 1: "I love Adidas sneakers" Day 30: "My Adidas broke after a month, terrible quality" Day 31: "I'm switching to Puma" Day 45: "What sneakers should I buy?" ``` ```python theme={null} # 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. ```python theme={null} # 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. ## 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 * Static documentation * Knowledge bases * Research queries * General Q\&A * Content that doesn't change per user * User preferences * Conversation history * Personal facts * Behavioral patterns * Anything that evolves over time ## Real-World Examples ### E-commerce Assistant Stores product catalogs, specifications, reviews ```python theme={null} # Good for RAG "What are the specs of iPhone 15?" "Compare Nike and Adidas running shoes" "Show me waterproof jackets" ``` Tracks user preferences, purchase history, interactions ```python theme={null} # Needs Memory "What size do I usually wear?" "Did I like my last purchase?" "What's my budget preference?" ``` ### Customer Support Bot FAQ documents, troubleshooting guides, policies ```python theme={null} # Good for RAG "How do I reset my password?" "What's your return policy?" "Troubleshooting WiFi issues" ``` Previous issues, user account details, conversation context ```python theme={null} # Needs Memory "Is my issue from last week resolved?" "What plan am I on?" "You were helping me with..." ``` ## How Supermemory Handles Both Supermemory provides a unified platform that correctly handles both patterns: ### 1. Document Storage (RAG) ```python theme={null} # Add a document for RAG-style retrieval client.add( content="iPhone 15 has a 48MP camera and A17 Pro chip", # No user association - universal knowledge ) ``` ### 2. Memory Creation ```python theme={null} # Add a user-specific memory client.add( content="User prefers Android over iOS", container_tags=["user_123"], # User-specific metadata={ "type": "preference", "confidence": "high" } ) ``` ### 3. Hybrid Retrieval ```python theme={null} # Search combines both approaches results = client.search.memories( q="What phone should I recommend?", container_tag="user_123", # Gets user memories search_mode="hybrid", # Also searches general knowledge ) # Results include: # - User's Android preference (memory) # - Latest Android phone specs (documents) ``` ## The Bottom Line **Key Insight**: RAG answers "What do I know?" while Memory answers "What do I remember about you?" Stop treating memory like a retrieval problem. Your agents need both: * **RAG** for accessing knowledge * **Memory** for understanding users Supermemory provides both capabilities in a unified platform, ensuring your agents have the right context at the right time. *** ## Next Steps How memory relationships work Our managed RAG solution Start ingesting content Query your memories and documents # Multi-tenancy Overview Source: https://supermemory.ai/docs/concepts/multi-tenancy How Supermemory isolates and organizes memories across users, tenants, and projects Most apps built on Supermemory serve more than one user, customer, or tenant out of a single Supermemory organization. Multi-tenancy is how you keep those memories apart — so User A's data is never visible to User B, and so you can still slice and query within a user's own data by things like category, status, or date. Supermemory gives you two complementary tools for this: **Isolation.** A container tag is a hard boundary — its own namespace. Memories in one tag are never returned by a search scoped to another tag. **Organization.** Metadata is a set of custom key/value properties on a memory that you filter by — category, priority, date, participants, anything you define. They solve different problems, and most production apps use both together. *** ## Why two mechanisms It's tempting to reach for one tool and make it do everything, but tags and metadata aren't interchangeable — they answer different questions. | Question | Answer | | ----------------------------------------------------------------- | -------------------------------------------------- | | "Which tenant does this memory belong to?" | **Container tag** | | "Within this tenant's memories, which ones match `status: open`?" | **Metadata filter** | | "Can this API key even see tenant X's data?" | **Container tag** (enforced as an access boundary) | | "Find memories tagged `engineering` created after March" | **Metadata filter** | A container tag decides **whether a memory is reachable at all** for a given request. Metadata decides **which of the reachable memories match**. Filtering never crosses a container tag boundary — you can't use metadata to peek into another tenant's container. *** ## How they work together A typical multi-tenant write scopes the memory to a tenant with a container tag, then attaches metadata for finer-grained querying later: ```typescript theme={null} await client.add({ content: "Customer requested a refund for order #4821", containerTag: "org_acme", // isolates to the "acme" tenant metadata: { category: "support", status: "open", priority: "high", }, }); ``` And a search combines both: the container tag restricts *which tenant's data* is in scope, and filters narrow down *which memories within that tenant* come back: ```typescript theme={null} const results = await client.search({ q: "refund request", containerTag: "org_acme", searchMode: "documents", filters: { AND: [ { key: "category", value: "support" }, { key: "status", value: "open" }, ], }, }); ``` Container tags are **required** for isolation and validated as an access boundary. Metadata filters are **optional** — a search with just `containerTag` and no `filters` still only returns that tenant's memories. *** ## Choosing your boundary Container tags are 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 container 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 | Everything *within* that boundary — categories, statuses, dates, custom fields — is metadata, not a new tag. Don't create a new container tag for every property you want to filter on; that's what metadata is for. *** ## Access control Container tags aren't just organizational — they're enforced as an authorization boundary. API keys and org members can be restricted to specific tags, so a request for a tag outside the caller's allowed set is rejected with `403 Forbidden` rather than silently filtered. See [Container Tags → Access control](/docs/concepts/container-tags#access-control) for the details. *** ## Next steps Personal agents, company agents, email assistants, and support platforms. How isolation works, naming rules, and access control. Metadata filter types, combining `AND`/`OR`, and query limits. Mint keys that can only touch one container tag. # Multi-tenancy Examples Source: https://supermemory.ai/docs/concepts/multi-tenancy-examples Common container tag and metadata patterns for personal agents, company agents, email assistants, and support platforms A few common shapes multi-tenancy takes in practice, combining [container tags](/docs/concepts/container-tags) for isolation with [metadata filters](/docs/concepts/filtering) for organization within a boundary. *** ## Personal agent A single container tag per user is enough — there's no shared data to leak, so metadata is optional. ```typescript theme={null} await client.add({ content: "User prefers morning workouts and vegetarian meals", containerTag: "user_123", }); const results = await client.search({ q: "workout preferences", containerTag: "user_123", }); ``` *** ## Company agent (shared + personal memory) A company-wide assistant usually needs two kinds of containers: one **shared** container the whole org reads from, and one **personal** container per employee that nobody else can see. ```typescript theme={null} // Shared org knowledge — visible to everyone at the company await client.add({ content: "Q3 roadmap: ship the mobile app redesign by end of August", containerTag: "org_acme_shared", metadata: { team: "product", type: "roadmap" }, }); // Personal memory — only this employee's agent should see this await client.add({ content: "Prefers async updates over meetings", containerTag: "org_acme_user_alex", }); ``` Inside the shared container, use metadata to scope queries to a team rather than creating a container tag per team: ```typescript theme={null} const results = await client.search({ q: "roadmap updates", containerTag: "org_acme_shared", searchMode: "documents", filters: { AND: [{ key: "team", value: "product" }], }, }); ``` An employee's agent typically queries both containers — their personal one plus the shared one — and merges the results, since the container tag boundary is per-request rather than per-user. *** ## Email assistant One container tag 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 theme={null} await client.add({ content: "Your Spotify Premium receipt for July — $11.99 charged", containerTag: "user_123", metadata: { source: "gmail", sender: "no-reply@spotify.com", label: "Promotional", }, }); const results = await client.search({ q: "spotify", containerTag: "user_123", searchMode: "documents", filters: { AND: [ { key: "source", value: "gmail" }, { key: "label", value: "Promotional" }, ], }, }); ``` *** ## Multi-tenant support platform Each customer gets their own container tag, and metadata tracks ticket-level fields like status and priority — so "open, high-priority tickets" is a filter, not a new tag, and it can never accidentally include another customer's tickets. ```typescript theme={null} await client.add({ content: "Customer reports checkout button unresponsive on Safari", containerTag: "org_customer_442", metadata: { status: "open", priority: "high", channel: "chat" }, }); const results = await client.search({ q: "checkout issue", containerTag: "org_customer_442", searchMode: "documents", filters: { AND: [ { key: "status", value: "open" }, { key: "priority", value: "high" }, ], }, }); ``` *** ## Next steps Why container tags and metadata are separate mechanisms. How isolation works, naming rules, and access control. Metadata filter types, combining `AND`/`OR`, and query limits. # Rules of supermemory Source: https://supermemory.ai/docs/concepts/rules Best practices and things to consider when using supermemory in your system 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](/docs/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](/docs/ingestion/add-memories); the pipeline derives memories from them and maintains a profile per [container tag](/docs/concepts/how-it-works). `client.search({ searchMode: "memories" })` recalls the derived facts. `client.search({ searchMode: "documents" })` 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: ```typescript TypeScript theme={null} await client.add({ content: "testing", containerTag: "test", taskType: "superrag" }); ``` ```python Python theme={null} client.add( content="testing", container_tag="test", task_type="superrag" ) ``` ```bash curl theme={null} curl -X POST "https://api.supermemory.ai/v3/documents" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "testing", "containerTag": "test", "taskType": "superrag" }' ``` #### Use hybrid mode when searching over SuperRag content `hybrid` mode makes it much easier to get complete results from supermemory when you have both memories and documents. ```typescript TypeScript theme={null} const results = await client.search({ q: "test", searchMode: "hybrid" }); ``` ```python Python theme={null} results = client.search.memories( q="test", search_mode="hybrid" ) ``` ```bash curl theme={null} curl -X POST "https://api.supermemory.ai/v4/search" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "q": "test", "searchMode": "hybrid" }' ``` The response comes back in this shape: ```ts theme={null} ({ memory: string } | { chunk: string })[] ``` 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 `containerTag` 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 a `customId` 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 container. 3. **Send the same prefix**: keep the start of the document identical across ingests under the same `customId` 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 `customId`. ``` 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 `entityContext` to prevent drift over time. Picture a third person watching a conversation between two people: what do they remember, and about whom? Giving supermemory context about the entity itself helps ground its learnings and prevents drift and decay over time. ```typescript TypeScript theme={null} const user = auth.user.name; await client.add({ content: "Hey, I'm doing great!", containerTag: user, entityContext: `User is ${user}, talking to assistant Kira` }); // -> supermemory learns "Dhravya is doing great" ``` ```python Python theme={null} user = auth.user.name client.add( content="Hey, I'm doing great!", container_tag=user, entity_context=f"User is {user}, talking to assistant Kira" ) # -> supermemory learns "Dhravya is doing great" ``` ```bash curl theme={null} curl -X POST "https://api.supermemory.ai/v3/documents" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "Hey, I'\''m doing great!", "containerTag": "dhravya", "entityContext": "User is dhravya, talking to assistant Kira" }' ``` #### Use containerTags, don't over-stuff a single one Use a containerTag wherever there's a hard permission boundary. * **Don't**: ingest everything into one container and filter through it with metadata. * **Do**: give each user their own container, and still filter by metadata inside it if needed. There's little correlation between the number of items in a container and its quality or latency. Supermemory is built for multi-tenant workloads and supports up to 1M documents and 10M memories per container. #### Use metadata filtering for detailed scoping inside containers You'll often want to ingest and search with filtering inside a single container. Say the engineering team ingests this: ```typescript TypeScript theme={null} await client.add({ content: "The team prefers TypeScript", metadata: { team: "Engineering" }, containerTag: "org-supermemory", filterByMetadata: { team: "Engineering" } }); ``` ```python Python theme={null} client.add( content="The team prefers TypeScript", metadata={"team": "Engineering"}, container_tag="org-supermemory", filter_by_metadata={"team": "Engineering"} ) ``` ```bash curl theme={null} curl -X POST "https://api.supermemory.ai/v3/documents" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "The team prefers TypeScript", "metadata": { "team": "Engineering" }, "containerTag": "org-supermemory", "filterByMetadata": { "team": "Engineering" } }' ``` > Tip: `filterByMetadata` 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, with the same `containerTag` but different `metadata`: ```json theme={null} { "content": "The team prefers Python", "metadata": { "team": "Research" }, "containerTag": "org-supermemory", "filterByMetadata": { "team": "Research" } } ``` This keeps research's and engineering's memories from mixing, even though they share a `containerTag`. When searching: ```typescript TypeScript theme={null} const results = await client.search({ q: "preferred language", containerTag: "org-supermemory", searchMode: "documents", filters: { AND: [{ key: "team", value: "research" }] } }); // -> "python" ``` ```python Python theme={null} results = client.search.documents( q="preferred language", container_tag="org-supermemory", filters={ "AND": [{"key": "team", "value": "research"}] } ) # -> "python" ``` ```bash curl theme={null} curl -X POST "https://api.supermemory.ai/v3/search" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "q": "preferred language", "containerTag": "org-supermemory", "filters": { "AND": [{ "key": "team", "value": "research" }] } }' ``` ## Thinking about harness Think about how to bring memory back into the harness itself. #### Embrace a little noise You might want to hyper-optimize everything that goes into the model's prompt, but counterintuitively, you sometimes want to embrace noise, since true personalization comes from distinctive information. Example: a user says "hi" and the LLM responds "Hey Dhravya! How's it going? How's the new office coming along?" instead of something generic. Supermemory is designed for this: it returns an average of 10 tokens per fact, so even 50 facts is just 500 tokens of context, cheap enough to stay generous. #### Tools, hooks, and making the choice Think about how supermemory fits into your harness. Example, a personal agent: * **Session start hook** → load profile * **On-message hook** → enrich the prompt with search * **On-stop hook** → save the conversation Play around with these options in our [playground](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/). # SuperRAG (Managed RAG as a service) Source: https://supermemory.ai/docs/concepts/super-rag Supermemory provides a managed RAG solution - extraction, indexing, storing, and retrieval. 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 theme={null} // Just upload — Supermemory handles the rest await client.documents.uploadFile({ 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 `client.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"`. It skips the memory pipeline entirely and only does the chunk → embed → index work needed to make the content searchable. ```typescript TypeScript theme={null} await client.add({ content: "...", // e.g. a long internal wiki page containerTag: "docs_kb", taskType: "superrag", }); ``` ```python Python theme={null} client.add( content="...", container_tag="docs_kb", task_type="superrag", ) ``` ```bash cURL theme={null} curl -X POST "https://api.supermemory.ai/v3/documents" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "...", "containerTag": "docs_kb", "taskType": "superrag" }' ``` | | `taskType: "memory"` (default) | `taskType: "superrag"` | | -------------------------------------------- | ------------------------------ | -------------------------------------------------------- | | Chunking, embedding, indexing | ✅ | ✅ — searchable immediately via `searchMode: "documents"` | | Fact extraction into memories | ✅ | ❌ skipped | | Profile (`static`/`dynamic`/buckets) updates | ✅ | ❌ skipped | | Graph linking (updates/extends/derives) | ✅ | ❌ skipped | | Price per ingested token | Full rate | **5x cheaper** | `taskType: "superrag"` is a **5x discount on ingested tokens** — `sm_superrag_text`/`sm_superrag_rich` are priced at 20% of `sm_tokens_text`/`sm_tokens_rich`. See [Billing → Memory vs SuperRAG tokens](/docs/overview/billing#memory-vs-superrag-tokens) for the exact rates. Content ingested as `superrag` is retrievable via document search (`searchMode: "documents"`), but it will **never** surface as a memory, contribute to a user's profile, or connect into the knowledge graph. Use it for reference material you want searchable, not for anything that should shape what Supermemory knows about a user — that still needs the default `taskType: "memory"`. When you're searching over a mix of both, `searchMode: "hybrid"` (below) is what pulls memory-path facts and superrag-path document chunks into one result set. More ingestion guidance: [Rules of supermemory → Ingest with SuperRag when you just need search](/docs/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 Code is chunked using [code-chunk](https://github.com/supermemoryai/code-chunk), our open-source library that understands AST (Abstract Syntax Tree) boundaries: * Functions and methods stay intact * Classes are chunked by method * Import statements grouped separately * Comments attached to their code blocks ```typescript theme={null} // A 500-line file becomes meaningful chunks: // - Imports + type definitions // - Each function as a separate chunk // - Class methods individually indexed ``` This means searching for "authentication middleware" finds the actual function, not a random slice of code. ### 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](/docs/concepts/content-types) for the full list of supported formats. *** ## Hybrid Memory + RAG Supermemory combines the best of both approaches in every search: * Finds similar document chunks * Great for knowledge retrieval * Stateless — same results for everyone * Extracts and tracks user facts * Understands temporal context * Personalizes results per user With `searchMode: "hybrid"` (the default), you get both: ```typescript theme={null} const results = await client.search({ q: "how do I deploy the app?", containerTag: "user_123", 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: ```typescript theme={null} const results = await client.search({ q: "complex technical question", rerank: true // +~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 theme={null} const results = await client.search({ q: "how to auth", rewriteQuery: true // Expands to "authentication login oauth jwt..." }); ``` **When to use:** Short queries, user-facing search, when recall matters. *** ## Why It's "Super" | Traditional RAG | SUPER RAG | | ------------------------ | -------------------------- | | Manual chunking config | Automatic per content type | | One-size-fits-all splits | AST-aware code chunking | | Just document retrieval | Hybrid memory + documents | | Static embeddings | Relationship-aware graph | | Generic search | Rerank + query rewriting | You focus on building your product. Supermemory handles the RAG complexity. *** ## Next Steps All supported formats and how they're processed The full processing pipeline When to use each approach Search parameters and optimization Exact meter rates for memory vs SuperRAG tokens `taskType` and other ingestion parameters # User Profiles Source: https://supermemory.ai/docs/concepts/user-profiles Automatically maintained context about your users 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 `containerTag` gets it's own profile. > Note: It's called "user" profile, but in reality it can be anything - an agent, organization, etc. No search needed — comprehensive user info always ready Profiles update as users interact with your system ## Why Profiles? Traditional memory systems rely entirely on search: | Problem | Search Only | With Profiles | | ----------------- | ------------------------- | ---------------- | | Context retrieval | 3-5 queries | 1 call | | Response time | 200-500ms | 50-100ms | | Basic user info | Requires specific queries | Always available | **Search is too narrow**: When you search for "project updates", you miss that the user prefers bullet points, works in PST, and uses specific terminology. **Profiles provide the foundation**: Instead of searching for basic context, profiles give your LLM a complete picture of who the user is. Search adds context to the prompt after a round trip; a profile rides along with every prompt for free 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 theme={null} // Weeks earlier, during onboarding await client.add({ content: "Call me Dhravya, not my full first name", containerTag: "user_123", }); // Later — an unrelated query const results = await client.search({ q: "help me plan a trip to Japan", containerTag: "user_123", }); // 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 client.profile({ containerTag: "user_123" }); console.log(profile.static); // ["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. Add your own in console settings at the organization level, or per space — space buckets are add-only, so a container tag always keeps every org-level bucket. ```typescript theme={null} const { profile } = await client.profile({ containerTag: "user_123", include: ["buckets"], buckets: ["preferences", "goals"], // optional — omit for all configured buckets }); console.log(profile.buckets.preferences); 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 [`filterPrompt`](/docs/concepts/customization), which controls what gets ingested at all — buckets only organize facts that already made it into the profile. Request bucketed profiles, create buckets at the org or space level, get AI-generated suggestions, and see validation limits. *** ## How It Works Profiles are built automatically through ingestion: 1. **Ingest content** — Users [add documents](/docs/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 You don't manually manage profiles — they build themselves as users interact. Start by [adding content](/docs/ingestion/add-memories) to see profiles in action. *** ## 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](/docs/concepts/filtering) as memory and document search. A profile is synthesized from the underlying memories in a container tag, so any `AND`/`OR` metadata filter you'd pass to `search` also narrows which memories are eligible to contribute to `static`, `dynamic`, and `buckets`. ```typescript theme={null} // Only build the profile from memories tagged as onboarding data const { profile } = await client.profile({ containerTag: "user_123", filters: { AND: [{ key: "source", value: "onboarding" }], }, }); ``` This is useful when a container tag 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 container: ```typescript theme={null} const { profile } = await client.profile({ containerTag: "org_customer_442", filters: { AND: [{ key: "channel", value: "support_ticket" }], }, include: ["static", "dynamic"], }); ``` Filters apply on top of the search query too — combine `q` and `filters` to scope both the profile synthesis and the accompanying search results in one call. See [Filtering Profiles](/docs/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 theme={null} const systemPrompt = `You are assisting ${userName}. Background: ${profile.static.join('\n')} Current focus: ${profile.dynamic.join('\n')} Adjust responses to their expertise and preferences.`; ``` ### Customer Support Profiles provide: product usage, previous issues, tech proficiency. * No more "let me look up your account" * Agents immediately understand context * AI support references past interactions naturally ### Educational Platforms Profiles provide: learning style, completed courses, strengths/weaknesses. ### Development Tools Profiles provide: preferred languages, coding style, current project context. *** ## Next Steps Fetch and use profiles via the API Create and configure topical buckets How the underlying knowledge graph works Automatic profile injection with AI SDK Build profiles by adding content # GitHub Connector Source: https://supermemory.ai/docs/connectors/github Connect GitHub repositories to sync documentation files into your Supermemory knowledge base Connect GitHub repositories to sync documentation files into your Supermemory knowledge base with OAuth authentication, webhook support, and automatic incremental syncing. The GitHub connector requires a **Scale Plan** or **Enterprise Plan**. ## Quick Setup ### 1. Create GitHub Connection ```typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY! }); const connection = await client.connections.create('github', { redirectUrl: 'https://yourapp.com/auth/github/callback', containerTag: 'user-123', documentLimit: 5000, metadata: { source: 'github', team: 'engineering' } }); // Redirect user to GitHub OAuth window.location.href = connection.authLink; console.log('Auth expires in:', connection.expiresIn); ``` ```python theme={null} from supermemory import Supermemory import os client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY")) connection = client.connections.create( 'github', redirect_url='https://yourapp.com/auth/github/callback', container_tag='user-123', document_limit=10000, metadata={ 'source': 'github', 'team': 'engineering' } ) # Redirect user to GitHub OAuth print(f'Redirect to: {connection.auth_link}') print(f'Expires in: {connection.expires_in}') ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/connections/github" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "redirectUrl": "https://yourapp.com/auth/github/callback", "containerTag": "user-123", "documentLimit": 5000, "metadata": { "source": "github", "team": "engineering" } }' ``` **OAuth Scopes:** The GitHub connector requires these scopes: * `repo` - Access to private and public repositories * `user:email` - Access to user's email address * `admin:repo_hook` - Manage webhooks for incremental sync ### 2. Handle OAuth Callback After the user grants permissions, GitHub redirects to your callback URL. The connection is automatically established, and the user can now select which repositories to sync. ### 3. List and Configure Repositories Unlike other connectors, GitHub requires repository selection before syncing begins. This gives your users control over which repositories to index. **Generic Endpoints:** GitHub uses the generic resource management endpoints (Get Resources and Configure Connection) that work for any provider supporting resource management. See [Managing Connection Resources](/docs/connectors/managing-resources) for detailed API documentation. ```typescript theme={null} // List available repositories for the user const repositories = await client.connections.github.listRepositories( connectionId, { page: 1, perPage: 100 } ); // Display repositories in your UI repositories.forEach(repo => { console.log(`${repo.full_name} - ${repo.description}`); console.log(`Private: ${repo.private}`); console.log(`Default branch: ${repo.default_branch}`); console.log(`Last updated: ${repo.updated_at}`); }); // After user selects repositories, configure them await client.connections.github.configure(connectionId, { repositories: [ { id: repo.id, name: repo.full_name, defaultBranch: repo.default_branch } ] }); console.log('Repository sync initiated'); ``` ```python theme={null} # List available repositories for the user repositories = client.connections.github.list_repositories( connection_id, page=1, per_page=100 ) # Display repositories in your UI for repo in repositories: print(f'{repo.full_name} - {repo.description}') print(f'Private: {repo.private}') print(f'Default branch: {repo.default_branch}') print(f'Last updated: {repo.updated_at}') # After user selects repositories, configure them client.connections.github.configure( connection_id, repositories=[ { 'id': repo.id, 'name': repo.full_name, 'defaultBranch': repo.default_branch } ] ) print('Repository sync initiated') ``` ```bash theme={null} # List available repositories curl -X GET "https://api.supermemory.ai/v3/connections/{connectionId}/resources?page=1&per_page=100" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # Configure selected repositories curl -X POST "https://api.supermemory.ai/v3/connections/{connectionId}/configure" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "resources": [ { "id": 123456789, "name": "your-org/documentation", "defaultBranch": "main" }, { "id": 987654321, "name": "your-org/api-docs", "defaultBranch": "main" } ] }' ``` **API-First Design:** Supermemory provides the API endpoints to list and configure repositories. As a Supermemory customer, you need to build the UI in your application where your end-users can: 1. View their available GitHub repositories 2. Select which repositories to sync 3. Confirm the selection This gives you complete control over the user experience and allows you to integrate repository selection seamlessly into your application's workflow. ## Supported Document Types The GitHub connector syncs documentation and text files with the following extensions: * **Markdown files**: `.md`, `.mdx`, `.markdown` * **Text files**: `.txt` * **reStructuredText**: `.rst` * **AsciiDoc**: `.adoc` * **Org-mode**: `.org` Files are indexed as `github_markdown` document type in Supermemory. Only text-based documentation files are synced. Binary files, images, and code files (`.js`, `.py`, `.go`, etc.) are excluded by default to focus on searchable documentation content. ## Incremental Sync with Webhooks The GitHub connector automatically sets up webhooks for real-time incremental syncing. When files are pushed or deleted in configured repositories, Supermemory is notified immediately. **Batch Processing:** Webhook events are processed in batches with a 10-minute delay to optimize performance and prevent excessive syncing during rapid commits. This means changes pushed to your repository will be reflected in Supermemory within approximately 10 minutes. ### How It Works 1. **Webhook Setup**: When you configure repositories, a webhook is automatically installed in each repository 2. **Push Events**: When commits are pushed to the default branch, changed documentation files are synced 3. **Delete Events**: When documentation files are deleted, they're removed from your Supermemory knowledge base 4. **Incremental Updates**: Only changed files are processed, keeping sync fast and efficient ### Webhook Security Webhooks are secured using HMAC-SHA256 signature validation with constant-time comparison. Supermemory automatically validates that webhook events come from GitHub before processing them. Each repository gets a unique webhook secret for maximum security. ```typescript theme={null} // Check webhook status const connection = await client.connections.get(connectionId); console.log('Webhooks configured:', connection.metadata.webhooks?.length); console.log('Last sync:', new Date(connection.metadata.lastSyncedAt)); console.log('Repositories:', connection.metadata.repositories); ``` ```python theme={null} # Check webhook status connection = client.connections.get(connection_id) print(f'Webhooks configured: {len(connection.metadata.get("webhooks", []))}') print(f'Last sync: {connection.metadata.get("lastSyncedAt")}') print(f'Repositories: {connection.metadata.get("repositories")}') ``` ```bash theme={null} # Get connection details including webhook status curl -X POST "https://api.supermemory.ai/v3/connections/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ## Connection Management ### List All Connections ```typescript theme={null} // List all GitHub connections for specific container tags const connections = await client.connections.list({ containerTags: ['user-123'], provider: 'github' }); connections.forEach(conn => { console.log(`Provider: ${conn.provider}`); console.log(`ID: ${conn.id}`); console.log(`Email: ${conn.email}`); console.log(`Created: ${conn.createdAt}`); console.log(`Document limit: ${conn.documentLimit}`); console.log(`Repositories: ${conn.metadata.repositories?.length || 0}`); console.log('---'); }); ``` ```python theme={null} # List all GitHub connections for specific container tags connections = client.connections.list( container_tags=['user-123'], provider='github' ) for conn in connections: print(f'Provider: {conn.provider}') print(f'ID: {conn.id}') print(f'Email: {conn.email}') print(f'Created: {conn.created_at}') print(f'Document limit: {conn.document_limit}') print(f'Repositories: {len(conn.metadata.get("repositories", []))}') print('---') ``` ```bash theme={null} # List all GitHub connections for specific container tags curl -X POST "https://api.supermemory.ai/v3/connections/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"], "provider": "github" }' ``` ### Update Repository Configuration You can update which repositories are synced at any time: ```typescript theme={null} // Add or remove repositories await client.connections.github.configure(connectionId, { repositories: [ { id: 123456789, name: 'your-org/documentation', defaultBranch: 'main' }, { id: 987654321, name: 'your-org/new-repo', defaultBranch: 'develop' // Can specify different branch } ] }); console.log('Repository configuration updated'); ``` ```python theme={null} # Add or remove repositories client.connections.github.configure( connection_id, repositories=[ { 'id': 123456789, 'name': 'your-org/documentation', 'defaultBranch': 'main' }, { 'id': 987654321, 'name': 'your-org/new-repo', 'defaultBranch': 'develop' # Can specify different branch } ] ) print('Repository configuration updated') ``` ```bash theme={null} # Update repository configuration curl -X POST "https://api.supermemory.ai/v3/connections/{connectionId}/configure" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "resources": [ { "id": 123456789, "name": "your-org/documentation", "defaultBranch": "main" }, { "id": 987654321, "name": "your-org/new-repo", "defaultBranch": "develop" } ] }' ``` When you update the repository configuration: * New repositories are added and synced immediately * Removed repositories have their webhooks deleted * Existing documents from removed repositories remain in Supermemory unless you delete them manually ### Delete Connection ```typescript theme={null} // Delete by connection ID const result = await client.connections.deleteByID(connectionId); // Or delete by provider (requires container tags) const result = await client.connections.deleteByProvider('github', { containerTags: ['user-123'] }); console.log('Deleted connection:', result.id); ``` ```python theme={null} # Delete by connection ID result = client.connections.delete_by_id(connection_id) # Or delete by provider (requires container tags) result = client.connections.delete_by_provider( provider='github', container_tags=['user-123'] ) print(f'Deleted connection: {result.id}') ``` ```bash theme={null} # Delete by connection ID curl -X DELETE "https://api.supermemory.ai/v3/connections/{connectionId}" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` Deleting a GitHub connection 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` as a query parameter to keep them) ### Manual Sync Trigger a manual synchronization for all configured repositories: ```typescript theme={null} // Trigger sync for GitHub connections await client.connections.import('github'); // Trigger sync for specific container tags await client.connections.import('github', { containerTags: ['user-123'] }); console.log('Manual sync initiated'); ``` ```python theme={null} # Trigger sync for GitHub connections client.connections.import_('github') # Trigger sync for specific container tags client.connections.import_( 'github', container_tags=['user-123'] ) print('Manual sync initiated') ``` ```bash theme={null} # Trigger sync for all GitHub connections curl -X POST "https://api.supermemory.ai/v3/connections/github/import" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # Trigger sync for specific container tags curl -X POST "https://api.supermemory.ai/v3/connections/github/import" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"] }' # Response: {"message": "Manual sync initiated", "provider": "github"} ``` ## Advanced Configuration ### Custom OAuth Application For white-label deployments or custom branding, configure your own GitHub OAuth app using the settings API: ```typescript theme={null} // Update organization settings with your GitHub OAuth app await client.settings.update({ githubCustomKeyEnabled: true, githubClientId: 'Iv1.1234567890abcdef', githubClientSecret: 'your-github-client-secret' }); // Get current settings const settings = await client.settings.get(); console.log('GitHub custom key enabled:', settings.githubCustomKeyEnabled); console.log('Client ID configured:', !!settings.githubClientId); ``` ```python theme={null} # Update organization settings with your GitHub OAuth app client.settings.update( github_custom_key_enabled=True, github_client_id='Iv1.1234567890abcdef', github_client_secret='your-github-client-secret' ) # Get current settings settings = client.settings.get() print(f'GitHub custom key enabled: {settings.github_custom_key_enabled}') print(f'Client ID configured: {bool(settings.github_client_id)}') ``` ```bash theme={null} # Update organization settings curl -X PATCH "https://api.supermemory.ai/v3/settings" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "githubCustomKeyEnabled": true, "githubClientId": "Iv1.1234567890abcdef", "githubClientSecret": "your-github-client-secret" }' # Get current settings curl -X GET "https://api.supermemory.ai/v3/settings" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` **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 5. Configure them in Supermemory using the settings API above After configuration, all new GitHub connections will use your custom OAuth app instead of Supermemory's default app. # Gmail Connector Source: https://supermemory.ai/docs/connectors/gmail Sync email threads from Gmail with real-time Pub/Sub webhooks and incremental sync Connect Gmail to automatically sync email threads into your supermemory knowledge base. Supports real-time updates via Google Cloud Pub/Sub webhooks and incremental synchronization. **Max Plan Required:** The Gmail connector is available on Max plan and above. ## Quick Setup ### 1. Create Gmail Connection ```typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY! }); const connection = await client.connections.create('gmail', { redirectUrl: 'https://yourapp.com/auth/gmail/callback', containerTag: 'user-123', documentLimit: 5000, metadata: { source: 'gmail', department: 'support' } }); // Redirect user to Google OAuth window.location.href = connection.authLink; console.log('Auth expires in:', connection.expiresIn); ``` ```python theme={null} from supermemory import Supermemory import os client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY")) connection = client.connections.create( 'gmail', redirect_url='https://yourapp.com/auth/gmail/callback', container_tag='user-123', document_limit=5000, metadata={ 'source': 'gmail', 'department': 'support' } ) # Redirect user to Google OAuth print(f'Redirect to: {connection.auth_link}') print(f'Expires in: {connection.expires_in}') ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/connections/gmail" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "redirectUrl": "https://yourapp.com/auth/gmail/callback", "containerTag": "user-123", "documentLimit": 5000, "metadata": { "source": "gmail", "department": "support" } }' ``` ### 2. Handle OAuth Callback After user grants permissions, Google redirects to your callback URL. The connection is automatically established and the initial sync begins. ### 3. Check Connection Status ```typescript theme={null} // Get connection details const connection = await client.connections.getByTags('gmail', { containerTags: ['user-123'] }); console.log('Connected email:', connection.email); console.log('Connection created:', connection.createdAt); // List synced email threads const documents = await client.documents.list({ containerTags: ['user-123'] }); console.log(`Synced ${documents.memories.length} email threads`); ``` ```python theme={null} # Get connection details connection = client.connections.get_by_tags( 'gmail', container_tags=['user-123'] ) print(f'Connected email: {connection.email}') print(f'Connection created: {connection.created_at}') # List synced email threads documents = client.documents.list( container_tags=['user-123'] ) print(f'Synced {len(documents.memories)} email threads') ``` ```bash theme={null} # Get connections by provider and tags curl -X POST "https://api.supermemory.ai/v3/connections/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"], "provider": "gmail" }' # List synced email threads curl -X POST "https://api.supermemory.ai/v3/documents/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"], "source": "gmail" }' ``` ## 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 email address | | `to` | Recipient email addresses | | `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 theme={null} const results = await client.search({ q: "project update", containerTag: 'user-123', searchMode: "documents", filters: JSON.stringify({ AND: [ { key: "type", value: "gmail_thread", negate: false }, { key: "from", value: "team@company.com", negate: false } ] }) }); ``` ## Connection Management ### List All Connections ```typescript theme={null} // List all connections for specific container tags const connections = await client.connections.list({ containerTags: ['user-123'] }); connections.forEach(conn => { console.log(`Provider: ${conn.provider}`); console.log(`ID: ${conn.id}`); console.log(`Email: ${conn.email}`); console.log(`Created: ${conn.createdAt}`); console.log(`Document limit: ${conn.documentLimit}`); console.log('---'); }); ``` ```python theme={null} # List all connections for specific container tags connections = client.connections.list( container_tags=['user-123'] ) for conn in connections: print(f'Provider: {conn.provider}') print(f'ID: {conn.id}') print(f'Email: {conn.email}') print(f'Created: {conn.created_at}') print(f'Document limit: {conn.document_limit}') print('---') ``` ```bash theme={null} # List all connections for specific container tags curl -X POST "https://api.supermemory.ai/v3/connections/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"] }' ``` ### Delete Connection ```typescript theme={null} // Delete by connection ID const result = await client.connections.deleteByID('connection_id_123'); console.log('Deleted connection:', result.id); // Delete by provider and container tags const providerResult = await client.connections.deleteByProvider('gmail', { containerTags: ['user-123'] }); console.log('Deleted Gmail connection:', providerResult.id); ``` ```python theme={null} # Delete by connection ID result = client.connections.delete_by_id('connection_id_123') print(f'Deleted connection: {result.id}') # Delete by provider and container tags provider_result = client.connections.delete_by_provider( 'gmail', container_tags=['user-123'] ) print(f'Deleted Gmail connection: {provider_result.id}') ``` ```bash theme={null} # Delete by connection ID curl -X DELETE "https://api.supermemory.ai/v3/connections/connection_id_123" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # Delete by provider and container tags curl -X DELETE "https://api.supermemory.ai/v3/connections/gmail" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"] }' ``` Deleting a connection will: * Stop all future syncs from Gmail * Remove the OAuth authorization * Keep existing synced documents in supermemory (they won't be deleted) ### Manual Sync Trigger a manual synchronization: ```typescript theme={null} // Trigger sync for Gmail connections await client.connections.import('gmail'); // Trigger sync for specific container tags await client.connections.import('gmail', { containerTags: ['user-123'] }); console.log('Manual sync initiated'); ``` ```python theme={null} # Trigger sync for Gmail connections client.connections.import_('gmail') # Trigger sync for specific container tags client.connections.import_( 'gmail', container_tags=['user-123'] ) print('Manual sync initiated') ``` ```bash theme={null} # Trigger sync for all Gmail connections curl -X POST "https://api.supermemory.ai/v3/connections/gmail/import" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # Trigger sync for specific container tags curl -X POST "https://api.supermemory.ai/v3/connections/gmail/import" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"] }' ``` ## 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. When a connection is created, supermemory registers a Gmail API "watch" subscription 2. Gmail sends notifications to a Google Cloud Pub/Sub topic when emails change 3. supermemory receives these notifications and fetches updated threads 4. Watch subscriptions expire after 7 days and are automatically renewed Real-time sync monitors the **INBOX** label. Emails in other labels are synced via scheduled/manual sync. ## 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 connection identification | **Read-only Access:** The Gmail connector only reads emails. It cannot send, delete, or modify any emails in the user's account. ## Limitations **Important Limitations:** * **Plan requirement**: Requires Scale Plan or Enterprise Plan * **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 connection (configurable via `documentLimit` parameter) * **Attachments**: Attachment metadata is stored, but attachment content is not downloaded * **Rate limits**: Gmail API rate limits may affect sync speed for accounts with many emails ## Troubleshooting ### OAuth Fails or Missing Refresh Token If OAuth fails or the connection stops syncing: 1. Delete the existing connection 2. Create a new connection 3. Ensure the user completes the full OAuth flow with consent ```typescript theme={null} // Re-create connection to get fresh tokens await client.connections.deleteByProvider('gmail', { containerTags: ['user-123'] }); const newConnection = await client.connections.create('gmail', { redirectUrl: 'https://yourapp.com/auth/gmail/callback', containerTag: 'user-123' }); // User must re-authenticate window.location.href = newConnection.authLink; ``` ### 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 connection was created recently (watch registration happens on creation) * Trigger a manual sync to verify the connection is working ### Permission Denied Errors If you see permission errors: * Ensure the user granted the required Gmail scopes during OAuth * Verify your organization has Scale Plan or Enterprise Plan access * Check if the user revoked app access in their Google Account settings # Google Drive Connector Source: https://supermemory.ai/docs/connectors/google-drive Connect Google Drive to sync documents into your Supermemory knowledge base Connect Google Drive to sync documents into your Supermemory knowledge base with OAuth authentication and custom app support. ## Sync scope **Default for new connections:** 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 `metadata.syncScope` to `"full"` when creating the connection so the entire Drive syncs without the picker. **Explicit scoped mode:** set `metadata.syncScope` to `"selected"` for the picker flow, or rely on the default for new connects. If you use scoped sync and the user has not finished the picker yet, **scheduled or manual import may skip that connection** until a selection is saved on the connection. ## Quick Setup ### 1. Create Google Drive Connection ```typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY! }); const connection = await client.connections.create('google-drive', { redirectUrl: 'https://yourapp.com/auth/google-drive/callback', containerTag: 'user-123', documentLimit: 3000, metadata: { source: 'google-drive', department: 'engineering', syncScope: 'selected' } }); // Redirect user to Google OAuth window.location.href = connection.authLink; console.log('Auth expires in:', connection.expiresIn); ``` ```python theme={null} from supermemory import Supermemory import os client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY")) connection = client.connections.create( 'google-drive', redirect_url='https://yourapp.com/auth/google-drive/callback', container_tag='user-123', document_limit=3000, metadata={ 'source': 'google-drive', 'department': 'engineering', 'syncScope': 'selected', } ) # Redirect user to Google OAuth print(f'Redirect to: {connection.auth_link}') print(f'Expires in: {connection.expires_in}') ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/connections/google-drive" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "redirectUrl": "https://yourapp.com/auth/google-drive/callback", "containerTag": "user-123", "documentLimit": 3000, "metadata": { "source": "google-drive", "department": "engineering", "syncScope": "selected" } }' ``` For **whole Drive** sync, include `"syncScope": "full"` in `metadata` on the same `POST /v3/connections/google-drive` request instead of `"selected"`. ### 2. Handle OAuth Callback After the user grants permissions, Google redirects through Supermemory to finish the connection. 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 imports run. With **`syncScope: "full"`**, Supermemory redirects to your `redirectUrl` (or returns connection details) **without** the picker. You can open the picker again later for an existing connection (Supermemory console, or `POST /v3/connections/{connectionId}/google-drive/hosted-picker` with an authenticated admin session). ### 3. Check Connection Status ```typescript theme={null} // Get connection details const connection = await client.connections.getByTags('google-drive', { containerTags: ['user-123'] }); ``` ```python theme={null} # Get connection details connection = client.connections.get_by_tags( 'google-drive', container_tags=['user-123'] ) # List synced documents documents = client.connections.list_documents( 'google-drive', container_tags=['user-123'] ) ``` ```bash theme={null} # Get connections by provider and tags curl -X POST "https://api.supermemory.ai/v3/connections/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"], "provider": "google-drive" }' # List synced documents curl -X POST "https://api.supermemory.ai/v3/documents/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"], "source": "google-drive" }' ``` ## 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 Drive documents are converted to markdown before ingestion. This conversion is lossy — some formatting may not be preserved. ## Connection Management ### List All Connections ```typescript theme={null} // List all connections for specific container tags const connections = await client.connections.list({ containerTags: ['user-123'] }); connections.forEach(conn => { console.log(`Provider: ${conn.provider}`); console.log(`ID: ${conn.id}`); console.log(`Email: ${conn.email}`); console.log(`Created: ${conn.createdAt}`); console.log(`Document limit: ${conn.documentLimit}`); console.log('---'); }); ``` ```python theme={null} # List all connections for specific container tags connections = client.connections.list( container_tags=['user-123'] ) for conn in connections: print(f'Provider: {conn.provider}') print(f'ID: {conn.id}') print(f'Email: {conn.email}') print(f'Created: {conn.created_at}') print(f'Document limit: {conn.document_limit}') print('---') ``` ```bash theme={null} # List all connections for specific container tags curl -X POST "https://api.supermemory.ai/v3/connections/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"] }' # Response example: # [ # { # "id": "conn_gd123", # "provider": "google-drive", # "email": "user@example.com", # "createdAt": "2024-01-15T10:30:00.000Z", # "documentLimit": 3000 # } # ] ``` ### Delete Connection ```typescript theme={null} // Delete by connection ID const result = await client.connections.deleteByID('connection_id_123'); console.log('Deleted connection:', result.id); // Delete by provider and container tags const providerResult = await client.connections.deleteByProvider('google-drive', { containerTags: ['user-123'] }); console.log('Deleted provider connection:', providerResult.id); ``` ```python theme={null} # Delete by connection ID result = client.connections.delete_by_id('connection_id_123') print(f'Deleted connection: {result.id}') # Delete by provider and container tags provider_result = client.connections.delete_by_provider( 'google-drive', container_tags=['user-123'] ) print(f'Deleted provider connection: {provider_result.id}') ``` ```bash theme={null} # Delete by connection ID curl -X DELETE "https://api.supermemory.ai/v3/connections/connection_id_123" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # Response: {"id": "connection_id_123", "provider": "google-drive"} # Delete by provider and container tags curl -X DELETE "https://api.supermemory.ai/v3/connections/google-drive" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"] }' # Response: {"id": "conn_gd123", "provider": "google-drive"} ``` Deleting a connection will: * Stop all future syncs from Google Drive * Remove the OAuth authorization * Keep existing synced documents in Supermemory (they won't be deleted) ### Manual Sync Trigger a manual synchronization: ```typescript theme={null} // Trigger sync for Google Drive connections await client.connections.import('google-drive'); // Trigger sync for specific container tags await client.connections.import('google-drive', { containerTags: ['user-123'] }); console.log('Manual sync initiated'); ``` ```python theme={null} # Trigger sync for Google Drive connections client.connections.import_('google-drive') # Trigger sync for specific container tags client.connections.import_( 'google-drive', container_tags=['user-123'] ) print('Manual sync initiated') ``` ```bash theme={null} # Trigger sync for all Google Drive connections curl -X POST "https://api.supermemory.ai/v3/connections/google-drive/import" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # Trigger sync for specific container tags curl -X POST "https://api.supermemory.ai/v3/connections/google-drive/import" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"] }' # Response: {"message": "Manual sync initiated", "provider": "google-drive"} ``` ## Advanced Configuration ### Custom OAuth Application Configure your own Google OAuth app using the settings API: ```typescript theme={null} // Update organization settings with your Google OAuth app await client.settings.update({ googleDriveCustomKeyEnabled: true, googleDriveClientId: 'your-google-client-id.googleusercontent.com', googleDriveClientSecret: 'your-google-client-secret' }); // Get current settings const settings = await client.settings.get(); console.log('Google Drive custom key enabled:', settings.googleDriveCustomKeyEnabled); console.log('Client ID configured:', !!settings.googleDriveClientId); ``` ```python theme={null} # Update organization settings with your Google OAuth app client.settings.update( google_drive_custom_key_enabled=True, google_drive_client_id='your-google-client-id.googleusercontent.com', google_drive_client_secret='your-google-client-secret' ) # Get current settings settings = client.settings.get() print(f'Google Drive custom key enabled: {settings.google_drive_custom_key_enabled}') print(f'Client ID configured: {bool(settings.google_drive_client_id)}') ``` ```bash theme={null} # Update organization settings curl -X PATCH "https://api.supermemory.ai/v3/settings" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "googleDriveCustomKeyEnabled": true, "googleDriveClientId": "your-google-client-id.googleusercontent.com", "googleDriveClientSecret": "your-google-client-secret" }' # Get current settings curl -X GET "https://api.supermemory.ai/v3/settings" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` ### Document Filtering Configure filtering using the settings API: ```typescript theme={null} await client.settings.update({ shouldLLMFilter: true, filterPrompt: "Only sync important business documents", includeItems: { // Your include patterns }, excludeItems: { // Your exclude patterns } }); ``` ```python theme={null} client.settings.update( should_llm_filter=True, filter_prompt="Only sync important business documents", include_items={ # Your include patterns }, exclude_items={ # Your exclude patterns } ) ``` ```bash theme={null} # Configure document filtering curl -X PATCH "https://api.supermemory.ai/v3/settings" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "shouldLLMFilter": true, "filterPrompt": "Only sync important business documents", "includeItems": { "patterns": ["*.pdf", "*.docx"], "folders": ["Important Documents", "Projects"] }, "excludeItems": { "patterns": ["*.tmp", "*.backup"], "folders": ["Archive", "Trash"] } }' # Response: { # "shouldLLMFilter": true, # "filterPrompt": "Only sync important business documents", # "includeItems": {...}, # "excludeItems": {...} # } ``` **Important Notes:** * OAuth tokens may expire - check `expiresAt` field * Document processing happens asynchronously * Use container tags consistently for filtering * Monitor document status for failed syncs # Granola Connector Source: https://supermemory.ai/docs/connectors/granola Sync AI meeting notes and transcripts from Granola into your Supermemory knowledge base Connect Granola to sync AI meeting notes and transcripts into your Supermemory knowledge base. The connector uses a Granola API key, so there is no OAuth redirect flow. The Granola connector requires a **Pro Plan** or higher in Supermemory and a Granola plan that can create API keys. In Granola, create one from **Settings > Connectors > API keys**. ## Quick Setup ### From the Console 1. Open the [Supermemory Console](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 container tag. 6. Click **Connect**. The console creates the connection and starts the initial sync automatically. The console limits connector setup to 500 documents. Use the API setup below for higher `documentLimit` values, up to 10,000. ### With the API ```typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY! }); const connection = await client.connections.create('granola', { metadata: { apiKey: process.env.GRANOLA_API_KEY! }, containerTag: 'org-123', documentLimit: 1000 }); console.log('Granola connection:', connection.id); ``` ```python theme={null} from supermemory import Supermemory import os client = Supermemory(api_key=os.environ["SUPERMEMORY_API_KEY"]) connection = client.connections.create( 'granola', metadata={ 'apiKey': os.environ["GRANOLA_API_KEY"] }, container_tag='org-123', document_limit=1000 ) print(f'Granola connection: {connection.id}') ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/connections/granola" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "apiKey": "'"$GRANOLA_API_KEY"'" }, "containerTag": "org-123", "documentLimit": 1000 }' ``` Supermemory validates the Granola API key before creating the connection. The initial sync starts automatically after the connection is created. ## Configuration Options For Granola, provider-specific fields are passed inside the top-level `metadata` object. General connection options stay top-level. | Parameter | Location | Required | Description | | --------------- | ----------------- | -------- | --------------------------------------------------------------------------- | | `apiKey` | `metadata.apiKey` | Yes | Granola API key from **Settings > Connectors > API keys** | | `containerTag` | top-level | No | Tag for organizing imported notes by user, organization, project, or tenant | | `documentLimit` | top-level | No | Maximum notes to sync per connection (default: 10,000) | In the Python SDK, use `container_tags` and `document_limit` for top-level options, but keep the Granola metadata key in camelCase: `apiKey`. ## 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 theme={null} const results = await client.search({ q: "customer onboarding discussion", containerTag: 'org-123', searchMode: "documents", filters: JSON.stringify({ AND: [ { key: "type", value: "granola", negate: false }, { key: "attendees", value: "alex@company.com", negate: false } ] }) }); ``` ## Connection Management ### Delete Connection ```typescript theme={null} await client.connections.deleteByID('conn_granola_abc123'); ``` ```python theme={null} client.connections.delete_by_id('conn_granola_abc123') ``` ```bash theme={null} curl -X DELETE "https://api.supermemory.ai/v3/connections/conn_granola_abc123" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` By default, deleting a connection removes all synced documents from Supermemory. To keep documents, pass `deleteDocuments=false` as a query parameter: `DELETE /v3/connections/:id?deleteDocuments=false` ### Manual Sync ```typescript theme={null} await client.connections.import('granola', { containerTags: ['org-123'] }); ``` ```python theme={null} client.connections.import_( 'granola', container_tags=['org-123'] ) ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/connections/granola/import" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["org-123"]}' ``` ## 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 connection creation + manual triggers | | **Document limit** | 10,000 notes per connection (default) | ## Troubleshooting | Error | Solution | | ----------------------------- | -------------------------------------------------------------------------------------------------------------- | | `Granola API key is required` | Include a non-empty `metadata.apiKey` value when creating the connection | | `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 | # Managing Connection Resources Source: https://supermemory.ai/docs/connectors/managing-resources Get and configure resources for connections that support resource management **Currently Available for GitHub:** Resource management endpoints are currently only available for the GitHub connector. These endpoints allow you to select which repositories to sync before syncing begins. Some connectors require you to select which resources (e.g., repositories) to sync before syncing begins. Use these generic endpoints to list and configure resources for connections that support resource management. ## Get Resources `GET /v3/connections/:connectionId/resources` Get available resources (e.g., repositories, folders) for a connection using stored credentials. ```typescript Typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env['SUPERMEMORY_API_KEY'], }); // Get resources with pagination const response = await fetch( `https://api.supermemory.ai/v3/connections/${connectionId}/resources?page=1&per_page=30`, { headers: { 'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`, }, } ); const data = await response.json(); console.log('Resources:', data.resources); console.log('Total count:', data.total_count); ``` ```python Python theme={null} import requests url = f"https://api.supermemory.ai/v3/connections/{connection_id}/resources" params = { "page": 1, "per_page": 30 } headers = { "Authorization": f"Bearer {api_key}", } response = requests.get(url, params=params, headers=headers) data = response.json() print(f"Resources: {data['resources']}") print(f"Total count: {data.get('total_count')}") ``` ```bash cURL theme={null} curl -X GET \ "https://api.supermemory.ai/v3/connections/{connectionId}/resources?page=1&per_page=30" \ -H "Authorization: Bearer " ``` ### Query Parameters * `page`: Optional. Page number for pagination. Default: `1` * `per_page`: Optional. Number of resources per page. Default: `30` ### Response ```json theme={null} { "resources": [ { "id": 123456789, "name": "your-org/documentation", "full_name": "your-org/documentation", "description": "Documentation repository", "private": false, "default_branch": "main", "updated_at": "2024-01-15T10:00:00Z" } ], "total_count": 45 } ``` ### Error Responses * `400`: Connection missing refresh token * `401`: Unauthorized * `404`: Connection not found * `501`: Provider does not support resource fetching **Provider Support:** Not all providers support resource fetching. This endpoint is only available for providers that implement the `fetchResources()` method (e.g., GitHub). For providers that don't support this, you'll receive a `501 Not Implemented` response. ## Configure Connection `POST /v3/connections/:connectionId/configure` Configure selected resources (e.g., repositories) for a connection and set up webhooks/subscriptions. ```typescript Typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env['SUPERMEMORY_API_KEY'], }); // Configure connection const response = await fetch( `https://api.supermemory.ai/v3/connections/${connectionId}/configure`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ resources: [ { id: 123456789, name: 'your-org/documentation', defaultBranch: 'main', }, { id: 987654321, name: 'your-org/api-docs', defaultBranch: 'main', }, ], }), } ); const data = await response.json(); console.log('Success:', data.success); console.log('Message:', data.message); console.log('Webhooks registered:', data.webhooksRegistered); ``` ```python Python theme={null} import requests url = f"https://api.supermemory.ai/v3/connections/{connection_id}/configure" headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", } payload = { "resources": [ { "id": 123456789, "name": "your-org/documentation", "defaultBranch": "main", }, { "id": 987654321, "name": "your-org/api-docs", "defaultBranch": "main", }, ] } response = requests.post(url, json=payload, headers=headers) data = response.json() print(f"Success: {data['success']}") print(f"Message: {data['message']}") print(f"Webhooks registered: {data.get('webhooksRegistered')}") ``` ```bash cURL theme={null} curl -X POST \ "https://api.supermemory.ai/v3/connections/{connectionId}/configure" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "resources": [ { "id": 123456789, "name": "your-org/documentation", "defaultBranch": "main" }, { "id": 987654321, "name": "your-org/api-docs", "defaultBranch": "main" } ] }' ``` ### Request Body ```json theme={null} { "resources": [ { "id": 123456789, "name": "your-org/documentation", "defaultBranch": "main" } ] } ``` The structure of each resource object depends on the provider. For GitHub, resources include: * `id`: Repository ID (number) * `name`: Repository full name (string) * `defaultBranch`: Default branch name (string) ### Response ```json theme={null} { "success": true, "message": "Resources configured successfully", "webhooksRegistered": 2 } ``` ### Error Responses * `400`: Connection missing refresh token * `401`: Unauthorized * `404`: Connection not found * `501`: Provider does not support resource configuration **Automatic Sync:** After successfully configuring resources, an initial sync is automatically triggered for the connection. You don't need to manually trigger a sync after configuration. **Provider Support:** Not all providers support resource configuration. This endpoint is only available for providers that implement the `configureConnection()` method (e.g., GitHub). For providers that don't support this, you'll receive a `501 Not Implemented` response. ## Example: GitHub Repository Selection Here's a complete example for GitHub: ```typescript Typescript theme={null} // 1. Create connection (see creating-connection.mdx) const connection = await client.connections.create('github', { redirectUrl: 'https://yourapp.com/callback', }); // 2. After OAuth callback, fetch available repositories const resourcesResponse = await fetch( `https://api.supermemory.ai/v3/connections/${connection.id}/resources?page=1&per_page=100`, { headers: { 'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`, }, } ); const { resources } = await resourcesResponse.json(); // 3. Display repositories to user and let them select // (Build your UI here) // 4. Configure selected repositories const configureResponse = await fetch( `https://api.supermemory.ai/v3/connections/${connection.id}/configure`, { method: 'POST', headers: { 'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ resources: selectedRepositories, // User's selection }), } ); const result = await configureResponse.json(); console.log('Sync initiated:', result.success); ``` ```python Python theme={null} # 1. Create connection (see creating-connection.mdx) connection = client.connections.create( 'github', redirect_url='https://yourapp.com/callback' ) # 2. After OAuth callback, fetch available repositories resources_response = requests.get( f"https://api.supermemory.ai/v3/connections/{connection.id}/resources", params={"page": 1, "per_page": 100}, headers={"Authorization": f"Bearer {api_key}"} ) resources = resources_response.json()["resources"] # 3. Display repositories to user and let them select # (Build your UI here) # 4. Configure selected repositories configure_response = requests.post( f"https://api.supermemory.ai/v3/connections/{connection.id}/configure", json={"resources": selected_repositories}, # User's selection headers={"Authorization": f"Bearer {api_key}"} ) result = configure_response.json() print(f"Sync initiated: {result['success']}") ``` ```bash cURL theme={null} # 1. Create connection (see creating-connection.mdx) # ... (OAuth flow) ... # 2. Fetch available repositories curl -X GET \ "https://api.supermemory.ai/v3/connections/{connectionId}/resources?page=1&per_page=100" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # 3. Configure selected repositories curl -X POST \ "https://api.supermemory.ai/v3/connections/{connectionId}/configure" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "resources": [ { "id": 123456789, "name": "your-org/documentation", "defaultBranch": "main" } ] }' ``` # Notion Connector Source: https://supermemory.ai/docs/connectors/notion Sync Notion pages, databases, and blocks with real-time webhooks and workspace integration 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 Connection ```typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY! }); const connection = await client.connections.create('notion', { redirectUrl: 'https://yourapp.com/auth/notion/callback', containerTag: 'user-123', documentLimit: 2000, metadata: { source: 'notion', workspaceType: 'team', department: 'product' } }); // Redirect user to Notion OAuth window.location.href = connection.authLink; ``` ```python theme={null} from supermemory import Supermemory import os client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY")) connection = client.connections.create( 'notion', redirect_url='https://yourapp.com/auth/notion/callback', container_tag='user-123', document_limit=2000, metadata={ 'source': 'notion', 'workspaceType': 'team', 'department': 'product' } ) # Redirect user to Notion OAuth print(f'Redirect to: {connection.auth_link}') ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/connections/notion" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "redirectUrl": "https://yourapp.com/auth/notion/callback", "containerTag": "user-123", "documentLimit": 2000, "metadata": { "source": "notion", "workspaceType": "team", "department": "product" } }' ``` ### 2. Handle OAuth Flow After user grants workspace access, Notion redirects to your callback URL. The connection is automatically established. ### 3. Monitor Sync Progress ```typescript theme={null} // Check connection details const connection = await client.connections.getByTags('notion', { containerTags: ['user-123'] }); console.log('Connected workspace:', connection.email); console.log('Connection created:', connection.createdAt); // List synced pages and databases const documents = await client.connections.listDocuments('notion', { containerTags: ['user-123'] }); ``` ```python theme={null} # Check connection details connection = client.connections.get_by_tags( 'notion', container_tags=['user-123'] ) print(f'Connected workspace: {connection.email}') print(f'Connection created: {connection.created_at}') # List synced pages and databases documents = client.connections.list_documents( 'notion', container_tags=['user-123'] ) ``` ```bash theme={null} # Get connection details by provider and tags curl -X POST "https://api.supermemory.ai/v3/connections/notion/connection" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"]}' # Response includes connection details: # { # "id": "conn_abc123", # "provider": "notion", # "email": "workspace@example.com", # "createdAt": "2024-01-15T10:00:00Z", # "documentLimit": 2000, # "metadata": {...} # } # List synced documents curl -X POST "https://api.supermemory.ai/v3/connections/notion/documents" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"]}' # Response: Array of document objects with sync status # [ # {"title": "Product Roadmap", "type": "notion_database", "status": "done"}, # {"title": "Meeting Notes", "type": "notion_page", "status": "done"} # ] ``` ## Document limit Each connection has a **`documentLimit`** (optional when creating the connection; 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. ## 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 | `![alt text](url)` | | | | | | | | **Embed** | Link with context | `[Embedded Content](url)` | | | | | | | ## Delete Connection Remove a Notion connection when no longer needed: ```typescript theme={null} // Delete by connection ID const result = await client.connections.delete('connection_id_123'); console.log('Deleted connection:', result.id); // Delete by provider and container tags const providerResult = await client.connections.deleteByProvider('notion', { containerTags: ['user-123'] }); console.log('Deleted Notion connection for user'); ``` ```python theme={null} # Delete by connection ID result = client.connections.delete('connection_id_123') print(f'Deleted connection: {result.id}') # Delete by provider and container tags provider_result = client.connections.delete_by_provider( 'notion', container_tags=['user-123'] ) print('Deleted Notion connection for user') ``` ```bash theme={null} # Delete by connection ID curl -X DELETE "https://api.supermemory.ai/v3/connections/connection_id_123" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # Delete by provider and container tags curl -X DELETE "https://api.supermemory.ai/v3/connections/notion" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"]}' ``` Deleting a connection will: * Stop all future syncs from Notion * Remove the OAuth authorization * Keep existing synced documents in Supermemory (they won't be deleted) ## Advanced Configuration ### Custom Notion Integration For production deployments, create your own Notion integration: ```typescript theme={null} // First, update organization settings with your Notion app credentials await client.settings.update({ notionCustomKeyEnabled: true, notionClientId: 'your-notion-client-id', notionClientSecret: 'your-notion-client-secret' }); // Then create connections using your custom integration const connection = await client.connections.create('notion', { redirectUrl: 'https://yourapp.com/callback', containerTag: 'user-789', metadata: { customIntegration: true } }); ``` ```python theme={null} # First, update organization settings with your Notion app credentials client.settings.update( notion_custom_key_enabled=True, notion_client_id='your-notion-client-id', notion_client_secret='your-notion-client-secret' ) # Then create connections using your custom integration connection = client.connections.create( 'notion', redirect_url='https://yourapp.com/callback', container_tag='user-789', metadata={'customIntegration': True} ) ``` ```bash theme={null} # Update organization settings curl -X PATCH "https://api.supermemory.ai/v3/settings" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "notionCustomKeyEnabled": true, "notionClientId": "your-notion-client-id", "notionClientSecret": "your-notion-client-secret" }' ``` ### Content Filtering Control which Notion content gets synced: ```typescript theme={null} // Configure intelligent filtering for Notion content await client.settings.update({ shouldLLMFilter: true, includeItems: { pageTypes: ['page', 'database'], titlePatterns: ['*Spec*', '*Documentation*', '*Meeting Notes*'], databases: ['Project Tracker', 'Knowledge Base', 'Team Wiki'] }, excludeItems: { titlePatterns: ['*Draft*', '*Personal*', '*Archive*'], databases: ['Personal Tasks', 'Scratchpad'] }, filterPrompt: "Sync professional documentation, project specs, meeting notes, and team knowledge. Skip personal notes, drafts, and archived content." }); ``` ```python theme={null} # Configure intelligent filtering for Notion content client.settings.update( should_llm_filter=True, include_items={ 'pageTypes': ['page', 'database'], 'titlePatterns': ['*Spec*', '*Documentation*', '*Meeting Notes*'], 'databases': ['Project Tracker', 'Knowledge Base', 'Team Wiki'] }, exclude_items={ 'titlePatterns': ['*Draft*', '*Personal*', '*Archive*'], 'databases': ['Personal Tasks', 'Scratchpad'] }, filter_prompt="Sync professional documentation, project specs, meeting notes, and team knowledge. Skip personal notes, drafts, and archived content." ) ``` ```bash theme={null} # Configure intelligent filtering for Notion content curl -X PATCH "https://api.supermemory.ai/v3/settings" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "shouldLLMFilter": true, "includeItems": { "pageTypes": ["page", "database"], "titlePatterns": ["*Spec*", "*Documentation*", "*Meeting Notes*"], "databases": ["Project Tracker", "Knowledge Base", "Team Wiki"] }, "excludeItems": { "titlePatterns": ["*Draft*", "*Personal*", "*Archive*"], "databases": ["Personal Tasks", "Scratchpad"] }, "filterPrompt": "Sync professional documentation, project specs, meeting notes, and team knowledge. Skip personal notes, drafts, and archived content." }' # Response: # { # "success": true, # "message": "Settings updated successfully" # } ``` ## 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: ```typescript theme={null} // Example: Project database with properties const documents = await client.connections.listDocuments('notion', { containerTags: ['user-123'] }); // Find database entries const projectEntries = documents.filter(doc => doc.metadata?.database === 'Projects' ); // Database properties become searchable metadata const projectWithStatus = await client.search({ q: "machine learning project", containerTag: 'user-123', searchMode: "documents", filters: JSON.stringify({ AND: [ { key: "status", value: "In Progress", negate: false }, { key: "priority", value: "High", negate: false } ] }) }); ``` ### Optimization Strategies 1. **Set `documentLimit` high enough** for your workspace size (see [Document limit](#document-limit)) 2. **Use targeted container tags** for efficient organization 3. **Monitor database sync performance** for large datasets 4. **Implement content filtering** to sync only relevant pages 5. **Handle webhook delays** gracefully in your application **Notion-Specific Benefits:** * Real-time sync via webhooks for instant updates * Rich formatting and block structure preserved * Database properties become searchable metadata * Hierarchical page structure maintained * Collaborative workspace support **Important Limitations:** * Complex block formatting may be simplified in markdown conversion * Large databases can take significant time to sync initially * Workspace permissions affect which content is accessible * Notion API rate limits may affect sync speed for large workspaces * Embedded files and images are referenced, not stored directly # OneDrive Connector Source: https://supermemory.ai/docs/connectors/onedrive Sync Microsoft Office documents from OneDrive with scheduled synchronization and business account support 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 Connection ```typescript Typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY! }); const connection = await client.connections.create('onedrive', { redirectUrl: 'https://yourapp.com/auth/onedrive/callback', containerTag: 'user-123', documentLimit: 1500, metadata: { source: 'onedrive', accountType: 'business', department: 'marketing' } }); // Redirect user to Microsoft OAuth window.location.href = connection.authLink; // Output: Redirects to OAuth provider // Output: Redirects to https://login.microsoftonline.com/oauth2/authorize?... ``` ```python Python theme={null} from supermemory import Supermemory import os client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY")) connection = client.connections.create( 'onedrive', redirect_url='https://yourapp.com/auth/onedrive/callback', container_tag='user-123', document_limit=1500, metadata={ 'source': 'onedrive', 'accountType': 'business', 'department': 'marketing' } ) # Redirect user to Microsoft OAuth print(f'Redirect to: {connection.auth_link}') # Output: Redirect to: https://oauth.provider.com/... # Output: Redirect to: https://login.microsoftonline.com/oauth2/authorize?... ``` ```bash cURL theme={null} curl -X POST "https://api.supermemory.ai/v3/connections/onedrive" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "redirectUrl": "https://yourapp.com/auth/onedrive/callback", "containerTag": "user-123", "documentLimit": 1500, "metadata": { "source": "onedrive", "accountType": "business", "department": "marketing" } }' # Response: { # "authLink": "https://login.microsoftonline.com/oauth2/authorize?...", # "expiresIn": "1 hour", # "id": "conn_od123" # } ``` ### 2. Handle Microsoft OAuth After user grants permissions, Microsoft redirects to your callback URL. The connection is automatically established and initial sync begins. ### 3. Monitor Sync Status ```typescript Typescript theme={null} // Check connection details const connection = await client.connections.getByTags('onedrive', { containerTags: ['user-123'] }); // List synced Office documents const documents = await client.connections.listDocuments('onedrive', { containerTags: ['user-123'] }); ``` ```python Python theme={null} # Check connection details connection = client.connections.get_by_tags( 'onedrive', container_tags=['user-123'] ) # List synced Office documents documents = client.connections.list_documents( 'onedrive', container_tags=['user-123'] ) ``` ```bash cURL theme={null} # Get connections by provider and tags curl -X POST "https://api.supermemory.ai/v3/connections/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"], "provider": "onedrive" }' # List synced Office documents curl -X POST "https://api.supermemory.ai/v3/documents/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user-123"], "source": "onedrive" }' ``` ## 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 ```typescript Typescript theme={null} // Trigger immediate sync for all OneDrive connections await client.connections.import('onedrive'); // Trigger sync for specific user await client.connections.import('onedrive', { containerTags: ['user-123'] }); console.log('Manual sync initiated - documents will update within 5-10 minutes'); ``` ```python Python theme={null} # Trigger immediate sync for all OneDrive connections client.connections.import_('onedrive') # Trigger sync for specific user client.connections.import_( 'onedrive', container_tags=['user-123'] ) print('Manual sync initiated - documents will update within 5-10 minutes') ``` ```bash cURL theme={null} # Trigger manual sync curl -X POST "https://api.supermemory.ai/v3/connections/onedrive/import" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"]}' ``` ## Delete Connection Remove a OneDrive connection when no longer needed: ```typescript Typescript theme={null} // Delete by connection ID const result = await client.connections.delete('connection_id_123'); console.log('Deleted connection:', result.id); // Delete by provider and container tags const providerResult = await client.connections.deleteByProvider('onedrive', { containerTags: ['user-123'] }); console.log('Deleted OneDrive connection for user'); ``` ```python Python theme={null} # Delete by connection ID result = client.connections.delete('connection_id_123') print(f'Deleted connection: {result.id}') # Delete by provider and container tags provider_result = client.connections.delete_by_provider( 'onedrive', container_tags=['user-123'] ) print('Deleted OneDrive connection for user') ``` ```bash cURL theme={null} # Delete by connection ID curl -X DELETE "https://api.supermemory.ai/v3/connections/connection_id_123" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # Delete by provider and container tags curl -X DELETE "https://api.supermemory.ai/v3/connections/onedrive" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"]}' ``` Deleting a connection will: * Stop all future syncs from OneDrive * Remove the OAuth authorization * Keep existing synced documents in Supermemory (they won't be deleted) ## Advanced Configuration ### Custom Microsoft App For production deployments, configure your own Microsoft application: ```typescript Typescript theme={null} // First, update organization settings with your Microsoft app credentials await client.settings.update({ onedriveCustomKeyEnabled: true, onedriveClientId: 'your-microsoft-app-id', onedriveClientSecret: 'your-microsoft-app-secret' }); // Then create connections using your custom app const connection = await client.connections.create('onedrive', { redirectUrl: 'https://yourapp.com/callback', containerTag: 'user-789', metadata: { customApp: true } }); ``` ```python Python theme={null} # First, update organization settings with your Microsoft app credentials client.settings.update( onedrive_custom_key_enabled=True, onedrive_client_id='your-microsoft-app-id', onedrive_client_secret='your-microsoft-app-secret' ) # Then create connections using your custom app connection = client.connections.create( 'onedrive', redirect_url='https://yourapp.com/callback', container_tag='user-789', metadata={'customApp': True} ) ``` ```bash cURL theme={null} # Update organization settings curl -X PATCH "https://api.supermemory.ai/v3/settings" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "onedriveCustomKeyEnabled": true, "onedriveClientId": "your-microsoft-app-id", "onedriveClientSecret": "your-microsoft-app-secret" }' ``` ### Document Filtering Control which OneDrive documents get synced: ```typescript Typescript theme={null} // Configure filtering for Office documents await client.settings.update({ shouldLLMFilter: true, includeItems: { fileTypes: ['docx', 'xlsx', 'pptx'], folderNames: ['Projects', 'Documentation', 'Reports'], titlePatterns: ['*Proposal*', '*Specification*', '*Analysis*'] }, excludeItems: { folderNames: ['Archive', 'Templates', 'Personal'], titlePatterns: ['*Draft*', '*Old*', '*Backup*', '*~$*'] }, filterPrompt: "Sync professional business documents, project files, reports, and presentations. Skip personal files, drafts, temporary files, and archived content." }); ``` ```python Python theme={null} # Configure filtering for Office documents client.settings.update( should_llm_filter=True, include_items={ 'fileTypes': ['docx', 'xlsx', 'pptx'], 'folderNames': ['Projects', 'Documentation', 'Reports'], 'titlePatterns': ['*Proposal*', '*Specification*', '*Analysis*'] }, exclude_items={ 'folderNames': ['Archive', 'Templates', 'Personal'], 'titlePatterns': ['*Draft*', '*Old*', '*Backup*', '*~$*'] }, filter_prompt="Sync professional business documents, project files, reports, and presentations. Skip personal files, drafts, temporary files, and archived content." ) ``` ```bash cURL theme={null} # Configure filtering for Office documents curl -X PATCH "https://api.supermemory.ai/v3/settings" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "shouldLLMFilter": true, "includeItems": { "fileTypes": ["docx", "xlsx", "pptx"], "folderNames": ["Projects", "Documentation", "Reports"], "titlePatterns": ["*Proposal*", "*Specification*", "*Analysis*"] }, "excludeItems": { "folderNames": ["Archive", "Templates", "Personal"], "titlePatterns": ["*Draft*", "*Old*", "*Backup*", "*~$*"] }, "filterPrompt": "Sync professional business documents, project files, reports, and presentations. Skip personal files, drafts, temporary files, and archived content." }' # Response: { # "shouldLLMFilter": true, # "includeItems": {...}, # "excludeItems": {...}, # "filterPrompt": "..." # } ``` ### Optimization Tips 1. **Set realistic document limits** based on storage and usage 2. **Use targeted filtering** to sync only business-critical documents 3. **Monitor sync health** regularly due to scheduled nature 4. **Trigger manual syncs** when immediate updates are needed 5. **Consider account type** when setting expectations **OneDrive-Specific Benefits:** * Supports both personal and business Microsoft accounts * Processes all major Office document formats * Preserves document structure and formatting * Handles large enterprise document collections **Important Limitations:** * Large Office documents may take significant time to process * Complex Excel formulas may not convert perfectly to markdown * Microsoft API rate limits may slow sync for large accounts # Connectors Overview Source: https://supermemory.ai/docs/connectors/overview Integrate Google Drive, Gmail, Notion, OneDrive, GitHub, Granola and Web Crawler to automatically sync documents into your knowledge base 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. ## Supported Connectors **Google Docs, Slides, Sheets** Real-time sync via webhooks. Supports shared drives, nested folders, and collaborative documents. **Email Threads** Real-time sync via Pub/Sub webhooks. Syncs threads with full conversation history and metadata. **Pages, Databases, Blocks** Instant sync of workspace content. Handles rich formatting, embeds, and database properties. **Word, Excel, PowerPoint** Scheduled sync every 4 hours. Supports personal and business accounts with file versioning. **GitHub Repositories** Real-time incremental sync via webhooks. Supports documentation files in repositories. **Meeting notes and transcripts** Syncs AI meeting notes, summaries, attendees, and transcripts from your Granola workspace. **Web Pages, Documentation** Crawl websites automatically with robots.txt compliance. Scheduled recrawling keeps content up to date. ## Quick Start ### 1. Create Connection ```typescript Typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY! }); const connection = await client.connections.create('notion', { redirectUrl: 'https://yourapp.com/callback', containerTag: 'user-123', documentLimit: 5000, metadata: { department: 'sales' } }); // Redirect user to complete OAuth console.log('Auth URL:', connection.authLink); console.log('Expires in:', connection.expiresIn); // Output: Auth URL: https://api.notion.com/v1/oauth/authorize?... // Output: Expires in: 1 hour ``` ```python Python theme={null} from supermemory import Supermemory import os client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY")) connection = client.connections.create( 'notion', redirect_url='https://yourapp.com/callback', container_tag='user-123', document_limit=5000, metadata={'department': 'sales'} ) # Redirect user to complete OAuth print(f'Auth URL: {connection.auth_link}') print(f'Expires in: {connection.expires_in}') # Output: Auth URL: https://api.notion.com/v1/oauth/authorize?... # Output: Expires in: 1 hour ``` ```bash cURL theme={null} curl -X POST "https://api.supermemory.ai/v3/connections/notion" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "redirectUrl": "https://yourapp.com/callback", "containerTag": "user-123", "documentLimit": 5000, "metadata": {"department": "sales"} }' # Response: { # "authLink": "https://api.notion.com/v1/oauth/authorize?...", # "expiresIn": "1 hour", # "id": "conn_abc123", # "redirectsTo": "https://yourapp.com/callback" # } ``` ### 2. Handle OAuth Callback After user completes OAuth, the connection is automatically established and sync begins. ### 3. Monitor Sync Status ```typescript Typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY! }); // List all connections using SDK const connections = await client.connections.list({ containerTags: ['user-123'] }); connections.forEach(conn => { console.log('Connection:', conn.id); console.log('Provider:', conn.provider); console.log('Email:', conn.email); console.log('Created:', conn.createdAt); }); // List synced documents (memories) using SDK const memories = await client.documents.list({ containerTags: ['user-123'] }); console.log(`Synced ${memories.memories.length} documents`); // Output: Synced 45 documents ``` ```python Python theme={null} from supermemory import Supermemory import os client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY")) # List all connections using SDK connections = client.connections.list( container_tags=['user-123'] ) for conn in connections: print(f'Connection: {conn.id}') print(f'Provider: {conn.provider}') print(f'Email: {conn.email}') print(f'Created: {conn.created_at}') # List synced documents (memories) using SDK memories = client.documents.list(container_tags=['user-123']) print(f'Synced {len(memories.memories)} documents') # Output: Synced 45 documents ``` ```bash cURL theme={null} # List all connections curl -X POST "https://api.supermemory.ai/v3/connections/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"]}' # Response: [{"id": "conn_abc", "provider": "notion", "email": "user@example.com", ...}] # List synced documents curl -X POST "https://api.supermemory.ai/v3/documents/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"]}' # Response: {"results": [...], "totalCount": 45} ``` ## How Connectors Work ### Authentication Flow 1. **Create Connection**: Call `/v3/connections/{provider}` to get an OAuth URL, or create a direct credential-based connection for Granola or Web Crawler 2. **User Authorization**: Redirect user to complete OAuth flow when the provider requires it 3. **Automatic Setup**: Connection established, sync begins immediately 4. **Continuous Sync**: Real-time updates via webhooks + scheduled sync every 4 hours (or scheduled recrawling for Web Crawler) ### Document Processing Pipeline ```mermaid theme={null} 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 | ## Connection Management ### List All Connections ```typescript Typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY! }); const connections = await client.connections.list({ containerTags: ['org-123'] }); ``` ```python Python theme={null} from supermemory import Supermemory import os client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY")) connections = client.connections.list(container_tags=['org-123']) for conn in connections: print(f"{conn.provider}: {conn.email} ({conn.id})") print(f"Documents: {conn.document_limit or 'unlimited'}") print(f"Expires: {conn.expires_at or 'never'}") # Output: notion: user@company.com (conn_abc123) # Output: Documents: 5000 # Output: Expires: never ``` ```bash cURL theme={null} curl -X POST "https://api.supermemory.ai/v3/connections/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["org-123"]}' # Response: [ # { # "id": "conn_abc123", # "provider": "notion", # "email": "user@company.com", # "documentLimit": 5000, # "createdAt": "2024-01-15T10:30:00.000Z" # } # ] ``` ### Delete Connections The `DELETE /v3/connections/:connectionId` endpoint accepts an optional `deleteDocuments` query parameter: | Parameter | Type | Default | Description | | ----------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `deleteDocuments` | boolean | `true` | When `true`, all documents imported by the connection are permanently deleted. When `false`, the connection is removed but documents are kept. | Setting `deleteDocuments=false` is useful when you want to disconnect an integration without losing the memories that were already imported. ```typescript Typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY! }); // Delete connection and all imported documents (default) const result = await client.connections.deleteByID(connectionId); // Delete connection but keep imported documents const result = await client.connections.deleteByID(connectionId, { deleteDocuments: false }); // Or delete by provider (requires container tags) const result = await client.connections.deleteByProvider('notion', { containerTags: ['user-123'] }); console.log('Deleted:', result.id, result.provider); ``` ```python Python theme={null} from supermemory import Supermemory import os client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY")) # Delete connection and all imported documents (default) result = client.connections.delete_by_id(connection_id) # Delete connection but keep imported documents result = client.connections.delete_by_id(connection_id, delete_documents=False) # Or delete by provider (requires container tags) result = client.connections.delete_by_provider( provider='notion', container_tags=['user-123'] ) print(f"Deleted: {result.id} {result.provider}") ``` ```bash cURL theme={null} # Delete connection and all imported documents (default) curl -X DELETE "https://api.supermemory.ai/v3/connections/conn_abc123" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # Delete connection but keep imported documents curl -X DELETE "https://api.supermemory.ai/v3/connections/conn_abc123?deleteDocuments=false" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # Response: { # "id": "conn_abc123", # "provider": "notion" # } ``` ## Custom OAuth Applications By default, Supermemory uses its own OAuth applications to connect to third-party providers. You can configure your own OAuth app credentials via `PATCH /v3/settings` for tighter control over data access — useful for enterprise customers. 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`). Enabling custom keys for a provider applies to all new connections for that provider — existing connections will need to be re-authorized. # S3 Connector Source: https://supermemory.ai/docs/connectors/s3 Connect Amazon S3 or S3-compatible storage to sync files into your Supermemory knowledge base Connect Amazon S3 buckets or S3-compatible storage services (MinIO, DigitalOcean Spaces, Cloudflare R2, Tigris) to sync files into your Supermemory knowledge base. The S3 connector requires a **Scale Plan** or higher. You can also create S3 connections directly from the [Supermemory Console](https://console.supermemory.ai). ## Quick Setup ```typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY! }); const connection = await client.connections.create('s3', { metadata: { accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, bucket: 'my-documents-bucket', region: 'us-east-1' }, containerTag: 'org-123' }); ``` ```python theme={null} from supermemory import Supermemory import os client = Supermemory(api_key=os.environ["SUPERMEMORY_API_KEY"]) connection = client.connections.create( 's3', metadata={ 'accessKeyId': os.environ["AWS_ACCESS_KEY_ID"], 'secretAccessKey': os.environ["AWS_SECRET_ACCESS_KEY"], 'bucket': 'my-documents-bucket', 'region': 'us-east-1' }, container_tag='org-123' ) ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/connections/s3" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "accessKeyId": "AKIAIOSFODNN7EXAMPLE", "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "bucket": "my-documents-bucket", "region": "us-east-1" }, "containerTag": "org-123" }' ``` ## Configuration Options For S3, provider-specific connection fields are passed inside the top-level `metadata` object. General connection options stay top-level. | Parameter | Location | Required | Description | | ------------------- | ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- | | `accessKeyId` | `metadata.accessKeyId` | Yes | AWS access key ID or S3-compatible service key | | `secretAccessKey` | `metadata.secretAccessKey` | Yes | AWS secret access key | | `bucket` | `metadata.bucket` | Yes | S3 bucket name | | `region` | `metadata.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). | | `endpoint` | `metadata.endpoint` | No | Custom endpoint for S3-compatible services | | `prefix` | `metadata.prefix` | No | Key prefix filter (e.g., `documents/`) | | `containerTagRegex` | `metadata.containerTagRegex` | No | Regex to extract container tags from file paths | | `containerTag` | top-level | No | Tag for organizing this connection | | `documentLimit` | top-level | No | Maximum documents to sync (default: 10,000) | In the Python SDK, use `container_tags` for the top-level option, but keep S3 metadata keys in camelCase: `accessKeyId`, `secretAccessKey`, and `containerTagRegex`. ## S3-Compatible Services Use `metadata.endpoint` to connect to S3-compatible storage. These services don't use AWS-style regions, so set `metadata.region` to `auto` — the value is still required for request signing but the service ignores it. ```typescript theme={null} // MinIO const connection = await client.connections.create('s3', { metadata: { accessKeyId: 'minio-key', secretAccessKey: 'minio-secret', bucket: 'my-bucket', region: 'auto', endpoint: 'https://minio.example.com' }, containerTag: 'minio-sync' }); ``` Common S3-compatible endpoint values: | Service | `metadata.endpoint` | `metadata.region` | | ------------------- | ----------------------------------------------- | ----------------- | | DigitalOcean Spaces | `https://nyc3.digitaloceanspaces.com` | `nyc3` | | Cloudflare R2 | `https://.r2.cloudflarestorage.com` | `auto` | | Tigris | `https://t3.storage.dev` | `auto` | Cloudflare R2 example: ```typescript theme={null} const connection = await client.connections.create('s3', { metadata: { accessKeyId: process.env.R2_ACCESS_KEY_ID!, secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!, bucket: 'my-bucket', region: 'auto', endpoint: 'https://.r2.cloudflarestorage.com' }, containerTag: 'r2-sync' }); ``` For S3-compatible services, `metadata.endpoint` is the base S3 endpoint. Do not include the bucket name in the endpoint URL; pass the bucket separately as `metadata.bucket`. ## Prefix Filtering Sync only files within a specific path: ```typescript theme={null} const connection = await client.connections.create('s3', { metadata: { accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, bucket: 'company-data', region: 'us-east-1', prefix: 'documents/engineering/' // Only syncs files under this path }, containerTag: 'engineering-docs' }); ``` ## Dynamic Container Tags Extract container tags from S3 key paths for multi-tenant setups: ```typescript theme={null} const connection = await client.connections.create('s3', { metadata: { accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, bucket: 'user-files', region: 'us-east-1', containerTagRegex: 'users/(?[^/]+)/' }, containerTag: 'user-files' }); // File: users/user-123/documents/notes.md → container tag: user-123 // File: users/user-456/reports/q4.pdf → container tag: user-456 ``` The regex must contain a named capture group `(?...)` and be less than 200 characters. ## Connection Management ### Delete Connection ```typescript theme={null} await client.connections.deleteByID('conn_s3_abc123'); ``` ```bash theme={null} curl -X DELETE "https://api.supermemory.ai/v3/connections/conn_s3_abc123" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` By default, deleting a connection removes all synced documents from Supermemory. To keep documents, pass `deleteDocuments=false` as a query parameter: `DELETE /v3/connections/:id?deleteDocuments=false` ### Manual Sync ```typescript theme={null} await client.connections.import('s3', { containerTags: ['org-123'] }); ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/connections/s3/import" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["org-123"]}' ``` ## 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 connection (default) | ## IAM Permissions Minimum required permissions: ```json theme={null} { "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 | Code | 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 | # Connector Troubleshooting Source: https://supermemory.ai/docs/connectors/troubleshooting Diagnose and resolve common issues with Google Drive, Gmail, Notion, and OneDrive connectors Quick guide to resolve common connector issues with authentication, syncing, and permissions. ## Quick Health Check Check if your connectors are working properly: ```typescript TypeScript theme={null} const connections = await client.connections.list({ containerTags: ['user-123'] }); connections.forEach(conn => { console.log(`${conn.provider}: ${conn.email} - Connected ${conn.createdAt}`); }); // Check for stuck documents const documents = await client.connections.listDocuments('notion', { containerTags: ['user-123'] }); const failed = documents.filter(doc => doc.status === 'failed'); if (failed.length > 0) { console.log(`⚠️ ${failed.length} documents failed to sync`); } ``` ```python Python theme={null} connections = client.connections.list(container_tags=['user-123']) for conn in connections: print(f"{conn.provider}: {conn.email} - Connected {conn.created_at}") # Check for stuck documents documents = client.connections.list_documents( 'notion', container_tags=['user-123'] ) failed = [doc for doc in documents if doc.status == 'failed'] if failed: print(f"⚠️ {len(failed)} documents failed to sync") ``` ```bash cURL theme={null} # List all connections curl -X POST "https://api.supermemory.ai/v3/connections/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"]}' # Check document status curl -X POST "https://api.supermemory.ai/v3/documents/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"], "source": "notion"}' ``` ## Common Issues ### OAuth Callback Fails **Problem:** "Invalid redirect URI" error after user grants permissions **Solution:** Ensure your redirect URL matches EXACTLY what's configured in your OAuth app: ```typescript theme={null} // correct - exact match with OAuth app settings const connection = await client.connections.create('notion', { redirectUrl: 'https://yourapp.com/auth/notion/callback', containerTag: 'user-123' }); // Wrong - URL doesn't match // redirectUrl: 'https://yourapp.com/callback' ``` **Prevention:** * Use HTTPS for production URLs * Copy the exact URL from your 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 theme={null} // Force sync for stuck documents await client.connections.import('notion', { containerTags: ['user-123'] }); ``` If documents consistently fail: * 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 theme={null} // Delete and recreate connection await client.connections.deleteByProvider('google-drive', { containerTags: ['user-123'] }); const newConnection = await client.connections.create('google-drive', { redirectUrl: 'https://yourapp.com/callback', containerTag: 'user-123' }); // User must re-authenticate window.location.href = newConnection.authLink; ``` ### Sync Takes Too Long **Problem:** Hundreds of documents taking hours to sync **Solution:** Set reasonable document limits: ```typescript theme={null} const connection = await client.connections.create('onedrive', { redirectUrl: 'https://yourapp.com/callback', containerTag: 'user-123', documentLimit: 500 // Start with fewer documents }); ``` ## 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 ### 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 connection is healthy: ```typescript theme={null} await client.connections.import('gmail', { containerTags: ['user-123'] }); ``` **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 connection 3. Ensure user completes full OAuth consent flow ```typescript theme={null} // Re-authenticate to get fresh tokens await client.connections.deleteByProvider('gmail', { containerTags: ['user-123'] }); const newConnection = await client.connections.create('gmail', { redirectUrl: 'https://yourapp.com/callback', containerTag: 'user-123' }); ``` **Scale Plan Required Error** Gmail connector requires Scale Plan or Enterprise Plan. 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 descriptive container tags** - Makes debugging easier 3. **Monitor failed documents** - Check weekly for sync issues 4. **Handle rate limits gracefully** - Implement exponential backoff 5. **Test OAuth in development** - Ensure redirect URLs work before production # Web Crawler Connector Source: https://supermemory.ai/docs/connectors/web-crawler Crawl and sync websites automatically with scheduled recrawling and robots.txt compliance Connect websites to automatically crawl and sync web pages into your Supermemory knowledge base. The web crawler respects robots.txt rules, includes SSRF protection, and automatically recrawls sites on a schedule. The web crawler connector requires a **Scale Plan** or **Enterprise Plan**. ## Quick Setup ### 1. Create Web Crawler Connection ```typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY! }); const connection = await client.connections.create('web-crawler', { redirectUrl: 'https://yourapp.com/callback', containerTag: 'user-123', documentLimit: 5000, metadata: { startUrl: 'https://docs.example.com' } }); // Web crawler doesn't require OAuth - connection is ready immediately console.log('Connection ID:', connection.id); console.log('Connection created:', connection.createdAt); // Note: connection.authLink is undefined for web-crawler ``` ```python theme={null} from supermemory import Supermemory import os client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY")) connection = client.connections.create( 'web-crawler', redirect_url='https://yourapp.com/callback', container_tag='user-123', document_limit=5000, metadata={ 'startUrl': 'https://docs.example.com' } ) # Web crawler doesn't require OAuth - connection is ready immediately print(f'Connection ID: {connection.id}') print(f'Connection created: {connection.created_at}') # Note: connection.auth_link is None for web-crawler ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/connections/web-crawler" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "redirectUrl": "https://yourapp.com/callback", "containerTag": "user-123", "documentLimit": 5000, "metadata": { "startUrl": "https://docs.example.com" } }' # Response: { # "id": "conn_wc123", # "redirectsTo": "https://yourapp.com/callback", # "authLink": null, # "expiresIn": null # } ``` ### 2. Connection Established Unlike other connectors, the web crawler doesn't require OAuth authentication. The connection is established immediately upon creation, and crawling begins automatically. ### 3. Monitor Sync Progress ```typescript theme={null} // Check connection details const connection = await client.connections.getByTags('web-crawler', { containerTags: ['user-123'] }); console.log('Start URL:', connection.metadata?.startUrl); console.log('Connection created:', connection.createdAt); // List synced web pages const documents = await client.connections.listDocuments('web-crawler', { containerTags: ['user-123'] }); console.log(`Synced ${documents.length} web pages`); ``` ```python theme={null} # Check connection details connection = client.connections.get_by_tags( 'web-crawler', container_tags=['user-123'] ) print(f'Start URL: {connection.metadata.get("startUrl")}') print(f'Connection created: {connection.created_at}') # List synced web pages documents = client.connections.list_documents( 'web-crawler', container_tags=['user-123'] ) print(f'Synced {len(documents)} web pages') ``` ```bash theme={null} # Get connection details by provider and tags curl -X POST "https://api.supermemory.ai/v3/connections/web-crawler/connection" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"]}' # Response includes connection details: # { # "id": "conn_wc123", # "provider": "web-crawler", # "createdAt": "2024-01-15T10:00:00Z", # "documentLimit": 5000, # "metadata": {"startUrl": "https://docs.example.com", ...} # } # List synced documents curl -X POST "https://api.supermemory.ai/v3/connections/web-crawler/documents" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"]}' # Response: Array of document objects # [ # {"title": "Home Page", "type": "webpage", "status": "done", "url": "https://docs.example.com"}, # {"title": "Getting Started", "type": "webpage", "status": "done", "url": "https://docs.example.com/getting-started"} # ] ``` ## 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 connection creation * **Scheduled Recrawling**: Automatically recrawls sites that haven't been synced in 7+ days * **No Real-time Updates**: Unlike other connectors, web crawler doesn't support webhook-based real-time sync The recrawl schedule is automatically assigned when the connection is created. Sites are recrawled periodically to keep content up to date, but updates are not instantaneous. ## Connection Management ### List All Connections ```typescript theme={null} // List all web crawler connections const connections = await client.connections.list({ containerTags: ['user-123'] }); const webCrawlerConnections = connections.filter( conn => conn.provider === 'web-crawler' ); webCrawlerConnections.forEach(conn => { console.log(`Start URL: ${conn.metadata?.startUrl}`); console.log(`Connection ID: ${conn.id}`); console.log(`Created: ${conn.createdAt}`); }); ``` ```python theme={null} # List all web crawler connections connections = client.connections.list(container_tags=['user-123']) web_crawler_connections = [ conn for conn in connections if conn.provider == 'web-crawler' ] for conn in web_crawler_connections: print(f'Start URL: {conn.metadata.get("startUrl")}') print(f'Connection ID: {conn.id}') print(f'Created: {conn.created_at}') ``` ```bash theme={null} # List all connections curl -X POST "https://api.supermemory.ai/v3/connections/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"]}' # Response: [ # { # "id": "conn_wc123", # "provider": "web-crawler", # "createdAt": "2024-01-15T10:30:00.000Z", # "documentLimit": 5000, # "metadata": {"startUrl": "https://docs.example.com", ...} # } # ] ``` ### Delete Connection Remove a web crawler connection when no longer needed: ```typescript theme={null} // Delete by connection ID const result = await client.connections.delete('connection_id_123'); console.log('Deleted connection:', result.id); // Delete by provider and container tags const providerResult = await client.connections.deleteByProvider('web-crawler', { containerTags: ['user-123'] }); console.log('Deleted web crawler connection for user'); ``` ```python theme={null} # Delete by connection ID result = client.connections.delete('connection_id_123') print(f'Deleted connection: {result.id}') # Delete by provider and container tags provider_result = client.connections.delete_by_provider( 'web-crawler', container_tags=['user-123'] ) print('Deleted web crawler connection for user') ``` ```bash theme={null} # Delete by connection ID curl -X DELETE "https://api.supermemory.ai/v3/connections/connection_id_123" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # Delete by provider and container tags curl -X DELETE "https://api.supermemory.ai/v3/connections/web-crawler" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTags": ["user-123"]}' ``` Deleting a connection will: * Stop all future crawls from the website * Keep existing synced documents in Supermemory (they won't be deleted) * Remove the connection configuration ## Advanced Configuration ### Content Filtering Control which web pages get synced using the settings API: ```typescript theme={null} // Configure intelligent filtering for web content await client.settings.update({ shouldLLMFilter: true, includeItems: { urlPatterns: ['*docs*', '*documentation*', '*guide*'], titlePatterns: ['*Getting Started*', '*API Reference*', '*Tutorial*'] }, excludeItems: { urlPatterns: ['*admin*', '*private*', '*test*'], titlePatterns: ['*Draft*', '*Archive*', '*Old*'] }, filterPrompt: "Sync documentation pages, guides, and API references. Skip admin pages, private content, drafts, and archived pages." }); ``` ```python theme={null} # Configure intelligent filtering for web content client.settings.update( should_llm_filter=True, include_items={ 'urlPatterns': ['*docs*', '*documentation*', '*guide*'], 'titlePatterns': ['*Getting Started*', '*API Reference*', '*Tutorial*'] }, exclude_items={ 'urlPatterns': ['*admin*', '*private*', '*test*'], 'titlePatterns': ['*Draft*', '*Archive*', '*Old*'] }, filter_prompt="Sync documentation pages, guides, and API references. Skip admin pages, private content, drafts, and archived pages." ) ``` ```bash theme={null} # Configure intelligent filtering for web content curl -X PATCH "https://api.supermemory.ai/v3/settings" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "shouldLLMFilter": true, "includeItems": { "urlPatterns": ["*docs*", "*documentation*", "*guide*"], "titlePatterns": ["*Getting Started*", "*API Reference*", "*Tutorial*"] }, "excludeItems": { "urlPatterns": ["*admin*", "*private*", "*test*"], "titlePatterns": ["*Draft*", "*Archive*", "*Old*"] }, "filterPrompt": "Sync documentation pages, guides, and API references. Skip admin pages, private content, drafts, and archived pages." }' ``` ## Security & Compliance ### SSRF Protection Built-in protection against Server-Side Request Forgery (SSRF) attacks: * Blocks private IP addresses (10.x.x.x, 192.168.x.x, 172.16-31.x.x) * Blocks localhost and internal domains * Blocks cloud metadata endpoints * Only allows public, internet-accessible URLs ### URL Validation All URLs are validated before crawling: * Must be valid HTTP/HTTPS URLs * Must be publicly accessible * Must return HTML content * Response size limited to 10MB **Important Limitations:** * Requires Scale Plan or Enterprise Plan * Only crawls same-domain URLs * Scheduled recrawling means updates are not real-time * Large websites may take significant time to crawl initially * Robots.txt restrictions may prevent crawling some pages * URLs must be publicly accessible (no authentication required) # Introduction Source: https://supermemory.ai/docs/index Context infrastructure for AI agents
# supermemory

Context infrastructure for AI agents. Use it with the API, your tools, your team, or run it yourself.

Developer platform Plugins and MCP Self-hosting
# Ingesting context to supermemory Source: https://supermemory.ai/docs/ingestion/add-memories Add text, files, and URLs to Supermemory Send any raw content to Supermemory — conversations, documents, files, URLs. We extract the memories automatically. Pass `customId` 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. ## Quick Start ```typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory(); // Add text content await client.add({ content: "Machine learning enables computers to learn from data", containerTag: "user_123", metadata: { category: "ai" } }); // Add a URL (auto-extracted) await client.add({ content: "https://youtube.com/watch?v=dQw4w9WgXcQ", containerTag: "user_123" }); ``` ```python theme={null} from supermemory import Supermemory client = Supermemory() # Add text content client.add( content="Machine learning enables computers to learn from data", container_tag="user_123", metadata={"category": "ai"} ) # Add a URL (auto-extracted) client.add( content="https://youtube.com/watch?v=dQw4w9WgXcQ", container_tag="user_123" ) ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/documents" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "Machine learning enables computers to learn from data", "containerTag": "user_123", "metadata": {"category": "ai"} }' ``` **Response:** ```json theme={null} { "id": "abc123", "status": "queued" } ``` If an irrecoverable processing error occurs, the document is automatically deleted after 2 minutes. *** ## Updating Content Use `customId` to update existing documents or conversations. When you send content with the same `customId`, Supermemory intelligently processes only what's new. ### Two ways to update: **Option 1: Send only the new content** ```typescript theme={null} // First request await client.add({ content: "user: Hi, I'm Sarah.\nassistant: Nice to meet you!", customId: "conv_123", containerTag: "user_sarah" }); // Later: send only new messages await client.add({ content: "user: What's the weather?\nassistant: It's sunny today.", customId: "conv_123", // Same ID — Supermemory links them containerTag: "user_sarah" }); ``` **Option 2: Send the full updated content** ```typescript theme={null} // Supermemory detects the diff and only processes new parts await client.add({ content: "user: Hi, I'm Sarah.\nassistant: Nice to meet you!\nuser: What's the weather?\nassistant: It's sunny today.", customId: "conv_123", containerTag: "user_sarah" }); ``` Both work — choose what fits your architecture. ### Replace entire document To completely replace a document's content (not append), use `memories.update()`: ```typescript theme={null} // Replace the entire document content await client.documents.update("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 theme={null} // 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. ```typescript theme={null} import fs from 'fs'; await client.documents.uploadFile({ file: fs.createReadStream('document.pdf'), containerTag: 'user_123' }); ``` ```python theme={null} with open('document.pdf', 'rb') as file: client.documents.upload_file( file=file, container_tag='user_123' ) ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/documents/file" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -F "file=@document.pdf" \ -F "containerTag=user_123" ``` ### 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 *** ## Parameters | Parameter | Type | Description | | ------------------ | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `content` | string | **Required.** Any raw content — text, conversations, URLs, HTML | | `customId` | string | **Recommended.** Your ID for the content (conversation ID, doc ID). Enables updates and deduplication | | `containerTag` | string | Group by user/project. Required for user profiles | | `metadata` | object | Key-value pairs for filtering (strings, numbers, booleans) | | `filterByMetadata` | object | Filter which existing memories are used as context during ingestion. See [Filtered Writes](#filtered-writes) | | `entityContext` | string | Context for memory extraction on this container tag. Max 1500 chars. See [Customization](/docs/concepts/customization#entity-context) | | `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](/docs/concepts/super-rag#ingesting-as-pure-superrag-tasktype-superrag) | **Content Types:** ```typescript theme={null} // 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" } ``` **Container Tags:** ```typescript theme={null} // By user { containerTag: "user_123" } // By project { containerTag: "project_alpha" } // Hierarchical { containerTag: "org_456_team_backend" } ``` **Custom IDs (Recommended):** ```typescript theme={null} // Use IDs from your system { customId: "conv_abc123" } // Conversation ID { customId: "doc_456" } // Document ID { customId: "thread_789" } // Thread ID { customId: "meeting_2024_01_15" } // Meeting ID // Updates: same customId = same document // Supermemory only processes new/changed content await client.add({ content: "Updated content...", customId: "doc_456" // Links to existing document }); ``` **Metadata:** ```typescript theme={null} { metadata: { source: "slack", author: "john", priority: 1, reviewed: true } } ``` * No nested objects or arrays * Values: string, number, or boolean only **Entity Context:** ```typescript theme={null} // Guide memory extraction for this container tag { containerTag: "session_abc123", entityContext: `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 container tag * Combines with org-level filter prompts *** ## 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. * `"instant"` — processes each document on its own right away, and bills one extra operation per document. ```json theme={null} { "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"`. ```json theme={null} { "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](/docs/concepts/super-rag#ingesting-as-pure-superrag-tasktype-superrag). *** ## Filtered Writes By default, when you add content, Supermemory uses **all** existing memories in the space 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 space but want new memories to build on top of a specific subset — for example, only memories from a particular source, category, or user. The metadata itself is still written to the document, but the memories will only be built on top of what's already there matching the filter. ```typescript theme={null} await client.add({ content: "New research findings on transformer architectures...", containerTag: "user_123", metadata: { category: "ml", source: "arxiv" }, filterByMetadata: { category: "ml" } }); ``` ```python theme={null} client.add( content="New research findings on transformer architectures...", container_tag="user_123", metadata={"category": "ml", "source": "arxiv"}, filter_by_metadata={"category": "ml"} ) ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/documents" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "content": "New research findings on transformer architectures...", "containerTag": "user_123", "metadata": {"category": "ml", "source": "arxiv"}, "filterByMetadata": {"category": "ml"} }' ``` ### How it works When `filterByMetadata` is provided: * **Profile memories** (static context) are filtered to only those from documents matching the 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 ### `filterByMetadata` parameter | Key | Type | Description | | ------------------ | --------------------------------------------------------- | ----------------------------------------------------------------------------- | | `filterByMetadata` | `Record` | Key-value pairs to filter existing memories by their 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 theme={null} // Match documents where category is "ml" AND source is either "arxiv" or "pubmed" await client.add({ content: "...", containerTag: "user_123", filterByMetadata: { 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 /v3/documents/{id}`: ```typescript theme={null} const doc = await client.documents.get("abc123"); console.log(doc.status); // "queued" | "processing" | "done" ``` Process multiple documents with rate limiting: ```typescript theme={null} async function batchUpload(documents: Array<{id: string, content: string}>) { const results = []; for (const doc of documents) { try { const result = await client.add({ content: doc.content, customId: doc.id, containerTag: "batch_import" }); 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 `customId` to track and deduplicate | Status | Error | Cause | | ------ | --------------------- | ------------------------------------------- | | 400 | BadRequestError | Missing required fields, invalid parameters | | 401 | AuthenticationError | Invalid or missing API key | | 403 | PermissionDeniedError | Insufficient permissions | | 429 | RateLimitError | Too many requests or quota exceeded | | 500 | InternalServerError | Processing failure | ```typescript theme={null} import { BadRequestError, RateLimitError } from 'supermemory'; try { await client.add({ content: "..." }); } catch (error) { if (error instanceof RateLimitError) { // Wait and retry await new Promise(r => setTimeout(r, 60000)); } else if (error instanceof BadRequestError) { // Fix request parameters console.error("Invalid request:", error.message); } } ``` **Single delete:** ```typescript theme={null} await client.documents.delete("doc_id_123"); ``` **Bulk delete by IDs:** ```typescript theme={null} await client.documents.deleteBulk({ ids: ["doc_1", "doc_2", "doc_3"] }); ``` **Bulk delete by container tag:** ```typescript theme={null} // Delete all content for a user await client.documents.deleteBulk({ containerTags: ["user_123"] }); ``` Deletes are permanent — no recovery. *** ## Next Steps * [How to backfill historical data](/docs/ingestion/batch-ingest-historical-data) — Import dated content with the batch API * [Search Memories](/docs/recall/search) — Query your content * [User Profiles](/docs/recall/user-profiles) — Get user context * [Organizing & Filtering](/docs/concepts/filtering) — Container tags and metadata # How to backfill historical data into Supermemory Source: https://supermemory.ai/docs/ingestion/batch-ingest-historical-data Backfill historical documents into Supermemory with documentDate, stable custom IDs, and the batch ingestion API. Use `POST /v3/documents/batch` to backfill exports, emails, messages, or other dated records. Sort the source data oldest to newest, add `documentDate` to every document. ## Backfill in batches Backfill dated content by setting `documentDate` 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 /v3/documents/batch`](/docs/api-reference/ingest/batch-add-documents) ```typescript TypeScript theme={null} import Supermemory from "supermemory"; type SourceDocument = { id: string; content: string; createdAt: string; }; const client = new Supermemory(); const batchSize = 100; async function backfillHistoricalData(sourceDocuments: SourceDocument[]) { const documents = sourceDocuments .map((document) => ({ content: document.content, customId: document.id, documentDate: new Date(document.createdAt).toISOString() })) .sort((a, b) => a.documentDate.localeCompare(b.documentDate)); for (let offset = 0; offset < documents.length; offset += batchSize) { const result = await client.documents.batchAdd({ containerTag: "historical_import", documents: documents.slice(offset, offset + batchSize) }); if (result.failed > 0) { throw new Error(`${result.failed} documents failed to ingest`); } } } ``` ```python Python theme={null} 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"], "custom_id": document["id"], "document_date": to_utc(document["created_at"]), } for document in source_documents ], key=lambda document: document["document_date"], ) for offset in range(0, len(documents), batch_size): result = client.documents.batch_add( container_tag="historical_import", documents=documents[offset : offset + batch_size], ) if result.failed > 0: raise RuntimeError(f"{result.failed} documents failed to ingest") ``` ## Optional: wait for processing to finish **Endpoint:** [`GET /v3/documents/{id}`](/docs/api-reference/documents/get-document) The batch endpoint returns after accepting the documents. If a later step depends on completed memory generation, poll the returned document IDs until both `status` and `dreamingStatus` are `done`. ```typescript TypeScript theme={null} async function waitUntilDone(ids: string[]) { while (true) { const documents = await Promise.all( ids.map((id) => client.documents.get(id)) ); if (documents.some((document) => document.status === "failed")) { throw new Error("A document failed to process"); } if ( documents.every( (document) => document.status === "done" && document.dreamingStatus === "done" ) ) { return; } await new Promise((resolve) => setTimeout(resolve, 10_000)); } } ``` ```python Python theme={null} import time def wait_until_done(ids: list[str]) -> None: while True: documents = [client.documents.get(document_id) for document_id in ids] if any(document.status == "failed" for document in documents): raise RuntimeError("A document failed to process") if all( document.status == "done" and document.dreaming_status == "done" for document in documents ): return time.sleep(10) ``` # Document Operations Source: https://supermemory.ai/docs/ingestion/document-operations List, get, update, and delete your ingested documents Manage documents after ingestion using the SDK. ## List Documents Retrieve paginated documents with filtering. ```typescript theme={null} const documents = await client.documents.list({ limit: 10, containerTags: ["user_123"] }); documents.memories.forEach(d => { console.log(d.id, d.title, d.status); }); ``` ```python theme={null} documents = client.documents.list( limit=10, container_tags=["user_123"] ) for doc in documents.memories: print(doc.id, doc.title, doc.status) ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/documents/list" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"limit": 10, "containerTags": ["user_123"]}' ``` **Response:** ```json theme={null} { "memories": [ { "id": "doc_abc123", "title": "Meeting notes", "status": "done", "type": "text", "createdAt": "2024-01-15T10:30:00Z", "containerTags": ["user_123"], "metadata": { "source": "slack" } } ], "pagination": { "currentPage": 1, "totalPages": 3, "totalItems": 25 } } ``` ### Parameters | Parameter | Type | Default | Description | | --------------- | --------- | ----------- | ---------------------------------- | | `limit` | number | 50 | Items per page (max 200) | | `page` | number | 1 | Page number | | `containerTags` | string\[] | — | Filter by tags | | `sort` | string | `createdAt` | Sort by `createdAt` or `updatedAt` | | `order` | string | `desc` | `desc` (newest) or `asc` (oldest) | ```typescript theme={null} async function getAllDocuments(containerTag: string) { const all = []; let page = 1; while (true) { const { memories, pagination } = await client.documents.list({ containerTags: [containerTag], limit: 100, page }); all.push(...memories); if (page >= pagination.totalPages) break; page++; } return all; } ``` ```typescript theme={null} const documents = await client.documents.list({ containerTags: ["user_123"], filters: { AND: [ { key: "status", value: "reviewed", negate: false }, { key: "priority", value: "high", negate: false } ] } }); ``` *** ## Get Document Get a specific document with its processing status. ```typescript theme={null} const doc = await client.documents.get("doc_abc123"); console.log(doc.status); // "queued" | "processing" | "done" | "failed" console.log(doc.content); ``` ```python theme={null} doc = client.documents.get("doc_abc123") print(doc.status) print(doc.content) ``` ```bash theme={null} curl "https://api.supermemory.ai/v3/documents/doc_abc123" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` ### 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 | ```typescript theme={null} async function waitForProcessing(docId: string) { while (true) { const doc = await client.documents.get(docId); if (doc.status === "done") return doc; if (doc.status === "failed") throw new Error("Processing failed"); await new Promise(r => setTimeout(r, 2000)); } } ``` *** ## 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. ```typescript theme={null} await client.documents.update("doc_abc123", { content: "Updated content here", metadata: { version: 2, reviewed: true } }); ``` ```python theme={null} client.documents.update( "doc_abc123", content="Updated content here", metadata={"version": 2, "reviewed": True} ) ``` ```bash theme={null} curl -X PATCH "https://api.supermemory.ai/v3/documents/doc_abc123" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"content": "Updated content here", "metadata": {"version": 2}}' ``` *** ## Delete Documents Permanently remove documents. ```typescript theme={null} // Single delete await client.documents.delete("doc_abc123"); // Bulk delete by IDs await client.documents.deleteBulk({ ids: ["doc_1", "doc_2", "doc_3"] }); // Bulk delete by container tag (delete all for a user) await client.documents.deleteBulk({ containerTags: ["user_123"] }); ``` ```python theme={null} # Single delete client.documents.delete("doc_abc123") # Bulk delete by IDs client.documents.delete_bulk(ids=["doc_1", "doc_2", "doc_3"]) # Bulk delete by container tag client.documents.delete_bulk(container_tags=["user_123"]) ``` ```bash theme={null} # Single delete curl -X DELETE "https://api.supermemory.ai/v3/documents/doc_abc123" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # Bulk delete by IDs curl -X DELETE "https://api.supermemory.ai/v3/documents/bulk" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ids": ["doc_1", "doc_2", "doc_3"]}' ``` Deletes are permanent — no recovery. *** ## Processing Queue Check documents currently being processed. ```typescript theme={null} const response = await client.documents.listProcessing(); console.log(`${response.documents.length} documents processing`); ``` ```python theme={null} response = client.documents.list_processing() print(f"{len(response.documents)} documents processing") ``` ```bash theme={null} curl "https://api.supermemory.ai/v3/documents/processing" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` *** ## Next Steps * [Memory Operations](/docs/recall/memory-operations) — Advanced v4 memory operations * [Search](/docs/recall/search) — Query your memories * [Ingesting Content](/docs/ingestion/add-memories) — Add new content # Microsoft Agent Framework Source: https://supermemory.ai/docs/integrations/agent-framework Add persistent memory to Microsoft Agent Framework agents with Supermemory 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 the package: ```bash theme={null} pip install --pre supermemory-agent-framework ``` Or with uv: ```bash theme={null} uv add --prerelease=allow supermemory-agent-framework ``` The `--pre` / `--prerelease=allow` flag is required because `agent-framework-core` depends on pre-release versions of Azure packages. Set up your environment: ```bash theme={null} # .env SUPERMEMORY_API_KEY=your-supermemory-api-key OPENAI_API_KEY=your-openai-api-key ``` Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai). *** ## Connection All integration points share a single `AgentSupermemory` connection. This ensures the same API client, container tag, and conversation ID are used across middleware, tools, and context providers. ```python theme={null} 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 theme={null} middleware = SupermemoryChatMiddleware(conn, options=...) tools = SupermemoryTools(conn) provider = SupermemoryContextProvider(conn, mode="full") ``` *** ## 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 theme={null} import asyncio from agent_framework import AgentSession from agent_framework.openai import OpenAIResponsesClient from supermemory_agent_framework import AgentSupermemory, SupermemoryContextProvider async def main(): conn = AgentSupermemory(container_tag="user-123") provider = SupermemoryContextProvider(conn, mode="full") agent = OpenAIResponsesClient().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 theme={null} import asyncio from agent_framework.openai import OpenAIResponsesClient from supermemory_agent_framework import AgentSupermemory, SupermemoryTools async def main(): conn = AgentSupermemory(container_tag="user-123") tools = SupermemoryTools(conn) agent = OpenAIResponsesClient().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) *** ## Chat middleware Intercept chat requests to automatically inject memory context. Useful when you want memory injection without the session-based context provider pattern. ```python theme={null} import asyncio from agent_framework.openai import OpenAIResponsesClient 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 = OpenAIResponsesClient().as_agent( name="MemoryAgent", instructions="You are a helpful assistant.", middleware=[middleware], ) response = await agent.run("What's my favorite programming language?") print(response.text) asyncio.run(main()) ``` *** ## Memory modes ```python theme={null} 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 | *** ## Example: support agent with memory A support agent that remembers customers across sessions: ```python theme={null} import asyncio from agent_framework import AgentSession from agent_framework.openai import OpenAIResponsesClient from supermemory_agent_framework import ( AgentSupermemory, SupermemoryChatMiddleware, SupermemoryMiddlewareOptions, SupermemoryContextProvider, SupermemoryTools, ) async def main(): conn = AgentSupermemory( container_tag="customer-456", conversation_id="support-session-789", entity_context="Enterprise customer on the Pro plan.", ) provider = SupermemoryContextProvider( conn, mode="full", store_conversations=True, ) middleware = SupermemoryChatMiddleware( conn, options=SupermemoryMiddlewareOptions( mode="full", add_memory="always", ), ) tools = SupermemoryTools(conn) agent = OpenAIResponsesClient().as_agent( name="SupportAgent", instructions="""You are a customer support agent. Use the user context provided to personalize your responses. Reference past interactions when relevant. Save important new information about the customer.""", context_providers=[provider], middleware=[middleware], ) session = AgentSession() # First interaction response = await agent.run( "My order hasn't arrived yet. Order ID is ORD-789.", session=session, tools=tools.get_tools(), ) print(response.text) # Follow-up — agent automatically has context from first message response = await agent.run( "Actually, can you also check my previous order?", session=session, tools=tools.get_tools(), ) print(response.text) asyncio.run(main()) ``` *** ## Error handling The package provides specific exception types: ```python theme={null} 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 How automatic profiling works Filtering and search modes Memory for OpenAI Agents SDK Memory for LangChain apps # Agno Source: https://supermemory.ai/docs/integrations/agno Add persistent memory to Agno agents with Supermemory 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 theme={null} pip install agno supermemory python-dotenv ``` Set up your environment: ```bash theme={null} # .env SUPERMEMORY_API_KEY=your-supermemory-api-key OPENAI_API_KEY=your-openai-api-key ``` Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai). ## Basic integration Fetch user context before running an agent, then store the interaction after. ```python theme={null} from agno.agent import Agent from agno.models.openai import OpenAIChat from supermemory import Supermemory from dotenv import load_dotenv load_dotenv() memory = Supermemory() def get_user_context(user_id: str, query: str) -> str: """Pull user profile and relevant memories.""" result = memory.profile(container_tag=user_id, q=query) static = result.profile.static or [] dynamic = result.profile.dynamic or [] memories = result.search_results.results if result.search_results else [] return f""" User background: {chr(10).join(static) if static else 'No profile yet.'} Recent activity: {chr(10).join(dynamic) if dynamic else 'Nothing recent.'} Related memories: {chr(10).join([m.memory or m.chunk for m in memories[:5]]) if memories else 'None.'} """ def create_agent(user_id: str, task: str) -> Agent: """Create an agent with user context.""" context = get_user_context(user_id, task) return Agent( name="assistant", model=OpenAIChat(id="gpt-4o"), description=f"""You are a helpful assistant. Here's what you know about this user: {context} Use this to personalize your responses.""", markdown=True ) def chat(user_id: str, message: str) -> str: """Run the agent and store the interaction.""" agent = create_agent(user_id, message) response = agent.run(message) # Save for next time memory.add( content=f"User: {message}\nAssistant: {response.content}", container_tag=user_id ) return response.content ``` *** ## 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 theme={null} result = memory.profile( container_tag="user_123", q="cooking help" # Also returns relevant memories ) print(result.profile.static) # ["Vegetarian", "Allergic to nuts"] print(result.profile.dynamic) # ["Learning Italian cuisine", "Meal prepping"] ``` ### Storing memories Save interactions so future sessions have context: ```python theme={null} def store_chat(user_id: str, user_msg: str, agent_response: str): memory.add( content=f"User asked: {user_msg}\nAgent said: {agent_response}", container_tag=user_id, metadata={"type": "conversation"} ) ``` ### Searching memories Look up past interactions: ```python theme={null} results = memory.search.memories( q="pasta recipes we discussed", container_tag="user_123", search_mode="hybrid", 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. ```python theme={null} from agno.agent import Agent from agno.models.openai import OpenAIChat from supermemory import Supermemory from dotenv import load_dotenv load_dotenv() class PersonalAssistant: def __init__(self): self.memory = Supermemory() def get_context(self, user_id: str, query: str) -> dict: """Fetch user profile and relevant history.""" result = self.memory.profile( container_tag=user_id, q=query, threshold=0.5 ) return { "profile": result.profile.static or [], "recent": result.profile.dynamic or [], "history": [m.memory for m in (result.search_results.results or [])[:3]] } def build_description(self, context: dict) -> str: """Turn context into agent description.""" parts = ["You are a helpful personal assistant."] if context["profile"]: parts.append(f"About this user: {', '.join(context['profile'])}") if context["recent"]: parts.append(f"They're currently: {', '.join(context['recent'])}") if context["history"]: parts.append(f"Past conversations: {'; '.join(context['history'])}") parts.append("Reference what you know about them when relevant.") return "\n\n".join(parts) def create_agent(self, context: dict) -> Agent: return Agent( name="assistant", model=OpenAIChat(id="gpt-4o"), description=self.build_description(context), markdown=True ) def chat(self, user_id: str, message: str) -> str: """Handle a message and remember the interaction.""" context = self.get_context(user_id, message) agent = self.create_agent(context) response = agent.run(message) # Store for future sessions self.memory.add( content=f"User: {message}\nAssistant: {response.content}", container_tag=user_id, metadata={"type": "chat"} ) return response.content def teach(self, user_id: str, fact: str): """Store a preference or fact about the user.""" self.memory.add( content=fact, container_tag=user_id, metadata={"type": "preference"} ) if __name__ == "__main__": assistant = PersonalAssistant() # Teach it some preferences assistant.teach("user_1", "Prefers concise answers") assistant.teach("user_1", "Works in software engineering") # Chat response = assistant.chat("user_1", "What's a good way to learn Rust?") print(response) ``` *** ## Using Agno tools with memory Give your agent tools that can search and store memories directly. ```python theme={null} 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.memories( q=query, container_tag=user_id, 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(content=content, container_tag=user_id) 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 theme={null} 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( content=f"User shared an image and asked: {question}\nAnalysis: {response.content}", container_tag=user_id, metadata={"type": "image_analysis", "image": image_path} ) return response.content ``` *** ## Metadata for filtering Tags let you narrow down searches: ```python theme={null} # Store with metadata memory.add( content="User prefers dark mode interfaces", container_tag="user_123", metadata={ "type": "preference", "category": "ui", "source": "onboarding" } ) # Search with filters results = memory.search.memories( q="interface preferences", container_tag="user_123", filters={ "AND": [ {"key": "type", "value": "preference"}, {"key": "category", "value": "ui"} ] } ) ``` *** ## Related docs How automatic profiling works Filtering and search modes Memory for LangChain apps Multi-agent systems with memory # Vercel AI SDK Source: https://supermemory.ai/docs/integrations/ai-sdk Use Supermemory with Vercel AI SDK for seamless memory management 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. Migrating to v2 from 1.4.x? Check the [migration guide](/docs/migration/tools-v2-upgrade). Check out the NPM page for more details ## Installation ```bash theme={null} 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 theme={null} import { generateText } from "ai" import { withSupermemory } from "@supermemory/tools/ai-sdk" import { openai } from "@ai-sdk/openai" const modelWithMemory = withSupermemory(openai("gpt-5"), { containerTag: "user-123", customId: "conversation-456", }) const result = await generateText({ model: modelWithMemory, messages: [{ role: "user", content: "What do you know about me?" }] }) ``` ### Required fields Both `containerTag` and `customId` are required. * **`containerTag`** — *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. * **`customId`** — *which conversation* this turn belongs to. Use it to group messages from the same chat session into a single document (e.g. `"chat-2026-04-25"`, a thread ID, or a UUID per session). **Memory saving is enabled by default** (`addMemory: "always"`). New conversations are persisted automatically. To opt out, set `addMemory: "never"`: ```typescript theme={null} const modelWithMemory = withSupermemory(openai("gpt-5"), { containerTag: "user-123", customId: "conversation-456", addMemory: "never", }) ``` ### Memory Search Modes **Profile Mode (Default)** - Retrieves the user's complete profile: ```typescript theme={null} const model = withSupermemory(openai("gpt-4"), { containerTag: "user-123", customId: "conv-1", mode: "profile" }) ``` **Query Mode** - Searches memories based on the user's message: ```typescript theme={null} const model = withSupermemory(openai("gpt-4"), { containerTag: "user-123", customId: "conv-1", mode: "query" }) ``` **Full Mode** - Combines profile AND query-based search: ```typescript theme={null} const model = withSupermemory(openai("gpt-4"), { containerTag: "user-123", customId: "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 theme={null} import { withSupermemory, type MemoryPromptData } from "@supermemory/tools/ai-sdk" const claudePrompt = (data: MemoryPromptData) => ` ${data.userMemories} ${data.generalSearchMemories} `.trim() const model = withSupermemory(anthropic("claude-3-sonnet"), { containerTag: "user-123", customId: "conv-1", mode: "full", promptTemplate: claudePrompt, }) ``` ### Verbose Logging ```typescript theme={null} const model = withSupermemory(openai("gpt-4"), { containerTag: "user-123", customId: "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 theme={null} const model = withSupermemory(openai("gpt-5"), { containerTag: "user-123", customId: "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 theme={null} const model = withSupermemory(openai("gpt-5"), { containerTag: "user-123", customId: "conv-1", includeToolCalls: true, }) ``` *** ## Memory Tools Add memory capabilities to AI agents with search, add, and fetch operations. ```typescript theme={null} 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 theme={null} 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 theme={null} 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 theme={null} 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", { projectId: "personal" }), createEvent: yourCustomTool, } }) ``` ### Tool Results ```typescript theme={null} // searchMemories result { success: true, results: [...], count: 5 } // addMemory result { success: true, memory: { id: "mem_123", ... } } ``` # Cartesia Source: https://supermemory.ai/docs/integrations/cartesia Integrate Supermemory with Cartesia for conversational memory in voice AI agents 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 theme={null} pip install supermemory-cartesia ``` Set up your API key as an environment variable: ```bash theme={null} 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 theme={null} 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 theme={null} 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 theme={null} 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 theme={null} 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 theme={null} 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 theme={null} { "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 theme={null} 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 theme={null} 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 theme={null} 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 theme={null} 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 theme={null} 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) # Claude Code Source: https://supermemory.ai/docs/integrations/claude-code Claude Code Supermemory Plugin — persistent memory across coding sessions
Claude Code + Supermemory
[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. **Prefer to keep everything on your machine?** This plugin works with [self-hosted Supermemory](/docs/self-hosting/overview) — run `npx supermemory local`, then set `baseUrl` in project config (or point your install at your local API) and use the API key printed on first boot. ## Install the Plugin > **Requires Node.js 18+** on your PATH — the memory hooks run as Node scripts. ```bash theme={null} # Add the plugin marketplace /plugin marketplace add supermemoryai/claude-supermemory # Install the plugin /plugin install supermemory ``` **Migrating from the old `claude-supermemory` plugin name?** It was renamed to `supermemory` and will not update in place: ```bash theme={null} /plugin marketplace update supermemory-plugins /plugin install supermemory@supermemory-plugins # Only if the old plugin is still installed: /plugin uninstall claude-supermemory@supermemory-plugins ``` ## Authenticate Create a Supermemory API key from the [API Keys](https://console.supermemory.ai/keys) page in the console, then add it to your shell profile: ```bash theme={null} echo 'export SUPERMEMORY_CC_API_KEY="sm_..."' >> ~/.zshrc source ~/.zshrc ``` ```bash theme={null} echo 'export SUPERMEMORY_CC_API_KEY="sm_..."' >> ~/.bashrc source ~/.bashrc ``` ```powershell theme={null} [System.Environment]::SetEnvironmentVariable("SUPERMEMORY_CC_API_KEY", "sm_...", "User") ``` Restart your terminal after running this. ## How It Works Once installed, the plugin runs automatically: * **Reasoned recall** — Before each turn, Claude decides whether recalling memory would help the current message, and only searches when it is worth it. * **Auto-capture** — Conversations and important tool usage are saved for later sessions. * **Team memory** — Project knowledge is shared separately from personal memories. * **Explicit skills** — Ask Claude to search or save memories when you need control. ## Commands | Command | Description | | ----------------------------- | ----------------------------------------------------- | | `/supermemory:index` | Index codebase architecture and patterns | | `/supermemory:project-config` | Configure project-level settings | | `/supermemory:logout` | Clear saved credentials | | `/supermemory:session` | Show a clickable URL for the current session document | | `/supermemory:status` | Show authentication status | ## Configuration ### Environment Variables ```bash theme={null} SUPERMEMORY_CC_API_KEY=sm_... # Required SUPERMEMORY_DEBUG=true # Optional: enable debug logging ``` ### Global Settings Create `~/.supermemory-claude/settings.json`: ```json theme={null} { "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 theme={null} { "apiKey": "sm_...", "baseUrl": "https://api.supermemory.ai", "repoContainerTag": "my-team-project", "signalExtraction": true } ``` | Option | Description | | ---------------------- | ----------------------------------------- | | `apiKey` | Project-specific API key | | `baseUrl` | Supermemory API URL (use for self-hosted) | | `personalContainerTag` | Override personal container | | `repoContainerTag` | Override team container tag | ## Next Steps Source code, issues, and detailed README. Memory for your Cursor chats. # Claude Memory Tool Source: https://supermemory.ai/docs/integrations/claude-memory Use Claude's native memory tool with Supermemory as the backend Claude has a native memory tool that allows it to store and retrieve information across conversations. Supermemory provides a backend implementation that maps Claude's memory commands to persistent storage. This integration works with Claude's built-in `memory` tool type, introduced in the Anthropic API. It requires the `context-management` beta flag. ## Installation ```bash theme={null} npm install @supermemory/tools @anthropic-ai/sdk ``` ## Quick Start ```typescript theme={null} import Anthropic from "@anthropic-ai/sdk" import { createClaudeMemoryTool } from "@supermemory/tools/claude-memory" const anthropic = new Anthropic() const memoryTool = createClaudeMemoryTool(process.env.SUPERMEMORY_API_KEY!, { projectId: "my-app", }) async function chatWithMemory(userMessage: string) { // Send message to Claude with memory tool const response = await anthropic.beta.messages.create({ model: "claude-sonnet-4-5", max_tokens: 2048, messages: [{ role: "user", content: userMessage }], tools: [{ type: "memory_20250818", name: "memory" }], betas: ["context-management-2025-06-27"], }) // Handle any memory tool calls const toolResults = [] for (const block of response.content) { if (block.type === "tool_use" && block.name === "memory") { const toolResult = await memoryTool.handleCommandForToolResult( block.input as any, block.id ) toolResults.push(toolResult) } } // Send tool results back to Claude if needed if (toolResults.length > 0) { const finalResponse = await anthropic.beta.messages.create({ model: "claude-sonnet-4-5", max_tokens: 2048, messages: [ { role: "user", content: userMessage }, { role: "assistant", content: response.content }, { role: "user", content: toolResults }, ], tools: [{ type: "memory_20250818", name: "memory" }], betas: ["context-management-2025-06-27"], }) return finalResponse } return response } // Example usage const response = await chatWithMemory( "Remember that I prefer React with TypeScript for my projects" ) console.log(response.content[0]) ``` ## Configuration ```typescript theme={null} import { createClaudeMemoryTool } from "@supermemory/tools/claude-memory" const memoryTool = createClaudeMemoryTool(process.env.SUPERMEMORY_API_KEY!, { // Scope memories to a project or user projectId: "my-app", // Or use container tags for more flexibility containerTags: ["user-123", "project-alpha"], // Custom memory container prefix (default: "claude_memory") memoryContainerTag: "my_memory_prefix", // 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 ``` Paths are normalized for storage: `/memories/preferences` is stored as `--memories--preferences`. ## Commands Reference ### View (Read/List) ```typescript theme={null} // 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 theme={null} { command: "create", path: "/memories/preferences.txt", file_text: "User prefers dark mode\nFavorite language: TypeScript" } ``` ### String Replace ```typescript theme={null} { command: "str_replace", path: "/memories/preferences.txt", old_str: "dark mode", new_str: "light mode" } ``` ### Insert ```typescript theme={null} { command: "insert", path: "/memories/notes.txt", insert_line: 5, insert_text: "New note added here" } ``` ### Delete ```typescript theme={null} { command: "delete", path: "/memories/old-notes.txt" } ``` ### Rename ```typescript theme={null} { command: "rename", path: "/memories/old-name.txt", new_path: "/memories/new-name.txt" } ``` ## Complete Example ```typescript theme={null} import Anthropic from "@anthropic-ai/sdk" import { createClaudeMemoryTool } from "@supermemory/tools/claude-memory" const anthropic = new Anthropic() const memoryTool = createClaudeMemoryTool(process.env.SUPERMEMORY_API_KEY!, { projectId: "assistant", }) async function runConversation() { const messages: Anthropic.MessageParam[] = [] // Helper to chat with memory async function chat(userMessage: string) { messages.push({ role: "user", content: userMessage }) let response = await anthropic.beta.messages.create({ model: "claude-sonnet-4-5", max_tokens: 2048, messages, tools: [{ type: "memory_20250818", name: "memory" }], betas: ["context-management-2025-06-27"], }) // Handle tool calls while (response.stop_reason === "tool_use") { const toolResults = [] for (const block of response.content) { if (block.type === "tool_use" && block.name === "memory") { const result = await memoryTool.handleCommandForToolResult( block.input as any, block.id ) toolResults.push(result) } } messages.push({ role: "assistant", content: response.content }) messages.push({ role: "user", content: toolResults }) response = await anthropic.beta.messages.create({ model: "claude-sonnet-4-5", max_tokens: 2048, messages, tools: [{ type: "memory_20250818", name: "memory" }], betas: ["context-management-2025-06-27"], }) } messages.push({ role: "assistant", content: response.content }) return response } // Have a conversation with persistent memory await chat("My name is Alex and I'm a backend developer") await chat("I prefer Go for systems programming") await chat("What do you remember about me?") } runConversation() ``` ## Environment Variables ```bash theme={null} SUPERMEMORY_API_KEY=your_supermemory_key ANTHROPIC_API_KEY=your_anthropic_key ``` ## Comparison with Other Approaches | Feature | Claude Memory Tool | OpenAI SDK Tools | AI SDK Tools | | ------------------- | ------------------- | ---------------- | ---------------- | | Automatic memory | ✅ Claude decides | ❌ Manual control | ❌ Manual control | | Filesystem metaphor | ✅ Files/directories | ❌ Flat storage | ❌ Flat storage | | Path organization | ✅ Hierarchical | ❌ Tags only | ❌ Tags only | | Integration | Anthropic SDK only | OpenAI SDK only | Vercel AI SDK | ## Next Steps Use with Vercel AI SDK for streamlined development Memory tools for OpenAI function calling # OpenAI Codex Source: https://supermemory.ai/docs/integrations/codex codex-supermemory — persistent memory for OpenAI Codex CLI [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. **Prefer to keep everything on your machine?** This plugin works with [self-hosted Supermemory](/docs/self-hosting/overview) — run `npx supermemory local`, then `export SUPERMEMORY_API_URL="http://localhost:6767"` (or set `baseUrl` in `~/.codex/supermemory.json`) and use the API key printed on first boot. ## Install the Plugin ```bash theme={null} 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): ```bash theme={null} echo 'export SUPERMEMORY_CODEX_API_KEY="sm_..."' >> ~/.zshrc source ~/.zshrc ``` ```bash theme={null} echo 'export SUPERMEMORY_CODEX_API_KEY="sm_..."' >> ~/.bashrc source ~/.bashrc ``` ```powershell theme={null} [System.Environment]::SetEnvironmentVariable("SUPERMEMORY_CODEX_API_KEY", "sm_...", "User") ``` Restart your terminal after running this. ## How It Works Once installed, the plugin runs on every Codex session: | Hook | Event | What it does | | -------- | ------------------ | ------------------------------------------------------------------------------------------------------------- | | `recall` | `UserPromptSubmit` | Captures new turns (every N prompts), searches Supermemory, injects memories + profile as `additionalContext` | | `flush` | `Stop` | Captures any remaining turns at session end so nothing is lost | * **Incremental capture** — Memories are saved every N turns (default: 3) so mid-session context is available for later prompts in the same session. * **Privacy** — Content wrapped in `...` is redacted before storage. ### Memory Scopes | Tag | Derived from | Description | | ------- | -------------------------------- | -------------------------------------------------- | | User | `git config user.email` (hashed) | Preferences and workflows across projects | | Project | Git common directory (hashed) | Repo-scoped knowledge (worktrees share by default) | Override tags in `~/.codex/supermemory.json` if needed: ```json theme={null} { "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 theme={null} npx codex-supermemory status ``` ## Uninstall ```bash theme={null} 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 theme={null} { "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 theme={null} export SUPERMEMORY_DEBUG=true tail -f ~/.codex-supermemory.log ``` ## Next Steps Source code, issues, and detailed README. Memory for your Cursor chats. # Convex Source: https://supermemory.ai/docs/integrations/convex Add persistent memory to Convex apps with Supermemory 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 theme={null} npm install supermemory convex ``` For the AI chat example, also install the AI SDK packages: ```bash theme={null} npm install @supermemory/tools @ai-sdk/openai ai ``` Set up your environment variable in Convex: ```bash theme={null} npx convex env set SUPERMEMORY_API_KEY your-supermemory-api-key ``` Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai). ## Basic integration Create simple helper functions for each Supermemory operation: ```typescript theme={null} // 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 and relevant memories export const getProfile = action({ args: { userId: v.string(), query: v.optional(v.string()) }, handler: async (ctx, { userId, query }) => { return await memory.profile({ containerTag: userId, q: query, }); }, }); // Add a memory export const addMemory = action({ args: { userId: v.string(), content: v.string() }, handler: async (ctx, { userId, content }) => { return await memory.add({ content, containerTag: userId, }); }, }); // 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({ q: query, containerTag: userId, 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 theme={null} // 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"), { containerTag: userId, customId: `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 theme={null} // 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 theme={null} // 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({ content, containerTag: userId }); // 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 How automatic profiling works Filtering and search modes Memory middleware for Next.js Memory for LangChain apps # CrewAI Source: https://supermemory.ai/docs/integrations/crewai Add persistent memory to CrewAI agents with Supermemory 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 theme={null} pip install crewai supermemory python-dotenv ``` Configure your environment: ```bash theme={null} # .env SUPERMEMORY_API_KEY=your-supermemory-api-key OPENAI_API_KEY=your-openai-api-key ``` Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai). ## Basic Integration Initialize Supermemory and inject user context into your agent's backstory: ```python theme={null} 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.""" result = memory.profile(container_tag=user_id, q=query) static = result.profile.static or [] dynamic = result.profile.dynamic or [] memories = result.search_results.results if result.search_results else [] 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 theme={null} result = memory.profile( container_tag="user_abc", q="project planning" # Optional: also returns relevant memories ) print(result.profile.static) # ["Prefers Agile methodology", "Senior engineer"] print(result.profile.dynamic) # ["Working on Q2 roadmap", "Focused on API design"] ``` ### Storing memories Save crew outputs so future runs can reference them: ```python theme={null} def store_crew_result(user_id: str, task_description: str, result: str): """Save crew output as a memory.""" memory.add( content=f"Task: {task_description}\nResult: {result}", container_tag=user_id, metadata={"type": "crew_execution"} ) ``` ### Searching memories Pull up past interactions before running a crew: ```python theme={null} results = memory.search.memories( q="previous project recommendations", container_tag="user_abc", search_mode="hybrid", 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. ```python theme={null} import os from crewai import Agent, Task, Crew, Process from crewai_tools import SerperDevTool from supermemory import Supermemory from dotenv import load_dotenv load_dotenv() class ResearchCrew: def __init__(self): self.memory = Supermemory() self.search_tool = SerperDevTool() def get_user_context(self, user_id: str, topic: str) -> dict: """Retrieve user profile and related research history.""" result = self.memory.profile( container_tag=user_id, q=topic, threshold=0.5 ) return { "expertise": result.profile.static or [], "focus": result.profile.dynamic or [], "history": [m.memory for m in (result.search_results.results or [])[:3]] } def create_researcher(self, context: dict) -> Agent: """Build a researcher agent with user context.""" expertise_note = "" if context["expertise"]: expertise_note = f"The user has this background: {', '.join(context['expertise'])}. Adjust technical depth accordingly." history_note = "" if context["history"]: history_note = f"Previous research on related topics: {'; '.join(context['history'])}" return Agent( role="Research Analyst", goal="Conduct research tailored to the user's expertise level", backstory=f"""You research topics and synthesize findings into clear summaries. {expertise_note} {history_note}""", tools=[self.search_tool], verbose=True ) def create_writer(self, context: dict) -> Agent: """Build a writer agent that matches user preferences.""" style_note = "Write in a clear, technical style." for fact in context.get("expertise", []): if "non-technical" in fact.lower(): style_note = "Write in plain language, avoiding jargon." break return Agent( role="Content Writer", goal="Transform research into readable content", backstory=f"""You write clear, engaging content. {style_note}""", verbose=True ) def research(self, user_id: str, topic: str) -> str: """Run the research crew and store results.""" context = self.get_user_context(user_id, topic) researcher = self.create_researcher(context) writer = self.create_writer(context) research_task = Task( description=f"Research the following topic: {topic}", expected_output="Detailed findings with sources", agent=researcher ) writing_task = Task( description="Write a summary based on the research findings", expected_output="A clear, structured summary", agent=writer ) crew = Crew( agents=[researcher, writer], tasks=[research_task, writing_task], process=Process.sequential, verbose=True ) result = crew.kickoff() # Store for future sessions self.memory.add( content=f"Research on '{topic}': {str(result)[:500]}", container_tag=user_id, metadata={"type": "research", "topic": topic} ) return str(result) if __name__ == "__main__": crew = ResearchCrew() # Teach preferences crew.memory.add( content="User prefers concise summaries with bullet points", container_tag="researcher_1" ) # Run research result = crew.research("researcher_1", "latest developments in AI agents") print(result) ``` *** ## More patterns ### Crews with multiple users Sometimes you need context from several users at once: ```python theme={null} def create_collaborative_context(user_ids: list[str], topic: str) -> str: """Aggregate context from multiple users.""" combined = [] for user_id in user_ids: result = memory.profile(container_tag=user_id, q=topic) if result.profile.static: combined.append(f"{user_id}: {', '.join(result.profile.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 theme={null} def store_if_successful(user_id: str, task: str, result: str, success: bool): """Only store successful task completions.""" if not success: return memory.add( content=f"Completed: {task}\nOutcome: {result}", container_tag=user_id, metadata={"type": "success", "task": task} ) ``` ### Using metadata to organize memories Metadata lets you filter memories by project, agent, or whatever else makes sense: ```python theme={null} # Store with metadata memory.add( content="Research findings on distributed systems", container_tag="user_123", metadata={ "project": "infrastructure-review", "agents": ["researcher", "writer"], "confidence": "high" } ) # Search with filters results = memory.search.memories( q="distributed systems", container_tag="user_123", filters={ "AND": [ {"key": "project", "value": "infrastructure-review"}, {"key": "confidence", "value": "high"} ] } ) ``` *** ## Related docs How automatic profiling works Filtering and search modes Memory for LangChain apps Memory middleware for Next.js # Cursor Source: https://supermemory.ai/docs/integrations/cursor cursor-supermemory: persistent memory across your Cursor chats Your agent remembers the decisions, bugs, and conventions from earlier chats instead of starting cold every time. ## Install Requires [Node.js](https://nodejs.org) on your `PATH`. Installing Cursor does not put one there. Run this in Cursor: ``` /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): ```bash theme={null} echo 'export SUPERMEMORY_API_KEY="sm_..."' >> ~/.zshrc source ~/.zshrc ``` ```bash theme={null} echo 'export SUPERMEMORY_API_KEY="sm_..."' >> ~/.bashrc source ~/.bashrc ``` ```powershell theme={null} [System.Environment]::SetEnvironmentVariable("SUPERMEMORY_API_KEY", "sm_...", "User") ``` Restart your terminal after running this. Restart Cursor after installing the plugin or changing credentials. Check the connection any time with `/supermemory-status`. The slash commands just run the plugin's CLI for you. To drive it yourself: ```bash theme={null} 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 ` from the installed plugin directory. Credentials are stored in `~/.supermemory-cursor/credentials.json`. ## How It Works | Layer | What it does | | ------------------- | ------------------------------------------------------------------------------------------------------------- | | Session profile | Loads your persistent profile when a Cursor conversation starts | | Automatic recall | Searches on substantive prompts, deduplicates results, and injects them after the first supported tool result | | Incremental capture | Saves each completed turn, and retries unsaved transcript deltas at session end | | MCP tools | Explicit memory control from any Cursor AI session | | Context gatherer | Fans out targeted searches before substantial work | | Always-on rule | Makes the agent recall relevant history proactively | ### Skills and Commands | Name | Type | Description | | ------------------------------ | ------- | ---------------------------------------------------- | | `memory-init` | Skill | Explore the codebase and initialize project memory | | `memory-save` | Skill | Save an insight, decision, or solution worth keeping | | `memory-search` | Skill | Search memory for past work, bugs, and decisions | | `supermemory-context-gatherer` | Agent | Gather deep background before substantial work | | `supermemory-setup` | Command | Connect Supermemory to Cursor | | `supermemory-status` | Command | Check authentication and live connectivity | | `supermemory-config` | Command | Create or edit the project config file | | `supermemory-logout` | Command | Disconnect Supermemory from Cursor | ## MCP Tools | Tool | Description | | ------------------------ | ------------------------------------------------------------------- | | `supermemory_get_config` | Show current config, resolved container tags, and config file paths | | `supermemory_set_config` | Update config at project or global scope | | `supermemory_containers` | Show what `user` and `project` container tags resolve to | | `supermemory_search` | Search memories by query | | `supermemory_add` | Save new information to memory | | `supermemory_list` | List stored memories | | `supermemory_forget` | Delete a memory by id or content | | `supermemory_profile` | Get your user profile summary | Every tool that takes a `container` argument accepts: * `"user"` (default): personal memories for the current repository * `"project"`: project knowledge for the current repository * `"both"`: both scopes plus compatible legacy memories * any custom string: used as a raw container tag `user` and `project` write to the same repository container. The `sm_scope` metadata field is what keeps personal and session memories separate from explicit project knowledge when an agent asks for one scope. ## Container Tags Cursor shares one repository tag with the [Claude Code](/docs/integrations/claude-code), [OpenAI Codex](/docs/integrations/codex), and [OpenCode](/docs/integrations/opencode) plugins, so agents working on the same repo read and write the same memory: ```text theme={null} repo___ ``` The project ID is a stable hash of the normalized Git remote. Repositories without a remote fall back to their resolved local path. Two repos with the same directory name never collide, and different agents on the same repository share memory. The plugin still reads the former `cursor_user_*` and `cursor_project_*` tags, along with legacy tags from the other agents. New writes only use the unified repository tag. Set `repoContainerTag` only when you need an explicit shared override. ## Configuration **Prefer to keep everything on your machine?** This plugin works with [self-hosted Supermemory](/docs/self-hosting/overview): run `npx supermemory local`, then set `SUPERMEMORY_API_URL="http://localhost:6767"` (or `baseUrl` in your config file) and use the API key printed on first boot. ### Environment variables | Variable | Description | | ------------------------- | ---------------------------------------------------- | | `SUPERMEMORY_API_KEY` | API key (overrides all other sources) | | `SUPERMEMORY_API_URL` | Override the Supermemory API base URL | | `SUPERMEMORY_REPO_TAG` | Override the unified repository container tag | | `SUPERMEMORY_USER_TAG` | Legacy Cursor personal container to continue reading | | `SUPERMEMORY_PROJECT_TAG` | Legacy Cursor project container to continue reading | | `CURSOR_USER_EMAIL` | Used only to find legacy Cursor personal memories | ### Global config `~/.config/cursor/supermemory.json` holds user-wide defaults and applies to all projects. ```json theme={null} { "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 theme={null} { "apiKey": "sm_...", "repoContainerTag": "repo_my_project__0123456789abcdef", "similarityThreshold": 0.55, "maxMemories": 10, "injectProfile": true } ``` | Option | Default | Description | | --------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------- | | `apiKey` | — | Project-specific API key | | `baseUrl` | Supermemory API | Override the Supermemory API base URL | | `repoContainerTag` | derived from normalized Git remote or project path | Override the unified repository container | | `userContainerTag` | — | Legacy Cursor personal container to continue reading | | `projectContainerTag` | — | Legacy Cursor project container to continue reading | | `similarityThreshold` | `0.55` | Minimum similarity for prompt recall. Values below `0.55` are floored. | | `maxMemories` | `10` | Max profile facts injected at session start | | `injectProfile` | `true` | Whether to inject the user profile at session start | | `signalExtraction` | `false` | Capture only turns containing durable-signal keywords | | `signalKeywords` | `remember`, `architecture`, `decision`, `bug`, `fix` | Keywords that trigger signal-based capture | | `signalTurnsBefore` | `3` | Number of nearby turns retained around a signal | You can also set these from the agent with `supermemory_set_config`, or edit the file by hand. ## Log Out Run `/supermemory-logout` in Cursor. This removes the stored credentials. Your memories in Supermemory are preserved. ## Next Steps Source code, issues, and detailed README. # Grok Bot Source: https://supermemory.ai/docs/integrations/grok-bot Supermemory for Grok Bot: persistent memory for your Grok Bots 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 Source code, issues, and detailed README. The same plugin, installed through Cursor. # Hermes Source: https://supermemory.ai/docs/integrations/hermes Hermes Supermemory Plugin — semantic memory, profiles, and search across Telegram, Discord, Slack, CLI, and more [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 includes built-in **`MEMORY.md`** and **`USER.md`**. 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 theme={null} pip install supermemory ``` ```bash theme={null} hermes memory setup ``` Select **supermemory** when prompted and paste your API key. Or set the provider and key manually: ```bash theme={null} 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 theme={null} 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 theme={null} { "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 theme={null} { "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 Full config table, env vars, and multi-container details. Multi-platform memory for Telegram, WhatsApp, Discord, and more. Questions about the API or product? [Discord](https://supermemory.link/discord) · [support@supermemory.com](mailto:support@supermemory.com) · [Developer docs](/docs/overview/what-is-supermemory) # LangChain Source: https://supermemory.ai/docs/integrations/langchain Build AI agents with persistent memory using LangChain and Supermemory 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 theme={null} pip install langchain langchain-openai supermemory python-dotenv ``` Configure your environment: ```bash theme={null} # .env SUPERMEMORY_API_KEY=your-supermemory-api-key OPENAI_API_KEY=your-openai-api-key ``` Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai). ## Basic Integration Initialize both clients and set up a simple chat function with memory: ```python theme={null} import os from langchain_openai import ChatOpenAI from langchain_core.messages import SystemMessage, HumanMessage from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder from supermemory import Supermemory from dotenv import load_dotenv load_dotenv() # Initialize clients llm = ChatOpenAI(model="gpt-4o") memory = Supermemory() def chat(user_id: str, message: str) -> str: # 1. Get user profile for context profile_result = memory.profile(container_tag=user_id, q=message) # 2. Build context from profile static_facts = profile_result.profile.static or [] dynamic_context = profile_result.profile.dynamic or [] search_results = profile_result.search_results.results if profile_result.search_results else [] context = f""" User Background: {chr(10).join(static_facts) if static_facts else 'No profile yet.'} Recent Context: {chr(10).join(dynamic_context) if dynamic_context else 'No recent activity.'} Relevant Memories: {chr(10).join([r.memory or r.chunk for r in search_results]) if search_results else 'None found.'} """ # 3. Generate response prompt = ChatPromptTemplate.from_messages([ SystemMessage(content=f"You are a helpful assistant. Use this context to personalize your response:\n{context}"), HumanMessage(content=message) ]) chain = prompt | llm response = chain.invoke({}) # 4. Store the interaction as memory memory.add( content=f"User: {message}\nAssistant: {response.content}", container_tag=user_id ) return response.content ``` *** ## 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 theme={null} # Fetch profile with optional search result = memory.profile( container_tag="user_123", q="optional search query" # Also returns relevant memories ) print(result.profile.static) # ["User is a Python developer", "Prefers dark mode"] print(result.profile.dynamic) # ["Currently working on API integration", "Debugging auth issues"] ``` ### Memory Storage Content you add is automatically processed into searchable memories: ```python theme={null} # Store a conversation memory.add( content="User asked about async Python patterns. Explained asyncio basics.", container_tag="user_123", metadata={"topic": "python", "type": "conversation"} ) # Store a document memory.add( content="https://docs.python.org/3/library/asyncio.html", container_tag="user_123" ) ``` ### Memory Search Search returns both extracted memories and document chunks: ```python theme={null} results = memory.search.memories( q="async programming", container_tag="user_123", 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: ````python theme={null} import os from typing import Optional from langchain_openai import ChatOpenAI from langchain_core.messages import SystemMessage, HumanMessage, AIMessage from langchain_core.prompts import ChatPromptTemplate from supermemory import Supermemory from dotenv import load_dotenv load_dotenv() class CodeReviewAssistant: def __init__(self): self.llm = ChatOpenAI(model="gpt-4o", temperature=0.3) self.memory = Supermemory() def get_context(self, user_id: str, code: str) -> str: """Retrieve user profile and relevant past reviews.""" # Get profile with search for similar code patterns result = self.memory.profile( container_tag=user_id, q=code[:500], # Use code snippet for semantic search threshold=0.6 ) static = result.profile.static or [] dynamic = result.profile.dynamic or [] memories = result.search_results.results if result.search_results else [] return f""" ## Developer Profile {chr(10).join(f"- {fact}" for fact in static) if static else "New developer, no profile yet."} ## Current Focus {chr(10).join(f"- {ctx}" for ctx in dynamic) if dynamic else "No recent context."} ## Relevant Past Reviews {chr(10).join(f"- {m.memory}" for m in memories[:3]) if memories else "No similar reviews found."} """ def review(self, user_id: str, code: str, language: Optional[str] = None) -> str: """Review code with personalized feedback.""" context = self.get_context(user_id, code) prompt = ChatPromptTemplate.from_messages([ SystemMessage(content=f"""You are a code review assistant. Provide constructive feedback tailored to the developer's experience level and preferences. {context} Guidelines: - Reference past feedback when relevant patterns appear - Adapt explanation depth to the developer's expertise - Focus on issues that matter most to this developer"""), HumanMessage(content=f"Review this {language or 'code'}:\n\n```\n{code}\n```") ]) chain = prompt | self.llm response = chain.invoke({}) # Store the review for future context self.memory.add( content=f"Code review feedback: {response.content[:500]}", container_tag=user_id, metadata={"type": "code_review", "language": language} ) return response.content def learn_preference(self, user_id: str, preference: str): """Store a coding preference or style guideline.""" self.memory.add( content=f"Developer preference: {preference}", container_tag=user_id, metadata={"type": "preference"} ) # 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: ```python theme={null} from langchain_core.messages import BaseMessage class ConversationalAgent: def __init__(self, user_id: str): self.user_id = user_id self.llm = ChatOpenAI(model="gpt-4o") self.memory = Supermemory() self.messages: list[BaseMessage] = [] def _build_system_prompt(self, query: str) -> str: """Build system prompt with user context.""" result = self.memory.profile( container_tag=self.user_id, q=query, threshold=0.5 ) profile = result.profile memories = result.search_results.results if result.search_results else [] return f"""You are a helpful assistant with memory of past conversations. About this user: {chr(10).join(profile.static) if profile.static else 'No profile yet.'} Current context: {chr(10).join(profile.dynamic) if profile.dynamic else 'No recent context.'} Relevant memories: {chr(10).join(m.memory or m.chunk for m in memories[:5]) if memories else 'None.'} Use this context to provide personalized, contextual responses.""" def chat(self, message: str) -> str: """Process a message and return response.""" # Add user message to conversation self.messages.append(HumanMessage(content=message)) # Build prompt with memory context system = SystemMessage(content=self._build_system_prompt(message)) # Generate response response = self.llm.invoke([system] + self.messages) self.messages.append(response) # Store interaction for long-term memory self.memory.add( content=f"User: {message}\nAssistant: {response.content}", container_tag=self.user_id ) return response.content def clear_session(self): """Clear conversation but keep long-term memory.""" self.messages = [] ``` ### Metadata Filtering Use metadata to organize and filter memories: ```python theme={null} # Store with metadata memory.add( content="Discussed React hooks and state management", container_tag="user_123", metadata={ "topic": "react", "type": "discussion", "project": "frontend-redesign" } ) # Search with filters results = memory.search.memories( q="state management", container_tag="user_123", filters={ "AND": [ {"key": "topic", "value": "react"}, {"key": "project", "value": "frontend-redesign"} ] } ) ``` ### Batch Memory Operations Efficiently store multiple memories: ```python theme={null} # 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( content=note, container_tag="team_standup", metadata={"date": "2024-01-15", "type": "decision"} ) ``` *** ## Next Steps Deep dive into automatic user profiling Advanced search patterns and filtering Native OpenAI integration with memory tools Memory middleware for Next.js apps # LangGraph Source: https://supermemory.ai/docs/integrations/langgraph Add persistent memory to LangGraph agents with Supermemory 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 theme={null} pip install langgraph langchain-openai supermemory python-dotenv ``` Configure your environment: ```bash theme={null} # .env SUPERMEMORY_API_KEY=your-supermemory-api-key OPENAI_API_KEY=your-openai-api-key ``` Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai). ## Basic integration A minimal agent that fetches user context before responding and stores the conversation after: ```python theme={null} from typing import Annotated, TypedDict from langgraph.graph import StateGraph, START, END from langgraph.graph.message import add_messages from langchain_openai import ChatOpenAI from langchain_core.messages import SystemMessage, HumanMessage from supermemory import Supermemory from dotenv import load_dotenv load_dotenv() llm = ChatOpenAI(model="gpt-4o") memory = Supermemory() class State(TypedDict): messages: Annotated[list, add_messages] user_id: str def agent(state: State): user_id = state["user_id"] messages = state["messages"] user_query = messages[-1].content # Fetch user profile with relevant memories profile_result = memory.profile(container_tag=user_id, q=user_query) # Build context from profile static_facts = profile_result.profile.static or [] dynamic_context = profile_result.profile.dynamic or [] search_results = profile_result.search_results.results if profile_result.search_results else [] context = f""" User Background: {chr(10).join(static_facts) if static_facts else 'No profile yet.'} Recent Context: {chr(10).join(dynamic_context) if dynamic_context else 'No recent activity.'} Relevant Memories: {chr(10).join([r.memory or r.chunk for r in search_results]) if search_results else 'None found.'} """ system = SystemMessage(content=f"You are a helpful assistant.\n\n{context}") response = llm.invoke([system] + messages) # Store the interaction memory.add( content=f"User: {user_query}\nAssistant: {response.content}", container_tag=user_id ) return {"messages": [response]} # Build the graph graph = StateGraph(State) graph.add_node("agent", agent) graph.add_edge(START, "agent") graph.add_edge("agent", END) app = graph.compile() # Run it result = app.invoke({ "messages": [HumanMessage(content="Hi! I'm working on a Python project.")], "user_id": "user_123" }) print(result["messages"][-1].content) ``` *** ## 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 theme={null} result = memory.profile( container_tag="user_123", q="optional search query" # Also returns relevant memories ) print(result.profile.static) # ["User is a Python developer", "Prefers functional style"] print(result.profile.dynamic) # ["Working on async patterns", "Debugging rate limiting"] ``` ### Memory storage Content you add gets processed into searchable memories: ```python theme={null} # Store a conversation memory.add( content="User asked about graph traversal. Explained BFS vs DFS.", container_tag="user_123", metadata={"topic": "algorithms", "type": "conversation"} ) # Store a document memory.add( content="https://langchain-ai.github.io/langgraph/", container_tag="user_123" ) ``` ### Memory search Search returns both extracted memories and document chunks: ```python theme={null} results = memory.search.memories( q="graph algorithms", container_tag="user_123", search_mode="hybrid", 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: ```python theme={null} from typing import Annotated, TypedDict, Optional from langgraph.graph import StateGraph, START, END from langgraph.graph.message import add_messages from langgraph.checkpoint.memory import MemorySaver from langchain_openai import ChatOpenAI from langchain_core.messages import SystemMessage, HumanMessage from supermemory import Supermemory from dotenv import load_dotenv load_dotenv() class SupportAgent: def __init__(self): self.llm = ChatOpenAI(model="gpt-4o", temperature=0.3) self.memory = Supermemory() self.app = self._build_graph() def _build_graph(self): class State(TypedDict): messages: Annotated[list, add_messages] user_id: str context: str category: Optional[str] def retrieve_context(state: State): """Fetch user profile and relevant past tickets.""" user_id = state["user_id"] query = state["messages"][-1].content result = self.memory.profile( container_tag=user_id, q=query, threshold=0.5 ) static = result.profile.static or [] dynamic = result.profile.dynamic or [] memories = result.search_results.results if result.search_results else [] context = f""" ## User Profile {chr(10).join(f"- {fact}" for fact in static) if static else "New user, no history."} ## Current Context {chr(10).join(f"- {ctx}" for ctx in dynamic) if dynamic else "No recent activity."} ## Related Past Tickets {chr(10).join(f"- {m.memory}" for m in memories[:3]) if memories else "No similar issues found."} """ return {"context": context} def categorize(state: State): """Determine ticket category for routing.""" query = state["messages"][-1].content.lower() if any(word in query for word in ["billing", "payment", "charge", "invoice"]): return {"category": "billing"} elif any(word in query for word in ["bug", "error", "broken", "crash"]): return {"category": "technical"} else: return {"category": "general"} def respond(state: State): """Generate a response using context.""" category = state.get("category", "general") context = state.get("context", "") system_prompt = f"""You are a support agent. Category: {category} {context} Guidelines: - Match explanation depth to the user's technical level - Reference past interactions when relevant - Be direct and helpful""" system = SystemMessage(content=system_prompt) response = self.llm.invoke([system] + state["messages"]) return {"messages": [response]} def store_interaction(state: State): """Save the ticket for future context.""" user_msg = state["messages"][-2].content ai_msg = state["messages"][-1].content category = state.get("category", "general") self.memory.add( content=f"Support ticket ({category}): {user_msg}\nResolution: {ai_msg[:300]}", container_tag=state["user_id"], metadata={"type": "support_ticket", "category": category} ) return {} # Build the graph graph = StateGraph(State) graph.add_node("retrieve", retrieve_context) graph.add_node("categorize", categorize) graph.add_node("respond", respond) graph.add_node("store", store_interaction) graph.add_edge(START, "retrieve") graph.add_edge("retrieve", "categorize") graph.add_edge("categorize", "respond") graph.add_edge("respond", "store") graph.add_edge("store", END) checkpointer = MemorySaver() return graph.compile(checkpointer=checkpointer) def handle(self, user_id: str, message: str, thread_id: str) -> str: """Process a support request.""" config = {"configurable": {"thread_id": thread_id}} result = self.app.invoke( {"messages": [HumanMessage(content=message)], "user_id": user_id}, config=config ) return result["messages"][-1].content # Usage if __name__ == "__main__": agent = SupportAgent() # First interaction response = agent.handle( user_id="customer_alice", message="The API is returning 429 errors when I make requests", thread_id="ticket_001" ) print(response) # Follow-up (agent remembers context) response = agent.handle( user_id="customer_alice", message="I'm only making 10 requests per minute though", thread_id="ticket_001" ) print(response) ``` *** ## Advanced patterns ### Conditional memory storage Not everything is worth remembering. Use conditional edges to filter: ```python theme={null} 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 theme={null} 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 theme={null} # Store with metadata memory.add( content="User prefers detailed error messages with stack traces", container_tag="user_123", metadata={ "type": "preference", "project": "api-v2", "priority": "high" } ) # Search with filters results = memory.search.memories( q="error handling preferences", container_tag="user_123", filters={ "AND": [ {"key": "type", "value": "preference"}, {"key": "project", "value": "api-v2"} ] } ) ``` ### Combining session and long-term memory LangGraph's checkpointer handles within-session state. Supermemory handles cross-session memory. Use both: ```python theme={null} from langgraph.checkpoint.memory import MemorySaver # Session memory (cleared when thread ends) checkpointer = MemorySaver() app = graph.compile(checkpointer=checkpointer) # Long-term memory (persists across sessions) # Handled by Supermemory in your nodes ``` *** ## Next steps Deep dive into automatic user profiling Advanced search patterns and filtering Native OpenAI integration with memory tools Memory middleware for Next.js apps # Mastra Source: https://supermemory.ai/docs/integrations/mastra Add persistent memory to Mastra AI agents with Supermemory processors 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. Migrating to v2 from 1.4.x? Check the [migration guide](/docs/migration/tools-v2-upgrade). Check out the NPM page for more details ## Installation ```bash theme={null} npm install @supermemory/tools @mastra/core ``` ## Quick Start Wrap your agent config with `withSupermemory` to add memory capabilities: ```typescript theme={null} 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.", }, { containerTag: "user-123", // Required: scopes memories to this user customId: "conv-456", // Required: groups messages for contextual memory mode: "full", } )) const response = await agent.generate("What do you know about me?") ``` **Memory saving is enabled by default.** Conversations are automatically saved to Supermemory. To disable saving: ```typescript theme={null} const agent = new Agent(withSupermemory( { id: "my-assistant", model: openai("gpt-4o"), ... }, { containerTag: "user-123", customId: "conv-456", addMemory: "never", // Disable automatic conversation saving } )) ``` *** ## 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 theme={null} 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 | | ---------------- | -------------------------------- | ---------------------------- | ------------------------------------------------------------ | | `containerTag` | `string` | **Required** | User/container tag for scoping memories | | `customId` | `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 theme={null} const agent = new Agent(withSupermemory(config, { containerTag: "user-123", customId: "conv-456", mode: "profile", })) ``` **Query Mode** - Searches memories based on the user's message: ```typescript theme={null} const agent = new Agent(withSupermemory(config, { containerTag: "user-123", customId: "conv-456", mode: "query", })) ``` **Full Mode** - Combines profile AND query-based search for maximum context: ````typescript theme={null} const agent = new Agent(withSupermemory(config, { containerTag: "user-123", customId: "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 `customId`: ```typescript const agent = new Agent(withSupermemory( { id: "my-assistant", model: openai("gpt-4o"), instructions: "..." }, { containerTag: "user-123", customId: "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 theme={null} const agent = new Agent(withSupermemory( { id: "my-assistant", model: openai("gpt-4o"), instructions: "..." }, { containerTag: "user-123", customId: "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 theme={null} import { Agent } from "@mastra/core/agent" import { withSupermemory } from "@supermemory/tools/mastra" import type { MemoryPromptData } from "@supermemory/tools/mastra" const claudePrompt = (data: MemoryPromptData) => ` ${data.userMemories} ${data.generalSearchMemories} `.trim() const agent = new Agent(withSupermemory( { id: "my-assistant", model: openai("gpt-4o"), instructions: "..." }, { containerTag: "user-123", customId: "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 theme={null} 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({ containerTag: "user-123", customId: "conv-456", mode: "full", addMemory: "never", verbose: true, }), ], }) ``` ### Output Processor Only Save conversations without memory injection: ```typescript theme={null} 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({ containerTag: "user-123", customId: "conv-456", }), ], }) ``` ### Both Processors Use the factory function for shared configuration: ```typescript theme={null} import { Agent } from "@mastra/core/agent" import { createSupermemoryProcessors } from "@supermemory/tools/mastra" import { openai } from "@ai-sdk/openai" const { input, output } = createSupermemoryProcessors({ containerTag: "user-123", customId: "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 `customId`: ```typescript theme={null} 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: "..." }, { containerTag: "user-123", customId: "fallback-conv", // Used only when RequestContext doesn't provide a threadId mode: "full", } )) // Per-request threadId takes precedence over customId const ctx = new RequestContext() ctx.set(MASTRA_THREAD_ID_KEY, "user-456-session-789") await agent.generate("Hello!", { requestContext: ctx }) // This conversation is stored under "user-456-session-789", not "fallback-conv" ``` **Server-side usage**: Always use `RequestContext` to pass unique conversation IDs per request. Using a fixed `customId` for all requests will merge conversations from different users. *** ## Verbose Logging Enable detailed logging for debugging: ```typescript theme={null} const agent = new Agent(withSupermemory( { id: "my-assistant", model: openai("gpt-4o"), instructions: "..." }, { containerTag: "user-123", customId: "conv-456", verbose: true, } )) // Console output: // [supermemory] Starting memory search { containerTag: "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 theme={null} // 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], }, { containerTag: "user-123", customId: "conv-456", } )) ``` *** ## API Reference ### `withSupermemory` Enhances a Mastra agent config with memory capabilities. ```typescript theme={null} function withSupermemory( config: T, options: SupermemoryMastraOptions ): T ``` **Parameters:** * `config` - The Mastra agent configuration object * `options` - Configuration options (includes required `containerTag` and `customId`) **Returns:** Enhanced config with Supermemory processors injected ### `createSupermemoryProcessor` Creates an input processor for memory injection. ```typescript theme={null} function createSupermemoryProcessor( options: SupermemoryMastraOptions ): SupermemoryInputProcessor ``` ### `createSupermemoryOutputProcessor` Creates an output processor for conversation saving. ```typescript theme={null} function createSupermemoryOutputProcessor( options: SupermemoryMastraOptions ): SupermemoryOutputProcessor ``` ### `createSupermemoryProcessors` Creates both processors with shared configuration. ```typescript theme={null} function createSupermemoryProcessors( options: SupermemoryMastraOptions ): { input: SupermemoryInputProcessor output: SupermemoryOutputProcessor } ``` ### `SupermemoryMastraOptions` ```typescript theme={null} interface SupermemoryMastraOptions { containerTag: string // Required: User/container tag for scoping memories customId: 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 theme={null} 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 theme={null} // Missing API key throws immediately const agent = new Agent(withSupermemory( { id: "my-assistant", model: openai("gpt-4o"), instructions: "..." }, { containerTag: "user-123", customId: "conv-456", apiKey: undefined, // Will check SUPERMEMORY_API_KEY env } )) // Error: SUPERMEMORY_API_KEY is not set ``` *** ## Next Steps Use with Vercel AI SDK for streamlined development Learn about user profile management # Memory Graph Source: https://supermemory.ai/docs/integrations/memory-graph Interactive visualization for documents, memories and connections Memory Graph is a React component that visualizes your Supermemory documents and memories as an interactive network. Documents appear as rectangular nodes, memories as hexagonal nodes, and connections between them show relationships and similarity. Check out the NPM page for more details ## Installation ```bash theme={null} npm install @supermemory/memory-graph ``` **Requirements:** React 18.0.0 or higher ## Quick Start ```tsx theme={null} '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([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { fetch('/api/graph') .then(res => res.json()) .then(data => { setDocuments(data.documents); setIsLoading(false); }) .catch(err => { setError(err); setIsLoading(false); }); }, []); return (
); } ``` ## Backend API Route Create an API route to fetch documents from Supermemory: ```typescript Next.js App Router theme={null} // 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 theme={null} // 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 theme={null} app.get('/api/graph', async (req, res) => { const response = await fetch('https://api.supermemory.ai/v3/documents/documents', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.SUPERMEMORY_API_KEY}`, }, body: JSON.stringify({ page: 1, limit: 500, sort: 'createdAt', order: 'desc' }), }); const data = await response.json(); res.json(data); }); ``` Never expose your Supermemory API key to the client. Always fetch data through your backend. *** ## Variants **Console Variant** - Full-featured dashboard view (0.8x zoom, space selector visible): ```tsx theme={null} ``` **Consumer Variant** - Embedded widget view (0.5x zoom, space selector hidden): ```tsx theme={null} ``` *** ## Examples ### With Pagination ```tsx theme={null} '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 ( ); } ``` ### Highlighting Search Results ```tsx theme={null} 0} /> ``` ### Controlled Space Selection ```tsx theme={null} ``` ### Custom Empty State ```tsx theme={null}

No memories yet

Add content to see your knowledge graph

``` *** ## 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` | - | 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 theme={null} 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 | null; createdAt: string | Date; updatedAt: string | Date; memoryEntries: MemoryEntry[]; } ``` ### MemoryEntry ```typescript theme={null} interface MemoryEntry { id: string; documentId: string; content: string | null; summary?: string | null; title?: string | null; type?: string | null; metadata?: Record | null; createdAt: string | Date; updatedAt: string | Date; spaceContainerTag?: string | null; relation?: 'updates' | 'extends' | 'derives' | null; isLatest?: boolean; spaceId?: string | null; } ``` *** ## Exports ### Components ```typescript theme={null} import { MemoryGraph, GraphCanvas, Legend, LoadingIndicator, NodeDetailPanel, SpacesDropdown } from '@supermemory/memory-graph'; ``` ### Hooks ```typescript theme={null} import { useGraphData, useGraphInteractions } from '@supermemory/memory-graph'; ``` ### Constants ```typescript theme={null} 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. # n8n Source: https://supermemory.ai/docs/integrations/n8n Automate knowledge management with Supermemory in n8n workflows 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/v3/documents` * Search memories: `https://api.supermemory.ai/v4/search` 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/v3/documents` 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 2 fields: 1. name: `content`, value: `{{ $json.snippet }}` 2. name: `containerTag`, value: gmail #### 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. # OpenAI SDK Source: https://supermemory.ai/docs/integrations/openai Memory tools for OpenAI function calling with Supermemory integration 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 Migrating to v2 from 1.4.x? Check the [migration guide](/docs/migration/tools-v2-upgrade). **New to Supermemory?** Start with `withSupermemory` for the simplest integration. It automatically injects relevant memories into your prompts. Check out the NPM page for more details Check out the PyPI page for more details *** ## 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 theme={null} npm install @supermemory/tools openai ``` ### Quick Start ```typescript theme={null} 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, { containerTag: "user-123", // Required: identifies the user/container customId: "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 theme={null} const client = withSupermemory(openai, { // Required: identifies the user/container containerTag: "user-123", // Required: Group messages into the same document customId: "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 theme={null} const client = withSupermemory(openai, { containerTag: "user-123", customId: "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 theme={null} 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 ```bash Python theme={null} # Using uv (recommended) uv add supermemory-openai-sdk # Or with pip pip install supermemory-openai-sdk ``` ```bash JavaScript/TypeScript theme={null} npm install @supermemory/tools ``` ## Quick Start ```python Python SDK theme={null} 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 theme={null} 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!, { projectId: "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) } } ``` ## Configuration ### Memory Tools Configuration ```python Python Configuration theme={null} 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 theme={null} import { supermemoryTools } from "@supermemory/tools/openai" const tools = supermemoryTools(process.env.SUPERMEMORY_API_KEY!, { containerTags: ["your-user-id"], baseUrl: "https://custom-endpoint.com", // optional }) ``` ## Available Tools ### Search Memories Search through user memories using semantic search: ```python Python theme={null} # 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 theme={null} // Search memories const searchResult = await tools.searchMemories({ informationToGet: "user preferences", limit: 10, }) console.log(`Found ${searchResult.memories.length} memories`) ``` ### Add Memory Store new information in memory: ```python Python theme={null} # Add memory result = await tools.add_memory( memory="User prefers tea over coffee" ) print(f"Added memory with ID: {result.memory.id}") ``` ```typescript JavaScript theme={null} // Add memory const addResult = await tools.addMemory({ memory: "User prefers dark roast coffee", }) console.log(`Added memory with ID: ${addResult.memory.id}`) ``` ## Individual Tools Use tools separately for more granular control: ```python Python Individual Tools theme={null} 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 theme={null} 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] ``` ## Complete Chat Example Here's a complete example showing a multi-turn conversation with memory: ```python Complete Python Example theme={null} import asyncio import openai from supermemory_openai import SupermemoryTools, execute_memory_tool_calls async def chat_with_memory(): client = openai.AsyncOpenAI() tools = SupermemoryTools( api_key="your-supermemory-api-key", config={"project_id": "chat-example"} ) messages = [ { "role": "system", "content": """You are a helpful assistant with memory capabilities. When users share personal information, remember it using addMemory. When they ask questions, search your memories to provide personalized responses.""" } ] while True: user_input = input("You: ") if user_input.lower() == 'quit': break messages.append({"role": "user", "content": user_input}) # Get AI response with tools response = await client.chat.completions.create( model="gpt-5", messages=messages, tools=tools.get_tool_definitions() ) # Handle tool calls if response.choices[0].message.tool_calls: messages.append(response.choices[0].message) tool_results = await execute_memory_tool_calls( api_key="your-supermemory-api-key", tool_calls=response.choices[0].message.tool_calls, config={"project_id": "chat-example"} ) messages.extend(tool_results) # Get final response after tool execution final_response = await client.chat.completions.create( model="gpt-5", messages=messages ) assistant_message = final_response.choices[0].message.content else: assistant_message = response.choices[0].message.content messages.append({"role": "assistant", "content": assistant_message}) print(f"Assistant: {assistant_message}") # Run the chat asyncio.run(chat_with_memory()) ``` ```typescript Complete JavaScript Example theme={null} import OpenAI from "openai" import { getToolDefinitions, createToolCallExecutor } from "@supermemory/tools/openai" import readline from 'readline' const client = new OpenAI() const executeToolCall = createToolCallExecutor(process.env.SUPERMEMORY_API_KEY!, { projectId: "chat-example", }) const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }) async function chatWithMemory() { const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [ { role: "system", content: `You are a helpful assistant with memory capabilities. When users share personal information, remember it using addMemory. When they ask questions, search your memories to provide personalized responses.` } ] const askQuestion = () => { rl.question("You: ", async (userInput) => { if (userInput.toLowerCase() === 'quit') { rl.close() return } messages.push({ role: "user", content: userInput }) // Get AI response with tools const response = await client.chat.completions.create({ model: "gpt-5", messages, tools: getToolDefinitions(), }) const choice = response.choices[0] if (choice?.message.tool_calls) { messages.push(choice.message) // Execute tool calls for (const toolCall of choice.message.tool_calls) { const result = await executeToolCall(toolCall) messages.push({ role: "tool", tool_call_id: toolCall.id, content: JSON.stringify(result), }) } // Get final response after tool execution const finalResponse = await client.chat.completions.create({ model: "gpt-5", messages, }) const assistantMessage = finalResponse.choices[0]?.message.content || "No response" console.log(`Assistant: ${assistantMessage}`) messages.push({ role: "assistant", content: assistantMessage }) } else { const assistantMessage = choice?.message.content || "No response" console.log(`Assistant: ${assistantMessage}`) messages.push({ role: "assistant", content: assistantMessage }) } askQuestion() }) } console.log("Chat with memory started. Type 'quit' to exit.") askQuestion() } chatWithMemory() ``` ## Error Handling Handle errors gracefully in your applications: ```python Python Error Handling theme={null} 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 theme={null} 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) } } } ``` ## API Reference ### Python SDK #### `SupermemoryTools` **Constructor** ```python theme={null} 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 theme={null} execute_memory_tool_calls( api_key: str, tool_calls: List[ToolCall], config: Optional[SupermemoryToolsConfig] = None ) -> List[dict] ``` ### JavaScript SDK #### `supermemoryTools` ```typescript theme={null} supermemoryTools( apiKey: string, config?: { projectId?: string; baseUrl?: string } ) ``` #### `createToolCallExecutor` ```typescript theme={null} createToolCallExecutor( apiKey: string, config?: { projectId?: string; baseUrl?: string } ) -> (toolCall: OpenAI.Chat.ChatCompletionMessageToolCall) => Promise ``` ## Environment Variables Set these environment variables: ```bash theme={null} SUPERMEMORY_API_KEY=your_supermemory_key OPENAI_API_KEY=your_openai_key SUPERMEMORY_BASE_URL=https://custom-endpoint.com # optional ``` ## Development ### Python Setup ```bash theme={null} # Install uv curl -LsSf https://astral.sh/uv/install.sh | sh # Setup project git clone 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 theme={null} # Install dependencies npm install # Run tests npm test # Type checking npm run type-check # Linting npm run lint ``` ## Next Steps Use with Vercel AI SDK for streamlined development Direct API access for advanced memory management # OpenAI Agents SDK Source: https://supermemory.ai/docs/integrations/openai-agents-sdk Add persistent memory to OpenAI agents with Supermemory 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 theme={null} pip install openai-agents supermemory python-dotenv ``` Set up your environment: ```bash theme={null} # .env SUPERMEMORY_API_KEY=your-supermemory-api-key OPENAI_API_KEY=your-openai-api-key ``` Get your Supermemory API key from [console.supermemory.ai](https://console.supermemory.ai). ## Basic integration The simplest approach: fetch user context and pass it in the agent's instructions. ```python theme={null} import os from agents import Agent, Runner from supermemory import Supermemory from dotenv import load_dotenv load_dotenv() memory = Supermemory() def get_user_context(user_id: str, query: str) -> str: """Fetch profile and relevant memories for a user.""" result = memory.profile(container_tag=user_id, q=query) static = result.profile.static or [] dynamic = result.profile.dynamic or [] memories = result.search_results.results if result.search_results else [] return f""" User background: {chr(10).join(static) if static else 'No profile yet.'} Current focus: {chr(10).join(dynamic) if dynamic else 'No recent activity.'} Related memories: {chr(10).join([m.memory or m.chunk for m in memories[:5]]) if memories else 'None.'} """ def create_agent(user_id: str, task: str) -> Agent: """Create an agent with user context in its instructions.""" context = get_user_context(user_id, task) return Agent( name="assistant", instructions=f"""You are a helpful assistant. Here's what you know about this user: {context} Use this to personalize your responses.""", model="gpt-4o" ) async def run_with_memory(user_id: str, message: str) -> str: """Run an agent and store the interaction.""" agent = create_agent(user_id, message) result = await Runner.run(agent, message) # Save for next time memory.add( content=f"User asked: {message}\nResponse: {result.final_output}", container_tag=user_id ) return result.final_output ``` *** ## 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 theme={null} result = memory.profile( container_tag="user_123", q="travel planning" # Also searches for relevant memories ) print(result.profile.static) # ["Prefers window seats", "Vegetarian"] print(result.profile.dynamic) # ["Planning trip to Japan", "Traveling in March"] ``` ### Storing memories Save agent interactions so future sessions have context: ```python theme={null} def store_interaction(user_id: str, task: str, result: str): memory.add( content=f"Task: {task}\nOutcome: {result}", container_tag=user_id, metadata={"type": "agent_run"} ) ``` ### Searching memories Look up past interactions before running an agent: ```python theme={null} results = memory.search.memories( q="previous travel recommendations", container_tag="user_123", search_mode="hybrid", 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 theme={null} 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.memories( q=query, container_tag=user_id, 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( content=content, container_tag=user_id ) 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. ```python theme={null} import os from agents import Agent, Runner, function_tool from supermemory import Supermemory from dotenv import load_dotenv load_dotenv() class SupportAgent: def __init__(self): self.memory = Supermemory() def get_customer_context(self, customer_id: str, issue: str) -> dict: """Pull customer profile and past support interactions.""" result = self.memory.profile( container_tag=customer_id, q=issue, threshold=0.5 ) return { "profile": result.profile.static or [], "recent": result.profile.dynamic or [], "history": [m.memory for m in (result.search_results.results or [])[:3]] } def build_instructions(self, context: dict) -> str: """Turn customer context into agent instructions.""" parts = ["You are a customer support agent."] if context["profile"]: parts.append(f"Customer info: {', '.join(context['profile'])}") if context["recent"]: parts.append(f"Recent activity: {', '.join(context['recent'])}") if context["history"]: parts.append(f"Past issues: {'; '.join(context['history'])}") parts.append("Be helpful and reference past interactions when relevant.") return "\n\n".join(parts) @function_tool def escalate_to_human(self, reason: str) -> str: """Escalate the issue to a human agent. Args: reason: Why escalation is needed """ return f"Escalated: {reason}. A human agent will follow up." @function_tool def check_order_status(self, order_id: str) -> str: """Check the status of an order. Args: order_id: The order identifier """ # In reality, this would call your order system return f"Order {order_id}: Shipped, arriving Thursday" def create_agent(self, context: dict) -> Agent: return Agent( name="support", instructions=self.build_instructions(context), tools=[self.escalate_to_human, self.check_order_status], model="gpt-4o" ) async def handle(self, customer_id: str, message: str) -> str: """Handle a support request.""" context = self.get_customer_context(customer_id, message) agent = self.create_agent(context) result = await Runner.run(agent, message) # Store the interaction self.memory.add( content=f"Support request: {message}\nResolution: {result.final_output}", container_tag=customer_id, metadata={"type": "support", "resolved": True} ) return result.final_output async def main(): support = SupportAgent() # Add some customer context support.memory.add( content="Premium customer since 2021. Prefers email communication.", container_tag="customer_456" ) response = await support.handle( "customer_456", "My order hasn't arrived yet. Order ID is ORD-789." ) print(response) if __name__ == "__main__": import asyncio asyncio.run(main()) ``` *** ## Multi-agent handoffs with shared memory Agents handing off to each other usually lose context. Not if they're sharing a memory store. ```python theme={null} from agents import Agent, Runner class AgentTeam: def __init__(self, user_id: str): self.user_id = user_id self.memory = Supermemory() def get_shared_context(self, topic: str) -> str: """Get context that all agents can use.""" result = self.memory.profile( container_tag=self.user_id, q=topic ) memories = result.search_results.results if result.search_results else [] return "\n".join([m.memory or m.chunk for m in memories[:5]]) def create_researcher(self) -> Agent: context = self.get_shared_context("research preferences") return Agent( name="researcher", instructions=f"""You research topics and gather information. User context: {context}""", model="gpt-4o" ) def create_writer(self) -> Agent: context = self.get_shared_context("writing style preferences") return Agent( name="writer", instructions=f"""You write clear, helpful content. User context: {context}""", model="gpt-4o" ) async def research_and_write(self, topic: str) -> str: """Research a topic, then write about it.""" # Research phase researcher = self.create_researcher() research = await Runner.run(researcher, f"Research: {topic}") # Store research for the writer self.memory.add( content=f"Research on {topic}: {research.final_output[:500]}", container_tag=self.user_id, metadata={"type": "research", "topic": topic} ) # Writing phase writer = self.create_writer() article = await Runner.run( writer, f"Write about {topic} using this research:\n{research.final_output}" ) return article.final_output ``` *** ## Metadata for filtering Tags let you narrow down searches later: ```python theme={null} # Store with metadata memory.add( content="User prefers detailed technical explanations", container_tag="user_123", metadata={ "type": "preference", "category": "communication_style", "source": "support_chat" } ) # Search with filters results = memory.search.memories( q="communication preferences", container_tag="user_123", filters={ "AND": [ {"key": "type", "value": "preference"}, {"key": "category", "value": "communication_style"} ] } ) ``` *** ## Related docs How automatic profiling works Filtering and search modes Function calling with the regular OpenAI SDK Memory for LangChain apps # OpenClaw Source: https://supermemory.ai/docs/integrations/openclaw OpenClaw Supermemory Plugin — works across Telegram, WhatsApp, Discord, Slack, and more [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. **Prefer to keep everything on your machine?** This plugin works with [self-hosted Supermemory](/docs/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. ## Install the Plugin ```bash theme={null} openclaw plugins install @supermemory/openclaw-supermemory ``` Restart OpenClaw after installing. ## Setup Run the setup command and enter your API key when prompted. ```bash theme={null} 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. Configure all options interactively with the advanced setup command: ```bash theme={null} 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. ## 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 theme={null} openclaw supermemory setup # Configure API key openclaw supermemory setup-advanced # Configure all options openclaw supermemory status # View current configuration openclaw supermemory search # Search memories openclaw supermemory profile # View user profile openclaw supermemory wipe # Delete all memories (requires confirmation) ``` ### Configuration Options Set API key (and, for self-hosted instances, the base URL) via environment variables: ```bash theme={null} 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 theme={null} { "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'" } } } } } ``` ## FAQ 1. Install the plugin: ```bash theme={null} openclaw plugins install @supermemory/openclaw-supermemory ``` 2. Run the setup wizard: ```bash theme={null} 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 theme={null} openclaw gateway restart ``` 5. Verify the connection: ```bash theme={null} openclaw supermemory status ``` That's it — Supermemory is now connected and will automatically recall and capture memories across all your channels. Use the advanced setup to enable custom container tags and add a read-only Twitter bookmarks container. ```bash theme={null} 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` Custom container tags use the format `tag:description`, separated by a colon. For example: `twitter-bookmarks:Twitter bookmarks saved from Twitter`. 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. Use custom container tags to route memories by channel. Run the advanced setup: ```bash theme={null} 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. 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. 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". 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. Email us with your use case. ## Next Steps Source code, issues, and detailed README. Use Claude's native memory tool with Supermemory as backend. # OpenCode Source: https://supermemory.ai/docs/integrations/opencode OpenCode Supermemory Plugin — persistent memory across coding sessions [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. **Prefer to keep everything on your machine?** This plugin works with [self-hosted Supermemory](/docs/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. ## Install the Plugin ```bash theme={null} bunx opencode-supermemory@latest install ``` For LLM agents (non-interactive): ```bash theme={null} bunx opencode-supermemory@latest install --no-tui ``` ## Authenticate Browser login (recommended): ```bash theme={null} bunx opencode-supermemory@latest login ``` Check the connection any time: ```bash theme={null} bunx opencode-supermemory@latest status ``` Or set an API key manually from the [API Keys](https://console.supermemory.ai/keys) page in the console: ```bash theme={null} echo 'export SUPERMEMORY_API_KEY="sm_..."' >> ~/.zshrc source ~/.zshrc ``` ```bash theme={null} echo 'export SUPERMEMORY_API_KEY="sm_..."' >> ~/.bashrc source ~/.bashrc ``` ```powershell theme={null} [System.Environment]::SetEnvironmentVariable("SUPERMEMORY_API_KEY", "sm_...", "User") ``` Restart your terminal after running this. Ensure your `~/.config/opencode/opencode.jsonc` contains: ```json theme={null} { "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 `` 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 theme={null} { "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 theme={null} tail -f ~/.opencode-supermemory.log ``` ## Next Steps Source code, issues, and detailed README. Memory plugin for Claude Code. # Pipecat Source: https://supermemory.ai/docs/integrations/pipecat Integrate Supermemory with Pipecat for conversational memory in voice AI agents 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 theme={null} pip install supermemory-pipecat ``` Set up your API key as an environment variable: ```bash theme={null} 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 theme={null} from supermemory_pipecat import SupermemoryPipecatService from supermemory_pipecat.service import InputParams 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 theme={null} 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 theme={null} 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 theme={null} 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: ```python theme={null} import os from fastapi import FastAPI, WebSocket from fastapi.middleware.cors import CORSMiddleware from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.frames.frames import LLMMessagesFrame from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext from pipecat.serializers.protobuf import ProtobufFrameSerializer from pipecat.services.openai.llm import OpenAILLMService from pipecat.services.openai.tts import OpenAITTSService from pipecat.services.openai.stt import OpenAISTTService from pipecat.transports.websocket.fastapi import ( FastAPIWebsocketParams, FastAPIWebsocketTransport, ) from supermemory_pipecat import SupermemoryPipecatService from supermemory_pipecat.service import InputParams app = FastAPI() SYSTEM_PROMPT = """You are a helpful voice assistant with memory capabilities. You remember information from past conversations and use it to provide personalized responses. Keep responses brief and conversational.""" async def run_bot(websocket_client, user_id: str, session_id: str): transport = FastAPIWebsocketTransport( websocket=websocket_client, params=FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, vad_enabled=True, vad_analyzer=SileroVADAnalyzer(), vad_audio_passthrough=True, serializer=ProtobufFrameSerializer(), ), ) stt = OpenAISTTService(api_key=os.getenv("OPENAI_API_KEY")) llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-5-mini") tts = OpenAITTSService(api_key=os.getenv("OPENAI_API_KEY"), voice="alloy") # Supermemory memory service memory = SupermemoryPipecatService( user_id=user_id, session_id=session_id, params=InputParams( mode="full", search_limit=10, search_threshold=0.1, ), ) context = OpenAILLMContext([{"role": "system", "content": SYSTEM_PROMPT}]) context_aggregator = llm.create_context_aggregator(context) pipeline = Pipeline([ transport.input(), stt, context_aggregator.user(), memory, llm, tts, transport.output(), context_aggregator.assistant(), ]) task = PipelineTask(pipeline, params=PipelineParams(allow_interruptions=True)) @transport.event_handler("on_client_disconnected") async def on_client_disconnected(transport, client): await task.cancel() runner = PipelineRunner(handle_sigint=False) await runner.run(task) @app.websocket("/ws") async def websocket_endpoint(websocket: WebSocket): await websocket.accept() await run_bot(websocket, user_id="alice", session_id="session-123") if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000) ``` ## Example: Gemini Live with Memory For a complete example using Gemini Live speech-to-speech with Supermemory, check out the reference implementation: Full working example with Gemini Live, including frontend and backend code. # Supermemory SDK Source: https://supermemory.ai/docs/integrations/supermemory-sdk Official Python and JavaScript SDKs for Supermemory pip install supermemory npm install supermemory Both SDKs also work against [self-hosted Supermemory](/docs/self-hosting/overview) — pass `baseURL: "http://localhost:6767"` (TypeScript) or `base_url="http://localhost:6767"` (Python) when creating the client. ## Installation ```bash theme={null} npm install supermemory ``` ## Quick Start ```typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY, // Default, can be omitted }); // Add a memory await client.add({ content: "Meeting notes from Q1 planning", containerTag: "user_123" }); // Search memories const response = await client.search({ q: "planning notes", searchMode: "documents", containerTag: "user_123" }); console.log(response.results); // Get user profile const profile = await client.profile({ containerTag: "user_123" }); console.log(profile.profile.static); console.log(profile.profile.dynamic); ``` ## Common Operations ```typescript theme={null} // Add with metadata await client.add({ content: "Technical design doc", containerTag: "user_123", metadata: { category: "engineering", priority: "high" } }); // Search with filters const results = await client.search({ q: "design document", searchMode: "documents", containerTag: "user_123", filters: { AND: [ { key: "category", value: "engineering" } ] } }); // List documents const docs = await client.documents.list({ containerTags: ["user_123"], limit: 10 }); // Delete a document await client.documents.delete({ docId: "doc_123" }); ``` ## Error Handling & Retries | Status | Error | | ------ | -------------------------- | | 400 | `BadRequestError` | | 401 | `AuthenticationError` | | 403 | `PermissionDeniedError` | | 404 | `NotFoundError` | | 409 | `ConflictError` | | 422 | `UnprocessableEntityError` | | 429 | `RateLimitError` | | >=500 | `InternalServerError` | Connection errors, 408, 409, 429, and >=500 responses are retried automatically (`maxRetries`, default 2, exponential backoff). Requests time out after 1 minute by default (`timeout` option). Set the `SUPERMEMORY_LOG` env var (or `logLevel` client option) to `debug`/`info`/`warn`/`error`/`off` — defaults to `warn`. Requires TypeScript >= 4.9, Node 20+, Deno 1.28+, or Bun 1.0+. ## Installation ```bash theme={null} pip install supermemory ``` ## Quick Start ```python theme={null} import os from supermemory import Supermemory client = Supermemory( api_key=os.environ.get("SUPERMEMORY_API_KEY"), # Default, can be omitted ) # Add a memory client.add(content="Meeting notes from Q1 planning", container_tag="user_123") # Search memories response = client.search.memories( q="planning notes", search_mode="documents", container_tag="user_123" ) print(response.results) # Get user profile profile = client.profile(container_tag="user_123") print(profile.profile.static) print(profile.profile.dynamic) ``` ## Common Operations ```python theme={null} # Add with metadata client.add( content="Technical design doc", container_tag="user_123", metadata={"category": "engineering", "priority": "high"} ) # Search with filters results = client.search.memories( q="design document", search_mode="documents", container_tag="user_123", filters={ "AND": [ {"key": "category", "value": "engineering"} ] } ) # List documents docs = client.documents.list(container_tags=["user_123"], limit=10) # Delete a document client.documents.delete(doc_id="doc_123") ``` ## Error Handling & Retries Same error classes as the TypeScript SDK (`BadRequestError`, `AuthenticationError`, `PermissionDeniedError`, `NotFoundError`, `ConflictError`, `UnprocessableEntityError`, `RateLimitError`, `InternalServerError`), all inheriting from `supermemory.APIError`. Connection errors, 408, 409, 429, and >=500 responses are retried automatically (`max_retries`, default 2). Requests time out after 1 minute by default (`timeout` option). Set `SUPERMEMORY_LOG=info` (or `debug`) to enable logging. Requires Python 3.9+. # viaSocket Source: https://supermemory.ai/docs/integrations/viasocket Connect Supermemory with viaSocket to build automation flows using triggers, API tokens, and actions like Gmail. 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 * 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. make a zap - annotated * 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**. make a zap - annotated * Click **Connect to Supermemory**. * Paste your Supermemory API key. * Click **Save** to store the connection. * Confirm the connection is successfully added before proceeding. make a zap - annotated * 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. make a zap - annotated * 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. make a zap - annotated * 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**. make a zap - annotated 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. You can extend this flow with other actions and services supported by viaSocket. # VoltAgent Source: https://supermemory.ai/docs/integrations/voltagent Integrate Supermemory with VoltAgent for long-term memory in AI agents 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. Migrating to v2 from 1.4.x? Check the [migration guide](/docs/migration/tools-v2-upgrade). Check out the NPM page for more details ## Installation ```bash theme={null} npm install @supermemory/tools @voltagent/core ai@^6 @ai-sdk/openai@^3 ``` Set up your API key as an environment variable: ```bash theme={null} 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 theme={null} 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"), }, containerTag: "user-123", customId: "conversation-123", }) const agent = new Agent(configWithMemory) // Memories are automatically injected and saved const result = await agent.generateText("What's my name?") ``` **Memory saving is enabled by default** in the VoltAgent integration. To disable it: ```typescript theme={null} const configWithMemory = withSupermemory({ agentConfig: { name: "my-agent", instructions: "You are a helpful assistant.", model: openai("gpt-4o"), }, containerTag: "user-123", customId: "conversation-123", addMemory: "never", }) ``` ## 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 `containerTag` * 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 `customId` 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 theme={null} const configWithMemory = withSupermemory({ agentConfig: { name: "my-agent", instructions: "You are a helpful assistant.", model: openai("gpt-4o"), }, containerTag: "user-123", customId: "conversation-123", mode: "full", }) ``` ## Configuration Options ```typescript theme={null} const configWithMemory = withSupermemory({ // Agent configuration agentConfig: { name: "my-agent", instructions: "You are a helpful assistant.", model: openai("gpt-4o"), }, // Required containerTag: "user-123", // User/project ID for scoping memories // Memory behavior mode: "full", // "profile" | "query" | "full" addMemory: "always", // "always" | "never" customId: "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 | | `containerTag` | string | **required** | User/project ID for scoping memories | | `mode` | string | `"profile"` | Memory retrieval mode | | `addMemory` | string | `"always"` | Whether to save conversations after each response | | `customId` | 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](/docs/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) | # Zapier Source: https://supermemory.ai/docs/integrations/zapier Automate memory management with Supermemory in Zapier workflows 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. Open your Zapier account and click on 'Zap' to make a new automation. make a zap - annotated Add a new Gmail node that gets triggered on every new email. Connect to your Google account. add gmail 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. Since we're ingesting data here, we'll use the add documents endpoint. Add the following code block: ```python theme={null} import requests url = "https://api.supermemory.ai/v3/documents" payload = { "content": inputData['content'], "containerTag": "gmail" } 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. Sometimes Zapier might show an error on the first test run. It usually works right after. Weird bug, we know. 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. # Building a Benchmark Source: https://supermemory.ai/docs/memorybench/extend-benchmark Add a custom benchmark dataset when the built-in ones don't match your use case 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 theme={null} interface Benchmark { name: string load(config?: BenchmarkConfig): Promise 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 theme={null} 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 theme={null} 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. 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. *** ## Next Register a memory system to run against your new benchmark. How accuracy, latency, and MemScore are computed. # Adding a Provider Source: https://supermemory.ai/docs/memorybench/extend-provider Register your own memory implementation so MemoryBench can score and compare it 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 theme={null} # 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](/docs/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). 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. ## Doing it by hand If you'd rather write the adapter yourself, every provider implements the same interface (`src/types/provider.ts`): ```typescript theme={null} interface Provider { name: string prompts?: ProviderPrompts initialize(config: ProviderConfig): Promise ingest(sessions: UnifiedSession[], options: IngestOptions): Promise awaitIndexing(result: IngestResult, containerTag: string): Promise search(query: string, options: SearchOptions): Promise clear(containerTag: string): Promise } ``` | 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 theme={null} 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 ``` 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. *** ## Next Run your provider against a dataset built for your own use case. What the report actually tells you. # Measuring Results Source: https://supermemory.ai/docs/memorybench/memscore How MemoryBench scores a run — qualitative judging and quantitative metrics Every question goes through the same pipeline — **ingest → search → answer → evaluate → report** — and produces two different kinds of signal: a **qualitative** judgment on whether the answer was actually right, and **quantitative** measurements of how fast and how expensive getting there was. 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 theme={null} { 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 theme={null} # 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 theme={null} 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 Get your own system into a run that produces these numbers. Measure against scenarios specific to your product. # MemoryBench Source: https://supermemory.ai/docs/memorybench/overview Open-source framework for benchmarking memory providers — including your own 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. Clone it, run it against your own data, or read the source for exactly how each provider is scored. *** ## Benchmark your own memory system MemoryBench ships a **Claude Code skill** that automates the entire process of benchmarking a custom memory implementation — yours — against Supermemory, Mem0, and Zep. Point it at your code and it handles discovery, integration, and the run: 1. Asks a few questions about your setup (provider name, where your memory code lives, which benchmark, which competitors, how many questions) 2. Analyzes your memory code to find its init, ingest, and search methods 3. Generates a provider adapter and registers it with the framework 4. Runs the full benchmark against your chosen competitors 5. Reports accuracy, latency, and context-token results side by side ```bash theme={null} # From your project root /memorybench ``` No manual TypeScript required to get a first result — see [Adding a Provider](/docs/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. Chunk-based semantic search LLM-powered memory extraction Knowledge graph construction | Benchmark | Tests | Source | | --------------- | ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | **LoCoMo** | Fact recall across extended, multi-session conversations — single-hop, multi-hop, temporal, adversarial | [snap-research/locomo](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](/docs/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 Test on scenarios that actually match your product, not just the built-in datasets. Register your own memory system so it can be scored and compared. MemScore, judge scoring, and how to read a report. Found a bug, or want a provider/benchmark we don't support yet? # Migrating from Mem0 to Supermemory Source: https://supermemory.ai/docs/migration/from-mem0 Complete guide to migrate your data and applications from Mem0 to Supermemory 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 theme={null} 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.memories.add( content=memory["content"], container_tags=["imported_from_mem0"] ) print(f"✅ {memory['content'][:50]}...") print("Migration complete!") ``` ## Step-by-Step Migration 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 theme={null} 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") ``` 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 theme={null} # Set your environment variable export SUPERMEMORY_API_KEY="your_supermemory_api_key" ``` Simple script to import your Mem0 memories into Supermemory: ```python theme={null} 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( content=content, container_tags=["imported_from_mem0"], 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!") ``` ## API Migration Reference Here's how common Mem0 operations map to Supermemory: ### Adding Memories ```python mem0 theme={null} from mem0 import MemoryClient client = MemoryClient(api_key="...") client.add( messages="User prefers dark mode", user_id="alice" ) ``` ```python Supermemory theme={null} from supermemory import Supermemory client = Supermemory(api_key="...") client.add( content="User prefers dark mode", container_tags=["user_alice"] ) ``` ### Searching Memories ```python Mem0 theme={null} results = client.search( query="user preferences", user_id="alice" ) ``` ```python Supermemory theme={null} results = client.search.memories( q="user preferences", container_tag="user_alice" ) ``` ### Getting All Memories ```python Mem0 theme={null} memories = client.get_all( user_id="alice" ) ``` ```python Supermemory theme={null} memories = client.documents.list( container_tags=["user_alice"], limit=100 ) ``` ### Deleting Memories ```python Mem0 theme={null} client.delete(memory_id="mem_123") ``` ```python Supermemory theme={null} client.documents.delete("mem_123") ``` For enterprise migrations, [contact us](mailto:support@supermemory.ai) for assistance. ## Next Steps 1. [Explore](/docs/concepts/how-it-works) how Supermemory works 2. Read the [quickstart](/docs/quickstart) and add and retrieve your first memories 3. [Connect](/docs/connectors/overview) to Google Drive, Notion, and OneDrive with automatic syncing # Migrating from Zep to Supermemory Source: https://supermemory.ai/docs/migration/from-zep Quick guide to migrate from Zep to Supermemory ## Key Differences | Zep AI | Supermemory | | ---------------------------------------- | ----------------------------------------------- | | Sessions & Messages | Documents & Container Tags | | `session.create()` | Use `containerTag` parameter | | `memory.add(session_id, ...)` | `add({containerTag: "..."})` | | `memory.search(session_id, {text: ...})` | `search.execute({q: ..., containerTag: "..."})` | ## Installation ```bash Python theme={null} pip install supermemory ``` ```bash TypeScript theme={null} npm install supermemory ``` ```python Python theme={null} from supermemory import Supermemory client = Supermemory(api_key="your-api-key") ``` ```typescript TypeScript theme={null} import { Supermemory } from "supermemory"; const client = new Supermemory({ apiKey: "your-api-key" }); ``` ## API Mapping ### Session Management ```python Zep AI theme={null} session = client.session.create( session_id="user_123", user_id="user_123" ) ``` ```python Supermemory theme={null} # No explicit session creation - use containerTag containerTag = "user_123" ``` ### Adding Memories ```python Zep AI theme={null} client.memory.add( session_id="user_123", memory={"content": "User prefers dark mode"} ) ``` ```python Supermemory theme={null} client.add({ "content": "User prefers dark mode", "containerTag": "user_123" }) ``` ### Searching ```python Zep AI theme={null} results = client.memory.search( session_id="user_123", search_payload={"text": "preferences", "limit": 5} ) ``` ```python Supermemory theme={null} results = client.search.execute({ "q": "preferences", "containerTag": "user_123", "limit": 5 }) ``` ### Getting All Memories ```python Zep AI theme={null} memories = client.memory.get(session_id="user_123") ``` ```python Supermemory theme={null} documents = client.documents.list({ "containerTags": ["user_123"], "limit": 100 }) ``` ## Migration Steps 1. **Replace client initialization** - Use Supermemory client instead of Zep 2. **Map sessions to container tags** - Replace `session_id="user_123"` with `containerTag: "user_123"` 3. **Update method calls** - Use `add()` and `search.execute()` instead of `memory.add()` and `memory.search()` 4. **Change search parameter** - Use `q` instead of `text` 5. **Handle async processing** - Documents process asynchronously (status: `queued` → `done`) ## Complete Example ```python Zep AI theme={null} 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 }) ``` ```python Supermemory theme={null} from supermemory import Supermemory client = Supermemory(api_key="...") containerTag = "user_123" client.add({ "content": "I love Python", "containerTag": containerTag, "metadata": {"role": "user"} }) results = client.search.execute({ "q": "programming", "containerTag": containerTag, "limit": 3 }) ``` ## Important Notes * **No session creation needed** - Just use `containerTag` in requests * **Messages are documents** - Store with `metadata.role` and `metadata.type` * **Async processing** - Documents may take a moment to be searchable * **Response structure** - Supermemory returns chunks with scores, not direct memory content ## Migrating Existing Data ### Quick Migration (All-in-One) Complete migration in one script: ```typescript TypeScript theme={null} 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({ content: mem.content, containerTag: `session:${sessionId}:user:${memory.user_id || "unknown"}`, 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 theme={null} 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({ "content": mem.content, "containerTag": f"session:{session_id}:user:{memory.user_id or 'unknown'}", "metadata": { "role": mem.role, "type": "message", "original_uuid": mem.uuid, **(mem.metadata or {}) } }) print(f"✅ Imported: {mem.content[:50]}...") print("Migration complete!") ``` ### Full Migration Script For a complete migration script with error handling, verification, and progress tracking, copy this TypeScript script: ```typescript theme={null} import { ZepClient } from "@getzep/zep-js"; import { Supermemory } from "supermemory"; import * as dotenv from "dotenv"; import * as fs from "fs"; dotenv.config(); interface MigrationStats { imported: number; failed: number; skipped: number; } async function migrateFromZep( zepApiKey: string, supermemoryApiKey: string, sessionIds: string[] ) { const zep = new ZepClient({ apiKey: zepApiKey }); const supermemory = new Supermemory({ apiKey: supermemoryApiKey }); const stats: MigrationStats = { imported: 0, failed: 0, skipped: 0 }; const exportedData: any = {}; console.log("🔄 Starting migration..."); // Export from Zep for (const sessionId of sessionIds) { try { const session = await zep.session.get(sessionId); const memory = await zep.memory.get(sessionId); const memories = memory?.memories || []; exportedData[sessionId] = { session: { session_id: sessionId, user_id: session?.user_id }, memories: memories.map((m: any) => ({ content: m.content, role: m.role, metadata: m.metadata, uuid: m.uuid, })), }; console.log(`✅ Exported ${memories.length} memories from ${sessionId}`); } catch (error: any) { console.log(`❌ Error exporting ${sessionId}: ${error.message}`); } } // Save backup const backupFile = `zep_export_${Date.now()}.json`; fs.writeFileSync(backupFile, JSON.stringify(exportedData, null, 2)); console.log(`💾 Backup saved to: ${backupFile}`); // Import to Supermemory let totalMemories = 0; for (const [sessionId, data] of Object.entries(exportedData) as any) { let containerTag = `imported_from_zep:session:${sessionId}`; if (data.session.user_id) { containerTag += `:user:${data.session.user_id}`; } for (const memory of data.memories) { totalMemories++; try { if (!memory.content?.trim()) { stats.skipped++; continue; } await supermemory.add({ content: memory.content, containerTag: containerTag, metadata: { source: "zep_migration", role: memory.role, type: "message", original_uuid: memory.uuid, ...memory.metadata, }, }); stats.imported++; console.log(`✅ [${stats.imported}/${totalMemories}] Imported`); } catch (error: any) { stats.failed++; console.log(`❌ Failed: ${error.message}`); } } } console.log("\n📊 Migration Summary:"); console.log(`✅ Imported: ${stats.imported}`); console.log(`⚠️ Skipped: ${stats.skipped}`); console.log(`❌ Failed: ${stats.failed}`); } // Usage const sessionIds = ["session_1", "session_2"]; // Add your session IDs migrateFromZep( process.env.ZEP_API_KEY!, process.env.SUPERMEMORY_API_KEY!, sessionIds ).catch(console.error); ``` ## Resources * [Supermemory SDKs](/docs/integrations/supermemory-sdk) * [API Reference](/docs/api-reference/overview) * [Search Documentation](/docs/recall/search) # Upgrading @supermemory/tools to v2.0.0 Source: https://supermemory.ai/docs/migration/tools-v2-upgrade Migrate your code from @supermemory/tools 1.4.x to 2.0.0 — config-object signature, customId, and new defaults `@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. This release is **breaking**. Update calls and re-test before bumping in production. ## 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 theme={null} npm install @supermemory/tools@^2.0.0 ``` ## 1. Vercel AI SDK ```typescript theme={null} // v1.4.x import { withSupermemory } from '@supermemory/tools/ai-sdk'; const model = withSupermemory(openai('gpt-4'), 'user-123', { conversationId: 'conv-456', mode: 'full', }); ``` ```typescript theme={null} // v2.0.0 import { withSupermemory } from '@supermemory/tools/ai-sdk'; const model = withSupermemory(openai('gpt-4'), { containerTag: 'user-123', customId: 'conv-456', mode: 'full', }); ``` `customId` is now **required**. Passing an empty string or omitting it throws at construction time. ## 2. OpenAI SDK ```typescript theme={null} // v1.4.x import { withSupermemory } from '@supermemory/tools/openai'; const client = withSupermemory(openai, 'user-123', { conversationId: 'conv-456', }); ``` ```typescript theme={null} // 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 theme={null} // 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 theme={null} // 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', }); ``` 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. ## 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 theme={null} 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 `npm install @supermemory/tools@^2.0.0` Grep your codebase for `withSupermemory(`, `SupermemoryInputProcessor`, `SupermemoryOutputProcessor`, `createSupermemoryProcessor`, `createSupermemoryOutputProcessor`. Drop the positional `containerTag` argument and add it to the options object. Make sure every call site provides a non-empty `customId`. If you depended on the old `"never"` default, pass `addMemory: "never"` explicitly. Validation throws happen at construction time, so missing fields surface immediately. ## Need help? * [Vercel AI SDK integration](/docs/integrations/ai-sdk) * [OpenAI integration](/docs/integrations/openai) * [Mastra integration](/docs/integrations/mastra) * [VoltAgent integration](/docs/integrations/voltagent) If you hit something this guide does not cover, open an issue on [GitHub](https://github.com/supermemoryai/supermemory). # Analytics & Monitoring Source: https://supermemory.ai/docs/overview/analytics Observe usage, errors, and logs to monitor your Supermemory integration 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**: Access complete request/response logs for debugging * **Performance Metrics**: Monitor average response times and processing duration Analytics data is available for your entire organization and can be filtered by time period. ## 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`: `1h`, `24h`, `7d`, `30d` | | `page` | integer | Page number for pagination (default: 1) | | `limit` | integer | Items per page (default: 20, max: 100) | ### Example Request ```typescript TypeScript theme={null} // 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 theme={null} 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 theme={null} # 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" ``` ### Response Schema ```json theme={null} { "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" } ], "totalMemories": 45678, "pagination": { "currentPage": 1, "limit": 20, "totalItems": 150, "totalPages": 8 } } ``` ## 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 ```typescript TypeScript theme={null} // 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 theme={null} # Monitor errors and alert on spikes response = requests.get( 'https://api.supermemory.ai/v3/analytics/errors?period=1h', headers={'Authorization': f'Bearer {SUPERMEMORY_API_KEY}'} ) data = response.json() ``` ```bash cURL theme={null} # 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" ``` ### Response Schema ```json theme={null} { "totalErrors": 234, "errorRate": 0.023, "topErrors": [ { "type": "ValidationError", "count": 89, "statusCodes": [400], "lastOccurred": "2024-01-15T14:30:00Z" }, { "type": "RateLimitError", "count": 45, "statusCodes": [429], "lastOccurred": "2024-01-15T13:15:00Z" } ], "timeline": [ { "time": "2024-01-15T14:00:00Z", "count": 12, "types": ["ValidationError", "NotFoundError"] } ], "byStatusCode": { "400": 89, "404": 34, "429": 45, "500": 66 } } ``` ## Detailed Logs Access complete request/response logs for debugging and auditing. ### Endpoint `GET /v3/analytics/logs` ### Parameters Same as usage endpoint, plus optional filters: * `type`: Filter by request type (add, search, update, delete) * `statusCode`: Filter by HTTP status code * `keyId`: Filter by specific API key ### Example Request ```typescript TypeScript theme={null} // Get recent failed requests const logs = await fetch('https://api.supermemory.ai/v3/analytics/logs?period=1h&statusCode=500', { headers: { 'Authorization': `Bearer ${SUPERMEMORY_API_KEY}` } }); const data = await logs.json(); ``` ```python Python theme={null} # Debug specific API key usage response = requests.get( 'https://api.supermemory.ai/v3/analytics/logs', params={ 'keyId': 'key_abc123', 'period': '24h' }, headers={'Authorization': f'Bearer {SUPERMEMORY_API_KEY}'} ) logs = response.json()['logs'] ``` ```bash cURL theme={null} # Get all logs for debugging curl -X GET "https://api.supermemory.ai/v3/analytics/logs?period=1h&limit=50" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` ### Response Schema ```json theme={null} { "logs": [ { "id": "req_xyz789", "createdAt": "2024-01-15T14:30:00Z", "type": "search", "statusCode": 200, "duration": 89, "input": { "q": "user query", "limit": 10 }, "output": { "results": 10, "processingTime": 85 } } ], "pagination": { "currentPage": 1, "limit": 20, "totalItems": 500, "totalPages": 25 } } ``` ## Rate Limits Analytics endpoints have the following rate limits: * 100 requests per minute per organization * Maximum time range: 90 days * Maximum page size: 100 items Analytics data is retained for 90 days. For longer retention, export and store the data in your own systems. # Billing & usage Source: https://supermemory.ai/docs/overview/billing Enterprise-grade reference for Supermemory metering — SM tokens, operations, search, SuperRAG, plans, diff billing, and programmatic usage APIs. 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. Console charts are **estimates**. Invoice / customer-portal line items are authoritative. ## Mental model ```text theme={null} PLAN (Free · Pro · 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 the same document under the same `customId` only bills the net-new token delta** — already-seen content is effectively **fully discounted**. *** ## 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 | Each search / profile call that runs retrieval (v3 search, v4 search, v4 profile with search) | queries | | `sm_operations` | Operations | Counted platform operations not fully attributed to token meters (e.g. certain ingest modes such as **instant dreaming**, and other non-token platform actions) | 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"`** — processes a document’s memory extraction immediately (does not wait for dynamic batch dreaming). Documented as **one extra operation per document** on top of normal token metering. See [Processing modes](/docs/ingestion/add-memories#processing-modes). * Other non-token platform actions the product attributes to the operations meter (console: “counted platform operations not attributed to token or search meters”). 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 /v3/search`, `POST /v4/search`, `client.search.*` | `sm_search_queries` (+1) | | `POST /v4/profile` when it runs retrieval | `sm_search_queries` (+1) | If the org is out of balance, search/profile can return **402** (payment required) depending on gate configuration. *** ## Full discount on already-seen tokens (diff billing) This is the most important cost control in production agents. ### How it works When you re-add content under the **same `customId`** (same org / document identity), the pipeline: 1. Loads the **previous extracted content** for that document 2. Computes **full** token count of the merged/updated document 3. Bills only: ```text theme={null} billableTokens = max(0, fullTokenCount - previousTokenCount) ``` So **tokens Supermemory has already processed are not billed again**. Unchanged content is a **full discount** on the token meters. Only the **net-new delta** (and any new rich extraction on that delta) draws credits. This is why long-lived agent loops stay cheap: re-sync the same conversation `customId`, re-upload the same policy doc, or connector re-sync with stable IDs — you pay for **new** material, not the whole history every time. ### Requirements | Requirement | Why | | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Stable `customId`** | Identity for “this is the same document.” Max length 255. | | **Same org / key** | Documents are org-scoped. | | **Update path, not full replace** | Full replace clears previous content for billing purposes (`isFullReplace` treats previous tokens as 0). Prefer append/update with the same `customId` for chat. | ### Practical patterns ```typescript theme={null} // Session stays one document — only new turns bill tokens await client.add({ content: "user: " + msg + "\nassistant: " + reply, containerTag: userId, customId: "chat_" + sessionId, // stable for the session dreaming: "instant", // optional: +1 operation, faster memory }); ``` Connectors already use stable IDs (e.g. Drive file id, Gmail thread id, `s3://bucket/key`) so re-syncs diff-bill automatically. ### What is not free * **First** ingest of content still bills full token count * **Search / profile** still bill query meters every call * **Instant dreaming** still bills the **operation** surcharge when used * **Brand-new `customId`** = 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 | | **Spend caps** | Scale+ supports hard caps so usage cannot run away | | **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** | | 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 | Evaluate API, prototypes | | **Pro** | 19 USD/mo | 20 USD | Developers, plugins, core connectors | | **Scale** | 399 USD/mo | 600 USD | Production, full connectors, teams | | **Enterprise** | Custom | Contract | SSO, custom metering, FDE, air-gap | ### Feature matrix (code gates) | Feature | Free | Pro | Scale | Enterprise | | ----------------------------------------- | ------- | --- | --------- | ---------- | | Memory API, search, profiles | Yes | Yes | Yes | Yes | | Diff / delta token billing | Yes | Yes | Yes | Yes | | Plugins (Claude Code, Cursor, Hermes, …) | — | Yes | Yes | Yes | | Team management | — | Yes | Yes | Yes | | Google Drive, OneDrive, Notion connectors | — | Yes | Yes | Yes | | Gmail, GitHub, S3, Web Crawler connectors | — | — | Yes | Yes | | User Insights | — | — | Yes | Yes | | Org seat limit (members) | 1 | 3 | Unlimited | Unlimited | | Auto top-up | Limited | Yes | Yes | Contract | | Spend caps | — | — | Yes | Contract | | Custom metering / SSO / dedicated deploy | — | — | — | Yes | 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 All of the following require an **org-admin capable** credential. **Scoped API keys cannot read billing** (403). Base: `https://api.supermemory.ai`\ Auth: `Authorization: Bearer YOUR_API_KEY` ### Summary — plan + high-level usage `GET /v3/auth/billing` ```bash theme={null} curl -s https://api.supermemory.ai/v3/auth/billing \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` Typical fields: ```json theme={null} { "plan": "pro", "usage": { "tokens": { "used": 120000, "limit": 0 }, "queries": { "used": 4200, "limit": 0 } }, "credits": {}, "resetDate": "2026-08-01T00:00:00.000Z", "orgName": "Acme", "billingEmail": "billing@acme.com" } ``` CLI: ```bash theme={null} 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 theme={null} 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=&end=` to override the billing period. ```bash theme={null} 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 theme={null} GET /v3/auth/billing/auto-topups PATCH /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](/docs/overview/analytics). ### TypeScript sketch ```typescript theme={null} 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 stable `customId`** 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 **`containerTag`** so you can delete/export a tenant without scanning the org. 6. Enable **spend caps / auto top-up** on Scale for predictable production. 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](/docs/ingestion/add-memories) — `customId`, `dreaming` * [Analytics](/docs/overview/analytics) — request-level observability * [Security & compliance](/docs/overview/security) — trust posture for enterprise review # Comparison - Should I use supermemory? Source: https://supermemory.ai/docs/overview/comparison When Supermemory is the right choice versus DIY vector stacks, thin memory layers, pure RAG, and building it yourself. The answer is: you should probably use supermemory > "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](/docs/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](/docs/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 container tags. * **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](/docs/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](/docs/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](/docs/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. 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](/docs/migration/from-mem0), that is where we name names and map APIs.

vs Rolling your own (vector DB + embeddings + glue)

**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** using our custom model, a **temporal vector-graph engine**, and **profiles**, with hybrid retrieval and isolation primitives built in. You keep the option to [self-host](/docs/self-hosting/overview) when you want the stack on your metal.

vs “Memory layers” that are thin wrappers

**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. Same store from API, MCP, and plugins, all primitives built in for you to compose for your use case. See [How it works](/docs/concepts/how-it-works) and [Graph memory](/docs/concepts/graph-memory).

vs Pure RAG / document search products

**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 container tags so isolation stays coherent. Deep dive: [Memory vs RAG](/docs/concepts/memory-vs-rag).

vs Building a full context engine in-house

**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](/docs/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, container 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).

Decision cheat sheet

| 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 containers) | | Full control, data never leaves your network | Supermemory [self-host](/docs/self-hosting/overview) / Enterprise | | Maximum control of every model weight and storage engine | Build in-house (or fork open pieces and accept the ops) |

Prove it yourself

We would rather you verify than trust a comparison page: 1. Run the [quickstart](/docs/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](/docs/migration/from-mem0)

Supermemory is never overkill

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](/docs/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.
## Related Product overview and one-engine mental model. Why nearest-neighbor text is not memory. Usage model if cost is part of the evaluation. Trust posture for production and enterprise buyers. # Security & compliance Source: https://supermemory.ai/docs/overview/security How Supermemory protects data — encryption, isolation, SOC 2, GDPR, HIPAA BAA, and deletion. 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 [Container tags](/docs/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 * **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 container. * **Organizations** in the console manage members, keys, and billing separation. A malicious or buggy client with a correctly scoped key cannot read another container’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** — full engine on your machine or VPC; embeddings and storage stay where you run it. See [Self-hosting](/docs/self-hosting/overview). * **Enterprise / dedicated** — for stricter residency, air-gap, or custom deployment requirements. See [Local vs Enterprise](/docs/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 **container tag**. 2. When the user requests deletion, delete that container’s content via the API / console workflows for documents and memories under that tag. 3. Revoke any **scoped keys** issued for that user. Designing isolation up front makes erasure a single boundary operation instead of a forensic search. ### 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](/docs/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 Which tiers include BAAs, seats, and self-host options. How isolation works in the data model. API keys and access for the Developer Platform. Keep memory on your infrastructure. # Use cases Source: https://supermemory.ai/docs/overview/use-cases What teams build with Supermemory — agents, knowledge, multi-tenant products, and tools. Supermemory is one context engine. These are the shapes teams ship on top of it. ## Agents that remember Preferences, people, projects, and decisions across sessions — without replaying the entire chat history into every prompt. Account history, past tickets, and product facts at answer time. Isolate each customer with a container tag. Remember the account, stakeholders, and last commitments. Profiles keep “always know” context warm. Project conventions, past decisions, and repo context via API, MCP, or plugins (Claude Code, Codex, OpenClaw, and more). ## Knowledge and retrieval Drive, Notion, Gmail, OneDrive, S3, GitHub, web crawler — sync sources, then ask questions with managed SuperRAG. Policies, wikis, and internal docs as shared memory for the team. Papers, notes, and long documents with multimodal ingestion (PDF, images, audio/video) and hybrid search. Contracts and policy corpora with strict isolation and audit-friendly deletion paths. See [Security](/docs/overview/security). ## Multi-tenant products If you are building a SaaS that needs memory **per end user** (or per workspace): 1. Map each user/workspace to a **container tag** 2. Ingest conversations and files into that tag 3. Search / load **profiles** only inside that tag 4. Issue **scoped keys** when the client must not cross tenants 5. On account deletion, erase that container’s data Deep dive lives in Developer Platform concepts ([container tags](/docs/concepts/container-tags), [user profiles](/docs/concepts/user-profiles)). ## Surfaces (same engine) | If you want to… | Start here | | ----------------------------------- | ------------------------------------------- | | Call the Memory API from your app | [Quickstart](/docs/quickstart) | | Use Claude / Cursor / coding agents | [Plugins & MCP](/docs/supermemory-mcp/mcp) | | Keep data on your machines | [Self-hosting](/docs/self-hosting/overview) | ## Related Product overview and mental model. Concepts, API guides, and integrations. # What is Supermemory? Source: https://supermemory.ai/docs/overview/what-is-supermemory Supermemory is the long term and short term context and memory infrastructure for agents. Supermemory is **context infrastructure for AI agents**. We're one of the leading memory providers, with components to go beyond memory and configure it to be perfect for every usecase. It provides all the building blocks — Memory, Retrieval, Profiles, Connectors, Extractors, Evals, observability, and more.
With supermemory, developers can provide perfect recall about their users to build AI agents that are more intelligent, more personalized, and more consistent. It is the [state of the art](https://supermemory.ai/research) across multiple different benchmarks, like LongMemEval and LoCoMo. It's also the best in a lot of independantly run benchmarks, like the [SWEContext](https://arxiv.org/pdf/2602.08316) bench. ## How does it work? (at a glance) * You send Supermemory raw data in any format - text, files, and chats, or connect it to the data sources * Supermemory [intelligently indexes them](/docs/concepts/how-it-works) using our user understanding model and builds a semantic understanding graph on top of an entity (e.g., a user, a document, a project, an organization). We call these entities `containerTag` * This knowledge is now traversed by the agent, and an automatic profile is built for it. The agent may now use it for memory operations or for retrieval. ## Why add memory to your agent? Without memory, every session starts from zero. The model cannot know what the user preferred last week, which project they are on, or that a fact has changed since yesterday. **Memory** gives an agent durable understanding of *people and entities over time* — preferences, decisions, relationships, corrections. **Retrieval (RAG)** grounds answers in *documents and knowledge bases*. You usually want both. By adding memory to your agent, you can: * **Personalize** — remember preferences, roles, and history across sessions without stuffing the full chat log into every prompt * **Stay correct as facts change** — “I love Adidas” then “switching to Puma” should not leave both preferences equally true * **Ground answers** — pull the right policy, ticket, or doc when the question needs source material * **Ship multi-tenant products** — isolate each user or workspace so one customer’s memory never leaks into another’s You can think of memory as the always-on context a skilled teammate would carry — not a search box over raw logs. For the full category argument (vs DIY vectors, thin memory wrappers, pure RAG), see [Comparison](/docs/overview/comparison) and [Memory vs RAG](/docs/concepts/memory-vs-rag). ## Why Supermemory? * **State of the art on long-horizon memory** — #1 on [LongMemEval](https://supermemory.ai/research), [LoCoMo](https://supermemory.ai/research), and [ConvoMem](https://supermemory.ai/research), plus independent benches like [SWEContext](https://arxiv.org/pdf/2602.08316) * **Memory is a graph, not a blob store** — facts [update, connect, and forget](/docs/concepts/graph-memory) in real time; not nearest-neighbor chunks alone * **User profiles built in** — static + dynamic context the agent should [always know](/docs/concepts/user-profiles), \~ready for the prompt * **Memory + SuperRAG in one engine** — personalize *and* ground on the same `containerTag` / context pool * **Every door, one store** — API, [MCP](/docs/supermemory-mcp/mcp), plugins, [SMFS](/docs/smfs/overview), and connectors share the same memories * **Multimodal by default** — text, chats, PDFs, images, video, code via [extractors](/docs/concepts/content-types) and [connectors](/docs/connectors/overview) * **Run it your way** — managed cloud or [self-host](/docs/self-hosting/overview) as a single binary (including offline) memory graph Memory, profiles, and SuperRAG share the **same context pool** when you use the same isolation (`containerTag`). Mix and match for your product! A container can be anything - a user, a project, team, organization, etc. ## Next steps Make your first API call in minutes Understand the knowledge graph architecture vs DIY vectors, thin memory layers, pure RAG One binary, zero config, fully offline Credits, SM tokens, and how usage works SOC 2, GDPR, HIPAA BAA, encryption # Quickstart Source: https://supermemory.ai/docs/quickstart Ingest a conversation and a document, then use document search, memory graph traversal, and profiles — and wire it into a chat harness. 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 `containerTag` for everything. One engine, three ways out. ## 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. ```bash TypeScript theme={null} npm install supermemory export SUPERMEMORY_API_KEY="sm_..." ``` ```bash Python theme={null} pip install supermemory export SUPERMEMORY_API_KEY="sm_..." ``` ```bash curl theme={null} export SUPERMEMORY_API_KEY="sm_..." ``` ## 1. Ingest a conversation Real apps do not push four isolated one-liners as separate “memories.” They send **conversation turns** — often the full session — under a stable `customId` so the pipeline can extract facts and link entities. We’ll use one user (`user_4f8a`) and one chat session. The turns never say “Sarah *is* my VP of Product” — that connection is what the graph should resolve later. ```typescript TypeScript theme={null} import Supermemory from "supermemory"; const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY }); const user = "user_4f8a"; 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 client.add({ content: conversation, containerTag: user, customId: "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 theme={null} 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( content=conversation, container_tag=user, custom_id="chat_offsite_2026", metadata={"type": "conversation"}, dreaming="instant", # process this document now — see note below ) print(conv.id, conv.status) ``` ```bash curl theme={null} curl -X POST "https://api.supermemory.ai/v3/documents" \ -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.", "containerTag": "user_4f8a", "customId": "chat_offsite_2026", "metadata": { "type": "conversation" }, "dreaming": "instant" }' ``` `add` returns immediately with `status: "queued"`. Processing is still **async** — wait until `done` before searching. **`dreaming: "instant"`** — By default, dreaming is `"dynamic"`: Supermemory may batch related documents so memories form from coherent units, which can lag after `status: "done"`. 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](/docs/ingestion/add-memories#processing-modes). ## 2. Ingest a document Now add **knowledge** the agent should ground on — a short internal note the conversation never fully spelled out. This is the SuperRAG / document path. ```typescript TypeScript theme={null} 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 client.add({ content: handbook, containerTag: user, customId: "doc_gift_policy", metadata: { type: "document", source: "handbook" }, taskType: "superrag" }); console.log(doc.id, doc.status); ``` ```python Python theme={null} 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( content=handbook, container_tag=user, custom_id="doc_gift_policy", metadata={"type": "document", "source": "handbook"}, task_type="superrag" ) print(doc.id, doc.status) ``` ```bash curl theme={null} curl -X POST "https://api.supermemory.ai/v3/documents" \ -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.", "containerTag": "user_4f8a", "customId": "doc_gift_policy", "metadata": { "type": "document", "source": "handbook" }, "taskType": "superrag" }' ``` ## 3. Wait until both are `done` Poll document status. With **`dreaming: "instant"`**, once status is `done` the document is indexed **and** memories for that document should be available for search and profiles (`queued → extracting → … → done`). > Note that the preferred way is to have `dreaming: dynamic`. supermemory charges one extra operation for instant dreaming. Instant is good for one off tests, setup, debugging and benchmarking. ```typescript TypeScript theme={null} async function waitUntilDone(id: string) { for (;;) { const d = await client.documents.get(id); if (d.status === "done" || d.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 theme={null} import time def wait_until_done(doc_id: str): while True: d = client.documents.get(doc_id) if d.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 theme={null} # replace DOC_ID with each document id from the add responses curl "https://api.supermemory.ai/v3/documents/DOC_ID" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" # repeat until "status": "done" ``` Short text with instant dreaming usually finishes in a few seconds. Larger PDFs take longer. If you omit `dreaming` (default `"dynamic"`), document RAG can work after `done` while memory extraction may still be batching — use `"instant"` when the next step is memory search or profiles. ## 4. Three ways to get context back ### A. Document search (RAG) Chunk-level retrieval over raw knowledge — use when you need **what the docs say**. ```typescript TypeScript theme={null} const rag = await client.search({ q: "gift ideas for a VP promotion after a Tokyo offsite", containerTag: user, searchMode: "documents", limit: 3, }); for (const hit of rag.results) { console.log(hit.title ?? hit.id); for (const chunk of hit.chunks ?? []) { console.log(" ", chunk.content?.slice(0, 160)); } } ``` ```python Python theme={null} rag = client.search.memories( q="gift ideas for a VP promotion after a Tokyo offsite", container_tag=user, search_mode="documents", limit=3, ) for hit in rag.results: print(getattr(hit, "title", None) or hit.id) for chunk in hit.chunks or []: print(" ", (chunk.content or "")[:160]) ``` ```bash curl theme={null} curl -X POST "https://api.supermemory.ai/v3/search" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "q": "gift ideas for a VP promotion after a Tokyo offsite", "containerTag": "user_4f8a", "searchMode": "documents", "limit": 3 }' ``` You should see chunks from the handbook (budget range, Tokyo offsite angle) — **document grounding**, not personal facts. Switch `searchMode` to `"hybrid"` to get extracted memories and document chunks together: ```typescript theme={null} await client.search({ q: "gift ideas for a VP promotion after a Tokyo offsite", containerTag: user, searchMode: "hybrid", limit: 5, }); ``` ### B. Memory graph traversal Search **extracted memories** with related edges. This is the entity-chain moment: gift → VP of Product → Sarah → Tokyo offsite. ```typescript TypeScript theme={null} const memories = await client.search({ q: "What gift should I get, and why?", containerTag: user, searchMode: "memories", include: { relatedMemories: true }, limit: 5, }); console.log(JSON.stringify(memories, null, 2)); ``` ```python Python theme={null} memories = client.search.memories( q="What gift should I get, and why?", container_tag=user, search_mode="memories", include={"relatedMemories": True}, limit=5, ) print(memories) ``` ```bash curl theme={null} curl -X POST "https://api.supermemory.ai/v4/search" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "q": "What gift should I get, and why?", "containerTag": "user_4f8a", "searchMode": "memories", "include": { "relatedMemories": true }, "limit": 5 }' ``` Abbreviated shape: ```json theme={null} { "results": [ { "memory": "Sarah is being promoted to VP of Product", "similarity": 0.81, "context": { "parents": [ { "memory": "Sarah presented the Q3 roadmap at the Tokyo offsite", "relation": "extends" } ], "children": [ { "memory": "User needs a gift idea for their VP of Product, Sarah", "relation": "derives" } ] } } ], "timing": 287 } ``` You never wrote “Sarah is my VP of Product” as one sentence. The graph connected sessions of speech. Deep dive: [graph memory](/docs/concepts/graph-memory). ### C. User profile Profiles are the **always-on** summary (static + recent dynamic) of an entity (or a `containerTag`) - what you inject every turn without re-searching the world. ```typescript TypeScript theme={null} const { profile, searchResults } = await client.profile({ containerTag: user, q: "gift for the person being promoted", // optional: also run search }); console.log("static:", profile.static); console.log("dynamic:", profile.dynamic); console.log("search hits:", searchResults?.results?.length ?? 0); ``` ```python Python theme={null} result = client.profile( container_tag=user, q="gift for the person being promoted", ) print("static:", result.profile.static) print("dynamic:", result.profile.dynamic) print("search hits:", len(result.search_results.results) if result.search_results else 0) ``` ```bash curl theme={null} curl -X POST "https://api.supermemory.ai/v4/profile" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTag": "user_4f8a", "q": "gift for the person being promoted" }' ``` > There is a lot more to profiles - with [Buckets](/docs/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 `containerTag` → same context pool. See [Memory vs RAG](/docs/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 `customId` so the session stays one document. Here are two example shapes — pick whatever matches your stack. ### Example: explicit profile + search + add ```typescript TypeScript theme={null} // npm install supermemory openai import Supermemory from "supermemory"; import OpenAI from "openai"; import * as readline from "node:readline/promises"; const memory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY }); const llm = new OpenAI(); const user = "user_4f8a"; const sessionId = "chat_live_session"; const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); while (true) { const question = await rl.question("you: "); const { profile, searchResults } = await memory.profile({ containerTag: user, q: question, }); // optional: also pull document chunks for grounding const rag = await memory.search({ q: question, containerTag: user, searchMode: "documents", limit: 3, }); const docBits = rag.results .flatMap((r) => r.chunks ?? []) .map((c) => c.content) .filter(Boolean) .slice(0, 3); const context = [ "## Profile (static)", ...profile.static, "## Profile (dynamic)", ...profile.dynamic, "## Related memories", ...(searchResults?.results?.map((m) => m.memory).filter(Boolean) ?? []), "## Docs", ...docBits, ].join("\n"); const res = await llm.chat.completions.create({ model: "gpt-4o", messages: [ { role: "system", content: `You help this user. Context:\n${context}` }, { role: "user", content: question }, ], }); const answer = res.choices[0].message.content ?? ""; console.log(`assistant: ${answer}`); // append this turn into the same conversation document await memory.add({ content: `user: ${question}\nassistant: ${answer}`, containerTag: user, customId: sessionId, }); } ``` ```python Python theme={null} # 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: ") result = memory.profile(container_tag=user, q=question) rag = memory.search.memories( q=question, container_tag=user, search_mode="documents", limit=3 ) doc_bits = [] for hit in rag.results: for chunk in hit.chunks or []: if chunk.content: doc_bits.append(chunk.content[:300]) if len(doc_bits) >= 3: break memories = result.search_results.results if result.search_results else [] context = "\n".join( [ "## Profile (static)", *result.profile.static, "## Profile (dynamic)", *result.profile.dynamic, "## Related memories", *[m.memory for m in memories if m.memory], "## Docs", *doc_bits, ] ) 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}") memory.add( content=f"user: {question}\nassistant: {answer}", container_tag=user, custom_id=session_id, ) ``` ### Example: Vercel AI SDK Same pattern, wrapped: `withSupermemory` injects context and can save the conversation for you. Details: [AI SDK integration](/docs/integrations/ai-sdk). ```typescript theme={null} // 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"), { containerTag: "user_4f8a", customId: "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** `containerTag` (and optional same `customId` 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 (customId) → status === done → Memory graph (+ related) Document (customId) → status === done → Document search (RAG) → Profile (static + dynamic) │ ▼ Chat harness ``` ## Where next Conversations, files, URLs, customId updates, and status. Hybrid vs memories, filters, thresholds, rerank. How relations and entity chains are produced. Static vs dynamic, and when to inject a profile every turn. withSupermemory modes, customId, addMemory. Isolation for multi-tenant products. # Memory Operations Source: https://supermemory.ai/docs/recall/memory-operations Advanced memory operations (v4 API) These v4 endpoints operate on extracted memories (not raw documents). SDK support coming soon — use fetch or cURL for now. For document management (list, get, update, delete), see [Document Operations](/docs/ingestion/document-operations). For ingesting raw content (text, files, URLs) through the processing pipeline, see [Add Context](/docs/ingestion/add-memories). ## Create Memories Create memories directly without going through the document ingestion workflow. Memories are embedded and immediately searchable. This is useful for storing user preferences, traits, or any structured facts where you already know the exact memory content. ```typescript theme={null} const response = await fetch("https://api.supermemory.ai/v4/memories", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ memories: [ { content: "John prefers dark mode", isStatic: false, metadata: { source: "user_preference" } }, { content: "John is from Seattle", isStatic: true } ], containerTag: "user_123" }) }); const data = await response.json(); // { // documentId: "abc123", // memories: [ // { id: "mem_1", memory: "John prefers dark mode", isStatic: false, createdAt: "2025-..." }, // { id: "mem_2", memory: "John is from Seattle", isStatic: true, createdAt: "2025-..." } // ] // } ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v4/memories" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "memories": [ { "content": "John prefers dark mode", "isStatic": false, "metadata": { "source": "user_preference" } }, { "content": "John is from Seattle", "isStatic": true } ], "containerTag": "user_123" }' ``` ### Parameters | Parameter | Type | Required | Description | | --------------------- | ------- | -------- | ------------------------------------------------------------------------------------------- | | `memories` | array | yes | Array of memory objects (1–100 items) | | `memories[].content` | string | yes | The memory text (max 10,000 chars). Should be entity-centric, e.g. "John prefers dark mode" | | `memories[].isStatic` | boolean | no | `true` for permanent identity traits (name, hometown). Defaults to `false` | | `memories[].metadata` | object | no | Key-value metadata (strings, numbers, booleans) | | `containerTag` | string | yes | Space / container tag these memories belong to | ### Response ```json theme={null} { "documentId": "abc123", "memories": [ { "id": "mem_1", "memory": "John prefers dark mode", "isStatic": false, "createdAt": "2025-01-15T10:30:00.000Z" } ] } ``` | Field | Type | Description | | ---------------------- | -------------- | -------------------------------------------------------------- | | `documentId` | string \| null | ID of the lightweight source document created for traceability | | `memories` | array | The created memory entries | | `memories[].id` | string | Unique memory ID | | `memories[].memory` | string | The memory content | | `memories[].isStatic` | boolean | Whether this is a permanent trait | | `memories[].createdAt` | string | ISO 8601 timestamp | **When to use this vs [Add Context](/docs/ingestion/add-memories)?** Use **Create Memories** when you already know the exact facts to store (user preferences, traits, structured data). Use **Add Context** when you have raw content (conversations, documents, URLs) that Supermemory should process and extract memories from. *** ## Forget Memory Soft-delete a single memory — excluded from search results but preserved in the database. Identify it by `id` or by exact `content`, scoped to its `containerTag`. ```typescript theme={null} await fetch("https://api.supermemory.ai/v4/memories", { method: "DELETE", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ // Identify by ID or by exact content id: "mem_abc123", // content: "John prefers dark mode", containerTag: "user_123", reason: "outdated information" }) }); ``` ```bash theme={null} curl -X DELETE "https://api.supermemory.ai/v4/memories" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "id": "mem_abc123", "containerTag": "user_123", "reason": "outdated information" }' ``` ### Parameters | Parameter | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------------------- | | `id` | string | \* | Memory ID to forget | | `content` | string | \* | Exact content match to forget (alternative to ID) | | `containerTag` | string | yes | Container tag / space the memory belongs to | | `reason` | string | no | Optional reason recorded as `forgetReason` | \* Either `id` or `content` must be provided. The memory will no longer appear in search results but remains in the database (`isForgotten=true`). *** ## Forget Matching Forget in bulk in one call, two ways. Give a **`query`** (a prompt or topic) and the service semantically searches the container, an LLM decides which memories are genuinely about your target, and those are soft-deleted — use this for "forget everything about X". Or give an explicit **`ids`** list to forget exactly those memories with no search. Provide one or the other. This is a bulk, destructive operation. Always **`dryRun` first** to review what would be forgotten, then re-run with `dryRun: false`. The match is semantic, so a too-broad query can select more than you intend — `threshold` and `maxForget` bound the blast radius. ```typescript theme={null} // 1) Preview const preview = await fetch("https://api.supermemory.ai/v4/memories/forget-matching", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ query: "forget everything about Project Titan", containerTag: "user_123", dryRun: true }) }).then((r) => r.json()); // preview.candidates → [{ id, memory, score }, ...] // 2) Apply — pass the ids from the preview to forget exactly that set const result = await fetch("https://api.supermemory.ai/v4/memories/forget-matching", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ ids: preview.candidates.map((c) => c.id), containerTag: "user_123", dryRun: false, reason: "project cancelled" }) }).then((r) => r.json()); // result.forgotten → [{ id, memory, score }, ...] // result.forgetBatchId → tagged on every forgotten memory for traceability ``` ```bash theme={null} # Preview curl -X POST "https://api.supermemory.ai/v4/memories/forget-matching" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "forget everything about Project Titan", "containerTag": "user_123", "dryRun": true }' # Apply — pass the ids from the preview to forget exactly that set curl -X POST "https://api.supermemory.ai/v4/memories/forget-matching" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "ids": ["abc123", "def456", "ghi789"], "containerTag": "user_123", "dryRun": false, "reason": "project cancelled" }' ``` ### Parameters | Parameter | Type | Required | Description | | -------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `query` | string | one of\* | What to forget — a natural-language instruction ("forget everything about Project Titan") or a bare topic ("Project Titan") | | `ids` | string\[] | one of\* | Exact memory ids to forget instead of a `query` — no semantic search. Ids are validated against `containerTag`, so unknown or out-of-scope ids are ignored | | `containerTag` | string | yes | Container tag / space to scope the operation to | | `dryRun` | boolean | no | When `true`, returns what *would* be forgotten without changing anything. Defaults to `false` | | `threshold` | number | no | Similarity floor (0–1) for candidate memories (`query` mode only). Lower casts a wider net. Defaults to `0.5` | | `maxForget` | number | no | Safety cap for **query mode** — the most matches one call may forget (1–500). Defaults to `100`. Ignored in id mode, which forgets exactly the ids you pass (bounded only by the 500-item array limit) | | `reason` | string | no | Reason recorded as `forgetReason` on each forgotten memory | \* Provide either `query` or `ids`. ### Response ```json theme={null} { "dryRun": false, "count": 3, "forgetBatchId": "VcuQoGRz4hA4ak5Xu6DRUN", "summary": "Forgot 3 memories about \"Project Titan\".", "forgotten": [ { "id": "mem_1", "memory": "Project Titan ships in Q3", "score": 0.82 } ] } ``` | Field | Type | Description | | --------------- | -------------- | ----------------------------------------------------------------------------------- | | `dryRun` | boolean | Whether this was a preview or a real forget | | `count` | number | Number of memories selected (dryRun) or forgotten (apply) | | `forgetBatchId` | string \| null | ID tagged on every memory forgotten in this call; `null` on dryRun | | `summary` | string | One-line summary of the operation (e.g. `Forgot 3 memories about "Project Titan".`) | | `candidates` | array | On `dryRun`: the memories that **would** be forgotten (`{ id, memory, score }`) | | `forgotten` | array | On apply: the memories that **were** forgotten (`{ id, memory, score }`) | Identity is server-owned: the LLM only ever references opaque handles for the memories a search returned, so it can never forget a memory outside the results it reviewed, and every operation is scoped to the `containerTag` you pass. **Exact, bound deletes.** Applying with a `query` re-runs the semantic match, so the result can drift from the preview if the container changed in between. To forget *precisely* what you reviewed, take the `id`s from a `dryRun` preview and send them back as `ids` on the apply — the delete is then bound to exactly that set. (`ids` with `dryRun: true` returns the validated set as `candidates` without deleting, so you can confirm first.) *** ## Update Memory (Versioned) Update a memory by creating a new version. The original is preserved with `isLatest=false`. ```typescript theme={null} await fetch("https://api.supermemory.ai/v4/memories", { method: "PATCH", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ // Identify by ID or content id: "mem_abc123", // content: "Original content to match", newContent: "Updated content goes here", metadata: { tags: ["updated"] } }) }); ``` ```bash theme={null} curl -X PATCH "https://api.supermemory.ai/v4/memories" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "id": "mem_abc123", "newContent": "Updated content goes here", "metadata": {"tags": ["updated"]} }' ``` ### Parameters | Parameter | Type | Required | Description | | ------------ | ------ | -------- | --------------------------------------------- | | `id` | string | \* | Memory ID to update | | `content` | string | \* | Original content to match (alternative to ID) | | `newContent` | string | yes | New content for the memory | | `metadata` | object | no | Updated metadata | \* Either `id` or `content` must be provided. *** ## Next Steps * [Review Inferred Memories](/docs/recall/memory-review) — Approve or decline low-confidence memories * [Document Operations](/docs/ingestion/document-operations) — Manage documents (SDK supported) * [Search](/docs/recall/search) — Query your memories * [Ingesting Content](/docs/ingestion/add-memories) — Add new content # Review Inferred Memories Source: https://supermemory.ai/docs/recall/memory-review List and act on low-confidence inferred memories — approve, decline, or undo Supermemory's graph automatically **derives** new facts from patterns across your existing memories (see [Graph Memory](/docs/concepts/graph-memory)). These derived facts are guesses — the engine wasn't told them directly — so they are flagged as **inferred** (`isInference: true`) and **down-weighted in search** until confirmed. These two endpoints let you build a review experience on top of that queue: list the inferred memories awaiting review, then **approve**, **decline**, or **undo** a decision on each one. These endpoints are scoped to a single [container tag](/docs/concepts/container-tags) (space), under `/v3/container-tags/{containerTag}`. ## How review affects ranking While a memory is unreviewed and inferred it is down-weighted in search, so the engine's guesses rank below facts you stated explicitly. Reviewing it resolves that either way: | Action | Result | Effect on search | | ----------- | --------------------- | ------------------------------------------------------------ | | **Approve** | `isInference` cleared | Ranks like a stated fact — no longer down-weighted | | **Decline** | `isForgotten` set | Removed from search entirely — a rejected guess is forgotten | | **Undo** | back to unreviewed | Returns to the queue; inferred and down-weighted again | A reviewed memory is stamped with `reviewStatus` in its metadata so it drops out of the review queue (declined memories also leave search, since they're forgotten). **Undo** clears that stamp — and un-forgets a declined memory — bringing it back. *** ## List Inferred Memories Return the inferred memories for a container tag that are still awaiting review (the review queue). Reviewed memories are excluded. ``` GET /v3/container-tags/{containerTag}/inferred ``` ```typescript theme={null} const res = await fetch( "https://api.supermemory.ai/v3/container-tags/user_123/inferred", { headers: { "Authorization": `Bearer ${API_KEY}` } } ); const { memories, total } = await res.json(); ``` ```bash theme={null} curl "https://api.supermemory.ai/v3/container-tags/user_123/inferred" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` ### Path parameters | Parameter | Type | Description | | -------------- | ------ | ------------------------------------------------------ | | `containerTag` | string | The container tag / space to read the review queue for | ### Response ```json theme={null} { "memories": [ { "id": "mem_abc123", "memory": "Alex likely works on Stripe's core payments product", "parentCount": 3, "createdAt": "2025-01-15T10:30:00.000Z", "updatedAt": "2025-01-15T10:30:00.000Z", "metadata": { "source": "derive" } } ], "total": 1 } ``` | Field | Type | Description | | ------------------------ | -------------- | ------------------------------------------------------------------------ | | `memories[].id` | string | Memory entry ID — pass to the review endpoint | | `memories[].memory` | string | The inferred memory text | | `memories[].parentCount` | number | How many source memories this was derived from. Higher = stronger signal | | `memories[].createdAt` | string | ISO 8601 timestamp | | `memories[].updatedAt` | string | ISO 8601 timestamp | | `memories[].metadata` | object \| null | Arbitrary metadata stored on the memory | | `total` | number | Count of unreviewed inferred memories returned | The queue returns up to **50** memories, ordered by `parentCount` descending (most strongly supported first), then by `createdAt` descending. It excludes anything that is forgotten, expired, or already reviewed. An unknown or empty container tag returns `{ "memories": [], "total": 0 }`. *** ## Review an Inferred Memory Record a decision on a single inferred memory. ``` POST /v3/container-tags/{containerTag}/inferred/{memoryId}/review ``` ```typescript theme={null} const res = await fetch( "https://api.supermemory.ai/v3/container-tags/user_123/inferred/mem_abc123/review", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ action: "approve" }) } ); const result = await res.json(); // { id: "mem_abc123", isInference: false, isForgotten: false, reviewStatus: "approved" } ``` ```bash theme={null} curl -X POST \ "https://api.supermemory.ai/v3/container-tags/user_123/inferred/mem_abc123/review" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"action": "approve"}' ``` ### Path parameters | Parameter | Type | Description | | -------------- | ------ | ----------------------------------------------- | | `containerTag` | string | The container tag / space the memory belongs to | | `memoryId` | string | The memory entry ID from the list endpoint | ### Body parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | -------------------------------------- | | `action` | string | yes | One of `approve`, `decline`, or `undo` | The reject action is named **`decline`**. There is no `reject` value. **Action semantics:** * **`approve`** — Promote the memory: clears `isInference`, so it ranks like a stated fact instead of a down-weighted guess. Stamps `reviewStatus: "approved"`. * **`decline`** — Reject the suggestion: the memory is **forgotten** (`isForgotten: true`) and stamped `reviewStatus: "declined"`, so it leaves both search and the review queue. * **`undo`** — Revert a prior `approve`/`decline` back to the unreviewed inferred state: restores `isInference: true`, un-forgets the memory (`isForgotten: false`), and clears the review stamp, so it returns to the queue. ### Response ```json theme={null} { "id": "mem_abc123", "isInference": false, "isForgotten": false, "reviewStatus": "approved" } ``` | Field | Type | Description | | -------------- | -------------------------------------- | ----------------------------------------------------------------- | | `id` | string | The reviewed memory ID | | `isInference` | boolean | `false` after approve; `true` after decline or undo | | `isForgotten` | boolean | `true` after decline (the memory is forgotten); `false` otherwise | | `reviewStatus` | `"approved"` \| `"declined"` \| `null` | The new status; `null` after an undo | ### Errors | Status | When | | ------ | ------------------------------------------------------------------------------------------------------------- | | `401` | Missing or invalid authentication | | `404` | The container tag or memory was not found in your organization | | `409` | The memory isn't reviewable for this action — it's not an inferred memory, or there's no prior review to undo | *** ## Building a review experience The endpoints are designed for an optimistic, one-at-a-time review UI (swipe to keep / decline, with undo). A typical client fetches the queue once, then pops each card off locally as the user decides — `undo` re-adds it. ```typescript theme={null} import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; const BASE = "https://api.supermemory.ai/v3"; const key = (tag: string) => ["inferred-memories", tag] as const; export type InferredMemory = { id: string; memory: string; parentCount: number; createdAt: string; updatedAt: string; metadata: Record | null; }; export type ReviewAction = "approve" | "decline" | "undo"; export function useInferredMemories(containerTag: string) { return useQuery({ queryKey: key(containerTag), queryFn: async (): Promise => { const res = await fetch(`${BASE}/container-tags/${containerTag}/inferred`, { headers: { Authorization: `Bearer ${API_KEY}` }, }); if (!res.ok) throw new Error("Failed to load review queue"); const data = await res.json(); return data.memories ?? []; }, staleTime: 60_000, }); } export function useReviewInferredMemory(containerTag: string) { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (vars: { memoryId: string; action: ReviewAction }) => { const res = await fetch( `${BASE}/container-tags/${containerTag}/inferred/${vars.memoryId}/review`, { method: "POST", headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ action: vars.action }), }, ); if (!res.ok) throw new Error("Review failed"); return res.json(); }, onSuccess: (_data, { memoryId, action }) => { // approve/decline remove the card; undo brings it back, so refetch. if (action === "undo") { queryClient.invalidateQueries({ queryKey: key(containerTag) }); return; } queryClient.setQueryData(key(containerTag), (prev) => prev?.filter((m) => m.id !== memoryId), ); }, }); } ``` There is no separate "skip" action. A swipe-to-skip is purely client-side — don't send a request and the memory simply stays in the queue for a later session. *** ## Next Steps * [Graph Memory](/docs/concepts/graph-memory) — How inferred (`derive`) memories are created * [Memory Operations](/docs/recall/memory-operations) — Create, forget, and update memories * [Search](/docs/recall/search) — How inferred memories are ranked in results # Search Source: https://supermemory.ai/docs/recall/search Semantic search across your memories and documents Search through your memories and documents with a single API call. **Use `searchMode: "hybrid"`** for best results. It searches both memories and document chunks, returning the most relevant content. **TypeScript SDK:** call `client.search({ q, searchMode })` directly — `searchMode` (`"memories"`, `"documents"`, or `"hybrid"`) picks what comes back. `client.search.memories()` and `client.search.documents()` still work but are deprecated; no migration is required, just use `client.search()` going forward. The Python SDK is unaffected — `client.search.memories()` remains the call there. ## Quick Start ```typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory(); const results = await client.search({ q: "machine learning", containerTag: "user_123", searchMode: "hybrid", limit: 5 }); results.results.forEach(result => { console.log(result.memory || result.chunk, result.similarity); }); ``` ```python theme={null} from supermemory import Supermemory client = Supermemory() results = client.search.memories( q="machine learning", container_tag="user_123", search_mode="hybrid", limit=5 ) for result in results.results: print(result.memory or result.chunk, result.similarity) ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v4/search" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "q": "machine learning", "containerTag": "user_123", "searchMode": "hybrid", "limit": 5 }' ``` **Response:** ```json theme={null} { "results": [ { "id": "mem_xyz", "memory": "User is interested in machine learning for product recommendations", "similarity": 0.91, "metadata": { "topic": "interests" }, "updatedAt": "2024-01-15T10:30:00.000Z", "version": 1 }, { "id": "chunk_abc", "chunk": "Machine learning enables personalized experiences at scale...", "similarity": 0.87, "metadata": { "source": "onboarding_doc" }, "updatedAt": "2024-01-14T09:15:00.000Z", "version": 1 } ], "timing": 92, "total": 5 } ``` In hybrid mode, results contain either a `memory` field (extracted facts) or a `chunk` field (document content), depending on the source. *** ## Parameters | Parameter | Type | Default | Description | | -------------- | ------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `q` | string | required | Search query | | `containerTag` | string | — | Filter by user/project | | `searchMode` | string | `"memories"` | `"memories"`, `"hybrid"` (recommended), or `"documents"` | | `limit` | number | 10 | Max results | | `threshold` | 0-1 | 0.5 | Similarity cutoff (higher = fewer, better results) | | `rerank` | boolean | false | Re-score for better relevance (+100ms) | | `rewriteQuery` | boolean | false | Generate multiple rewrites, search all of them, and merge results. No extra cost, but adds latency. Composes with filtering, hybrid search, and recency bias | | `filters` | object | — | Metadata filters (`AND`/`OR` structure) | | `include` | object | — | `{ documents, summaries, relatedMemories, forgottenMemories }` — opt in to extra context per result | ### Search Modes * **`hybrid`** (recommended) — Searches both memories and document chunks, and returns both in the response * **`memories`** — Only searches extracted memories * **`documents`** — Only searches raw document/chunk content, skipping extracted memories ```typescript theme={null} // Hybrid: memories + document chunks (recommended) await client.search({ q: "quarterly goals", containerTag: "user_123", searchMode: "hybrid" }); // Memories only: just extracted facts await client.search({ q: "user preferences", containerTag: "user_123", searchMode: "memories" }); ``` *** ## Filtering Filter by `containerTag` to scope results to a user or project: ```typescript theme={null} const results = await client.search({ q: "project updates", containerTag: "user_123", searchMode: "hybrid" }); ``` Use `filters` for metadata-based filtering: ```typescript theme={null} const results = await client.search({ q: "meeting notes", containerTag: "user_123", filters: { AND: [ { key: "type", value: "meeting" }, { key: "year", value: "2024" } ] } }); ``` * **String equality:** `{ key: "status", value: "active" }` * **String contains:** `{ filterType: "string_contains", key: "title", value: "react" }` * **Numeric:** `{ filterType: "numeric", key: "priority", value: "5", numericOperator: ">=" }` * **Array contains:** `{ filterType: "array_contains", key: "tags", value: "important" }` * **Negate:** `{ key: "status", value: "draft", negate: true }` See [Organizing & Filtering](/docs/concepts/filtering) for full syntax. *** ## Query Optimization ### Reranking Re-scores results for better relevance. Adds \~100ms latency. ```typescript theme={null} const results = await client.search({ q: "complex technical question", containerTag: "user_123", rerank: true }); ``` ### Threshold Control result quality vs quantity: ```typescript theme={null} // Broad search — more results await client.search({ q: "...", threshold: 0.3 }); // Precise search — fewer, better results await client.search({ q: "...", threshold: 0.8 }); ``` ### Including Forgotten Memories By default, search excludes memories that have been forgotten or have passed their `forgetAfter` expiration. Set `include.forgottenMemories` to `true` to recover them: ```typescript theme={null} await client.search({ q: "old project notes", include: { forgottenMemories: true } }); ``` *** ## Chatbot Example Optimal configuration for conversational AI: ```typescript theme={null} async function getContext(userId: string, message: string) { const results = await client.search({ q: message, containerTag: userId, searchMode: "hybrid", threshold: 0.6, limit: 5 }); return results.results .map(r => r.memory || r.chunk) .join('\n\n'); } ``` ```typescript theme={null} interface SearchResult { id: string; memory?: string; // Present for memory results chunk?: string; // Present for document chunk results similarity: number; // 0-1 metadata: object | null; updatedAt: string; version: number; } interface SearchResponse { results: SearchResult[]; timing: number; // ms total: number; } ``` *** ## Next Steps * [Ingesting Content](/docs/ingestion/add-memories) — Add content to search * [User Profiles](/docs/recall/user-profiles) — Get user context with search * [Organizing & Filtering](/docs/concepts/filtering) — Container tags and metadata # User Profiles Source: https://supermemory.ai/docs/recall/user-profiles Fetch and use automatically maintained user context User profiles are extremely short summaries of context about an entity (Usually a user, but can be anything) which includes both the *static* facts about them, as well as a few recent episodes. > You can think of these as a dynamic compaction that's done by supermemory in real-time. This profile should be injected into the agent context for truly personalized experiences. To read more, visit [User profiles - Concept](/docs/concepts/user-profiles) Get a user's profile — their static facts and dynamic context — with a single API call. Profiles are built automatically as you [ingest content](/docs/ingestion/add-memories). No setup required. ## Quick Start ```typescript theme={null} import Supermemory from 'supermemory'; const client = new Supermemory(); const { profile } = await client.profile({ containerTag: "user_123" }); console.log(profile.static); // Long-term facts console.log(profile.dynamic); // Recent context ``` ```python theme={null} from supermemory import Supermemory client = Supermemory() result = client.profile(container_tag="user_123") print(result.profile.static) # Long-term facts print(result.profile.dynamic) # Recent context ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v4/profile" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTag": "user_123"}' ``` **Response:** ```json theme={null} { "profile": { "static": [ "User is a software engineer", "User specializes in Python and React", "User prefers dark mode interfaces" ], "dynamic": [ "User is working on Project Alpha", "User recently started learning Rust", "User is debugging authentication issues" ] } } ``` *** ## Profile + Search Get profile and search results in one call by adding the `q` parameter: ```typescript theme={null} const result = await client.profile({ containerTag: "user_123", q: "deployment errors" }); // Profile data const { static: facts, dynamic: context } = result.profile; // Search results (only if q was provided) const memories = result.searchResults?.results || []; ``` ```python theme={null} result = client.profile( container_tag="user_123", q="deployment errors" ) # Profile data facts = result.profile.static context = result.profile.dynamic # Search results memories = result.search_results.results if result.search_results else [] ``` *** ## Parameters | Parameter | Type | Required | Description | | -------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `containerTag` | string | Yes | User/project identifier | | `q` | string | No | Search query (includes search results in response) | | `threshold` | 0-1 | No | Filter search results by relevance score | | `filters` | object | No | Metadata filters applied to profile and search results | | `include` | string\[] | No | Sections to return — any of `"static"`, `"dynamic"`, `"buckets"`. Omit to return all | | `buckets` | string\[] | No | Restrict the `buckets` section to specific keys. Omit for all configured buckets. See [Profile Buckets](/docs/user-profiles/buckets) | *** ## Filtering Profiles Profiles support the same [metadata filters](/docs/concepts/filtering) as `/search` and `/documents/list` — `filters` narrows which memories are eligible to contribute to `static`, `dynamic`, and `buckets`, not just which search results come back. ```typescript theme={null} const { profile } = await client.profile({ containerTag: "user_123", filters: { AND: [{ key: "source", value: "onboarding" }], }, }); ``` ```python theme={null} result = client.profile( container_tag="user_123", filters={"AND": [{"key": "source", "value": "onboarding"}]}, ) ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v4/profile" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTag": "user_123", "filters": { "AND": [{ "key": "source", "value": "onboarding" }] } }' ``` Combine `filters` with `q` to scope both the profile synthesis and the accompanying search results in one call: ```typescript theme={null} const result = await client.profile({ containerTag: "org_customer_442", q: "billing issue", filters: { AND: [{ key: "channel", value: "support_ticket" }], }, }); ``` All filter types from [Organizing & Filtering](/docs/concepts/filtering) are supported — string equality, `string_contains`, `numeric`, `array_contains`, nested `AND`/`OR`, and `negate`. *** ## Building Prompts The most common pattern — inject profile into your LLM's system prompt: ```typescript theme={null} async function chat(userId: string, message: string) { const { profile } = await client.profile({ containerTag: userId }); const systemPrompt = `You are assisting a user. ABOUT THE USER: ${profile.static?.join('\n') || 'No profile yet.'} CURRENT CONTEXT: ${profile.dynamic?.join('\n') || 'No recent activity.'} Personalize responses to their expertise and preferences.`; return llm.chat({ messages: [ { role: "system", content: systemPrompt }, { role: "user", content: message } ] }); } ``` *** ## Full Context Pattern Get profile + query-specific memories in one call: ```typescript theme={null} async function getContext(userId: string, query: string) { const result = await client.profile({ containerTag: userId, q: query, threshold: 0.6 }); return ` User Background: ${result.profile.static.join('\n')} Current Context: ${result.profile.dynamic.join('\n')} Relevant Memories: ${result.searchResults?.results.map(m => m.memory).join('\n') || 'None'} `; } ``` *** ## Profile Buckets Buckets are **custom topical categories** for a profile — an axis that sits alongside `static` and `dynamic`, grouping facts by subject (e.g. `preferences`, `goals`, `work`) instead of by how long-lived they are. Read and configure buckets — request bucketed profiles, create org/space buckets, get AI-generated bucket suggestions, and see validation limits. *** ## Framework Examples ```typescript theme={null} async function withProfile(req, res, next) { if (!req.user?.id) return next(); try { const { profile } = await client.profile({ containerTag: req.user.id }); req.userProfile = profile; } catch (e) { req.userProfile = null; } next(); } app.use(withProfile); app.post('/chat', (req, res) => { // req.userProfile available in all routes }); ``` ```typescript theme={null} // app/api/chat/route.ts export async function POST(req: NextRequest) { const { userId, message } = await req.json(); const { profile } = await client.profile({ containerTag: userId }); const response = await generateResponse(message, profile); return NextResponse.json({ response }); } ``` ```typescript theme={null} import { withSupermemory } from "@supermemory/tools/ai-sdk" import { openai } from "@ai-sdk/openai" // Profiles automatically injected const model = withSupermemory(openai("gpt-4"), { containerTag: "user-123", customId: "conv-1", }) const result = await generateText({ model, messages: [{ role: "user", content: "Help with my project" }] }); ``` See [AI SDK Integration](/docs/integrations/ai-sdk) for details. *** ## Response Schema ```typescript theme={null} interface ProfileResponse { profile: { static?: string[]; // Long-term facts dynamic?: string[]; // Recent context buckets?: Record; // Topical buckets, keyed by bucket key }; searchResults?: { // Only if q parameter provided results: SearchResult[]; total: number; timing: number; }; } ``` *** ## Next Steps * [Profile Buckets](/docs/user-profiles/buckets) — Custom topical categories for profiles * [User Profiles Concept](/docs/concepts/user-profiles) — Understand static vs dynamic * [Ingesting Content](/docs/ingestion/add-memories) — Build profiles by adding content * [AI SDK Integration](/docs/integrations/ai-sdk) — Automatic profile injection # Self-Hosting Configuration Source: https://supermemory.ai/docs/self-hosting/configuration Every environment variable the self-hosted server understands. The self-hosted server aims for **zero configuration** — the only required input is one LLM provider key, which the first-boot wizard collects interactively (or set via env var for non-interactive deployments). Embeddings default to local English; you can pick another provider in the optional wizard step or via env. Everything else below is opt-in. The installer writes API keys to `~/.supermemory/env`, which is loaded on every launch. You can also set variables in your shell or a process manager. ## Core | Variable | Purpose | Default | | ------------------------------ | ---------------------------------------------------------------- | ---------------- | | `PORT` (or `SUPERMEMORY_PORT`) | HTTP listen port | `6767` | | `SUPERMEMORY_DATA_DIR` | Where the graph engine's data, auth secret, and model cache live | `./.supermemory` | ## LLM providers In production, Supermemory uses its own proprietary models tuned for long-horizon data understanding. Self-hosted, you bring your own LLM for the intelligent steps — summaries, contextual chunking, and memory extraction. Embeddings default to a local model (no API key) and can optionally use OpenAI, Gemini, or Ollama — see [Embeddings](/docs/self-hosting/embeddings). Configure **at least one** LLM provider: | Variable | Provider | | ----------------------------------------------------- | ----------------------------------------------------- | | `OPENAI_API_KEY` | OpenAI — or any OpenAI-compatible endpoint, see below | | `ANTHROPIC_API_KEY` | Anthropic | | `GEMINI_API_KEY` | Google AI Studio (Gemini) | | `GROQ_API_KEY` | Groq | | `WORKERS_AI_API_KEY` + `CLOUDFLARE_ACCOUNT_ID` | Cloudflare Workers AI | | `GOOGLE_VERTEX_PROJECT_ID` + `GOOGLE_VERTEX_LOCATION` | GCP Vertex AI | No key set? The server walks you through it. On first boot, an interactive setup wizard asks which provider you want, securely prompts for the key, and saves it encrypted — including a custom base URL and model name if you pick an OpenAI-compatible endpoint. With multiple providers configured, the first one in the order above is used. Image, video, and high-fidelity PDF understanding require a Gemini or Vertex AI key. Text ingestion, memory extraction, and search work with any provider. ### Fully offline with local models `OPENAI_API_KEY` + `OPENAI_BASE_URL` covers any OpenAI-compatible endpoint: Ollama, LM Studio, vLLM, llama.cpp server, Together, Fireworks, and more. ```bash theme={null} # Ollama example — gpt-oss-20b works great OPENAI_BASE_URL=http://localhost:11434/v1 OPENAI_API_KEY=ollama # any non-empty string for local runners OPENAI_MODEL=gpt-oss:20b ``` | Variable | Purpose | Default | | ------------------- | ------------------------------- | -------------- | | `OPENAI_BASE_URL` | OpenAI-compatible endpoint URL | OpenAI | | `OPENAI_MODEL` | Model ID sent to that endpoint | `gpt-5.1` | | `OPENAI_FAST_MODEL` | Override for fast/light tasks | `OPENAI_MODEL` | | `OPENAI_TEXT_MODEL` | Override for heavier text tasks | `OPENAI_MODEL` | ## File storage Nothing to configure. Uploaded files (PDFs, images) are stored on local disk inside `$SUPERMEMORY_DATA_DIR` and served by the server at `/files/:key`. ## Embeddings By default, vectors are computed locally with `Xenova/bge-base-en-v1.5` (768d) — no embedding API key. On interactive first boot you can pick a different provider after the LLM key step; for Docker/CI set env vars instead. Full provider table, multilingual guidance, remote examples (OpenAI / Gemini / Ollama), and the re-ingestion / dimension-lock warning: **[Embeddings (self-hosted)](/docs/self-hosting/embeddings)**. | Variable | Purpose | Default | | ---------------------------------- | -------------------------------------------------------- | ------------------------- | | `SUPERMEMORY_EMBEDDING_PROVIDER` | `local`, `openai`, `gemini`, or OpenAI-compatible remote | `local` | | `SUPERMEMORY_EMBEDDING_MODEL` | Model id for the chosen provider | `Xenova/bge-base-en-v1.5` | | `SUPERMEMORY_EMBEDDING_DIMENSIONS` | Vector size; must match model and stored data | `768` | | `SUPERMEMORY_EMBEDDING_BASE_URL` | Base URL for OpenAI-compatible embedding APIs | unset | ### Embedding performance Local embeddings are prewarmed at startup with conservative defaults — one worker, minimal CPU footprint. Turn these up if you're ingesting heavily and prefer throughput over headroom (remote embedding providers ignore these — there's no local worker pool to tune): | Variable | Purpose | Default | | --------------------------------------------- | --------------------------------------- | -------- | | `SUPERMEMORY_LOCAL_EMBEDDING_POOL_SIZE` | Number of embedding workers | `1` | | `SUPERMEMORY_LOCAL_EMBEDDING_WASM_THREADS` | Compute threads per worker | `1` | | `SUPERMEMORY_LOCAL_EMBEDDING_BATCH_SIZE` | Texts per worker dispatch | `8` | | `SUPERMEMORY_LOCAL_EMBEDDING_IDLE_TIMEOUT_MS` | Idle time before workers shut down | `120000` | | `SUPERMEMORY_SKIP_EMBEDDING_PREWARM` | Skip startup prewarm, load on first use | unset | ## Memory limits & ingestion queue The server manages memory for you and separates the two kinds of work you send it: * **Searches are always served immediately.** They never wait behind ingestion, regardless of how much is queued. * **Adds are accepted instantly but processed through a queue.** A `POST /v3/documents` call returns in milliseconds with status `queued`; extraction, embedding, and indexing happen in the background at a controlled pace. Ingestion may grow the server's memory usage by at most `SUPERMEMORY_EMBEDDING_RAM_LIMIT` (default **1 GB**) above its post-boot baseline. Past that, new documents simply wait in the queue until memory drops back under the limit — nothing is dropped, ingestion just slows down. The limit is measured above the boot baseline because the built-in local embeddings and storage engine have a fixed footprint that exists before any document is processed. The limit is printed at boot, and whenever adds are waiting the binary shows a live status line in the terminal: ``` [ingest] memory limit 1.0 GB above baseline (1.6 GB) · 2 concurrent — set SUPERMEMORY_EMBEDDING_RAM_LIMIT=ngb to change [ingest] 2 running · 193 queued · 0.4 GB / 1.0 GB ingest memory [ingest] 2 running · 193 queued · paused — 1.1 GB / 1.0 GB ingest memory, waiting for it to drop [ingest] resumed — memory back under the 1.0 GB ingest limit ``` | Variable | Purpose | Default | | --------------------------------- | --------------------------------------------------------------------------------------------------------- | ------- | | `SUPERMEMORY_EMBEDDING_RAM_LIMIT` | Memory ingestion may use above the boot baseline. Accepts `1gb`, `1.5gb`, `512mb`, or a bare number (GB). | `1gb` | | `SUPERMEMORY_INGEST_CONCURRENCY` | Documents processed concurrently | `2` | ```bash theme={null} # Give ingestion 4 GB of headroom on a larger machine SUPERMEMORY_EMBEDDING_RAM_LIMIT=4gb ./supermemory-server ``` Raise the limit and concurrency on machines with spare RAM for faster bulk imports; lower them on small VPSes where you want the server to stay lean and don't mind adds draining slowly. ## Telemetry The self-hosted binary sends no analytics — there is nothing to opt out of. The only related switch: | Variable | Purpose | Default | | ------------------------------- | -------------------------------------------------------------------- | ------- | | `SUPERMEMORY_DISABLE_TELEMETRY` | Set to `1` to also disable internal AI SDK telemetry instrumentation | unset | ## Platform-only features These exist in the codebase but are exclusive to the [hosted platform](https://console.supermemory.ai) — the self-hosted binary doesn't include them: * **Connectors** — Google Drive, Notion, Gmail, OneDrive background sync * **Supermemory MCP** — managed MCP server endpoints * **Optimized memory extraction** — the platform's extraction pipeline is tuned for higher quality at lower cost than bring-your-own-key * **Managed scale** — globally distributed infrastructure, no capacity planning Any other environment variables you may find referenced in the codebase are platform-only: the self-hosted binary ignores them even when set. ## Example: production-ish `.env` ```dotenv theme={null} # Persistent data location SUPERMEMORY_DATA_DIR=/var/lib/supermemory # One LLM provider (required for extraction) OPENAI_API_KEY=sk-... # Optional — omit to keep local Xenova/bge-base-en-v1.5 (768d) # SUPERMEMORY_EMBEDDING_PROVIDER=openai # SUPERMEMORY_EMBEDDING_MODEL=text-embedding-3-small # SUPERMEMORY_EMBEDDING_DIMENSIONS=1536 ``` That's enough for full ingestion, memory extraction, and hybrid search with the default local embeddings. # Embeddings (self-hosted) Source: https://supermemory.ai/docs/self-hosting/embeddings Local and remote embedding providers for Supermemory local — defaults, env vars, multilingual options, and dimension lock. Self-hosted Supermemory uses the **same embedding provider stack** as the hosted platform: local ONNX models, OpenAI, Gemini, or any OpenAI-compatible embeddings endpoint (including Ollama). LLM keys power extraction and summarization; embeddings are configured separately. ## Defaults | | | | ---------- | --------------------------- | | Provider | `local` | | Model | `Xenova/bge-base-en-v1.5` | | Dimensions | `768` | | API key | None — runs on your machine | Press Enter at the optional first-boot picker to keep this default. Nothing is sent off-box to embed. The default local model is **English-only**. Non-English content can ingest successfully while dense semantic recall stays weak. See [Multilingual](#multilingual). ## First-time setup (interactive) On first boot with a TTY, Supermemory asks for an LLM API key (required), then optionally which embedding model to use. 1. Choose or paste an LLM provider key (OpenAI, Anthropic, Gemini, Groq, or OpenAI-compatible). 2. Optionally pick an embedding provider/model. **Press Enter to keep the local English model.** 3. Choices are saved encrypted under your data directory (`$SUPERMEMORY_DATA_DIR`, typically `./.supermemory` / `~/.supermemory`). Boot order is intentional: LLM keys load first so remote embedding options can reuse them (for example OpenAI or Gemini embeddings with the same key). **First boot (terminal):** Supermemory asks for an LLM API key (required), then optionally which embedding model to use. Press Enter to keep the local English model. Choices are saved encrypted under your data directory. ## Configuration (env) For Docker, CI, or any non-interactive deploy, set env vars — there is **no interactive prompt without a TTY**. | Variable | Purpose | Default | | ---------------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------- | | `SUPERMEMORY_EMBEDDING_PROVIDER` | Embedding backend: `local`, `openai`, `gemini`, or an OpenAI-compatible remote (`ollama` / custom base URL) | `local` | | `SUPERMEMORY_EMBEDDING_MODEL` | Model id for the chosen provider | `Xenova/bge-base-en-v1.5` (local) | | `SUPERMEMORY_EMBEDDING_DIMENSIONS` | Vector size; must match the model and any already-stored data | `768` (local default) | | `SUPERMEMORY_EMBEDDING_BASE_URL` | Base URL for OpenAI-compatible embedding APIs (Ollama, vLLM, etc.) | unset | | `OPENAI_API_KEY` | Used when provider is `openai` (or compatible) if not otherwise supplied | unset | | `GEMINI_API_KEY` | Used when provider is `gemini` | unset | Local worker tuning (throughput only — does not change model or dimensions): | Variable | Purpose | Default | | --------------------------------------------- | --------------------------------------- | -------- | | `SUPERMEMORY_LOCAL_EMBEDDING_POOL_SIZE` | Number of embedding workers | `1` | | `SUPERMEMORY_LOCAL_EMBEDDING_WASM_THREADS` | Compute threads per worker | `1` | | `SUPERMEMORY_LOCAL_EMBEDDING_BATCH_SIZE` | Texts per worker dispatch | `8` | | `SUPERMEMORY_LOCAL_EMBEDDING_IDLE_TIMEOUT_MS` | Idle time before workers shut down | `120000` | | `SUPERMEMORY_SKIP_EMBEDDING_PREWARM` | Skip startup prewarm, load on first use | unset | Ingestion memory headroom is controlled by `SUPERMEMORY_EMBEDDING_RAM_LIMIT` — see [Memory limits & ingestion queue](/docs/self-hosting/configuration#memory-limits-&-ingestion-queue). **Docker / production:** Set at least one LLM key and, if you don’t want local embeddings, set `SUPERMEMORY_EMBEDDING_PROVIDER` / `SUPERMEMORY_EMBEDDING_MODEL` / `SUPERMEMORY_EMBEDDING_DIMENSIONS` (and base URL or API key as needed). There is no interactive prompt without a TTY. ## Multilingual The default `Xenova/bge-base-en-v1.5` model is trained for English. For German, Dutch, and other non-English corpora, dense recall can fail even when hybrid keyword search still finds rare tokens. For multilingual or non-English deployments, switch **before** large backfills: ```bash theme={null} # Example: local multilingual (set dimensions to match the model) SUPERMEMORY_EMBEDDING_PROVIDER=local SUPERMEMORY_EMBEDDING_MODEL=Xenova/bge-m3 SUPERMEMORY_EMBEDDING_DIMENSIONS=1024 ``` Or use a remote multilingual embedding API (OpenAI, Gemini, or Ollama with a multilingual embed model). Set provider, model, and dimensions together. Changing them later requires a fresh data directory or full re-ingestion — see below. ## Remote providers ### Local (default) ```bash theme={null} # Explicit local default — no embedding API key SUPERMEMORY_EMBEDDING_PROVIDER=local SUPERMEMORY_EMBEDDING_MODEL=Xenova/bge-base-en-v1.5 SUPERMEMORY_EMBEDDING_DIMENSIONS=768 ``` ### OpenAI ```bash theme={null} OPENAI_API_KEY=sk-... SUPERMEMORY_EMBEDDING_PROVIDER=openai SUPERMEMORY_EMBEDDING_MODEL=text-embedding-3-small SUPERMEMORY_EMBEDDING_DIMENSIONS=1536 ``` ### Gemini ```bash theme={null} GEMINI_API_KEY=... SUPERMEMORY_EMBEDDING_PROVIDER=gemini SUPERMEMORY_EMBEDDING_MODEL=text-embedding-004 SUPERMEMORY_EMBEDDING_DIMENSIONS=768 ``` ### Ollama (OpenAI-compatible) ```bash theme={null} SUPERMEMORY_EMBEDDING_PROVIDER=openai SUPERMEMORY_EMBEDDING_BASE_URL=http://localhost:11434/v1 OPENAI_API_KEY=ollama SUPERMEMORY_EMBEDDING_MODEL=nomic-embed-text SUPERMEMORY_EMBEDDING_DIMENSIONS=768 ``` Use the dimension published for your chosen model. A mismatch with vectors already in the store fails boot. ## Changing models later **Not supported in place.** Embeddings from different models (or different dimensions) are not comparable. Start from a fresh data directory or re-ingest all content so vectors stay in one space. If configured dimensions disagree with stored data, the server **refuses to boot**. **Changing embeddings later:** Not supported in place. Start from a fresh data directory or re-ingest all content so vectors stay comparable. > \[!IMPORTANT] > **Model Mixing Bug in v0.0.5 (Exact match returns nothing)** > > In version `v0.0.5`, there was a bug where the server could mix different embedding models between write and read paths (e.g., document ingestion using OpenAI but memory queries using local default embeddings). In multilingual contexts like Japanese (which lacks space tokenization for fallback lexical FTS matching), this caused exact-text memory searches through `/v4/search` and `/v4/profile` to silently return `{"results":[],"total":0}`. > > **Resolution:** > This was fully resolved in `v0.0.7` by locking the embedding plan uniformly across all document and query embedding paths (enforced via a locked plan in the database store). If you are running `v0.0.5` and experiencing this issue, you should upgrade to `v0.0.7` or later. ## Related * [Configuration](/docs/self-hosting/configuration) — LLM providers, storage, ingestion limits * [Quickstart](/docs/self-hosting/quickstart) — install and first memory # Local vs. Enterprise Source: https://supermemory.ai/docs/self-hosting/local-vs-enterprise Supermemory local is for builders. Supermemory Enterprise is for organizations. Supermemory local — the self-hosted binary — is free, open source, and built for individual developers: local-first workflows, prototyping, air-gapped experiments, privacy-sensitive side projects. **Supermemory Enterprise** is the full platform, run for your organization: the same memory engine with proprietary models, organizational controls, and infrastructure that scales with you — without you operating any of it. ## At a glance | | Supermemory local | Enterprise | | ------------------ | ------------------------------------------------------ | ---------------------------------------------------------------------- | | **Memory engine** | Full graph engine, embedded | Full graph engine, managed | | **Models** | Bring your own key (any provider, incl. fully offline) | Proprietary models tuned for long-horizon data understanding | | **Authentication** | Single auto-generated API key | Organization-wide authentication and access controls | | **Team access** | Single org on one machine | Multi-member organizations, roles, and scoped API keys | | **Observability** | Server logs | Control dashboard: usage analytics, ingestion monitoring, request logs | | **Control** | Env vars on your box | Org-wide settings, key management, and governance from the console | | **Connectors** | — | Google Drive, Notion, Gmail, OneDrive with continuous background sync | | **Scalability** | One machine, one process | Globally distributed, scales elastically with your workload | | **Hosting** | You run it | Fully managed — or dedicated deployments for compliance needs | | **Support** | Community ([GitHub](https://git.new/memory)) | Dedicated support, onboarding, and SLAs | ## What Enterprise adds ### Auth and team access Local runs as a single-tenant server with one API key. Enterprise gives your whole organization structured access: member roles, and API keys scoped per environment, per team, or per app — all revocable from one place. ### Observability and the control dashboard Local gives you logs. Enterprise gives you the console: live usage analytics, ingestion pipeline visibility, search and request logs, and per-key attribution — so you always know what your agents are remembering, and what it costs. ### Memory quality Local runs the extraction pipeline on whatever model you bring. Enterprise runs it on Supermemory's proprietary models, purpose-tuned for long-horizon data understanding — higher-quality memories at a lower effective cost than any bring-your-own-key setup. ### Scale and hosting Local is bounded by one machine — which is the point. Enterprise runs on globally distributed infrastructure that scales with your ingestion volume and query load, with no capacity planning on your side. For strict data residency or compliance requirements, dedicated deployment options are available. ## Moving between them The two speak the same API. Code written against your local server moves to Enterprise by changing the `baseURL` — and vice versa. Prototype locally, ship on Enterprise. Get a walkthrough of Supermemory Enterprise for your team Install the binary and build against the same API today # Supermemory local Source: https://supermemory.ai/docs/self-hosting/overview State-of-the-art memory, running on your machine. One binary, zero config. 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. ```bash curl theme={null} curl -fsSL https://supermemory.ai/install | bash ``` ```bash npx theme={null} npx supermemory local ``` No Docker. No database to provision. No config files. It boots in seconds with everything built in, and it's [open source](https://git.new/memory). ## 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. Same provider stack as cloud if you opt into OpenAI, Gemini, or Ollama — see [Embeddings](/docs/self-hosting/embeddings). * **An API key, generated for you** — printed on first boot, ready to paste into any SDK. * **The full Memory API** — `/v3/documents`, `/v4/search`, `/v4/profile`, spaces, 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 theme={null} 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. Your data never leaves the building. ## Drop-in with your existing code The self-hosted server speaks the same API as the hosted platform. Point any Supermemory SDK at it with a one-line change: ```typescript theme={null} const client = new Supermemory({ apiKey: "sm_...", // printed on first boot baseURL: "http://localhost:6767", }) ``` Everything in the [Memory API docs](/docs/quickstart) works the same way. The coding plugins do too — [Claude Code](/docs/integrations/claude-code), [Codex](/docs/integrations/codex), and [OpenCode](/docs/integrations/opencode) all target your local server with `SUPERMEMORY_API_URL=http://localhost:6767`. ## Self-hosted vs. the platform Self-hosted is free, open source, and great for local development, air-gapped environments, and privacy-sensitive workloads. 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) | Same provider stack, managed | | File ingestion (PDFs, images) | ✅ | ✅ | | [Connectors](/docs/connectors/overview) (Google Drive, Notion, Gmail, OneDrive) | — | ✅ | | [Supermemory MCP](/docs/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 — [the platform](https://console.supermemory.ai) is one `baseURL` change away. Running this for a team or organization? See [Local vs. Enterprise](/docs/self-hosting/local-vs-enterprise). ## Next steps Install, run, and store your first memory in under two minutes Every environment variable: LLM providers, storage, auth, tuning Local default, remote providers, multilingual, dimension lock # Using supermemory local with different providers Source: https://supermemory.ai/docs/self-hosting/providers Copy-paste .env setup for Ollama, OpenAI, Anthropic, Gemini, and OpenRouter Supermemory local needs one model provider to power summaries, contextual chunking, and memory extraction. Pick the tab for whichever one you already have a key for. For the full variable reference (fast/text model overrides, offline setup, tuning), see [Configuration](/docs/self-hosting/configuration). Fully offline — no API key leaves your machine. Any OpenAI-compatible local runner works the same way (LM Studio, vLLM, llama.cpp server); this is the Ollama version. ```bash theme={null} ollama pull gpt-oss:20b ``` ```bash .env theme={null} OPENAI_BASE_URL=http://localhost:11434/v1 OPENAI_API_KEY=ollama # any non-empty string — Ollama doesn't check it OPENAI_MODEL=gpt-oss:20b ``` `gpt-oss:20b` is a good default for a laptop-class GPU. Bigger models work if you have the VRAM — set `OPENAI_MODEL` to whatever you've pulled. ```bash .env theme={null} OPENAI_API_KEY=sk-... ``` That's it — defaults to `gpt-5.1`. Override with `OPENAI_MODEL` if you want a different one. ```bash .env theme={null} ANTHROPIC_API_KEY=sk-ant-... ``` Runs on `claude-haiku-4-5` — this one isn't currently configurable via env var. ```bash .env theme={null} GEMINI_API_KEY=... ``` Runs on `gemini-3.1-flash-lite-preview` — also not currently overridable. This is the only key that also unlocks image, video, and high-fidelity PDF understanding (see the [full provider table](/docs/self-hosting/configuration#llm-providers)). OpenRouter isn't a native provider — it's OpenAI-compatible, so it slots into the same `OPENAI_BASE_URL` path as Ollama: ```bash .env theme={null} OPENAI_BASE_URL=https://openrouter.ai/api/v1 OPENAI_API_KEY=sk-or-... OPENAI_MODEL=openai/gpt-4o-mini ``` Set `OPENAI_MODEL` to any model slug from [OpenRouter's model list](https://openrouter.ai/models) — routing, fallback, and pricing all follow OpenRouter's own rules from there. # Self-Hosting Quickstart Source: https://supermemory.ai/docs/self-hosting/quickstart From zero to your first memory in under two minutes. ## Install ```bash theme={null} curl -fsSL https://supermemory.ai/install | bash ``` ```bash theme={null} npx supermemory local ``` ```bash theme={null} bunx supermemory local ``` The installer detects your OS and architecture, downloads the right binary, verifies it, and (when run interactively) prompts you for an LLM API key. Supported platforms: macOS (Apple Silicon & Intel), Linux (x64 & arm64). ### Pin or change versions Pass an explicit version to install (or roll back to) a specific release instead of `latest`: ```bash theme={null} curl -fsSL https://supermemory.ai/install | bash -s -- 0.0.3 ``` Before rolling back, back up your [data directory](#where-things-live). The installer replaces the binary, but an older server may not understand data or schema changes made by a newer release. Release tags are `server-v` 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 theme={null} 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 theme={null} supermemory-server ``` First boot sets everything up — the embedded Supermemory graph engine, local embeddings, and your credentials: ``` ┌──────────────────────────────────────────────────┐ │ url http://localhost:6767 │ │ database ./.supermemory │ │ api key sm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx │ │ org id xxxxxxxxxxxxxxxxxxxxxx │ └──────────────────────────────────────────────────┘ ``` Save that API key — it's your bearer token for every request. In production, Supermemory runs proprietary models tuned for long-horizon data understanding. Self-hosted, you bring any model: if no provider key is set, first boot launches an interactive setup wizard — pick a provider (OpenAI, Anthropic, Gemini, Groq, or any OpenAI-compatible endpoint like Ollama), paste your key, and it's saved encrypted for every future launch. After the LLM key, you can optionally pick an embedding model (press Enter to keep local `Xenova/bge-base-en-v1.5`). See [all providers](/docs/self-hosting/configuration#llm-providers), [embeddings](/docs/self-hosting/embeddings), and [fully-offline local models](/docs/self-hosting/configuration#fully-offline-with-local-models). **Docker / non-interactive:** set an LLM key via env and, if you don’t want local embeddings, set `SUPERMEMORY_EMBEDDING_PROVIDER` / `MODEL` / `DIMENSIONS`. There is no wizard without a TTY. ## Add your first memory ```typescript theme={null} import Supermemory from "supermemory" const client = new Supermemory({ apiKey: "sm_...", baseURL: "http://localhost:6767", }) await client.memories.add({ content: "I'm Dhravya. I love building dev tools and I'm allergic to peanuts.", containerTag: "user_dhravya", }) ``` ```python theme={null} from supermemory import Supermemory client = Supermemory( api_key="sm_...", base_url="http://localhost:6767", ) client.memories.add( content="I'm Dhravya. I love building dev tools and I'm allergic to peanuts.", container_tag="user_dhravya", ) ``` ```bash theme={null} curl http://localhost:6767/v3/documents \ -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.", "containerTag": "user_dhravya" }' ``` ## Search it ```typescript theme={null} const results = await client.search({ q: "what food should I avoid?", containerTag: "user_dhravya", }) ``` ```python theme={null} results = client.search( q="what food should I avoid?", container_tag="user_dhravya", ) ``` ```bash theme={null} curl http://localhost:6767/v3/search \ -H "Authorization: Bearer sm_..." \ -H "Content-Type: application/json" \ -d '{ "q": "what food should I avoid?", "containerTag": "user_dhravya" }' ``` That's it. Everything in the [Memory API](/docs/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`) | The Supermemory graph engine's data, auth secret, embedding model cache | | `~/.supermemory/env` | API keys saved by the installer, loaded on every launch | ## Next steps LLM providers, local models, performance tuning Local default, OpenAI / Gemini / Ollama, multilingual The full API — it all works against your local server # Bash Tool Source: https://supermemory.ai/docs/smfs/bash-tool @supermemory/bash. The SMFS idea wrapped as a single agent tool, for serverless and edge runtimes. `@supermemory/bash` is the SMFS idea wrapped as a single agent tool: `run_bash(command)`. The "filesystem" is your Supermemory container. Runs anywhere TypeScript runs. Cloudflare Workers, AWS Lambda, Vercel, Node, the browser. No mount, no FUSE, no local disk. Reach for the Bash Tool when your agent runs somewhere it can't mount a real filesystem. ## Install ```bash theme={null} npm install @supermemory/bash ``` Or with bun: ```bash theme={null} bun add @supermemory/bash ``` ## Quickstart ```typescript theme={null} import { createBash } from "@supermemory/bash"; const { bash, toolDescription } = await createBash({ apiKey: process.env.SUPERMEMORY_API_KEY!, containerTag: "user_42", }); const result = await bash.exec("ls /"); console.log(result.stdout); ``` `createBash` returns: * `bash`: the instance with `.exec(cmd)` * `toolDescription`: a pre-written tool description ready to hand to the model * `configureMemoryPaths(paths)`: scope which paths get extracted into Supermemory * `refresh()`: re-prime the path index after external writes ## Use it as a model tool ### Vercel AI SDK ```typescript theme={null} import { generateText, tool } from "ai"; import { openai } from "@ai-sdk/openai"; import { z } from "zod"; const { bash, toolDescription } = await createBash({ apiKey: process.env.SUPERMEMORY_API_KEY!, containerTag: "user_42", }); const result = await generateText({ model: openai("gpt-4o"), tools: { bash: tool({ description: toolDescription, inputSchema: z.object({ cmd: z.string() }), execute: async ({ cmd }) => bash.exec(cmd), }), }, prompt: "What's in my notes about the Q3 launch?", }); ``` ### Anthropic SDK ```typescript theme={null} import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! }); const { bash, toolDescription } = await createBash({ apiKey: process.env.SUPERMEMORY_API_KEY!, containerTag: "user_42", }); const response = await client.messages.create({ model: "claude-opus-4-7", max_tokens: 4096, tools: [ { name: "bash", description: toolDescription, input_schema: { type: "object", properties: { cmd: { type: "string" } }, required: ["cmd"], }, }, ], messages: [{ role: "user", content: "List my notes" }], }); ``` ### OpenAI SDK ```typescript theme={null} import OpenAI from "openai"; const client = new OpenAI(); const { bash, toolDescription } = await createBash({ apiKey: process.env.SUPERMEMORY_API_KEY!, containerTag: "user_42", }); const response = await client.chat.completions.create({ model: "gpt-4o", messages: [{ role: "user", content: "List my notes" }], tools: [ { type: "function", function: { name: "bash", description: toolDescription, parameters: { type: "object", properties: { cmd: { type: "string" } }, required: ["cmd"], }, }, }, ], }); ``` ## Memory The Bash Tool inherits SMFS memory semantics. By default, files named `user.md` or `memory.md` are extracted as memories. Configure additional memory paths after construction: ```typescript theme={null} const { configureMemoryPaths } = await createBash({ apiKey, containerTag }); await configureMemoryPaths(["/notes/", "/journal.md"]); ``` Trailing `/` matches recursively. No slash matches an exact file. Pass `[]` to disable memory generation. The container also exposes a virtual `profile.md` at the root: a live digest of everything in the container. Read it once at the start of a session to give the model context without walking every file. ```typescript theme={null} const { stdout } = await bash.exec("cat /profile.md"); ``` ## Commands the agent can run Standard Unix surface, plus one custom command. Each does what you'd expect. ### Filesystem * `pwd`: print working directory * `cd`: change directory * `ls`, `ls -la`: list * `cat`: read a file * `stat`: file metadata * `mkdir`: create directory * `rm`, `rm -rf`: delete * `rmdir`: delete empty directory * `mv`: move or rename * `cp`: copy * `echo`: write or append (`echo "x" > file`, `echo "x" >> file`) ### Search and text * `grep`: literal substring match against a known path * `sgrep [path]`: **semantic** search across the container. Trailing `/` on path scopes to a directory. No path searches everything. * `find`: search by name or properties * `head`, `tail`: first or last N lines * `wc`: word, line, byte counts * `sort`: sort lines * `sed`, `awk`: text transformation ### Shell features * Pipes (`|`) * Redirects (`>`, `>>`) * Conditionals (`&&`, `||`) * Loops (`for`, `while`) * File tests (`[ -f ]`, `[ -d ]`, `[ -e ]`) ## Configuration | Option | Default | Purpose | | -------------- | ----------- | -------------------------------------------------------------------------------- | | `apiKey` | required | Supermemory API key | | `containerTag` | required | Container to expose as the filesystem | | `baseURL` | SDK default | Override the API endpoint | | `eagerLoad` | `true` | Warm the path index when the instance starts | | `eagerContent` | `true` | Also warm the content cache during eager load | | `cacheTtlMs` | `150_000` | Content cache TTL in ms. `null` = never expires (single-writer). `0` = no cache. | Other options (`customCommands`, `logger`, plus `just-bash` pass-throughs like `executionLimits`, `network`, `python`, `javascript`, `cwd`, `env`) exist but aren't part of the supported surface for the SMFS use case. The container is what defines the filesystem; setting `cwd` or extra `env` from the host doesn't change that. ## Limitations * `chmod`, `utimes`, and symlinks (`ln -s`, `readlink`) throw `ENOSYS`. * `/dev/null` as a redirect target isn't supported. Write to `/tmp/discard.log` instead. * Binary uploads aren't supported. Text is extracted server-side. # Bash Tool (Python) Source: https://supermemory.ai/docs/smfs/bash-tool-python supermemory-bash. The SMFS idea wrapped as a single agent tool, for Python agents and serverless runtimes. `supermemory-bash` is the SMFS idea wrapped as a single agent tool: `run_bash(command)`. The "filesystem" is your Supermemory container. Runs anywhere Python runs. AWS Lambda, Modal, Fly Machines, Cloud Run, your laptop. No mount, no FUSE, no local disk. Reach for the Bash Tool when your agent runs somewhere it can't mount a real filesystem. ## Install ```bash theme={null} pip install supermemory-bash ``` Or with uv: ```bash theme={null} uv add supermemory-bash ``` ## Quickstart ```python theme={null} import asyncio import os from supermemory_bash import create_bash async def main() -> None: result = await create_bash( api_key=os.environ["SUPERMEMORY_API_KEY"], container_tag="user_42", ) bash = result.bash r = await bash.exec("ls /") print(r.stdout) asyncio.run(main()) ``` `create_bash` returns a `CreateBashResult` with: * `bash`: a `Shell` instance with `.exec(cmd)` * `tool_description`: a pre-written tool description ready to hand to the model * `configure_memory_paths(paths)`: scope which paths get extracted into Supermemory * `refresh()`: re-prime the path index after external writes ## Use it as a model tool ### Anthropic SDK Pass `tool_description` straight into Claude's tool definition and run a normal agent loop. Each `tool_use` block calls `bash.exec` and the result goes back as a `tool_result`. ```python theme={null} import asyncio import os import anthropic from supermemory_bash import create_bash async def run_agent(user_message: str) -> str: result = await create_bash( api_key=os.environ["SUPERMEMORY_API_KEY"], container_tag="user_42", ) bash = result.bash client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"]) tools = [ { "name": "bash", "description": result.tool_description, "input_schema": { "type": "object", "properties": { "cmd": {"type": "string", "description": "The bash command to run."} }, "required": ["cmd"], }, } ] messages = [{"role": "user", "content": user_message}] for _ in range(10): response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=4096, tools=tools, messages=messages, ) if response.stop_reason == "end_turn": for block in response.content: if hasattr(block, "text"): return block.text return "" messages.append({"role": "assistant", "content": response.content}) tool_results = [] for block in response.content: if block.type == "tool_use": cmd = block.input.get("cmd", "") r = await bash.exec(cmd) output = r.stdout if r.stderr: output += f"\n[stderr]: {r.stderr}" if r.exit_code != 0: output += f"\n[exit_code]: {r.exit_code}" tool_results.append( { "type": "tool_result", "tool_use_id": block.id, "content": output or "(no output)", } ) messages.append({"role": "user", "content": tool_results}) return "(max steps reached)" asyncio.run(run_agent("What's in my notes about the Q3 launch?")) ``` ### OpenAI SDK Same idea with OpenAI's function-calling format. Define a single `bash` function, dispatch each `tool_calls` entry to `bash.exec`, and feed the output back as a `tool` message. ```python theme={null} import asyncio import json import os from openai import OpenAI from supermemory_bash import create_bash async def run_agent(user_message: str) -> str: result = await create_bash( api_key=os.environ["SUPERMEMORY_API_KEY"], container_tag="user_42", ) bash = result.bash client = OpenAI() tools = [ { "type": "function", "function": { "name": "bash", "description": result.tool_description, "parameters": { "type": "object", "properties": {"cmd": {"type": "string"}}, "required": ["cmd"], }, }, } ] messages = [{"role": "user", "content": user_message}] for _ in range(10): response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, ) message = response.choices[0].message if not message.tool_calls: return message.content or "" messages.append(message.model_dump(exclude_none=True)) for call in message.tool_calls: args = json.loads(call.function.arguments or "{}") r = await bash.exec(args.get("cmd", "")) output = r.stdout if r.stderr: output += f"\n[stderr]: {r.stderr}" if r.exit_code != 0: output += f"\n[exit_code]: {r.exit_code}" messages.append( { "role": "tool", "tool_call_id": call.id, "content": output or "(no output)", } ) return "(max steps reached)" asyncio.run(run_agent("List my notes")) ``` ### Claude Agent SDK The [Claude Agent SDK](https://docs.claude.com/en/api/agent-sdk/overview) ships with built-in `Bash`, `Read`, and `Write` tools. If your agent runs somewhere SMFS can be mounted (a long-lived process on macOS or Linux), point those built-ins at an SMFS mount and you don't need `supermemory-bash` at all — the agent just sees your container as a directory. See [Mount SMFS](/docs/smfs/mount) for setup, or the [provider guides](/docs/smfs/overview#use-smfs-with-your-sandbox-provider) for sandbox-specific instructions. ## Memory The Bash Tool inherits SMFS memory semantics. By default, files named `user.md` or `memory.md` are extracted as memories. Configure additional memory paths after construction: ```python theme={null} result = await create_bash(api_key=api_key, container_tag=container_tag) bash = result.bash await result.configure_memory_paths(["/notes/", "/journal.md"]) ``` Trailing `/` matches recursively. No slash matches an exact file. Pass `[]` to disable memory generation. The container also exposes a virtual `profile.md` at the root: a live digest of everything in the container. Read it once at the start of a session to give the model context without walking every file. ```python theme={null} r = await bash.exec("cat /profile.md") print(r.stdout) ``` ## Commands the agent can run The Python tool exposes the same command surface as the TypeScript version: standard Unix builtins (`pwd`, `cd`, `ls`, `cat`, `stat`, `mkdir`, `rm`, `mv`, `cp`, `echo`), search and text utilities (`grep`, `find`, `head`, `tail`, `wc`, `sort`, `sed`, `awk`), plus the custom `sgrep [path]` for semantic search across the container. Pipes, redirects, conditionals, loops, and file tests all work. See the [TypeScript Bash Tool reference](/docs/smfs/bash-tool#commands-the-agent-can-run) for the full list. ## Configuration | Option | Default | Purpose | | --------------- | -------------- | -------------------------------------------------------------------------------- | | `api_key` | required | Supermemory API key | | `container_tag` | required | Container to expose as the filesystem | | `base_url` | `None` | Override the API endpoint | | `eager_load` | `True` | Warm the path index when the instance starts | | `eager_content` | `True` | Also warm the content cache during eager load | | `cwd` | `"/home/user"` | Initial working directory | | `env` | `None` | Extra environment variables | | `cache_ttl_ms` | `150_000` | Content cache TTL in ms. `None` = never expires (single-writer). `0` = no cache. | The container is what defines the filesystem; setting `cwd` or extra `env` from the host doesn't change the files the agent sees. ## Limitations * `chmod`, `utimes`, and symlinks (`ln -s`, `readlink`) raise `ENOSYS`. * `/dev/null` as a redirect target isn't supported. Write to `/tmp/discard.log` instead. * Binary uploads aren't supported. Text is extracted server-side. # Examples Source: https://supermemory.ai/docs/smfs/examples Full web-based demo apps you can clone and run. Web-based example apps showing SMFS in realistic use cases. Each one is a complete project with its own README, dependencies, and a working UI. Upload documents and chat with an AI that can search and cite them. Next.js + TypeScript + `@supermemory/bash`. Add notes and chat with an AI that can search your knowledge base. FastAPI + Python + `supermemory-bash`. Write and run code in an E2B sandbox with persistent AI memory. Next.js + E2B SDK + SMFS mount. The Research Assistant and Knowledge Base examples use the [Bash Tool](/docs/smfs/bash-tool) — the serverless-friendly way to give an agent a Supermemory-backed filesystem. The Code Sandbox example uses an [E2B](/docs/smfs/providers/e2b) sandbox with a real SMFS mount. ## Running an example 1. Clone the [examples repo](https://github.com/supermemoryai/examples) 2. `cd` into the example you want 3. Follow the README — typically: install deps, copy `.env.example` to `.env`, fill in your API keys, and start the dev server # Install SMFS Source: https://supermemory.ai/docs/smfs/install Install, log in, mount. ## 1. Install the binary ```bash theme={null} curl -fsSL https://smfs.ai/install | bash ``` Drops `smfs` into `~/.local/bin`. Works on macOS (arm64, x64) and Linux (arm64, x64). If `smfs` isn't on your `PATH` after install, add `~/.local/bin` to your shell profile and reopen the terminal. ## 2. Log in ```bash theme={null} smfs login ``` One-time. Prompts you for your Supermemory API key and stores it in your global credentials. Get a key at [console.supermemory.ai](https://console.supermemory.ai). You can also pass the key directly: ```bash theme={null} smfs login --key sm_... ``` ## 3. Mount a container ```bash theme={null} smfs mount agent_memory ``` `agent_memory` is your container tag. SMFS creates a folder named `agent_memory/` in the current directory and mounts the container there. That's it. Read it with `ls`, `cat`, `grep`. See [Mount](/docs/smfs/mount) for memory paths, sync modes, flags, and every subcommand. To mount somewhere else, pass `--path`: ```bash theme={null} smfs mount agent_memory --path ~/memory ``` ## Optional: refresh the semantic grep wrapper `smfs mount` installs the shell wrapper automatically the first time you mount. If you ever need to force a clean reinstall (after upgrading the binary, for example): ```bash theme={null} smfs init ``` It writes the wrapper into your `~/.zshrc` directly. Then reopen your terminal (or `source ~/.zshrc`) so the new shell picks it up. Inside any mount, plain `grep` becomes semantic. Outside a mount, your normal `grep` is untouched. Pass any flag (`grep -r`, `grep -i`, anything) and you get the real `grep` back. ## Refresh the binary If anything ever feels broken: ```bash theme={null} smfs install ``` Re-copies the binary into `~/.local/bin` and resets permissions. # Mount Source: https://supermemory.ai/docs/smfs/mount Mount a Supermemory container, generate memories, and sync. A mount turns a Supermemory container into a directory on your machine. macOS uses NFSv3, Linux uses FUSE. Both are handled for you. ```bash theme={null} smfs mount ``` Example: ```bash theme={null} smfs mount agent_memory ``` `agent_memory` is the container tag. SMFS creates a folder named `agent_memory/` in the current directory and mounts the container there. The mount runs as a background daemon. A marker file `.smfs` is written at the mount root so other tools (and the semantic `grep` wrapper from `smfs init`) can find the mount. To mount at a different path: ```bash theme={null} smfs mount agent_memory --path ~/memory ``` ## Memory This is the part most people miss. SMFS isn't a normal filesystem. It generates **memories** from files at specific paths. Memories are extracted, summarized, and indexed by Supermemory. Files outside those paths are still semantically searchable; they're indexed through **SuperRAG** by default. Nothing in the mount is dead weight. ### Defaults By default, files named `user.md` or `memory.md` are treated as memory paths. Drop those files anywhere in your mount and Supermemory generates memories from them automatically. ### Configure your own memory paths Pass `--memory-paths` at mount time to control which files become memories: ```bash theme={null} smfs mount agent_memory --memory-paths "/notes/,/journal.md" ``` Rules: * Paths are **absolute**, anchored at the mount root. Always start with `/`. * Trailing `/` matches every file inside that folder, recursively (`/notes/` covers `/notes/foo.md`, `/notes/2026/march.md`, etc.). * No trailing slash matches one exact file (`/journal.md`). * Comma-separated. Multiple paths are fine. * Empty string disables memory generation entirely (`--memory-paths ""`). * Omit the flag and Supermemory keeps whatever the container tag already has, falling back to `user.md` and `memory.md`. ### profile.md Every mount has a virtual file at the root called `profile.md`. It's auto-generated, read-only, and backed by Supermemory. The model can `cat profile.md` to get a live digest of everything in the container without walking every file. Useful as a first call at the start of a session. ```bash theme={null} cat agent_memory/profile.md ``` You can't write to it. As the underlying memories change, Supermemory regenerates it. ## Sync modes Three modes plus a force-sync command. Pick by what your agent actually needs. ### Bidirectional (default) Local reads hit the cache. Local writes queue and push to Supermemory in the background. Remote changes are pulled on a poll. This is what you get if you pass no flags. ```bash theme={null} smfs mount agent_memory ``` Use this when more than one writer (you, another agent, the dashboard) might touch the container. ### No-sync Writes still push to Supermemory. Polling for remote changes is off. The agent sees a view that doesn't shift under it mid-task. ```bash theme={null} smfs mount agent_memory --no-sync ``` Use this when your agent is the only writer, or when you want predictable reads. ### Ephemeral Cache is in memory only. Nothing persists after unmount. Writes still push. ```bash theme={null} smfs mount agent_memory --ephemeral ``` Use this for short-lived sandboxes. CI jobs, throwaway containers, one-shot agent runs. ### Force a sync now ```bash theme={null} smfs sync ``` Pushes pending writes and pulls remote changes immediately. Useful right before tearing down a sandbox. ## All mount flags | Flag | What it does | | ------------------------ | -------------------------------------------------------------- | | `--path ` | Override the default mount path (`.//`). | | `--memory-paths ` | Scope which files become memories. See [Memory](#memory). | | `--no-sync` | Stop polling for remote changes. Writes still push. | | `--clean` | Wipe local cache before mounting. Pulls fresh from the API. | | `--ephemeral` | In-memory cache. Nothing persists after unmount. | | `--sync-interval ` | Remote-change poll interval. Default `30`. | | `--drain-timeout ` | Max time to flush pending writes during unmount. Default `30`. | | `--foreground` | Run the daemon inline instead of detaching. | | `--backend ` | Linux only. `fuse` (default) or `nfs`. | | `--key ` | Pass an API key explicitly. Saved to project credentials. | ## Multiple agents and multiple containers * **Different devices, same container tag**: fully supported. Many agents can mount the same container concurrently from different machines. * **Same device, same container tag, mounted twice**: not supported. Use one mount per container per device. * **Same device, different containers**: mount as many as you want in parallel. ## Commands Every `smfs` subcommand. Click any one to expand. Mount a container. Defaults to `.//`; pass `--path` to mount elsewhere. ```bash theme={null} smfs mount agent_memory smfs mount agent_memory --path ~/memory ``` See the flags table above for everything you can pass. Unmount a running mount. Drains pending writes up to `--drain-timeout`, then exits the daemon. Anything not drained resumes on the next mount. ```bash theme={null} smfs unmount agent_memory ``` Inside the mount, you can omit the tag and let SMFS resolve it from the nearest `.smfs` marker. ```bash theme={null} smfs unmount smfs unmount --force ``` List every SMFS mount running on this machine. ```bash theme={null} smfs list ``` Show daemon status for a mount: connectivity, queue depth, last sync. Auto-detects the tag via the nearest `.smfs` marker. ```bash theme={null} smfs status smfs status agent_memory smfs status --json ``` Tail the daemon log for a mount. Auto-detects the tag via the nearest `.smfs` marker. ```bash theme={null} smfs logs smfs logs -f smfs logs -n 500 ``` Force an immediate sync cycle. Push pending writes, pull remote changes. ```bash theme={null} smfs sync agent_memory ``` Inside the mount, the tag is optional (resolved from the nearest `.smfs` marker). ```bash theme={null} smfs sync ``` Semantic search across a container without being inside the mount. The optional second argument scopes the search to a subpath inside the container. Inside a mount, plain `grep` already does this; `smfs grep` is the explicit form for scripts. ```bash theme={null} smfs grep "deadline" smfs grep "deadline" /notes/ ``` One-time auth. Prompts for your Supermemory API key and stores it in your global credentials. You can also pass it directly with `--key`. ```bash theme={null} smfs login smfs login --key sm_... ``` Print the currently-authenticated user, organization, and API endpoint. ```bash theme={null} smfs whoami ``` Remove stored credentials. Active mounts keep running until you `smfs unmount` them. ```bash theme={null} smfs logout ``` Force-installs the shell wrapper that makes plain `grep` semantic inside mounts. Writes directly to `~/.zshrc`. `smfs mount` also installs it automatically the first time, so you only need this to refresh after an upgrade. ```bash theme={null} smfs init ``` Reopen your terminal (or `source ~/.zshrc`) after running it. Self-install. Copies the running binary to `~/.local/bin` and resets permissions. Run this if your `smfs` install ever feels broken. ```bash theme={null} smfs install ``` ## FAQ Run `smfs init` to force-install the shell wrapper. It writes directly to `~/.zshrc`. Then reopen your terminal so the new shell picks it up. The wrapper only triggers when you're inside a mount (it looks for the `.smfs` marker file at the mount root). Outside a mount, `grep` stays normal. Inside a mount, any flag you pass falls through to the real `grep`. Not yet. SMFS supports macOS (arm64, x64) and Linux (arm64, x64) for now. Windows isn't on the v0 roadmap. On Windows, use the [Bash Tool](/docs/smfs/bash-tool) instead. It runs anywhere TypeScript runs and gives your agent the same `ls`, `cat`, `grep`, `sgrep` surface without needing a mount. Re-mount with `--clean` to wipe the local cache and pull everything fresh from the API: ```bash theme={null} smfs unmount agent_memory smfs mount agent_memory --clean ``` Nothing on the server changes; only the local SQLite cache gets reset. Yes. Once a container is mounted, anything on that machine can read and write through the mount path. The constraint is one mount per container tag per device. Mount it once, point both agents at the same folder. Yes, absolutely. Mount the same container tag from each sandbox. Bidirectional sync keeps everything in step as either side writes, so Agent A in sandbox 1 sees Agent B's writes from sandbox 2 within a sync interval. To avoid stepping on each other, give each agent its own subdirectory (`/agent_a/`, `/agent_b/`, etc.). They can still read across the whole mount, cross-reference each other's findings, and build on each other's work. The shared container is the point. # SMFS Source: https://supermemory.ai/docs/smfs/overview Memory your agent can grep. **SMFS** mounts your Supermemory container as a real directory. Agents read it with `ls`, `cat`, and `grep`. No SDK to learn, no client to wire up, no embeddings to think about. SMFS is open source and free for everyone. ## Why a filesystem Every model already knows how a filesystem works. It can `ls`, `cat`, `grep`, `find`, redirect with `>`, pipe with `|`. You don't have to teach it a new API surface, and the grammar carries across runtimes. The catch: a filesystem on its own isn't great for memory. Search means walking the tree. Long files burn through context. The model has to hold the directory structure in its head. None of that scales as memory grows. SMFS fixes the catch. The shell is real, but underneath: * **Semantic `grep` by default.** One call surfaces what matters across the whole container, ranked by meaning. Pass any flag and you fall through to the real `grep` for exact matches. * **Memory paths get distilled.** Files marked as memory paths are extracted and indexed by Supermemory. They don't bloat the model's context. * **Virtual `profile.md`.** A live digest of the container at the mount root. The model can `cat profile.md` for a one-shot summary instead of walking every file. * **Bidirectional sync** runs in the background. Local reads hit cache; writes push to Supermemory. You get filesystem ergonomics without paying the filesystem tax in tokens. ## Two ways to use SMFS Pick by where your agent runs. For agents and tools with a real filesystem. Claude Code, Cursor, devcontainers, Docker, Codespaces. NFSv3 on macOS, FUSE on Linux. For agents running serverless or at the edge. Cloudflare Workers, AWS Lambda, Vercel, Modal. A virtual bash where the filesystem is your container. Available as [`@supermemory/bash`](/docs/smfs/bash-tool) for TypeScript and [`supermemory-bash`](/docs/smfs/bash-tool-python) for Python. ## Use SMFS with your sandbox provider Already using a sandbox or agent platform? Jump straight to the guide for your provider. Isolated Linux sandboxes with millisecond boot times. Mount SMFS inside or use the bash tool from your orchestrating code. Firecracker microVMs for AI code execution. Install SMFS directly or use a custom template with it pre-installed. The most popular TypeScript agent framework. Add memory as a tool with one function call. Edge-first agents. Use the bash tool in Workers, or mount SMFS in Cloudflare Containers. ## Next steps One curl, one mount, you're done. Drop SMFS into a TypeScript or Python agent without mounting anything. Full working apps you can clone and run — legal docs, support agents, and more. # Cloudflare Source: https://supermemory.ai/docs/smfs/providers/cloudflare Give your AI agent persistent memory inside a Cloudflare Container using SMFS Mount a Supermemory container inside a [Cloudflare Container](https://developers.cloudflare.com/containers/) so your agent can read and write memory using standard filesystem commands. ## How it works There are two ways to wire SMFS into a Cloudflare Container — pick the one that fits your architecture. ### Agent inside the container The agent process runs inside the container with direct access to the SMFS mount. The entrypoint sets up the mount and starts the agent. ```mermaid theme={null} graph LR subgraph Cloudflare Container Agent["Claude Agent"] -->|"cat, ls, echo"| Mount["/memory
(SMFS mount)"] end Mount -->|sync| SM["Supermemory"] ``` ### Agent outside the container The agent runs in a Cloudflare Worker and sends commands to the container over HTTP. The container exposes a simple exec endpoint. ```mermaid theme={null} graph LR Agent["Worker
(agent logic)"] -->|"containerFetch('/exec')"| Container subgraph Container ["Cloudflare Container"] Mount["/memory
(SMFS mount)"] end Mount -->|sync| SM["Supermemory"] ``` ## Prerequisites * A [Supermemory API key](https://supermemory.ai) * An [Anthropic API key](https://console.anthropic.com) * A [Cloudflare account](https://dash.cloudflare.com) with Containers enabled (Workers Paid plan) * [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/install-and-update/) * The [`@cloudflare/containers`](https://www.npmjs.com/package/@cloudflare/containers) package: `npm install @cloudflare/containers` Cloudflare Containers are implemented as container-enabled Durable Objects. You declare a `Container` subclass, bind it as a Durable Object, and reference its image in the `containers` array. Worker secrets are **not** automatically visible inside the container — you have to pass them through `envVars` when starting the container (see below). *** ## Pattern A: Agent inside the container SMFS and the Claude Agent SDK are baked into the container image. On startup, the entrypoint mounts memory and runs the agent. ### Dockerfile ```dockerfile Dockerfile theme={null} FROM python:3.12-slim RUN apt-get update && apt-get install -y fuse3 curl bash && rm -rf /var/lib/apt/lists/* RUN echo 'user_allow_other' >> /etc/fuse.conf RUN curl -fsSL https://smfs.ai/install | bash -s -- 0.0.1-rc2 ENV PATH="/root/.local/bin:$PATH" RUN pip install claude-agent-sdk COPY agent.py /app/agent.py COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] ``` ### Entrypoint ```bash entrypoint.sh theme={null} #!/bin/bash set -e smfs login --key "$SUPERMEMORY_API_KEY" smfs mount my_agent --ephemeral --path /memory --foreground & sleep 3 exec python3 /app/agent.py ``` ### Agent code ```python agent.py theme={null} import asyncio from claude_agent_sdk import query, ClaudeAgentOptions MEMORY = "/memory" async def main(): async for message in query( prompt=f"You have a persistent memory filesystem at {MEMORY}. " "Read profile.md to learn about the user, then create " "session_notes.md summarizing what you found.", options=ClaudeAgentOptions( allowed_tools=["Bash", "Read", "Write"], cwd=MEMORY, ), ): print(message) asyncio.run(main()) ``` ### Worker The Worker defines the `Container` subclass and forwards Worker secrets into the container via `envVars`: ```typescript worker.ts theme={null} import { Container, getContainer } from "@cloudflare/containers"; export class MyAgentContainer extends Container { defaultPort = 8080; // Forward Worker secrets into the container at start time. // `this.env` is the Worker env object, populated from wrangler secrets. envVars = { SUPERMEMORY_API_KEY: this.env.SUPERMEMORY_API_KEY, ANTHROPIC_API_KEY: this.env.ANTHROPIC_API_KEY, }; } export default { async fetch(request: Request, env: Env) { // The container runs the agent and exits; this Worker route just kicks // it off (e.g. on a queue message or scheduled trigger). const container = getContainer(env.MY_CONTAINER, "agent-singleton"); return container.fetch(request); }, }; interface Env { MY_CONTAINER: DurableObjectNamespace; SUPERMEMORY_API_KEY: string; ANTHROPIC_API_KEY: string; } ``` ### Config ```jsonc wrangler.jsonc theme={null} { "name": "memory-agent", "main": "worker.ts", "compatibility_date": "2025-04-03", "containers": [ { "class_name": "MyAgentContainer", "image": "./Dockerfile", "max_instances": 5 } ], "durable_objects": { "bindings": [ { "name": "MY_CONTAINER", "class_name": "MyAgentContainer" } ] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": ["MyAgentContainer"] } ] } ``` ```bash theme={null} wrangler secret put SUPERMEMORY_API_KEY wrangler secret put ANTHROPIC_API_KEY wrangler deploy ``` *** ## Pattern B: Agent outside the container The agent logic lives in a Worker. The container just runs SMFS and exposes an HTTP endpoint for executing commands against the mount. The `/exec` endpoint below runs arbitrary shell commands inside the container. **Only call it from your Worker** — never expose it publicly, and never pass user input straight into `command` without validation. Cloudflare Containers are addressable only through their Worker by default, so this is safe as long as you don't add a public route that proxies to `/exec`. ### Container (exec server) The Dockerfile and entrypoint are nearly identical to Pattern A — the only differences are the Python deps (`flask` instead of `claude-agent-sdk`) and the file we exec at the end. ```dockerfile Dockerfile theme={null} FROM python:3.12-slim RUN apt-get update && apt-get install -y fuse3 curl bash && rm -rf /var/lib/apt/lists/* RUN echo 'user_allow_other' >> /etc/fuse.conf RUN curl -fsSL https://smfs.ai/install | bash -s -- 0.0.1-rc2 ENV PATH="/root/.local/bin:$PATH" RUN pip install flask gunicorn COPY server.py /app/server.py COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] ``` The entrypoint differs from Pattern A only in the final `exec` line — we run gunicorn against the Flask app instead of `python3 agent.py`: ```bash entrypoint.sh theme={null} #!/bin/bash set -e smfs login --key "$SUPERMEMORY_API_KEY" smfs mount my_agent --ephemeral --path /memory --foreground & sleep 3 exec gunicorn -b 0.0.0.0:8080 --chdir /app server:app ``` ```python server.py theme={null} import subprocess from flask import Flask, request, jsonify app = Flask(__name__) @app.route("/exec", methods=["POST"]) def exec_command(): cmd = request.json["command"] result = subprocess.run( cmd, shell=True, capture_output=True, text=True, cwd="/memory", timeout=10 ) return jsonify(stdout=result.stdout, stderr=result.stderr, code=result.returncode) ``` We use gunicorn instead of `app.run(...)` because Flask's built-in dev server isn't meant for production traffic. If you'd rather just see it work, you can replace the `exec` line with `exec python3 /app/server.py` and add `app.run(host="0.0.0.0", port=8080)` to `server.py` — but switch back to gunicorn before you ship. ### Worker (agent logic) ```typescript worker.ts theme={null} import { Container, getContainer } from "@cloudflare/containers"; export class ExecContainer extends Container { defaultPort = 8080; envVars = { SUPERMEMORY_API_KEY: this.env.SUPERMEMORY_API_KEY, }; } export default { async fetch(_request: Request, env: Env) { const container = getContainer(env.MY_CONTAINER, "agent-singleton"); const profile = await container .fetch(new Request("http://container/exec", { method: "POST", body: JSON.stringify({ command: "cat /memory/profile.md" }), headers: { "Content-Type": "application/json" }, })) .then((r) => r.json<{ stdout: string }>()); return Response.json({ profile: profile.stdout }); }, }; interface Env { MY_CONTAINER: DurableObjectNamespace; SUPERMEMORY_API_KEY: string; } ``` ### Config ```jsonc wrangler.jsonc theme={null} { "name": "memory-exec", "main": "worker.ts", "compatibility_date": "2025-04-03", "containers": [ { "class_name": "ExecContainer", "image": "./Dockerfile", "max_instances": 5 } ], "durable_objects": { "bindings": [ { "name": "MY_CONTAINER", "class_name": "ExecContainer" } ] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": ["ExecContainer"] } ] } ``` ```bash theme={null} wrangler secret put SUPERMEMORY_API_KEY wrangler deploy ``` *** ## Tips * Use `--ephemeral` for container mounts — keeps the cache in memory only, but writes still push to Supermemory * Use `smfs grep 'query'` for semantic search across all files * Worker secrets aren't automatically visible inside the container. Pass each one through the `envVars` field on your `Container` subclass (see the Worker snippets above) * Use `containerFetch` from within a Container class method (e.g., lifecycle hooks) to call the container's own HTTP server. From the Worker, use the stub's `.fetch()` method instead # Daytona Source: https://supermemory.ai/docs/smfs/providers/daytona Give your AI agent persistent memory inside a Daytona sandbox using SMFS Mount a Supermemory container inside a [Daytona](https://daytona.io) sandbox so your agent can read and write memory using standard filesystem commands. Daytona sandboxes currently cannot reach `api.supermemory.ai` from their datacenter IPs. The SMFS binary still installs (we download it directly from GitHub Releases), the FUSE mount still starts, and `pip install claude-agent-sdk` still works — but the runtime sync to Supermemory fails. We're working with Daytona to resolve this. In the meantime, use [E2B](/docs/smfs/providers/e2b) or a [self-hosted mount](/docs/smfs/providers/vercel). ## How it works There are two ways to wire SMFS into a Daytona sandbox — pick the one that fits your architecture. ### Agent inside the sandbox The agent process runs inside the sandbox and accesses the SMFS mount directly. ```mermaid theme={null} graph LR subgraph Daytona Sandbox Agent["Claude Agent"] -->|"cat, ls, echo"| Mount["/home/daytona/memory
(SMFS mount)"] end Mount -->|sync| SM["Supermemory"] ``` ### Agent outside the sandbox The agent runs in your orchestrating code and executes commands inside the sandbox remotely. ```mermaid theme={null} graph LR Agent["Claude Agent
(your server)"] -->|"sandbox.process.exec()"| Sandbox subgraph Sandbox ["Daytona Sandbox"] Mount["/home/daytona/memory
(SMFS mount)"] end Mount -->|sync| SM["Supermemory"] ``` ## Prerequisites * A [Supermemory API key](https://supermemory.ai) * A [Daytona API key](https://app.daytona.io) — go to **API Keys** in the sidebar * An [Anthropic API key](https://console.anthropic.com) *** ## Install SMFS in a Daytona sandbox Both patterns below run the same setup snippet inside the sandbox before mounting. Daytona can't reach `smfs.ai`, so we download the binary directly from GitHub Releases and add `~/.local/bin` to PATH. ```python theme={null} SMFS_INSTALL = ( "mkdir -p $HOME/.local/bin && " "curl -sL https://github.com/supermemoryai/smfs/releases/download/" "v0.0.1-rc2/smfs-linux-x64 -o $HOME/.local/bin/smfs && " "chmod +x $HOME/.local/bin/smfs && " "echo 'user_allow_other' | sudo tee -a /etc/fuse.conf > /dev/null && " "pip install claude-agent-sdk" ) ``` ```typescript theme={null} const SMFS_INSTALL = "mkdir -p $HOME/.local/bin && " + "curl -sL https://github.com/supermemoryai/smfs/releases/download/" + "v0.0.1-rc2/smfs-linux-x64 -o $HOME/.local/bin/smfs && " + "chmod +x $HOME/.local/bin/smfs && " + "echo 'user_allow_other' | sudo tee -a /etc/fuse.conf > /dev/null && " + "pip install claude-agent-sdk"; ``` *** ## Pattern A: Agent inside the sandbox ### Agent code ```python agent.py theme={null} import asyncio from claude_agent_sdk import query, ClaudeAgentOptions MEMORY = "/home/daytona/memory" async def main(): async for message in query( prompt=f"You have a persistent memory filesystem at {MEMORY}. " "Read profile.md to learn about the user, then create " "session_notes.md summarizing what you found.", options=ClaudeAgentOptions( allowed_tools=["Bash", "Read", "Write"], cwd=MEMORY, ), ): print(message) asyncio.run(main()) ``` ### Orchestration ```python run.py theme={null} import os from pathlib import Path from daytona_sdk import Daytona, DaytonaConfig daytona = Daytona(DaytonaConfig( api_key=os.environ["DAYTONA_API_KEY"], )) sandbox = daytona.create( env_vars={ "SUPERMEMORY_API_KEY": os.environ["SUPERMEMORY_API_KEY"], "ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"], }, ) # See "Install SMFS in a Daytona sandbox" above sandbox.process.exec(SMFS_INSTALL) # Mount memory sandbox.process.exec("$HOME/.local/bin/smfs login --key $SUPERMEMORY_API_KEY") sandbox.process.exec( "bash -c '$HOME/.local/bin/smfs mount my_agent --ephemeral" " --path /home/daytona/memory --foreground &' && sleep 3" ) # Upload and run the agent sandbox.fs.upload_file(Path("agent.py").read_bytes(), "agent.py") result = sandbox.process.exec("python3 agent.py") print(result.result) daytona.delete(sandbox) ``` ```typescript run.ts theme={null} import { Daytona } from "@daytonaio/sdk"; import { readFileSync } from "fs"; const daytona = new Daytona({ apiKey: process.env.DAYTONA_API_KEY!, }); const sandbox = await daytona.create({ envVars: { SUPERMEMORY_API_KEY: process.env.SUPERMEMORY_API_KEY!, ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, }, }); // See "Install SMFS in a Daytona sandbox" above await sandbox.process.exec(SMFS_INSTALL); // Mount memory await sandbox.process.exec( "$HOME/.local/bin/smfs login --key $SUPERMEMORY_API_KEY" ); await sandbox.process.exec( "bash -c '$HOME/.local/bin/smfs mount my_agent --ephemeral " + "--path /home/daytona/memory --foreground &' && sleep 3" ); // Upload and run the agent await sandbox.fs.uploadFile(readFileSync("agent.py"), "agent.py"); const result = await sandbox.process.exec("python3 agent.py"); console.log(result.result); await daytona.delete(sandbox); ``` *** ## Pattern B: Agent outside the sandbox The agent runs in your server process and executes commands inside the sandbox remotely via `sandbox.process.exec()`. ```python run.py theme={null} import os from daytona_sdk import Daytona, DaytonaConfig daytona = Daytona(DaytonaConfig( api_key=os.environ["DAYTONA_API_KEY"], )) sandbox = daytona.create( env_vars={ "SUPERMEMORY_API_KEY": os.environ["SUPERMEMORY_API_KEY"], }, ) # See "Install SMFS in a Daytona sandbox" above sandbox.process.exec(SMFS_INSTALL) sandbox.process.exec("$HOME/.local/bin/smfs login --key $SUPERMEMORY_API_KEY") sandbox.process.exec( "bash -c '$HOME/.local/bin/smfs mount my_agent --ephemeral" " --path /home/daytona/memory --foreground &' && sleep 3" ) # Agent runs here — executes commands in the sandbox profile = sandbox.process.exec("cat /home/daytona/memory/profile.md") print("Profile:", profile.result) sandbox.process.exec( "bash -c 'echo \"Session started at $(date)\" > /home/daytona/memory/session_notes.md'" ) files = sandbox.process.exec("ls /home/daytona/memory") print("Files:", files.result) daytona.delete(sandbox) ``` ```typescript run.ts theme={null} import { Daytona } from "@daytonaio/sdk"; const daytona = new Daytona({ apiKey: process.env.DAYTONA_API_KEY!, }); const sandbox = await daytona.create({ envVars: { SUPERMEMORY_API_KEY: process.env.SUPERMEMORY_API_KEY!, }, }); // See "Install SMFS in a Daytona sandbox" above await sandbox.process.exec(SMFS_INSTALL); await sandbox.process.exec( "$HOME/.local/bin/smfs login --key $SUPERMEMORY_API_KEY" ); await sandbox.process.exec( "bash -c '$HOME/.local/bin/smfs mount my_agent --ephemeral " + "--path /home/daytona/memory --foreground &' && sleep 3" ); // Agent runs here — executes commands in the sandbox const profile = await sandbox.process.exec("cat /home/daytona/memory/profile.md"); console.log("Profile:", profile.result); await sandbox.process.exec( `bash -c 'echo "Session started at $(date)" > /home/daytona/memory/session_notes.md'` ); const files = await sandbox.process.exec("ls /home/daytona/memory"); console.log("Files:", files.result); await daytona.delete(sandbox); ``` *** ## Tips * FUSE is available in Daytona sandboxes but `user_allow_other` needs to be added to `/etc/fuse.conf` * We invoke SMFS as `$HOME/.local/bin/smfs` in the examples because Daytona's default zsh PATH doesn't include `~/.local/bin`. Alternatively, prepend it once with `export PATH=$HOME/.local/bin:$PATH` * Use `pip install claude-agent-sdk` to install the agent SDK (PyPI is reachable) # E2B Source: https://supermemory.ai/docs/smfs/providers/e2b Give your AI agent persistent memory inside an E2B sandbox using SMFS Mount a Supermemory container inside an [E2B](https://e2b.dev) sandbox so your agent can read and write memory using standard filesystem commands. ## How it works There are two ways to wire SMFS into an E2B sandbox — pick the one that fits your architecture. ### Agent inside the sandbox The agent process runs inside the sandbox and accesses the SMFS mount directly. Your orchestrating code just boots the sandbox and kicks off the agent. ```mermaid theme={null} graph LR subgraph E2B Sandbox Agent["Claude Agent"] -->|"cat, ls, echo"| Mount["/home/user/memory
(SMFS mount)"] end Mount -->|sync| SM["Supermemory"] ``` ### Agent outside the sandbox The agent runs in your orchestrating code and executes commands inside the sandbox remotely. Useful when you want to keep the agent loop in your own infra. ```mermaid theme={null} graph LR Agent["Claude Agent
(your server)"] -->|"sbx.commands.run()"| Sandbox subgraph Sandbox ["E2B Sandbox"] Mount["/home/user/memory
(SMFS mount)"] end Mount -->|sync| SM["Supermemory"] ``` ## Prerequisites * A [Supermemory API key](https://supermemory.ai) * An [E2B API key](https://e2b.dev) * An [Anthropic API key](https://console.anthropic.com) ## 1. Create a custom template Bake SMFS and the Claude Agent SDK into a template so sandboxes start ready: ```dockerfile e2b.Dockerfile theme={null} FROM e2b/code-interpreter:latest RUN apt-get update && apt-get install -y fuse3 && rm -rf /var/lib/apt/lists/* RUN echo 'user_allow_other' >> /etc/fuse.conf RUN curl -fsSL https://smfs.ai/install | bash -s -- 0.0.1-rc2 ENV PATH="/root/.local/bin:$PATH" RUN pip install claude-agent-sdk ``` ```bash theme={null} e2b template build -d e2b.Dockerfile ``` *** ## Pattern A: Agent inside the sandbox The agent runs inside the sandbox as a Python script. Your orchestrating code just sets up the mount and starts it. ### Agent code ```python agent.py theme={null} import asyncio from claude_agent_sdk import query, ClaudeAgentOptions MEMORY = "/home/user/memory" async def main(): async for message in query( prompt=f"You have a persistent memory filesystem at {MEMORY}. " "Read profile.md to learn about the user, then create " "session_notes.md summarizing what you found.", options=ClaudeAgentOptions( allowed_tools=["Bash", "Read", "Write"], cwd=MEMORY, ), ): print(message) asyncio.run(main()) ``` ### Orchestration ```python run.py theme={null} import os from pathlib import Path from e2b_code_interpreter import Sandbox sbx = Sandbox.create( template="your-template-id", timeout=300, envs={ "SUPERMEMORY_API_KEY": os.environ["SUPERMEMORY_API_KEY"], "ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"], }, ) # /dev/fuse exists in E2B but is root-only by default. chmod once per sandbox. sbx.commands.run("sudo chmod 666 /dev/fuse") # Mount memory. We background the foreground daemon so this command returns, # then sleep briefly to let the FUSE mount come up before the agent reads it. sbx.commands.run("smfs login --key $SUPERMEMORY_API_KEY") sbx.commands.run( "bash -c 'smfs mount my_agent --ephemeral" " --path /home/user/memory --foreground &' && sleep 3" ) # Upload and run the agent sbx.files.write("/home/user/agent.py", Path("agent.py").read_text()) result = sbx.commands.run("python3 /home/user/agent.py", timeout=120) print(result.stdout) sbx.kill() ``` ```typescript run.ts theme={null} import { Sandbox } from "@e2b/code-interpreter"; import { readFileSync } from "fs"; const sbx = await Sandbox.create({ template: "your-template-id", timeoutMs: 300_000, envs: { SUPERMEMORY_API_KEY: process.env.SUPERMEMORY_API_KEY!, ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, }, }); // /dev/fuse exists in E2B but is root-only by default. chmod once per sandbox. await sbx.commands.run("sudo chmod 666 /dev/fuse"); // Mount memory. We background the foreground daemon so this command returns, // then sleep briefly to let the FUSE mount come up before the agent reads it. await sbx.commands.run("smfs login --key $SUPERMEMORY_API_KEY"); await sbx.commands.run( "bash -c 'smfs mount my_agent --ephemeral --path /home/user/memory --foreground &' && sleep 3" ); // Upload and run the agent await sbx.files.write("/home/user/agent.py", readFileSync("agent.py", "utf-8")); const result = await sbx.commands.run("python3 /home/user/agent.py", { timeoutMs: 120_000, }); console.log(result.stdout); await sbx.kill(); ``` *** ## Pattern B: Agent outside the sandbox The agent runs in your server process and executes commands inside the sandbox remotely via `sbx.commands.run()`. The SMFS mount lives inside the sandbox — the agent never touches the filesystem directly. The FUSE mount is owned by root inside the sandbox. When writing to it from outside the agent, wrap the command in `sudo bash -c '…'` so the redirect runs with the right permissions. You'll see this in the write examples below. ```python run.py theme={null} import os from e2b_code_interpreter import Sandbox sbx = Sandbox.create( template="your-template-id", timeout=300, envs={ "SUPERMEMORY_API_KEY": os.environ["SUPERMEMORY_API_KEY"], }, ) # Set up SMFS inside the sandbox sbx.commands.run("sudo chmod 666 /dev/fuse") sbx.commands.run("smfs login --key $SUPERMEMORY_API_KEY") sbx.commands.run( "bash -c 'smfs mount my_agent --ephemeral" " --path /home/user/memory --foreground &' && sleep 3" ) # Agent runs here — executes commands in the sandbox profile = sbx.commands.run("cat /home/user/memory/profile.md").stdout print("Profile:", profile) sbx.commands.run( "sudo bash -c 'echo \"Session started at $(date)\" > /home/user/memory/session_notes.md'" ) files = sbx.commands.run("ls /home/user/memory").stdout print("Files:", files) sbx.kill() ``` ```typescript run.ts theme={null} import { Sandbox } from "@e2b/code-interpreter"; const sbx = await Sandbox.create({ template: "your-template-id", timeoutMs: 300_000, envs: { SUPERMEMORY_API_KEY: process.env.SUPERMEMORY_API_KEY!, }, }); // Set up SMFS inside the sandbox await sbx.commands.run("sudo chmod 666 /dev/fuse"); await sbx.commands.run("smfs login --key $SUPERMEMORY_API_KEY"); await sbx.commands.run( "bash -c 'smfs mount my_agent --ephemeral --path /home/user/memory --foreground &' && sleep 3" ); // Agent runs here — executes commands in the sandbox const profile = await sbx.commands.run("cat /home/user/memory/profile.md"); console.log("Profile:", profile.stdout); await sbx.commands.run( `sudo bash -c 'echo "Session started at $(date)" > /home/user/memory/session_notes.md'` ); const files = await sbx.commands.run("ls /home/user/memory"); console.log("Files:", files.stdout); await sbx.kill(); ``` *** ## Tips * Use `--ephemeral` for sandbox mounts — keeps the cache in memory only, but writes still push to Supermemory * Use `smfs grep 'query'` for semantic search across all files in the container * Without a custom template, add the install steps to your run script: ```python theme={null} sbx.commands.run("curl -fsSL https://smfs.ai/install | bash -s -- 0.0.1-rc2", timeout=60) sbx.commands.run("pip install claude-agent-sdk", timeout=60) ``` # Vercel AI SDK Source: https://supermemory.ai/docs/smfs/providers/vercel Give your AI agent persistent memory using SMFS with the Vercel AI SDK This guide is about the [Vercel AI SDK](https://ai-sdk.dev) — the TypeScript agent framework — not Vercel hosting. The choice of pattern depends on where your code actually runs: * **Self-hosted Node** (your own VM, ECS, Fly.io, Railway, a Vercel Sandbox, etc.): you can mount SMFS as a real filesystem on the server. * **Vercel Functions / serverless / edge**: there's no long-lived process to hold a FUSE mount, so use the [Bash Tool](/docs/smfs/bash-tool) (`@supermemory/bash`) instead. The container becomes the filesystem; no mount needed. ## How it works ### Self-hosted Node (real mount) The agent runs as a separate process with direct access to the SMFS mount. Best when you want full bash, read, and write capabilities and your server is long-lived. ```mermaid theme={null} graph LR subgraph Your Server Agent["Claude Agent"] -->|"cat, ls, echo"| Mount["./memory
(SMFS mount)"] end Mount -->|sync| SM["Supermemory"] ``` ### Vercel Functions / serverless (Bash Tool) The agent runs inside `generateText` and accesses memory through `@supermemory/bash`, which proxies bash commands to your Supermemory container over HTTP. No mount, no FUSE, no long-lived process required. ```mermaid theme={null} graph LR subgraph Vercel Function AI["generateText()"] -->|"bash tool"| Bash["@supermemory/bash"] end Bash -->|HTTPS| SM["Supermemory"] ``` ## Prerequisites * A [Supermemory API key](https://supermemory.ai) * An [Anthropic API key](https://console.anthropic.com) * For Pattern A only: SMFS installed on your server (`curl -fsSL https://smfs.ai/install | bash`) *** ## Pattern A: Claude Agent SDK on self-hosted Node Use this when the Vercel AI SDK is just the orchestrator and your real workload is a Claude agent running on a long-lived server you control. Start the mount once when your server boots — not per-request: ```bash theme={null} smfs login --key $SUPERMEMORY_API_KEY smfs mount my_agent --path ./memory ``` This won't work on Vercel Functions or any serverless runtime: there's no process between requests to hold the mount, and FUSE isn't available. For those targets, jump to Pattern B. Write a standalone agent script. Nothing server-specific — just Python that reads and writes files: ```python agent.py theme={null} import asyncio from claude_agent_sdk import query, ClaudeAgentOptions MEMORY = "./memory" async def main(): async for message in query( prompt=f"You have a persistent memory filesystem at {MEMORY}. " "Read profile.md to learn about the user, then create " "session_notes.md summarizing what you found.", options=ClaudeAgentOptions( allowed_tools=["Bash", "Read", "Write"], cwd=MEMORY, ), ): print(message) asyncio.run(main()) ``` ```bash theme={null} python3 agent.py ``` *** ## Pattern B: Vercel AI SDK + Bash Tool (serverless-friendly) `@supermemory/bash` exposes your Supermemory container as a single agent tool — `run_bash(command)` — without mounting anything. It runs anywhere TypeScript runs, including Vercel Functions, edge runtimes, and Lambda. ```bash theme={null} npm install @supermemory/bash ai @ai-sdk/anthropic zod ``` ```typescript api/agent.ts theme={null} import { generateText, tool } from "ai"; import { anthropic } from "@ai-sdk/anthropic"; import { createBash } from "@supermemory/bash"; import { z } from "zod"; export async function POST(req: Request) { const { prompt } = await req.json(); const { bash, toolDescription } = await createBash({ apiKey: process.env.SUPERMEMORY_API_KEY!, containerTag: "my_agent", }); const result = await generateText({ model: anthropic("claude-sonnet-4-5"), tools: { bash: tool({ description: toolDescription, inputSchema: z.object({ cmd: z.string() }), execute: async ({ cmd }) => bash.exec(cmd), }), }, maxSteps: 10, prompt, }); return Response.json({ text: result.text }); } ``` A few things worth calling out: * **`maxSteps: 10`** lets the agent chain multiple bash calls per request (read `profile.md`, then `cat` a few notes, then write a summary). Bump it if your agent needs deeper chains; lower it to cap cost per request. * **`toolDescription`** is a pre-written description of the available bash surface (semantic `sgrep`, `cat`, `ls`, redirects, etc.). Hand it straight to the model — don't roll your own. * **No timeout/abort plumbing.** `bash.exec` already runs against the container over HTTPS, so it returns when the command returns. No event-loop blocking and no FUSE. See the [Bash Tool reference](/docs/smfs/bash-tool) for the full command surface, memory path configuration, and other framework integrations. *** ## Tips * **Pattern A**: mount SMFS once when your server starts, not per-request. Use `--ephemeral` if you don't need a local cache on the server. * **Pattern B**: configure memory paths once at startup with `configureMemoryPaths(["/notes/", "/journal.md"])` to control which files get distilled into Supermemory memories. * Both: use `smfs grep 'query'` (Pattern A) or `sgrep 'query'` inside the bash tool (Pattern B) for semantic search across all files. # ChatGPT Web Source: https://supermemory.ai/docs/supermemory-mcp/chatgpt-web Connect Supermemory MCP to ChatGPT Web and make Supermemory the default for memory, recall, and saved context Connect Supermemory MCP to [ChatGPT Web](https://chatgpt.com) to use your Supermemory context directly in a conversation. ## Step 1: Open Settings Open your profile menu in the lower-left corner of ChatGPT Web and select **Settings**. ChatGPT Web profile menu in dark mode with Settings highlighted ChatGPT Web profile menu in light mode with Settings highlighted ## Step 2: Open Plugins Select **Plugins** in the Settings sidebar. ChatGPT Web Plugins settings in dark mode ChatGPT Web Plugins settings in light mode ## Step 3: Open Developer mode Scroll to the bottom of **Plugins** and select **Developer mode**. Developer mode and Browse plugins at the bottom of ChatGPT Web Plugins settings in dark mode Developer mode and Browse plugins at the bottom of ChatGPT Web Plugins settings in light mode ## Step 4: Enable Developer mode ChatGPT opens the **Developer mode** section under **Security and login**. Turn **Developer mode** on. Developer mode section under Security and login in ChatGPT Web dark mode Developer mode section under Security and login in ChatGPT Web light mode Developer mode enabled in ChatGPT Web dark mode Developer mode enabled in ChatGPT Web light mode ## Step 5: Open Browse plugins Return to **Plugins**, scroll to the bottom, and select **Browse plugins**. Browse plugins at the bottom of ChatGPT Web Plugins settings in dark mode Browse plugins at the bottom of ChatGPT Web Plugins settings in light mode ## Step 6: Add a plugin On the Plugins page, select the **plus icon** in the upper-right corner. ChatGPT Web Plugins page in dark mode with the plus icon ChatGPT Web Plugins page in light mode with the plus icon ## Step 7: Enter the Supermemory details Complete the **New Plugin** form: * In **Name**, enter `Supermemory MCP`. * In **Description**, enter `Memory/context for your AI agents`. * Under **Connection**, select **Server URL** instead of **Tunnel**. * In the URL field, paste `https://mcp.supermemory.ai/mcp`. The icon is optional. Leave **Advanced OAuth settings** unchanged. New Plugin form in dark mode filled with the Supermemory MCP details New Plugin form in light mode filled with the Supermemory MCP details ## Step 8: Confirm and create Scroll to the bottom of the form. Select **I understand and want to continue**, then select **Create**. New Plugin confirmation in dark mode with the acknowledgement selected New Plugin confirmation in light mode with the acknowledgement selected ## Step 9: Sign in to Supermemory ChatGPT opens the **Add Supermemory MCP to ChatGPT** dialog. Select **Sign in with Supermemory MCP**. ChatGPT authorization dialog for Supermemory MCP in dark mode ChatGPT authorization dialog for Supermemory MCP in light mode Accounts with access to more than one organization may see an organization picker. Select the organization you want ChatGPT Web to use. Supermemory organization picker shown for accounts with multiple organizations ## Step 10: Approve access On the Supermemory authorization page: * Select **Read + Write** to let ChatGPT search and save Supermemory context. Select **Read only** if you only want search access. * Select **Full access** to use every available space, or **Scoped** to choose specific spaces. * Choose how long the connection should remain active. * Select **Approve**. Supermemory MCP authorization page with permission, access, and expiration controls ## Step 11: Start using Supermemory After approval, ChatGPT returns to the Supermemory MCP plugin page. Select **Try in chat**. Connected Supermemory MCP page in ChatGPT Web dark mode Connected Supermemory MCP page in ChatGPT Web light mode ## Done Supermemory MCP is now ready to use in ChatGPT Web. You can manage or disconnect it later from the [Plugins page](https://chatgpt.com/plugins). ## Frequently asked questions Turn **Enable memory** off. ChatGPT Personalization settings in dark mode showing the Enable memory switch and Memory summary ChatGPT Personalization settings in light mode showing the Enable memory switch and Memory summary Turning memory off does not delete your past chats. If you want to keep a copy of ChatGPT's existing memory, select **Manage** and copy the memory summary. Open a chat with Supermemory enabled, paste the summary, and say `Add this to Supermemory`. Paste this into **Custom instructions**: ChatGPT Personalization settings in dark mode showing the Custom instructions field ChatGPT Personalization settings in light mode showing the Custom instructions field ```text theme={null} Use Supermemory MCP as my default memory and workspace memory whenever its tools are available. Use it first for both recall and saving new memories. ``` # Claude Desktop Source: https://supermemory.ai/docs/supermemory-mcp/claude-desktop Connect Supermemory MCP in Claude via Settings → Connectors This guide walks through adding Supermemory as a **custom connector** in [Claude](https://claude.ai) (Desktop or web): open **Settings → Connectors**, add the MCP URL, connect, and authorize. For other clients, see [Setup and Usage](/docs/supermemory-mcp/setup). ## Step 1 — Open Connectors and add a custom connector In Claude, open **Settings → Connectors**. Click **Add**, then choose **Add custom connector**. Claude settings Connectors page with Add custom connector highlighted ## Step 2 — Enter name and remote MCP URL In the **Add custom connector** dialog: | Field | Value | | ------------------------- | --------------------------------------- | | **Name** | `Supermemory` (or any label you prefer) | | **Remote MCP server URL** | `https://mcp.supermemory.ai/mcp` | Leave **OAuth Client ID** and **OAuth Client Secret** empty unless you have custom OAuth credentials. Click **Add**. Add custom connector dialog with Supermemory name and mcp.supermemory.ai URL ## Step 3 — Connect Supermemory appears under your connectors. Click **Connect**. Connectors list with supermemory and Connect button ## Step 4 — Authorize You’ll be redirected to Supermemory to sign in and choose access scopes (for example **Read + Write** or **Full access**). Select the scopes you want, then click **Authorize**. Authorize MCP screen with scope options and Authorize button ## Done Supermemory is connected and ready to use in Claude. You can change or revoke access later from **Settings → Connectors**. *** **See also:** [Overview](/docs/supermemory-mcp/mcp) · [Setup and Usage](/docs/supermemory-mcp/setup) # Overview Source: https://supermemory.ai/docs/supermemory-mcp/mcp Give every MCP-compatible assistant shared memory, team spaces, and interactive workflows Supermemory MCP gives every MCP-compatible assistant a shared memory layer, so technical and non-technical teams can collaborate with AI using the same authorized context. Connect once to search decisions, save knowledge, upload source material, and explore relationships from the AI tools your team already uses. Engineering and research teams can carry project context across assistants, while finance, legal, medical, and operations teams can work from their own source material. Ask naturally. The assistant selects the right Supermemory tool without requiring code or tool names. See the [tools reference](#tools) for exact inputs and results. ## Connect Add this remote MCP server to any compatible client: ```text theme={null} https://mcp.supermemory.ai/mcp ``` After you connect, your client opens Supermemory in a browser. Sign in or create an account, then choose which spaces the client can access. Supermemory uses OAuth, so no API key is required. For client-specific instructions, see [Setup and Usage](/docs/supermemory-mcp/setup) or the [Claude Desktop guide](/docs/supermemory-mcp/claude-desktop). MCP clients that use JSON configuration generally accept this shape: ```json theme={null} { "mcpServers": { "supermemory": { "url": "https://mcp.supermemory.ai/mcp" } } } ``` ## How spaces work A **space** keeps a team's documents, memories, and profile context focused, so AI retrieves the right knowledge without mixing unrelated work. Use separate spaces for an engineering launch, research project, finance workflow, legal matter, medical knowledge base, or any other shared initiative. Teammates collaborate within the spaces they are allowed to read or write. Name a space to use it for one request without changing your active space. Otherwise, Supermemory uses your active space or account default. Ask to switch spaces when you want future requests to use a different active space. ## Tools Your assistant chooses these tools automatically. Use this table when you need the exact inputs and results. | Tool | Use it for | Inputs | Result | | --------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------- | | `search_memory` | Semantic recall from one space, with optional profile context | `query` (required), `includeProfile`, `containerTag` | Profile context and matching memories | | `add_memory` | Save information or forget outdated information | `content` (required), `action` (`save` or `forget`), `containerTag` | Save or forget confirmation | | `listDocuments` | Browse stored source documents and their summaries | `page`, `limit`, `containerTag` | Document IDs, titles, types, status, dates, and summaries | | `getDocument` | Read the available content of one document | `documentId` (required) | Document metadata, summary, and available content | | `listMemories` | Browse recent extracted memory entries and their source document IDs | `page`, `limit`, `containerTag` | Memory IDs, text, versions, and source document IDs | | `listSpaces` | List accessible spaces and resolve a space name to its key | None | Formatted list plus structured `spaces` and `count` fields | | `whoAmI` | Inspect the authenticated account, permissions, scope, and active space | None | Account and access context | ### Search `search_memory` accepts a natural-language query and returns semantically relevant memories. By default, it also includes stable and recent profile context from the same space. Set `includeProfile` to `false` when only matching memories are needed. Use the retrieval tools for different questions: * Use `search_memory` to answer a question from remembered context. * Use `listDocuments` to discover stored sources, then `getDocument` to read one source in full. * Use `listMemories` to inspect the extracted memory entries themselves, including their IDs and source document IDs. `listDocuments` and `listMemories` default to 10 results per page and accept up to 50. ### Save or forget `add_memory` saves the supplied `content` by default. Set `action` to `forget` when a fact is outdated or should be removed. There is no separate forget tool. If the content is already final, the assistant should use `add_memory`. If you want to review, edit, or choose a space before saving, it should open the `guided-save` widget instead. ### Access control `whoAmI` returns the current identity, role, access type, granted scope, and active space. `listSpaces` returns only spaces the authenticated account can access, with names, keys, document and memory counts, and recent activity. ## Interactive widgets Clients that support [MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview) can open these Supermemory widgets directly inside the conversation. | Tool | What it opens | | -------------- | ---------------------------------------------- | | `select-space` | A searchable space picker | | `guided-save` | An editable memory form with a space selector | | `upload-file` | A local file picker and upload form | | `memory-graph` | An interactive graph of documents and memories | ### Select a space `select-space` opens a searchable space picker and changes the active space for future Supermemory actions. A one-off request in another space does not change the active space. Supermemory MCP searchable space picker Supermemory MCP searchable space picker ### Guided save `guided-save` opens a draft with editable memory content and a writable-space selector. The assistant can prefill the draft, but nothing is saved until you submit it. Supermemory MCP guided save draft with editable memory content and a space selector Supermemory MCP guided save draft with editable memory content and a space selector ### File upload `upload-file` opens a local file picker and uploads one file at a time to a writable space. Supported types include text, Markdown, PDF, Word, CSV, common images, MP3, WAV, M4A, MP4, and WebM. Supermemory MCP file picker with a writable-space selector Supermemory MCP file picker with a writable-space selector ### Memory graph `memory-graph` renders the selected space as an interactive graph of source documents and extracted memories. When no space is named, the server automatically uses the active space or account default. If you name a specific space, the assistant calls `listSpaces` to resolve its name to a space key, then opens that space's graph. Supermemory MCP interactive graph of documents and memories Supermemory MCP interactive graph of documents and memories ## Resources and context prompt Some MCP clients also expose resources and prompts: | Kind | Name or URI | What it returns | | -------- | ----------------------- | ----------------------------------------------------------------------------------------------- | | Resource | `supermemory://profile` | Stable and recent profile context for the active space | | Resource | `supermemory://spaces` | A compact list of accessible spaces with the active space marked | | Prompt | `context` | A ready-to-attach context message for the active space, plus up to three recently active spaces | The `context` prompt takes no arguments. It returns profile context for the active space and up to three recently active spaces. Configure Supermemory in supported MCP clients. View the source code. # Setup and Usage Source: https://supermemory.ai/docs/supermemory-mcp/setup Connect Supermemory to an MCP client with OAuth and choose how requests use spaces ## Server URL ```text theme={null} https://mcp.supermemory.ai/mcp ``` Add the remote server to your MCP client: ```json theme={null} { "mcpServers": { "supermemory": { "url": "https://mcp.supermemory.ai/mcp" } } } ``` Supermemory MCP uses OAuth. Your client opens the authorization page so you can sign in and approve access. No API key or custom header is required. ## Choose a space After connecting, space-aware tools use your active Supermemory space or account default. You can work in another space in two ways: * Name a space for a one-off action without changing your active space. * Ask to switch your active space for future actions. See [How spaces work](/docs/supermemory-mcp/mcp#how-spaces-work) for examples and routing rules. ## Client-specific setup ### ChatGPT Web Enable developer mode, add Supermemory from the ChatGPT Plugins page, and complete OAuth. See the [ChatGPT Web guide](/docs/supermemory-mcp/chatgpt-web) for the complete flow. ### Claude Desktop and Claude web Open **Settings > Connectors**, add a custom connector, and enter the server URL. See the [Claude Desktop guide](/docs/supermemory-mcp/claude-desktop) for the complete flow. ### Cursor Add the server to `~/.cursor/mcp.json`: ```json theme={null} { "mcpServers": { "supermemory": { "url": "https://mcp.supermemory.ai/mcp" } } } ``` ### Other MCP clients Add `https://mcp.supermemory.ai/mcp` as a remote HTTP MCP server and complete the OAuth flow opened by your client. Exact menu names and configuration locations vary between clients. Clients with MCP Apps support render the space picker, guided save, file upload, and memory graph directly in the conversation. ## Verify the connection Start with any of these requests: * "What Supermemory spaces can I access?" * "What is my active Supermemory space?" * "Search my saved context for the launch plan." * "I want to upload a file to Supermemory." * "Show my memory graph." The [MCP overview](/docs/supermemory-mcp/mcp) documents every tool, widget, resource, and the `context` prompt. # Profile Buckets Source: https://supermemory.ai/docs/user-profiles/buckets Custom topical categories for user profiles Buckets are **custom topical categories** for a profile — an axis that sits alongside `static` and `dynamic`. Where static/dynamic split facts by how long-lived they are, buckets group them by subject (e.g. `preferences`, `goals`, `work`). As content is ingested, a classifier assigns each memory to the buckets it matches, so you can pull just the slice of context a given surface needs. New to buckets? Read the [conceptual overview](/docs/concepts/user-profiles#buckets) first — this page is the API reference for reading, creating, and managing them. Every org starts with a built-in `preferences` bucket. You can define your own at the organization level, add more at the space (container tag) level, or get AI-generated suggestions — all covered below. *** ## Reading buckets ### Requesting bucketed profiles Pass `include: ["buckets"]` to `/v4/profile` to return bucket-organized memories, and optionally `buckets` to limit the response to specific keys. `include` also lets you skip sections you don't need — `["buckets"]` alone omits `static` and `dynamic`. ```typescript theme={null} const res = await fetch("https://api.supermemory.ai/v4/profile", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ containerTag: "user_123", include: ["buckets"], buckets: ["preferences", "goals"] // optional — omit for all buckets }) }); const { profile } = await res.json(); console.log(profile.buckets.preferences); console.log(profile.buckets.goals); ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v4/profile" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTag": "user_123", "include": ["buckets"], "buckets": ["preferences", "goals"] }' ``` **Response:** ```json theme={null} { "profile": { "buckets": { "preferences": [ "[Summary] Prefers concise, technical answers and dark-mode tooling", "[Recent] Switched their editor to Zed" ], "goals": [ "[Recent] Wants to ship the billing revamp this quarter" ] } } } ``` **`[Recent]` and `[Summary]` labels.** To keep profiles dense, an entity's older memories are periodically aggregated into a short synthesis. Entries prefixed `[Summary]` are that aggregated context; entries prefixed `[Recent]` were ingested since the last aggregation and aren't summarized yet. The `dynamic` section uses the same `[Recent]` prefix (plus a `[YYYY-MM-DD]` date). Strip the prefixes if you only want raw text, or keep them to signal recency to your model. ### List bucket definitions To see which buckets are configured for a container tag (org buckets merged with any space-level additions), call `/v4/profile/buckets`: ```typescript theme={null} const res = await fetch("https://api.supermemory.ai/v4/profile/buckets", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ containerTag: "user_123" }) }); const { buckets } = await res.json(); // [{ key: "preferences", description: "..." }, ...] ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v4/profile/buckets" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"containerTag": "user_123"}' ``` **Response:** ```json theme={null} { "buckets": [ { "key": "preferences", "description": "Explicit first-person preferences the person directly stated." } ] } ``` | Field | Type | Description | | ----------------------- | ------ | ---------------------------------------------------------------------------------------- | | `buckets[].key` | string | Stable slug, also stored on each memory. Lowercase alphanumeric with `-`/`_`, 1–64 chars | | `buckets[].description` | string | What belongs in the bucket — guides the ingestion classifier | This endpoint requires only that the caller belongs to the org — any role, and any API key (scoped keys included) can read bucket definitions. *** ## Creating and configuring buckets Bucket definitions live at two levels: **organization** (the default set every container tag gets) and **space** (per-container-tag additions). Both are configured through the settings API — there's no console-only path; these are regular authenticated endpoints. Writing buckets requires an **admin or owner** role in the org, and a **full-access API key** — project/container-tag-**scoped** keys cannot call these endpoints and will get a `403`. Reading buckets (the endpoints above) has no such restriction. ### Organization-level buckets `PATCH /v3/settings` sets the org's bucket list. The `profileBuckets` array **replaces the entire stored list** — it's not a merge, so always send the full set you want. ```typescript theme={null} const res = await fetch("https://api.supermemory.ai/v3/settings", { method: "PATCH", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ profileBuckets: [ { key: "work", description: "Professional role, employer, projects, and work-related decisions." }, { key: "health", description: "Physical and mental wellbeing, habits, and health-related goals." } ] }) }); const { updated } = await res.json(); ``` ```bash theme={null} curl -X PATCH "https://api.supermemory.ai/v3/settings" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "profileBuckets": [ { "key": "work", "description": "Professional role, employer, projects, and work-related decisions." }, { "key": "health", "description": "Physical and mental wellbeing, habits, and health-related goals." } ] }' ``` **Response:** ```json theme={null} { "orgId": "org_abc123xyz", "orgSlug": "acme-inc", "updated": { "profileBuckets": [ { "key": "work", "description": "Professional role, employer, projects, and work-related decisions." }, { "key": "health", "description": "Physical and mental wellbeing, habits, and health-related goals." } ] // ...other org settings fields } } ``` `GET /v3/settings` returns the current org settings, including `profileBuckets`, without changing anything. ### Space (container tag) buckets `PATCH /v3/container-tags/{containerTag}` sets a container tag's own bucket list. These are **add-only** on top of org buckets — a tag always keeps every org bucket, and if a space bucket's key collides with an org bucket, the org's definition wins in the merged, effective set used at ingestion and read time. ```typescript theme={null} const res = await fetch("https://api.supermemory.ai/v3/container-tags/user_alex", { method: "PATCH", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ profileBuckets: [ { key: "trip_planning", description: "Upcoming trip details specific to this user." } ] }) }); ``` ```bash theme={null} curl -X PATCH "https://api.supermemory.ai/v3/container-tags/user_alex" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "profileBuckets": [ { "key": "trip_planning", "description": "Upcoming trip details specific to this user." } ] }' ``` **Response:** ```json theme={null} { "containerTag": "user_alex", "name": "user_alex", "entityContext": null, "memoryFilesystemPaths": null, "profileBuckets": [ { "key": "trip_planning", "description": "Upcoming trip details specific to this user." } ], "updatedAt": "2026-07-18T00:00:00.000Z" } ``` Like the org endpoint, this **replaces the tag's own bucket list**, not the merged/effective set — `profileBuckets` in the response is only what this space added, not the org buckets it inherits. Call `/v4/profile/buckets` to see the merged, effective list for a tag. ### AI-generated suggestions `POST /v3/settings/suggest-buckets` returns 3–6 bucket suggestions tailored to your org, generated from the `filterPrompt` already configured in your org settings. It doesn't save anything — pass the results into the `PATCH /v3/settings` call above to apply them. ```typescript theme={null} const res = await fetch("https://api.supermemory.ai/v3/settings/suggest-buckets", { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}` } }); const { suggestions } = await res.json(); // [{ key: "customer_support", description: "..." }, ...] ``` ```bash theme={null} curl -X POST "https://api.supermemory.ai/v3/settings/suggest-buckets" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" ``` Requires a `filterPrompt` already set on your org (via `PATCH /v3/settings`) — without one, this returns `400 { "error": "No organization context configured..." }`, since suggestions are tailored from it. ### Starter presets If you'd rather start from a template than write descriptions from scratch, these are the same presets available in the console UI: | Key | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------- | | `preferences` | Stated likes, dislikes, and personal settings choices — food, media, tools, aesthetics, and other expressed tastes. | | `interests` | Topics, hobbies, and domains the person is curious about or actively follows, even if not yet a firm preference. | | `goals` | Short- and long-term objectives, aspirations, and things the person wants to achieve or work toward. | | `work` | Professional context: current role, employer, projects, colleagues, career trajectory, and work-related decisions. | | `relationships` | People in the person's life — family, friends, colleagues, partners — and the nature of those connections. | | `health` | Physical and mental wellbeing: conditions, habits, medications, fitness routines, and health-related goals. | | `skills` | Competencies, expertise areas, tools mastered, and things the person is actively learning. | | `finances` | Financial habits, spending patterns, savings goals, income context, and money-related decisions. | | `education` | Academic background, current courses, learning goals, and educational achievements. | | `travel` | Places visited, travel preferences, upcoming trips, and destinations the person wants to visit. | | `values` | Core beliefs, ethical stances, principles, and things that matter most to the person. | | `projects` | Personal and professional side projects, creative endeavors, and things being built outside of primary work. | ### Default bucket If neither the org nor the space has configured any buckets, ingestion falls back to a single built-in `preferences` bucket, scoped tightly to explicit first-person statements ("prefers X over Y", "always uses W") — not inferred traits or general observations. Configuring your own buckets replaces this default. *** ## Validation & limits | Rule | Detail | | -------------- | ------------------------------------------------------------------------------------- | | Key format | Lowercase alphanumeric, starting with a letter/digit, may contain `-`/`_`. 1–64 chars | | Reserved keys | `static` and `dynamic` can't be used as bucket keys | | Max buckets | 50 per array — applies separately to an org's list and to each space's list | | Duplicate keys | Rejected within a single request's array | | Description | Optional, up to 2,000 chars. Defaults to empty if omitted | Bucket descriptions steer classification. A precise description ("Explicit first-person preferences only — exclude inferred traits") yields cleaner buckets than a vague one. *** ## Configure ### Instructions Bucket `description`s only steer classification *within* a bucket — they don't tell the model anything about the space itself. For that, set [`entityContext`](/docs/concepts/customization#entity-context) on the container tag: a free-text field that's appended alongside `filterPrompt` into the same prompt the extraction/classification step uses, so it shapes bucket assignment too, not just fact extraction. `PATCH /v3/container-tags/{containerTag}`: ```typescript theme={null} await fetch("https://api.supermemory.ai/v3/container-tags/user_alex", { method: "PATCH", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ entityContext: "This tag belongs to a solo founder juggling sales, hiring, and product." }) }); ``` ```bash theme={null} curl -X PATCH "https://api.supermemory.ai/v3/container-tags/user_alex" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "entityContext": "This tag belongs to a solo founder juggling sales, hiring, and product." }' ``` | Field | Type | Limit | | --------------- | -------------- | -------------------------------------------- | | `entityContext` | string \| null | Up to 1,500 characters. Pass `null` to clear | `entityContext` is per-container-tag, so use it for context specific to that user/space (who they are, what the space is for) — use org-level [`filterPrompt`](/docs/concepts/customization) for guidance that should apply everywhere. Both are combined into the same prompt, so keep them complementary rather than redundant. You can also set `entityContext` inline when adding content, via `entityContext` on [`POST /v4/memories`](/docs/ingestion/add-memories) — useful if you don't want a separate settings call. ### Model selection The model behind extraction and bucket classification isn't configurable through the API on supermemory Cloud — it's managed for you. If you're self-hosting, you choose the provider and model yourself via environment variables (`OPENAI_MODEL` and related) — see [Self-hosting Configuration](/docs/self-hosting/configuration). *** ## Next Steps * [User Profiles](/docs/recall/user-profiles) — Fetch and use profiles via the API * [User Profiles Concept](/docs/concepts/user-profiles) — Static vs dynamic vs buckets * [Container Tags](/docs/concepts/container-tags) — How spaces and container tags work # Using Supermemory Source: https://supermemory.ai/docs/using-supermemory The full loop: authenticate, put context in, get it back out, keep it correct. 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 `containerTag`. Raw input becomes **memories** (the knowledge graph) and/or indexed **document chunks** (for RAG) automatically — see [how it works](/docs/concepts/how-it-works). You don't choose one or the other; both build from the same write. Retrieval gives you three ways to read that same pool back — see [Memory vs RAG](/docs/concepts/memory-vs-rag) if you're not sure which one fits. ## Where next Walk the whole loop end to end with one working example. What happens between `add()` and a memory showing up in search. The isolation boundary every ingest and retrieve call is scoped to. When to reach for search, profiles, or both. Run standardized, reproducible evals against Supermemory and other providers — including your own.