From 672defc08b8617f6fa57e903ae48dd7d7e5661a5 Mon Sep 17 00:00:00 2001
From: MaheshtheDev <38828053+MaheshtheDev@users.noreply.github.com>
Date: Tue, 6 Oct 2026 17:06:38 +0000
Subject: [PATCH] docs: move SDK snippets to the shipped v5 call shape and
finish the namespace rename (#1772)
Rewrites 339 TypeScript calls across 50 pages from the rc.5 `method({ namespace, body })` form to the shipped `method(namespace, { ... })` form, and aligns field names with the live v5 spec: `attach` to `include`, `authUrl` to `authorization`, `lastSync` to `latestRun`, `deletedCount` to `count`, and the paginated `namespaces.list()`.
Renames container tags to namespaces across concepts, connectors, integrations and snippets. The namespace pages keep container tag in the description, search keywords and a rename note so old searches still land, and the v3 reference page points at v5.
The migration guide's SDK table now covers both 5.0.0 SDKs, and the SDK integration page uses the real client options (`baseUrl`, `timeoutInSeconds`, `maxRetries`) and error classes.
---
apps/docs/agents-and-mcp.mdx | 41 +-
apps/docs/api-reference/container-tags.mdx | 4 +
apps/docs/authentication.mdx | 32 +-
apps/docs/concepts/container-tags.mdx | 175 ++++---
apps/docs/concepts/content-types.mdx | 82 ++-
apps/docs/concepts/customization.mdx | 75 ++-
apps/docs/concepts/filtering.mdx | 374 +++++++------
apps/docs/concepts/graph-memory.mdx | 36 +-
apps/docs/concepts/how-it-works.mdx | 71 +--
apps/docs/concepts/memory-vs-rag.mdx | 22 +-
apps/docs/concepts/multi-tenancy-examples.mdx | 89 ++--
apps/docs/concepts/multi-tenancy.mdx | 65 ++-
apps/docs/concepts/rules.mdx | 137 +++--
apps/docs/concepts/super-rag.mdx | 50 +-
apps/docs/concepts/user-profiles.mdx | 58 +--
apps/docs/connectors/github.mdx | 493 +++++++-----------
apps/docs/connectors/gmail.mdx | 330 +++++-------
apps/docs/connectors/google-drive.mdx | 486 +++++++----------
apps/docs/connectors/granola.mdx | 149 +++---
apps/docs/connectors/managing-resources.mdx | 381 ++++++--------
apps/docs/connectors/notion.mdx | 370 +++++--------
apps/docs/connectors/onedrive.mdx | 328 ++++--------
apps/docs/connectors/overview.mdx | 359 +++++++------
apps/docs/connectors/s3.mdx | 205 ++++----
apps/docs/connectors/troubleshooting.mdx | 166 +++---
apps/docs/connectors/web-crawler.mdx | 389 ++++++--------
apps/docs/docs.json | 31 +-
apps/docs/index.mdx | 2 +-
apps/docs/ingestion/add-memories.mdx | 242 ++++-----
.../batch-ingest-historical-data.mdx | 79 +--
apps/docs/ingestion/document-operations.mdx | 198 ++++---
apps/docs/integrations/agno.mdx | 87 ++--
apps/docs/integrations/ai-sdk.mdx | 38 +-
apps/docs/integrations/claude-memory.mdx | 11 +-
apps/docs/integrations/convex.mdx | 30 +-
apps/docs/integrations/crewai.mdx | 80 +--
apps/docs/integrations/langchain.mdx | 107 ++--
apps/docs/integrations/langgraph.mdx | 76 +--
apps/docs/integrations/mastra.mdx | 80 +--
apps/docs/integrations/n8n.mdx | 11 +-
apps/docs/integrations/openai-agents-sdk.mdx | 97 ++--
apps/docs/integrations/openai.mdx | 20 +-
apps/docs/integrations/supermemory-sdk.mdx | 379 ++++++++++----
apps/docs/integrations/voltagent.mdx | 24 +-
apps/docs/integrations/zapier.mdx | 6 +-
apps/docs/migration/from-mem0.mdx | 45 +-
apps/docs/migration/from-zep.mdx | 107 ++--
apps/docs/migration/tools-v3-upgrade.mdx | 64 +++
apps/docs/overview/billing.mdx | 27 +-
apps/docs/overview/comparison.mdx | 8 +-
apps/docs/overview/security.mdx | 14 +-
apps/docs/overview/use-cases.mdx | 8 +-
apps/docs/overview/what-is-supermemory.mdx | 24 +-
apps/docs/quickstart.mdx | 297 +++++------
apps/docs/recall/memory-operations.mdx | 326 +++---------
apps/docs/recall/memory-review.mdx | 32 +-
apps/docs/recall/search.mdx | 190 ++++---
apps/docs/recall/user-profiles.mdx | 190 ++++---
apps/docs/self-hosting/configuration.mdx | 2 +-
.../docs/self-hosting/local-vs-enterprise.mdx | 2 +-
apps/docs/self-hosting/overview.mdx | 10 +-
apps/docs/self-hosting/quickstart.mdx | 37 +-
apps/docs/snippets/api-v5-agent-prompt.mdx | 2 +-
apps/docs/snippets/api-v5-completion.mdx | 4 +-
.../snippets/api-v5-content-management.mdx | 32 ++
.../snippets/api-v5-document-ingestion.mdx | 44 ++
.../docs/snippets/api-v5-document-updates.mdx | 29 ++
apps/docs/snippets/api-v5-filters.mdx | 33 ++
.../snippets/api-v5-memory-forgetting.mdx | 24 +-
apps/docs/snippets/api-v5-namespaces.mdx | 29 ++
apps/docs/snippets/api-v5-organization.mdx | 17 +
apps/docs/snippets/api-v5-overview.mdx | 88 +++-
apps/docs/snippets/api-v5-profiles.mdx | 33 +-
apps/docs/snippets/api-v5-rollout.mdx | 2 +-
apps/docs/snippets/api-v5-sdks.mdx | 53 +-
apps/docs/snippets/api-v5-search.mdx | 38 ++
apps/docs/user-profiles/buckets.mdx | 342 ++++--------
apps/docs/using-supermemory.mdx | 6 +-
apps/docs/v5/api-reference/connectors.mdx | 54 ++
.../v5/api-reference/content-management.mdx | 27 +-
apps/docs/v5/api-reference/ingest.mdx | 22 +-
apps/docs/v5/api-reference/namespaces.mdx | 27 +-
apps/docs/v5/api-reference/organization.mdx | 22 +-
apps/docs/v5/api-reference/overview.mdx | 16 +-
apps/docs/v5/api-reference/profiles.mdx | 24 +-
apps/docs/v5/api-reference/search.mdx | 24 +-
86 files changed, 4423 insertions(+), 4517 deletions(-)
create mode 100644 apps/docs/migration/tools-v3-upgrade.mdx
create mode 100644 apps/docs/v5/api-reference/connectors.mdx
diff --git a/apps/docs/agents-and-mcp.mdx b/apps/docs/agents-and-mcp.mdx
index 92e4ed8c..b0f73162 100644
--- a/apps/docs/agents-and-mcp.mdx
+++ b/apps/docs/agents-and-mcp.mdx
@@ -36,14 +36,14 @@ 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
-npx supermemory add "User prefers TypeScript" --tag user_123
-npx supermemory search "language preference" --tag user_123
-npx supermemory profile --tag user_123
+npx supermemory add "User prefers TypeScript" --namespace user_123
+npx supermemory search "language preference" --namespace user_123
+npx supermemory profile --namespace user_123
```
## Skill
-Install the official skill so the agent uses the real endpoints, auth, and `containerTag` rules instead of hallucinating APIs:
+Install the official skill so the agent uses the real endpoints, auth, and `namespace` rules instead of hallucinating APIs:
```bash
npx skills add https://github.com/supermemoryai/skills --skill supermemory
@@ -175,10 +175,11 @@ https://supermemory.ai/docs/mcp
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
+- Prefer `npx supermemory setup` / the supermemory skill for correct auth, namespace, and SDK usage.
+- Canonical writes: POST /ns/{namespace}/document · search: POST /ns/{namespace}/search · profile: POST /ns/{namespace}/profile
- Auth: Authorization: Bearer $SUPERMEMORY_API_KEY only
-- Always scope with containerTag (singular) on write and search
+- Always scope with one namespace in the URL path on write and search (never containerTag in the body)
+- SDK: import { Supermemory } from "supermemory"; every call takes { namespace, ...queryParams, body }
- For demos use dreaming: "instant" when memories must be ready right after status done
```
@@ -195,26 +196,26 @@ Note: You can always reference the documentation by using the **supermemory-docs
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()`
+- Write content: POST https://api.supermemory.ai/ns/{namespace}/document
+- Search: POST https://api.supermemory.ai/ns/{namespace}/search
+- Profile: POST https://api.supermemory.ai/ns/{namespace}/profile
+- Org settings: PATCH https://api.supermemory.ai/organization
+- Scoping: one `namespace` in the URL path — never in the body or a header
+- SDK: `import { Supermemory } from "supermemory"`; `supermemory.add(namespace, { content, id?, metadata? })`, `supermemory.search(namespace, { query })`, `supermemory.profile(namespace)`
DO NOT USE — deprecated, undocumented, or fabricated:
-- Endpoints: /v1/anything, /v3/memories, /v3/search (use /v3/documents and /v4/search)
+- Endpoints: /v1/anything, /v3/documents, /v3/search, /v4/search, /v4/profile, /v4/memories (use /ns/{namespace}/...)
- 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
+- Body keys: containerTag, containerTags, customId (use `id`), entityContext (use `supportingContext`), filterByMetadata (use `group`), documentDate (use `date`), q (use `query`), filters (use `filter`), userId, spaces
+- SDK: `client.add({ containerTag })`, `client.search.execute`, `client.memories.*`, `client.profile({ q })`
-SCOPING IS LOAD-BEARING. Every write and every search MUST include `containerTag`.
+SCOPING IS LOAD-BEARING. Every write and every search MUST name one namespace in the path.
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
+- Ingest conversations with a stable `id` + dreaming: "instant" when you need memories immediately
+- Wait until document system.status is done before search
+- search with searchMode: "chunks" for RAG, searchMode: "memories" (+ include.related) 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
diff --git a/apps/docs/api-reference/container-tags.mdx b/apps/docs/api-reference/container-tags.mdx
index 5969f61b..3dd5ac08 100644
--- a/apps/docs/api-reference/container-tags.mdx
+++ b/apps/docs/api-reference/container-tags.mdx
@@ -5,6 +5,10 @@ description: "Settings, merge and delete for multi-tenant containers."
icon: "/icons/hugeicons/book-open-01.svg"
---
+
+v5 calls these **namespaces**. Same values. New integrations should use [v5 namespaces](/v5/api-reference/namespaces).
+
+
`containerTag` is the primary multi-tenant key (user id, workspace id, etc.). These endpoints manage settings and lifecycle for a tag.
| Endpoint | Use when |
diff --git a/apps/docs/authentication.mdx b/apps/docs/authentication.mdx
index 95ae3564..9c90b9c6 100644
--- a/apps/docs/authentication.mdx
+++ b/apps/docs/authentication.mdx
@@ -1,6 +1,6 @@
---
title: "API keys & auth"
-description: "Org API keys, container-scoped keys, and connector branding."
+description: "Org API keys, namespace-scoped keys, and connector branding."
sidebarTitle: "API keys"
icon: "/icons/hugeicons/key-01.svg"
---
@@ -16,17 +16,17 @@ Include your key in all requests:
```bash cURL
-curl https://api.supermemory.ai/v3/search \
+curl -X POST https://api.supermemory.ai/ns/user_123/search \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
- -d '{"q": "hello"}'
+ -d '{"query": "hello"}'
```
```typescript TypeScript
-import Supermemory from "supermemory";
+import { Supermemory } from "supermemory";
-const client = new Supermemory({ apiKey: "YOUR_API_KEY" });
+const supermemory = new Supermemory({ apiKey: "YOUR_API_KEY" });
```
@@ -42,15 +42,7 @@ 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
-await client.settings.update({
- googleDriveCustomKeyEnabled: true,
- googleDriveClientId: "your-client-id.apps.googleusercontent.com",
- googleDriveClientSecret: "your-client-secret"
-});
-```
+When users connect external services (Google Drive, Notion, OneDrive), they see a "Log in to **Supermemory**" prompt by default. You can replace this with your own app name by providing your own OAuth credentials.
This works for Google Drive, Notion, and OneDrive. See the full setup in [Customization](/concepts/customization).
@@ -58,9 +50,9 @@ This works for Google Drive, Notion, and OneDrive. See the full setup in [Custom
## 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.
+Scoped keys are restricted to one namespace (what v3/v4 called a container tag). They can only access documents and search within that namespace — use them to give a client, session, or tenant limited access without shipping your org master key.
-Pairs with [container tags](/concepts/container-tags) for multi-tenant isolation.
+Pairs with [namespaces](/concepts/container-tags) for multi-tenant isolation.
**Allowed endpoints:** `/v3/documents`, `/v3/memories`, `/v4/memories`, `/v3/search`, `/v4/search`, `/v4/profile`
@@ -68,6 +60,8 @@ Scoped keys **cannot** read billing, manage org settings, or mint further keys.
### Create a scoped key
+The scoped-key endpoint still takes the namespace in a field named `containerTag`.
+
```bash
curl https://api.supermemory.ai/v3/auth/scoped-key \
--request POST \
@@ -84,7 +78,7 @@ curl https://api.supermemory.ai/v3/auth/scoped-key \
| Parameter | Required | Default | Description |
| --- | --- | --- | --- |
-| `containerTag` | Yes | — | Alphanumeric, hyphens, underscores, colons, dots |
+| `containerTag` | Yes | — | The namespace to scope the key to. 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) |
@@ -103,11 +97,11 @@ curl https://api.supermemory.ai/v3/auth/scoped-key \
}
```
-Use the returned key like a normal API key — it just will not work outside its container scope.
+Use the returned key like a normal API key — it just will not work outside its namespace.
### Disable a scoped key
-Revoke with the `id` from creation. Subsequent requests get `401`. Memories and container tags are **not** deleted.
+Revoke with the `id` from creation. Subsequent requests get `401`. Memories and namespaces are **not** deleted.
```bash
curl https://api.supermemory.ai/v3/auth/scoped-key/KEY_ID \
diff --git a/apps/docs/concepts/container-tags.mdx b/apps/docs/concepts/container-tags.mdx
index 2046bf10..f555cbd8 100644
--- a/apps/docs/concepts/container-tags.mdx
+++ b/apps/docs/concepts/container-tags.mdx
@@ -1,20 +1,25 @@
---
-title: "Container tags"
-sidebarTitle: "Container tags"
-description: "The isolation boundary that groups and partitions memories by user, project, or any logical scope"
+title: "Namespaces"
+sidebarTitle: "Namespaces"
+description: "Namespaces, formerly container tags, are the isolation boundary that partitions memories by user, project, or any logical scope"
icon: "/icons/hugeicons/folder-01.svg"
+keywords: ["container tag", "container tags", "containerTag", "containerTags", "namespace"]
---
-A **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.
+A **namespace** is the primary way you organize and isolate memories in Supermemory. It is a string identifier you choose, and it scopes every call: add, search, list, update, and delete all happen inside one namespace.
-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.
+
+Namespaces were called **container tags** (`containerTag`) in v3 and v4. Same values, nothing moved. See the [v5 migration guide](/migration/api-v5).
+
+
+Every memory in `user_alex` lives in its own isolated space, completely separate from memories in `user_jordan`. This is what makes Supermemory safe to use in multi-tenant applications: one user can never see another user's memories unless you explicitly query both namespaces.
Bucket memories by user, project, agent, workspace, or any boundary that makes sense for your app.
- Each container tag maps to its own vector namespace, so search and retrieval never leak across boundaries.
+ Each namespace maps to its own vector index, so search and retrieval never leak across boundaries.
@@ -22,33 +27,51 @@ Think of a container tag as a **namespace**: every memory tagged with `user_alex
## 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.
+When you add a document to a namespace, Supermemory creates that namespace (scoped to your organization) if it does not exist yet. You don't need to provision anything ahead of time. The first write to a new namespace creates it, and later writes reuse it.
-```typescript
-// First call auto-creates the "user_alex" container
-await client.add({
+
+
+```typescript TypeScript
+import { Supermemory } from "supermemory";
+
+const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
+
+// First call auto-creates the "user_alex" namespace
+await supermemory.add("user_alex", {
content: "Alex prefers dark mode and concise answers",
- 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",
+const results = await supermemory.search("user_alex", {
+ query: "what are the user's UI preferences?",
});
```
-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.
+```bash curl
+curl -X POST "https://api.supermemory.ai/ns/user_alex/document" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{ "content": "Alex prefers dark mode and concise answers" }'
+
+curl -X POST "https://api.supermemory.ai/ns/user_alex/search" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{ "query": "what are the user'\''s UI preferences?" }'
+```
+
+
+
+Under the hood, each namespace is backed by a dedicated vector index. Embeddings, chunks, and memory entries for one namespace are stored and searched independently of every other namespace. There is no shared index to filter through, which is why isolation is strict rather than best-effort.
-A 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.
+A namespace is an **opaque identifier you choose**. Supermemory does not parse meaning out of it: `user_123`, `project_mobile`, and `org:acme:team:growth` are all equally valid. Pick a convention that mirrors the access boundaries in your own application.
---
## Naming rules
-Container tags are validated on every request. A tag must:
+Namespaces are validated on every request. A namespace must:
- Be **100 characters or less**
- Contain only **alphanumeric characters, hyphens (`-`), underscores (`_`), and colons (`:`)**
@@ -68,88 +91,118 @@ Matching pattern: `^[a-zA-Z0-9_:-]+$`
"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.
+The colon is intentionally allowed so you can build **hierarchical** namespaces (for example `org:acme:user:john`) that encode several levels of structure in a single identifier.
---
-## `containerTag` vs `containerTags`
+## Scope lives in the URL
-Supermemory's current API uses a **single** `containerTag` string per request.
+In v5 the namespace is a path segment, not a body field. Every scoped endpoint starts with `/ns/{namespace}/`, and every SDK call takes `namespace` as a top-level key next to `body`.
+
+| Legacy (v3/v4) | v5 |
+|-----------|------|
+| `containerTag` string in the request body | `/ns/{namespace}` in the URL |
+| `containerTags` array | Not supported. One namespace 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`.
+A request can only touch one namespace. To read across several namespaces, make one request per namespace and merge the results in your application.
-| API field | Type | Status |
-|-----------|------|--------|
-| `containerTag` | `string` | ✅ Current — use this |
-| `containerTags` | `string[]` | ⚠️ Deprecated |
-
---
-## Where container tags are used
+## Where namespaces are used
-The same tag flows through the entire lifecycle of a memory. Pass it consistently and your data stays neatly partitioned.
+The same namespace flows through the entire lifecycle of a memory. Pass it consistently and your data stays neatly partitioned.
| Operation | Behavior |
|-----------|----------|
-| **Add** | Writes the 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. |
+| **Add** | Writes the document into the namespace (auto-creating it). |
+| **Search** | Restricts retrieval to the namespace. |
+| **List** | Returns only documents, chunks, or memories in the namespace. |
+| **Update / Delete** | Targets the document inside the namespace. |
```typescript
// Add
-await client.add({ content: "Q1 planning notes", containerTag: "project_q1" });
+await supermemory.add("project_q1", { content: "Q1 planning notes" });
-// Search within the same container
-await client.search({ q: "planning", containerTag: "project_q1" });
+// Search within the same namespace
+await supermemory.search("project_q1", { query: "planning" });
-// List everything in the container
-await client.documents.list({ containerTags: ["project_q1"] });
+// List every document in the namespace
+await supermemory.list("project_q1", "documents");
```
---
## 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:
+Namespaces are also an **authorization boundary**, not just an organizational one. Two mechanisms can restrict which namespaces a given caller may touch:
-- **API key scopes** — an API key can be limited to a specific set of container tags, with read or write permission per tag.
-- **Member restrictions** — an organization member can be granted access to only certain container tags.
+- **API key scopes** — an API key can be limited to a specific set of namespaces, with read or write permission per namespace.
+- **Member restrictions** — an organization member can be granted access to only certain namespaces.
-When a request is restricted, Supermemory validates the requested tag against the caller's allowed set:
+When a request is restricted, Supermemory validates the namespace in the URL 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).
+- Requesting a namespace outside the allowed set returns `403 Forbidden`.
+- A write (add/update/delete) to a read-only namespace returns `403 Forbidden`.
This means you can hand out an API key that is physically incapable of reading or writing another tenant's data, enforced at the data layer rather than in your application code.
---
-## Per-container settings
+## Managing namespaces
-Each container tag can carry its own configuration, independent of other tags in the same organization:
+The `namespaces.*` SDK methods read and change a namespace itself, as opposed to the documents inside it.
-| 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. |
+| Method | What it does |
+|--------|--------------|
+| `supermemory.namespaces.list()` | Page through namespaces with `documentCount` and `memoryCount`. |
+| `supermemory.namespaces.get(namespace)` | Read one namespace and its `supportingContext`. |
+| `supermemory.namespaces.update(namespace, { supportingContext })` | Set a context prompt applied when processing documents in this namespace. Useful for steering extraction per project or tenant. |
+| `supermemory.namespaces.delete(namespace)` | Delete the namespace and all its documents and memories. |
+| `supermemory.namespaces.delete(namespace, { moveTo })` | Move the content into another namespace, then remove this one. Queued; returns an `operationId`. |
-```typescript
-await client.containerTags.update("project_research", {
- entityContext: "This project contains research papers about machine learning.",
+`supportingContext` is what v3/v4 called `entityContext`. The legacy "merge" operation is now a delete with `moveTo`, one source namespace per request.
+
+
+
+```typescript TypeScript
+await supermemory.namespaces.update("project_research", {
+ supportingContext: "This project contains research papers about machine learning.",
});
+
+const { namespaces, pagination } = await supermemory.namespaces.list();
+// namespaces: [{ id, namespace, documentCount, memoryCount, description, system }]
+
+// Consolidate: move everything from the old namespace, then remove it
+await supermemory.namespaces.delete("project_research_old", {
+ moveTo: "project_research",
+});
+// { success, status: "queued", operationId, namespace, moveTo }
```
-Container tags can also be **merged** when you need to consolidate two buckets of memories into one.
+```bash curl
+curl -X PATCH "https://api.supermemory.ai/ns/project_research" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{ "supportingContext": "This project contains research papers about machine learning." }'
+
+curl "https://api.supermemory.ai/namespaces" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+curl -X DELETE "https://api.supermemory.ai/ns/project_research_old" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{ "moveTo": "project_research" }'
+```
+
+
---
## Choosing a convention
-Pick a tagging scheme that maps onto the isolation boundaries your application actually needs.
+Pick a naming scheme that maps onto the isolation boundaries your application actually needs.
| Pattern | Example | Use case |
|---------|---------|----------|
@@ -159,7 +212,7 @@ Pick a tagging scheme that maps onto the isolation boundaries your application a
| 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.
+Keep namespaces **deterministic** — derive them directly from IDs you already have (a user ID, a tenant ID) so you can always reconstruct the right namespace at query time without a lookup.
---
@@ -167,13 +220,13 @@ Keep tags **deterministic** — derive them directly from IDs you already have (
## Next steps
-
- Combine container tags with metadata filters for precise retrieval.
+
+ Combine namespaces with metadata filters for precise retrieval.
- Mint keys that can only touch one container — multi-tenant clients without the org master key.
+ Mint keys that can only touch one namespace — multi-tenant clients without the org master key.
-
- See container tags in action across the add API.
+
+ See namespaces in action across the add API.
diff --git a/apps/docs/concepts/content-types.mdx b/apps/docs/concepts/content-types.mdx
index a9a4a342..939c6d9b 100644
--- a/apps/docs/concepts/content-types.mdx
+++ b/apps/docs/concepts/content-types.mdx
@@ -5,16 +5,15 @@ description: "All the content formats Supermemory can ingest and process"
icon: "/icons/hugeicons/files-01.svg"
---
-Supermemory automatically extracts and indexes content from various formats. There are two entry points: `client.add()` for text and URLs, `client.documents.uploadFile()` for actual files. See [Add Memories](/ingestion/add-memories) to learn how to ingest content via the API.
+Supermemory automatically extracts and indexes content from various formats. There are two entry points: `supermemory.add()` for text and URLs, `supermemory.documents.uploadFile()` for actual files. Both take the target `namespace` as a top-level key. See [Add Memories](/ingestion/add-memories) to learn how to ingest content via the API.
## Text content
Raw text, conversations, notes, or any string content.
```typescript
-await client.add({
+await supermemory.add("user_123", {
content: "User prefers dark mode and uses vim keybindings",
- containerTag: "user_123"
});
```
@@ -27,9 +26,8 @@ await client.add({
Send a URL and Supermemory fetches, extracts, and indexes the content.
```typescript
-await client.add({
- content: "https://docs.example.com/api-reference",
- containerTag: "documentation"
+await supermemory.add("documentation", {
+ content: "https://docs.example.com/api-reference"
});
```
@@ -41,15 +39,14 @@ await client.add({
### PDF
-Files are binary, so they go through `uploadFile`, not `add` — pass a stream, not base64:
+Files are binary, so they go through `uploadFile`, not `add` — pass a stream, not base64. The upload is multipart, so `metadata` is a JSON string here:
```typescript
import fs from 'fs';
-await client.documents.uploadFile({
+await supermemory.documents.uploadFile("user_123", {
file: fs.createReadStream('report.pdf'),
- containerTag: "user_123",
- metadata: JSON.stringify({ title: "Q4 Financial Report" })
+ metadata: JSON.stringify({ title: "Q4 Financial Report" }),
});
```
@@ -70,10 +67,9 @@ See [Billing & usage](/overview/billing#feature-availability) for plan availabil
Word, Excel, and PowerPoint files upload the same way — Supermemory detects the type from the file itself:
```typescript
-await client.documents.uploadFile({
+await supermemory.documents.uploadFile("user_123", {
file: fs.createReadStream('roadmap.docx'),
- containerTag: "user_123",
- metadata: JSON.stringify({ title: "Product Roadmap" })
+ metadata: JSON.stringify({ title: "Product Roadmap" }),
});
```
@@ -92,17 +88,15 @@ Both are plain text, so they go through `add` like any other string content —
```typescript
// Markdown
-await client.add({
+await supermemory.add("user_123", {
content: markdownContent,
- containerTag: "user_123",
- metadata: { title: "README.md" }
+ metadata: { title: "README.md" },
});
// Code (language auto-detected)
-await client.add({
+await supermemory.add("user_123", {
content: codeContent,
- containerTag: "user_123",
- metadata: { language: "typescript" }
+ metadata: { language: "typescript" },
});
```
@@ -114,15 +108,14 @@ Code is chunked using [code-chunk](https://github.com/supermemoryai/code-chunk),
## Images
-`fileType: "image"` and `mimeType` are both required so Supermemory knows exactly how to process it:
+Set `fileType: "image"` and `mimeType` so Supermemory knows exactly how to process it. Both are query parameters, so they sit next to `namespace` rather than inside `body`:
```typescript
-await client.documents.uploadFile({
+await supermemory.documents.uploadFile("user_123", {
file: fs.createReadStream('diagram.png'),
+ metadata: JSON.stringify({ title: "Architecture Diagram" }),
fileType: "image",
mimeType: "image/png",
- containerTag: "user_123",
- metadata: JSON.stringify({ title: "Architecture Diagram" })
});
```
@@ -138,20 +131,18 @@ Video has a dedicated `fileType`; audio is uploaded the same way and detected fr
```typescript
// Video
-await client.documents.uploadFile({
+await supermemory.documents.uploadFile("user_123", {
file: fs.createReadStream('demo.mp4'),
+ metadata: JSON.stringify({ title: "Product Demo" }),
fileType: "video",
mimeType: "video/mp4",
- containerTag: "user_123",
- metadata: JSON.stringify({ title: "Product Demo" })
});
// Audio
-await client.documents.uploadFile({
+await supermemory.documents.uploadFile("user_123", {
file: fs.createReadStream('call-recording.mp3'),
+ metadata: JSON.stringify({ title: "Customer Call Recording" }),
mimeType: "audio/mpeg",
- containerTag: "user_123",
- metadata: JSON.stringify({ title: "Customer Call Recording" })
});
```
@@ -168,20 +159,18 @@ JSON and CSV are text — stringify and send them through `add()`, no file uploa
### JSON
```typescript
-await client.add({
+await supermemory.add("user_123", {
content: JSON.stringify(userData),
- containerTag: "user_123",
- metadata: { title: "User Profile Data", format: "json" }
+ metadata: { title: "User Profile Data", format: "json" },
});
```
### CSV
```typescript
-await client.add({
+await supermemory.add("user_123", {
content: csvContent,
- containerTag: "user_123",
- metadata: { title: "Sales Data Q4", format: "csv" }
+ metadata: { title: "Sales Data Q4", format: "csv" },
});
```
@@ -194,21 +183,22 @@ For any binary file, use `uploadFile` — it accepts a stream, not base64:
```typescript
import fs from 'fs';
-await client.documents.uploadFile({
+await supermemory.documents.uploadFile("user_123", {
file: fs.createReadStream('./document.pdf'),
- containerTag: "user_123",
- metadata: JSON.stringify({ title: "document.pdf" })
+ 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:
+No Node `fs` access? `uploadFile` also accepts a web `File` or `Blob`:
```typescript
-import Supermemory, { toFile } from 'supermemory';
+await supermemory.documents.uploadFile("user_123", {
+ file: new File(['my bytes'], 'file.txt'),
+});
-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') });
+await supermemory.documents.uploadFile("user_123", {
+ file: await fetch('https://somesite/file').then((r) => r.blob())
+});
```
---
@@ -219,13 +209,13 @@ await client.documents.uploadFile({ file: await toFile(Buffer.from('my bytes'),
```typescript
// URL detected automatically
-await client.add({ content: "https://example.com/page" });
+await supermemory.add("user_123", { content: "https://example.com/page" });
// Plain text detected automatically
-await client.add({ content: "User said they prefer email contact" });
+await supermemory.add("user_123", { content: "User said they prefer email contact" });
```
-For files, `uploadFile` detects type from the file itself in most cases. `fileType` only exists to force specific processing — and it's required (along with `mimeType`) for images and video.
+For files, `uploadFile` detects type from the file itself in most cases. `fileType` and `mimeType` only exist to force specific processing when inference is not enough — set them for images and video.
---
diff --git a/apps/docs/concepts/customization.mdx b/apps/docs/concepts/customization.mdx
index bd2b1cb9..8f43df9b 100644
--- a/apps/docs/concepts/customization.mdx
+++ b/apps/docs/concepts/customization.mdx
@@ -7,15 +7,14 @@ icon: "/icons/hugeicons/settings-02.svg"
Configure how Supermemory processes and retrieves content for your specific use case.
-## Filter prompts
+## Organizational Context
-Tell Supermemory what content matters during ingestion. This helps filter and prioritize what gets indexed.
+Tell Supermemory what your organization does and what content matters during ingestion. This steers what gets extracted into memories across every namespace. In v3/v4 this field was called `filterPrompt`.
```typescript
// Example: Brand guidelines assistant
-await client.settings.update({
- shouldLLMFilter: true,
- filterPrompt: `You are ingesting content for Brand.ai's brand guidelines system.
+await supermemory.organization.update({
+ organizationalContext: `You are ingesting content for Brand.ai's brand guidelines system.
Index:
- Official brand values and mission statements
@@ -27,87 +26,79 @@ await client.settings.update({
- Draft documents and work-in-progress
- Outdated brand materials (pre-2024)
- Internal discussions about brand changes
- - Competitor analysis docs`
+ - Competitor analysis docs`,
+ },
});
```
```typescript
- filterPrompt: `Personal AI assistant. Prioritize recent content, action items,
+ organizationalContext: `Personal AI assistant. Prioritize recent content, action items,
and personal context. Exclude spam and duplicates.`
```
```typescript
- filterPrompt: `Customer support agent. Prioritize verified solutions, official docs,
+ organizationalContext: `Customer support agent. Prioritize verified solutions, official docs,
and resolved tickets. Exclude internal discussions and PII.`
```
```typescript
- filterPrompt: `Legal research assistant. Prioritize precedents, current regulations,
+ organizationalContext: `Legal research assistant. Prioritize precedents, current regulations,
and approved contract language. Exclude privileged communications.`
```
```typescript
- filterPrompt: `Financial analysis assistant. Prioritize latest reports, verified data,
+ organizationalContext: `Financial analysis assistant. Prioritize latest reports, verified data,
and regulatory filings. Exclude speculative data and MNPI.`
```
```typescript
- filterPrompt: `Healthcare information assistant. Prioritize evidence-based guidelines
+ organizationalContext: `Healthcare information assistant. Prioritize evidence-based guidelines
and FDA-approved info. Exclude PHI and outdated recommendations.`
```
```typescript
- filterPrompt: `Developer documentation assistant. Prioritize current APIs, working
+ organizationalContext: `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 |
+The value replaces the existing context in full. Send `null` to clear it. The legacy `shouldLLMFilter`, `categories`, `includeItems`, and `excludeItems` settings are not part of the v5 organization resource.
---
-## Entity context
+## Supporting Context
-Guide memory extraction for a specific container tag. Filter prompts are org-wide; entity context is per container.
+Guide memory extraction for a specific namespace. Organizational context is org-wide; supporting context is per namespace. In v3/v4 this field was called `entityContext`.
```typescript
-await client.add({
+await supermemory.add("session_abc123", {
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.`
+ supportingContext: `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.
+
+ Set the supporting context for a namespace without uploading content.
```typescript
- 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.`
+ await supermemory.namespaces.update("session_abc123", {
+ supportingContext: `Design exploration conversation between john@acme.com and Brand.ai assistant.
+ Focus on John's design preferences and brand requirements.`,
});
```
-Entity context persists on the container tag and combines with org-level filter prompts.
+Supporting context persists on the namespace and combines with the org-level organizational context.
---
@@ -183,19 +174,21 @@ Show "Log in to **YourApp**" instead of "Log in to Supermemory" when users conne
## API reference
```typescript
-// Get current settings
-const settings = await client.settings.get();
+// Read organization settings
+const { organizationalContext, namespaceCount } = await supermemory.organization.get();
-// Update settings
-await client.settings.update({
- shouldLLMFilter: true,
- filterPrompt: "...",
- chunkSize: 512
+// Update organization context
+await supermemory.organization.update({ organizationalContext: "..." });
+
+// Read and update one namespace
+const ns = await supermemory.namespaces.get("session_abc123");
+await supermemory.namespaces.update("session_abc123", {
+ supportingContext: "...",
});
```
-Settings are organization-wide. Changes apply to new content only—existing memories aren't reprocessed.
+Organization settings are organization-wide and require an organization administrator. Changes apply to new content only—existing memories aren't reprocessed.
---
diff --git a/apps/docs/concepts/filtering.mdx b/apps/docs/concepts/filtering.mdx
index 149bdd8c..da2b513c 100644
--- a/apps/docs/concepts/filtering.mdx
+++ b/apps/docs/concepts/filtering.mdx
@@ -1,14 +1,14 @@
---
title: "Organizing & filtering memories"
sidebarTitle: "Metadata filtering"
-description: "Use container tags and metadata to organize and retrieve memories"
+description: "Use namespaces and metadata to organize and retrieve memories"
icon: "/icons/hugeicons/filter.svg"
---
Supermemory provides two ways to organize your memories:
-
+
**Organize memories** into isolated spaces by user, project, or workspace
@@ -20,31 +20,29 @@ Both can be used independently or together for precise filtering.
---
-## Container tags
+## Namespaces
-Container tags create isolated memory spaces. Use them to separate memories by user, project, or any logical boundary.
+Namespaces create isolated memory spaces. Use them to separate memories by user, project, or any logical boundary. The namespace is part of the URL (`/ns/{namespace}/...`), so every SDK call takes it as a top-level key.
-### Adding memories with tags
+### Adding Memories to a Namespace
```typescript
-await client.add({
+await supermemory.add("user_123", {
content: "Meeting notes from Q1 planning",
- containerTag: "user_123"
});
```
-### Searching with tags
+### Searching a Namespace
```typescript
-const results = await client.search({
- q: "planning notes",
- containerTag: "user_123",
- searchMode: "documents"
+const results = await supermemory.search("user_123", {
+ query: "planning notes",
+ searchMode: "chunks",
});
```
-Each search is scoped to a single container tag. Passing `containerTag: "user_123"` restricts results to memories stored in that container.
+Each search is scoped to a single namespace. Passing `namespace: "user_123"` restricts results to memories stored in that namespace.
### Recommended patterns
@@ -56,39 +54,31 @@ Each search is scoped to a single container tag. Passing `containerTag: "user_12
| Hierarchical | `org_{orgId}_team_{teamId}` | Multi-level organization |
-
+
```typescript
// Multi-tenant SaaS - isolate by organization and user
- await client.add({
+ await supermemory.add("org_acme_user_john", {
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"
+ const results = await supermemory.search("org_acme_user_john", {
+ query: "vacation policy",
+ searchMode: "chunks",
});
// Project-based isolation
- await client.add({
+ await supermemory.add("project_mobile_app", {
content: "Sprint 5 retrospective notes",
- containerTag: "project_mobile_app"
});
// Time-based segmentation
- await client.add({
+ await supermemory.add("user_cfo_2024_q1", {
content: "Q1 2024 financial report",
- containerTag: "user_cfo_2024_q1"
});
```
- **API field differences:**
- | Operation | Field | Type |
- |-----------|-------|------|
- | Search | `containerTag` | String |
- | Documents list | `containerTags` | Array |
+ The namespace is a path parameter on every v5 call (`add`, `search`, `list`, `profile`, `documents.*`). There is no array form; one request touches one namespace.
@@ -101,229 +91,225 @@ Metadata lets you attach custom properties to memories and filter by them later.
### Adding memories with metadata
```typescript
-await client.add({
+await supermemory.add("user_123", {
content: "Technical design document for auth system",
- containerTag: "user_123",
metadata: {
category: "engineering",
priority: "high",
- year: 2024
- }
+ year: 2024,
+ },
});
```
-### Searching with metadata filters
+### Searching with a Filter
-Filters must be wrapped in `AND` or `OR` arrays:
+A filter is a typed expression. One condition is an object with `field`, `operator`, and `value`. Several conditions are combined with an `and` or `or` node that lists them in `operands`:
```typescript
-const results = await client.search({
- q: "design document",
- containerTag: "user_123",
- searchMode: "documents",
- filters: {
- AND: [
- { key: "category", value: "engineering" },
- { key: "priority", value: "high" }
- ]
- }
+const results = await supermemory.search("user_123", {
+ query: "design document",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "category", operator: "eq", value: "engineering" },
+ { field: "priority", operator: "eq", value: "high" },
+ ],
+ },
+ searchMode: "chunks",
});
```
-### Filter types
+A single condition needs no wrapper:
-| 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 |
+```typescript
+filter: { field: "status", operator: "eq", value: "published" }
+```
+
+### Operators
+
+| Operator | Value type | Example |
+|----------|------------|---------|
+| `eq`, `neq` | string, number, boolean | `{ field: "status", operator: "eq", value: "published" }` |
+| `gt`, `gte`, `lt`, `lte` | number | `{ field: "priority", operator: "gte", value: 5 }` |
+| `contains`, `notContains` | string | `{ field: "title", operator: "contains", value: "react" }` |
+| `arrayContains`, `arrayNotContains` | string | `{ field: "tags", operator: "arrayContains", value: "important" }` |
+
+String conditions accept an optional `caseSensitive` boolean. Numeric values are JSON numbers, not numeric strings.
### Combining filters
-Use `AND` and `OR` for complex queries:
+Nest `and` and `or` nodes for complex queries:
```typescript
-const results = await client.search({
- q: "meeting notes",
- searchMode: "documents",
- filters: {
- AND: [
- { key: "type", value: "meeting" },
+const results = await supermemory.search("user_123", {
+ query: "meeting notes",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "type", operator: "eq", value: "meeting" },
{
- OR: [
- { key: "team", value: "engineering" },
- { key: "team", value: "product" }
- ]
- }
- ]
- }
+ operator: "or",
+ operands: [
+ { field: "team", operator: "eq", value: "engineering" },
+ { field: "team", operator: "eq", value: "product" },
+ ],
+ },
+ ],
+ },
+ searchMode: "chunks",
});
```
### Excluding results
-Use `negate: true` to exclude matches:
+Use the negative operators (`neq`, `notContains`, `arrayNotContains`) to exclude matches:
```typescript
-const results = await client.search({
- q: "documentation",
- searchMode: "documents",
- filters: {
- AND: [
- { key: "status", value: "draft", negate: true }
- ]
- }
+const results = await supermemory.search("user_123", {
+ query: "documentation",
+ filter: { field: "status", operator: "neq", value: "draft" },
+ searchMode: "chunks",
});
```
-
- **String contains (substring search):**
+
+ **Substring match, case-insensitive:**
```typescript
// 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
- }
- ]
- }
+ const results = await supermemory.search("user_123", {
+ query: "AI research",
+ filter: {
+ field: "description",
+ operator: "contains",
+ value: "machine learning",
+ caseSensitive: false,
+ },
+ searchMode: "chunks",
});
```
**Numeric comparisons:**
```typescript
// Find high-priority items created after a specific date
- const results = await 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: ">="
- }
- ]
- }
+ const results = await supermemory.search("user_123", {
+ query: "tasks",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "priority", operator: "gte", value: 7 },
+ { field: "created_timestamp", operator: "gte", value: 1704067200 }, // Unix timestamp
+ ],
+ },
+ searchMode: "chunks",
});
```
- **Array contains (check array membership):**
+ **Array membership:**
```typescript
// 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"
- }
- ]
- }
+ const results = await supermemory.search("user_123", {
+ query: "meeting notes",
+ filter: { field: "participants", operator: "arrayContains", value: "alice@company.com" },
+ searchMode: "chunks",
});
```
**Complex nested filters:**
```typescript
// (category = "tech" OR category = "science") AND status != "archived"
- const results = await client.search({
- q: "research papers",
- searchMode: "documents",
- filters: {
- AND: [
+ const results = await supermemory.search("user_123", {
+ query: "research papers",
+ filter: {
+ operator: "and",
+ operands: [
{
- OR: [
- { key: "category", value: "tech" },
- { key: "category", value: "science" }
- ]
+ operator: "or",
+ operands: [
+ { field: "category", operator: "eq", value: "tech" },
+ { field: "category", operator: "eq", value: "science" },
+ ],
},
- { key: "status", value: "archived", negate: true }
- ]
- }
+ { field: "status", operator: "neq", value: "archived" },
+ ],
+ },
+ searchMode: "chunks",
});
```
-
- **Numeric operator negation mapping:**
- When using `negate: true`, operators flip:
- - `<` becomes `>=`
- - `<=` becomes `>`
- - `>` becomes `<=`
- - `>=` becomes `<`
- - `=` becomes `!=`
**User's work documents from 2024:**
```typescript
- 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: "=" }
- ]
- }
+ const results = await supermemory.search("user_123", {
+ query: "quarterly report",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "category", operator: "eq", value: "work" },
+ { field: "type", operator: "eq", value: "report" },
+ { field: "year", operator: "eq", value: 2024 },
+ ],
+ },
+ searchMode: "chunks",
});
```
**Team meeting notes with specific participants:**
```typescript
- const results = await client.search({
- q: "sprint planning",
- containerTag: "project_alpha",
- searchMode: "documents",
- filters: {
- AND: [
- { key: "type", value: "meeting" },
+ const results = await supermemory.search("project_alpha", {
+ query: "sprint planning",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "type", operator: "eq", value: "meeting" },
{
- OR: [
- { filterType: "array_contains", key: "participants", value: "alice" },
- { filterType: "array_contains", key: "participants", value: "bob" }
- ]
- }
- ]
- }
+ operator: "or",
+ operands: [
+ { field: "participants", operator: "arrayContains", value: "alice" },
+ { field: "participants", operator: "arrayContains", value: "bob" },
+ ],
+ },
+ ],
+ },
+ searchMode: "chunks",
});
```
**Exclude drafts and deprecated content:**
```typescript
- const results = await 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 }
- ]
- }
+ const results = await supermemory.search("user_123", {
+ query: "documentation",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "status", operator: "neq", value: "draft" },
+ { field: "content", operator: "notContains", value: "deprecated" },
+ { field: "tags", operator: "arrayNotContains", value: "archived" },
+ ],
+ },
+ searchMode: "chunks",
});
```
+### Filters Beyond Search
+
+The same `filter` expression works on list and profile calls:
+
+```typescript
+// Page through matching documents
+const { documents, pagination } = await supermemory.list("user_123", "documents", {
+ filter: { field: "category", operator: "eq", value: "engineering" },
+});
+
+// Build the profile only from matching memories
+const { profile } = await supermemory.profile("user_123", {
+ filter: { field: "source", operator: "eq", value: "onboarding" },
+});
+```
+
---
## Quick reference
@@ -331,23 +317,19 @@ const results = await client.search({
### When adding memories
```typescript
-await client.add({
+await supermemory.add("user_123", { // Isolation
content: "Your content here",
- containerTag: "user_123", // Isolation
- metadata: { key: "value" } // Custom properties
+ metadata: { key: "value" }, // Custom properties
});
```
### When searching
```typescript
-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" }]
- }
+const results = await supermemory.search("user_123", { // Scopes results to this namespace
+ query: "search query",
+ filter: { field: "status", operator: "eq", value: "published" }, // Optional
+ searchMode: "chunks",
});
```
@@ -356,24 +338,24 @@ const results = await client.search({
- Allowed characters: `a-z`, `A-Z`, `0-9`, `_`, `-`, `.`
- Max length: 64 characters
- No spaces or special characters
+- Keys are literal: `customer.plan` is one key named `customer.plan`, not a nested path
### Query complexity limits
-- Maximum 200 conditions per query
-- Maximum 8 levels of nested `AND`/`OR` expressions
+- Maximum 5 levels of nested `and`/`or` expressions
+- Maximum 200 operands per logical group
If you need more conditions than these limits allow, break your query into multiple requests or use broader search terms with post-processing.
-### Searching within a document
+### Reading One Document's Chunks
-Use `docId` to scope a search to chunks within one large document — useful for books, podcasts, or other long-form content:
+Scoping a search to a single document (`docId`) is not part of v5. To work with the chunks of one long document, fetch the document with its chunks attached:
```typescript
-const results = await client.search({
- q: "machine learning",
- docId: "doc_123"
+const doc = await supermemory.documents.get("user_123", "doc_123", {
+ include: ["chunks"],
});
```
@@ -385,7 +367,7 @@ const results = await client.search({
Apply filters in search queries
-
- Add content with container tags and metadata
+
+ Add content with namespaces and metadata
diff --git a/apps/docs/concepts/graph-memory.mdx b/apps/docs/concepts/graph-memory.mdx
index eced5485..2d70a342 100644
--- a/apps/docs/concepts/graph-memory.mdx
+++ b/apps/docs/concepts/graph-memory.mdx
@@ -18,19 +18,19 @@ This page is the **model**: what a memory is, how edges form, and why agents uti
Get a key from the [developer console](https://console.supermemory.ai) — **API Keys → Create API Key** — then add a memory and pull it back with related edges:
```typescript
-import Supermemory from "supermemory";
+import { Supermemory } from "supermemory";
-const client = new Supermemory({ apiKey: "sm_..." }); // from console.supermemory.ai → API Keys
+const supermemory = new Supermemory({ apiKey: "sm_..." }); // from console.supermemory.ai → API Keys
-await client.add({
+await supermemory.add("user_123", {
content: "Alex mentioned he just started at Stripe",
- containerTag: "user_123",
+ dreaming: "instant", // memories land in the graph right away
});
-const results = await client.search({
- q: "where does Alex work?",
- containerTag: "user_123",
- include: { relatedMemories: true },
+const results = await supermemory.search("user_123", {
+ query: "where does Alex work?",
+ include: { related: true },
+ searchMode: "memories",
});
```
@@ -98,7 +98,7 @@ Memory 2: "Alex frequently discusses payment APIs and fraud detection"
→ Derived: "Alex likely works on Stripe's core payments product"
```
-That is the same class of “entity chain” you see in the [quickstart](/quickstart) (gift → VP of Product → Sarah → Tokyo offsite). Search can expose edges via `include.relatedMemories` — see [Search API](/recall/search).
+That is the same class of “entity chain” you see in the [quickstart](/quickstart) (gift → VP of Product → Sarah → Tokyo offsite). Search can expose edges via `include: { related: true }`; they come back under `included.related` — see [Search API](/recall/search).
## Automatic extraction (one input → many facts)
@@ -119,7 +119,7 @@ You do not define schema or draw edges. You [add content](/ingestion/add-memorie
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.
+By default Supermemory uses **`dreaming: "dynamic"`** — related documents are grouped so memories form from **coherent units** (e.g. a real multi-turn session), not each isolated write in isolation. That is why production quality is higher when you keep a stable `id` on conversations and let dynamic dreaming do its job.
Use **`dreaming: "instant"`** when this document must hit the graph immediately (demos, “search right after add”). That path processes the document alone and costs an extra operation.
@@ -145,22 +145,22 @@ For explicit product controls (forget, review low-confidence derives), see [Forg
You do **not** hand-maintain the graph. You:
-1. Ingest under a [container tag](/concepts/container-tags)
+1. Ingest under a [namespace](/concepts/container-tags)
2. Wait for the [pipeline](/concepts/how-it-works) when needed
3. [Search](/recall/search) or load a [profile](/recall/user-profiles)
```typescript
-await client.add({
+await supermemory.add("user_123", {
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 },
+const results = await supermemory.search("user_123", {
+ query: "where does Alex work?",
+ include: { related: true },
+ searchMode: "memories",
});
-// Prefer latest work fact (Stripe); history remains in the graph
+// Prefer latest work fact (Stripe); history remains in the graph.
+// Related edges come back under results[i].included.related
```
## Related in the docs
diff --git a/apps/docs/concepts/how-it-works.mdx b/apps/docs/concepts/how-it-works.mdx
index 01f2005c..cd58d90d 100644
--- a/apps/docs/concepts/how-it-works.mdx
+++ b/apps/docs/concepts/how-it-works.mdx
@@ -31,16 +31,17 @@ But, you don't have to think about the above. The interface for users is as simp
```typescript TypeScript
-// npm install supermemory
-import Supermemory from "supermemory";
+import { Supermemory } from "supermemory";
-const client = new Supermemory({ apiKey: "sm_..." }); // from console.supermemory.ai → API Keys
+const supermemory = new Supermemory({ apiKey: "sm_..." }); // from console.supermemory.ai → API Keys
-await client.add({ content: "The user loves Paris.", containerTag: "user_123" });
+await supermemory.add("user_123", {
+ content: "The user loves Paris.",
+ dreaming: "instant", // memories available right after processing
+});
-const { results } = await client.search({
- q: "where does the user want to travel?",
- containerTag: "user_123",
+const { results } = await supermemory.search("user_123", {
+ query: "where does the user want to travel?",
});
```
@@ -50,22 +51,25 @@ 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",
+client.add(
+ "user_123",
+ content="The user loves Paris.",
+ dreaming="instant", # memories available right after processing
)
+
+results = client.search("user_123", query="where does the user want to travel?")
```
```bash curl
-curl -X POST https://api.supermemory.ai/v3/documents \
+curl -X POST "https://api.supermemory.ai/ns/user_123/document?dreaming=instant" \
-H "Authorization: Bearer sm_..." \
-H "Content-Type: application/json" \
- -d '{
- "content": "The user loves Paris.",
- "containerTag": "user_123"
- }'
+ -d '{ "content": "The user loves Paris." }'
+
+curl -X POST "https://api.supermemory.ai/ns/user_123/search" \
+ -H "Authorization: Bearer sm_..." \
+ -H "Content-Type: application/json" \
+ -d '{ "query": "where does the user want to travel?" }'
```
@@ -82,7 +86,7 @@ You do not pre-chunk or pick an embedding model. See [Multi-modal ingestion](/co
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](/overview/billing#full-discount-on-already-seen-tokens-diff-billing) on re-ingest.
+> Use a stable **`id`** when the same conversation or file will be updated later (sessions, connector syncs). That identity also drives [diff billing](/overview/billing#full-discount-on-already-seen-tokens-diff-billing) on re-ingest.
## What the pipeline does
@@ -96,15 +100,14 @@ Supermemory handles the ingestion and extraction for you. This also gives us a b
| **Done** | Document path is ready for search |
```typescript
-const doc = await client.add({
+const doc = await supermemory.add("user_123", {
content: conversationText,
- containerTag: "user_123",
- customId: "chat_session_1",
+ id: "chat_session_1",
});
// Poll until ready
-const status = await client.documents.get(doc.id);
-// status.status → "queued" | "extracting" | ... | "done" | "failed"
+const { system } = await supermemory.documents.get("user_123", doc.id);
+// system.status → "queued" | "extracting" | ... | "done" | "failed"
```
Larger PDFs and long video take longer. Short chat turns usually finish in seconds.
@@ -115,7 +118,7 @@ A document with status `done` has its chunks indexed for search. Memories (the g
This is when the content is passed through the memory model and merged, arranged and organized for the future.
-Pass `dreaming` on [add](/ingestion/add-memories):
+Pass `dreaming` on [add](/ingestion/add-memories) (a query parameter, so a top-level key in the SDK):
| Mode | Default? | Behavior | When to use |
| --- | --- | --- | --- |
@@ -124,18 +127,16 @@ Pass `dreaming` on [add](/ingestion/add-memories):
```typescript
// Production default — omit or set explicitly
-await client.add({
+await supermemory.add("user_123", {
content: conversationText,
- containerTag: "user_123",
- customId: "chat_session_1",
+ id: "chat_session_1",
dreaming: "dynamic",
});
// Need memories immediately (e.g. tutorial)
-await client.add({
+await supermemory.add("user_123", {
content: conversationText,
- containerTag: "user_123",
- customId: "chat_session_1",
+ id: "chat_session_1",
dreaming: "instant",
});
```
@@ -146,7 +147,7 @@ How those memories connect and stay true over time is [Graph memory](/concepts/g
## What you get out
-After the pipeline runs, the same document leads to three things -> Chunks, Memories and Profile. (in the same `containerTag`):
+After the pipeline runs, the same document leads to three things -> Chunks, Memories and Profile. (in the same `namespace`):
| Output | Role | Go deeper |
| --- | --- | --- |
@@ -158,9 +159,9 @@ Supermemory does more than store the file. It derives memories (what it understo
## Isolation and identity
-- **`containerTag`** — hard isolation boundary (user, tenant, project). See [Container tags](/concepts/container-tags).
-- **Metadata**: extra dimensions inside a tag that you can filter on. See [metadata filtering](/concepts/filtering).
-- **Scoped API keys** — credentials that cannot cross a container. See [API keys](/authentication#scoped-api-keys).
+- **`namespace`** — hard isolation boundary (user, tenant, project), carried in the URL. See [Namespaces](/concepts/container-tags).
+- **Metadata** — soft dimensions *inside* a namespace for filtering. See [Metadata filtering](/concepts/filtering).
+- **Scoped API keys** — credentials that cannot cross a namespace. See [API keys](/authentication#scoped-api-keys).
## Next steps
@@ -172,7 +173,7 @@ Supermemory does more than store the file. It derives memories (what it understo
Formats, extractors, and what you can send.
- API: add, customId, files, dreaming, status.
+ API: add, id, files, dreaming, status.
Query documents and memories after the pipeline finishes.
diff --git a/apps/docs/concepts/memory-vs-rag.mdx b/apps/docs/concepts/memory-vs-rag.mdx
index c3b3af2b..a8ac5da5 100644
--- a/apps/docs/concepts/memory-vs-rag.mdx
+++ b/apps/docs/concepts/memory-vs-rag.mdx
@@ -195,8 +195,9 @@ Supermemory provides a unified platform that correctly handles both patterns:
```python
# Add a document for RAG-style retrieval
client.add(
+ "product_knowledge", # Shared namespace, not tied to a user
content="iPhone 15 has a 48MP camera and A17 Pro chip",
- # No user association - universal knowledge
+ dreaming="instant"
)
```
@@ -204,8 +205,9 @@ client.add(
```python
# Add a user-specific memory
client.add(
+ "user_123", # User-specific namespace
content="User prefers Android over iOS",
- container_tags=["user_123"], # User-specific
+ dreaming="instant",
metadata={
"type": "preference",
"confidence": "high"
@@ -215,16 +217,16 @@ client.add(
### 3. Hybrid retrieval
```python
-# 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
+# Search is scoped to one namespace, so query each side on its own
+user = client.profile("user_123") # User's Android preference (memory)
+
+specs = client.search(
+ "product_knowledge", # Latest Android phone specs (document chunks)
+ query="What phone should I recommend?",
+ search_mode="chunks",
)
-# Results include:
-# - User's Android preference (memory)
-# - Latest Android phone specs (documents)
+# Hand both to the model: user.profile + specs.results
```
## The bottom line
diff --git a/apps/docs/concepts/multi-tenancy-examples.mdx b/apps/docs/concepts/multi-tenancy-examples.mdx
index 62ba38b1..abf4c819 100644
--- a/apps/docs/concepts/multi-tenancy-examples.mdx
+++ b/apps/docs/concepts/multi-tenancy-examples.mdx
@@ -1,27 +1,25 @@
---
title: "Multi-tenancy examples"
sidebarTitle: "Examples"
-description: "Common container tag and metadata patterns for personal agents, company agents, email assistants, and support platforms"
+description: "Common namespace and metadata patterns for personal agents, company agents, email assistants, and support platforms"
icon: "/icons/hugeicons/check-list.svg"
---
-A few common shapes multi-tenancy takes in practice, combining [container tags](/concepts/container-tags) for isolation with [metadata filters](/concepts/filtering) for organization within a boundary.
+A few common shapes multi-tenancy takes in practice, combining [namespaces](/concepts/container-tags) for isolation with [metadata filters](/concepts/filtering) for organization within a boundary.
---
## Personal agent
-A single container tag per user is enough — there's no shared data to leak, so metadata is optional.
+A single namespace per user is enough — there's no shared data to leak, so metadata is optional.
```typescript
-await client.add({
+await supermemory.add(userId, { // one namespace per user
content: "User prefers morning workouts and vegetarian meals",
- containerTag: "user_123",
});
-const results = await client.search({
- q: "workout preferences",
- containerTag: "user_123",
+const results = await supermemory.search(userId, {
+ query: "workout preferences",
});
```
@@ -29,48 +27,42 @@ const results = await client.search({
## 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.
+A company-wide assistant usually needs two kinds of namespaces: one **shared** namespace the whole org reads from, and one **personal** namespace per employee that nobody else can see.
```typescript
// Shared org knowledge — visible to everyone at the company
-await client.add({
+await supermemory.add("org_acme_shared", {
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({
+await supermemory.add("org_acme_user_alex", {
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:
+Inside the shared namespace, use metadata to scope queries to a team rather than creating a namespace per team:
```typescript
-const results = await client.search({
- q: "roadmap updates",
- containerTag: "org_acme_shared",
- searchMode: "documents",
- filters: {
- AND: [{ key: "team", value: "product" }],
- },
+const results = await supermemory.search("org_acme_shared", {
+ query: "roadmap updates",
+ filter: { field: "team", operator: "eq", value: "product" },
+ searchMode: "chunks",
});
```
-An employee's agent typically queries both containers — their personal one plus the shared one — and merges the results, since the container tag boundary is per-request rather than per-user.
+An employee's agent typically queries both namespaces — their personal one plus the shared one — and merges the results, since a request is scoped to exactly one namespace.
---
## Email assistant
-One 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"*.
+One namespace per user, with metadata carrying email-specific properties like label, sender, or folder — so the assistant can answer things like *"find the Spotify email tagged Promotional"*.
```typescript
-await client.add({
+await supermemory.add(userId, {
content: "Your Spotify Premium receipt for July — $11.99 charged",
- containerTag: "user_123",
metadata: {
source: "gmail",
sender: "no-reply@spotify.com",
@@ -78,16 +70,16 @@ await client.add({
},
});
-const results = await client.search({
- q: "spotify",
- containerTag: "user_123",
- searchMode: "documents",
- filters: {
- AND: [
- { key: "source", value: "gmail" },
- { key: "label", value: "Promotional" },
+const results = await supermemory.search(userId, {
+ query: "spotify",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "source", operator: "eq", value: "gmail" },
+ { field: "label", operator: "eq", value: "Promotional" },
],
},
+ searchMode: "chunks",
});
```
@@ -95,25 +87,24 @@ const results = await client.search({
## 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.
+Each customer gets their own namespace, and metadata tracks ticket-level fields like status and priority — so "open, high-priority tickets" is a filter, not a new namespace, and it can never accidentally include another customer's tickets.
```typescript
-await client.add({
+await supermemory.add("org_customer_442", {
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" },
+const results = await supermemory.search("org_customer_442", {
+ query: "checkout issue",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "status", operator: "eq", value: "open" },
+ { field: "priority", operator: "eq", value: "high" },
],
},
+ searchMode: "chunks",
});
```
@@ -122,13 +113,13 @@ const results = await client.search({
## Next steps
-
- Why container tags and metadata are separate mechanisms.
+
+ Why namespaces and metadata are separate mechanisms.
-
+
How isolation works, naming rules, and access control.
-
- Metadata filter types, combining `AND`/`OR`, and query limits.
+
+ Metadata filter operators, combining `and`/`or`, and query limits.
diff --git a/apps/docs/concepts/multi-tenancy.mdx b/apps/docs/concepts/multi-tenancy.mdx
index 03f07b25..1643d182 100644
--- a/apps/docs/concepts/multi-tenancy.mdx
+++ b/apps/docs/concepts/multi-tenancy.mdx
@@ -10,8 +10,8 @@ Most apps built on Supermemory serve more than one user, customer, or tenant out
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.
+
+ **Isolation.** A namespace is a hard boundary. Memories in one namespace are never returned by a search scoped to another namespace.
**Organization.** Metadata is a set of custom key/value properties on a memory that you filter by — category, priority, date, participants, anything you define.
@@ -24,27 +24,26 @@ 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.
+It's tempting to reach for one tool and make it do everything, but namespaces and metadata aren't interchangeable — they answer different questions.
| Question | Answer |
|----------|--------|
-| "Which tenant does this memory belong to?" | **Container tag** |
+| "Which tenant does this memory belong to?" | **Namespace** |
| "Within this tenant's memories, which ones match `status: open`?" | **Metadata filter** |
-| "Can this API key even see tenant X's data?" | **Container tag** (enforced as an access boundary) |
+| "Can this API key even see tenant X's data?" | **Namespace** (enforced as an access boundary) |
| "Find memories tagged `engineering` created after March" | **Metadata filter** |
-A 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.
+A namespace decides **whether a memory is reachable at all** for a given request. Metadata decides **which of the reachable memories match**. Filtering never crosses a namespace boundary — you can't use metadata to peek into another tenant's namespace.
---
## How they work together
-A typical multi-tenant write scopes the memory to a tenant with a container tag, then attaches metadata for finer-grained querying later:
+A typical multi-tenant write scopes the memory to a tenant with a namespace, then attaches metadata for finer-grained querying later:
```typescript
-await client.add({
+await supermemory.add("org_acme", { // isolates to the "acme" tenant
content: "Customer requested a refund for order #4821",
- containerTag: "org_acme", // isolates to the "acme" tenant
metadata: {
category: "support",
status: "open",
@@ -53,46 +52,58 @@ await client.add({
});
```
-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:
+And a search combines both: the namespace restricts *which tenant's data* is in scope, and the filter narrows down *which memories within that tenant* come back:
```typescript
-const results = await client.search({
- q: "refund request",
- containerTag: "org_acme",
- searchMode: "documents",
- filters: {
- AND: [
- { key: "category", value: "support" },
- { key: "status", value: "open" },
+const results = await supermemory.search("org_acme", {
+ query: "refund request",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "category", operator: "eq", value: "support" },
+ { field: "status", operator: "eq", value: "open" },
],
},
+ searchMode: "chunks",
});
```
-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.
+The namespace is **required** on every call (it is part of the URL) and validated as an access boundary. Metadata filters are **optional** — a search with just `namespace` and no `filter` still only returns that tenant's memories.
---
## Choosing your boundary
-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:
+The namespace is the layer that should map to your actual tenancy model — pick the level that matches what "one isolated space" means in your app:
| Pattern | Example | Use case |
|---------|---------|----------|
| Per-user | `user_{userId}` | Consumer app, personal memory per user |
-| Per-tenant/org | `org_{orgId}` | B2B SaaS, one container per customer org |
+| Per-tenant/org | `org_{orgId}` | B2B SaaS, one namespace per customer org |
| Hierarchical | `org:{orgId}:user:{userId}` | Multi-level — isolate by org, and optionally drill into a user within it |
| Per-project | `project_{projectId}` | Workspace- or project-scoped content |
-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.
+The simplest form is one namespace per user, derived from an ID you already have:
+
+```typescript
+await supermemory.add(userId, { // one namespace per user
+ content: "User prefers morning workouts",
+});
+
+const results = await supermemory.search(userId, {
+ query: "workout preferences",
+});
+```
+
+Everything *within* that boundary — categories, statuses, dates, custom fields — is metadata, not a new namespace. Don't create a new namespace for every property you want to filter on; that's what metadata is for.
---
## Access control
-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](/concepts/container-tags#access-control) for the details.
+Namespaces aren't just organizational — they're enforced as an authorization boundary. API keys and org members can be restricted to specific namespaces, so a request for a namespace outside the caller's allowed set is rejected with `403 Forbidden` rather than silently filtered. See [Namespaces → Access control](/concepts/container-tags#access-control) for the details.
---
@@ -102,13 +113,13 @@ Container tags aren't just organizational — they're enforced as an authorizati
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.
+
+ Metadata filter operators, combining `and`/`or`, and query limits.
- Mint keys that can only touch one container tag.
+ Mint keys that can only touch one namespace.
diff --git a/apps/docs/concepts/rules.mdx b/apps/docs/concepts/rules.mdx
index 1464068b..dab3452a 100644
--- a/apps/docs/concepts/rules.mdx
+++ b/apps/docs/concepts/rules.mdx
@@ -30,7 +30,7 @@ Agents benefit most from having a _general_ idea of the topic alongside tools to
Two things about this table that trip people up.
-**"Remember" and "look up" are both supermemory, but different reads.** You [ingest documents](/ingestion/add-memories); the pipeline derives memories from them and maintains a profile per container tag. `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.
+**"Remember" and "look up" are both supermemory, but different reads.** You [ingest documents](/ingestion/add-memories); the pipeline derives memories from them and maintains a profile per [namespace](/concepts/container-tags). `supermemory.search({ searchMode: "memories" })` recalls the derived facts. `supermemory.search({ searchMode: "chunks" })` recalls the source material itself. A support agent usually needs both: memories for "this customer runs self-hosted and already tried reinstalling", documents for the actual troubleshooting guide.
**Supermemory is not your system of record.** There's no SQL over memories, no joins, no aggregates, no querying by primary key. Keep transactional data in your database, and ingest the narrative *around* it ("the customer disputed invoice #4821 and churned over it") so your AI understands what the rows mean.
@@ -41,70 +41,63 @@ When you know you only want search, you can cut costs by 5x. Just set `taskType`
```typescript TypeScript
-await client.add({
+await supermemory.add("test", {
content: "testing",
- containerTag: "test",
- taskType: "superrag"
+ taskType: "superrag",
});
```
```python Python
client.add(
+ "test",
content="testing",
- container_tag="test",
- task_type="superrag"
+ task_type="superrag",
)
```
```bash curl
-curl -X POST "https://api.supermemory.ai/v3/documents" \
+curl -X POST "https://api.supermemory.ai/ns/test/document?taskType=superrag" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{
- "content": "testing",
- "containerTag": "test",
- "taskType": "superrag"
- }'
+ -d '{ "content": "testing" }'
```
#### 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.
+`hybrid` mode (the v5 default) makes it much easier to get complete results from supermemory when you have both memories and documents.
```typescript TypeScript
-const results = await client.search({
- q: "test",
- searchMode: "hybrid"
+const { results } = await supermemory.search("test", {
+ query: "test",
+ searchMode: "hybrid",
});
```
```python Python
-results = client.search.memories(
- q="test",
- search_mode="hybrid"
+results = client.search(
+ "test",
+ query="test",
+ search_mode="hybrid",
)
```
```bash curl
-curl -X POST "https://api.supermemory.ai/v4/search" \
+curl -X POST "https://api.supermemory.ai/ns/test/search?searchMode=hybrid" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{
- "q": "test",
- "searchMode": "hybrid"
- }'
+ -d '{ "query": "test" }'
```
-The response comes back in this shape:
+Each entry in `results` carries either `memory` or `chunk`:
```ts
-({ memory: string } | { chunk: string })[]
+{ results: ({ memory: string } | { chunk: string })[], searchTime: number }
```
Use `item.memory || item.chunk` when reading results.
@@ -113,15 +106,15 @@ Use `item.memory || item.chunk` when reading results.
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).
+We also recommend ingesting documents sequentially within a single namespace where possible, since that's how supermemory determines what came first (used for `updates` relations and temporal reasoning).
#### Handling single-threaded chatbots
Many agent harnesses, like `openclaw`, `hermes`, and other single-threaded custom agents, run one long conversation with compaction. Some tips for managing single-threaded (and other long-running) conversations:
-1. **Send 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`.
+1. **Send an `id` when you can**: a sessionId, conversationId, document ID, or any representation of a "session" in your application.
+2. **Generate one if you don't have one**, e.g. the current 4-hour window: `${new Date().toISOString().slice(0,10)}-${new Date().getHours()>>2}`. Adjust the window size based on traffic per namespace.
+3. **Send the same prefix**: keep the start of the document identical across ingests under the same `id` so supermemory can diff cleanly. You can either resend the full growing transcript each time, or send only the new turns since your last ingest. Just don't mix the two for the same `id`.
```
Ingestion 1:
@@ -147,136 +140,124 @@ Don't pass content through an additional LLM before sending it to supermemory. S
#### 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.
+Ground it with `supportingContext` (called `entityContext` in v3/v4) to prevent drift over time. Picture a third person watching a conversation between two people: what do they remember, and about whom? Giving supermemory context about the entity itself helps ground its learnings and prevents drift and decay over time.
```typescript TypeScript
const user = auth.user.name;
-await client.add({
+await supermemory.add(user, {
content: "Hey, I'm doing great!",
- containerTag: user,
- entityContext: `User is ${user}, talking to assistant Kira`
+ supportingContext: `User is ${user}, talking to assistant Kira`,
}); // -> supermemory learns "Dhravya is doing great"
```
```python Python
user = auth.user.name
client.add(
+ user,
content="Hey, I'm doing great!",
- container_tag=user,
- entity_context=f"User is {user}, talking to assistant Kira"
+ supporting_context=f"User is {user}, talking to assistant Kira",
) # -> supermemory learns "Dhravya is doing great"
```
```bash curl
-curl -X POST "https://api.supermemory.ai/v3/documents" \
+curl -X POST "https://api.supermemory.ai/ns/dhravya/document" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Hey, I'\''m doing great!",
- "containerTag": "dhravya",
- "entityContext": "User is dhravya, talking to assistant Kira"
+ "supportingContext": "User is dhravya, talking to assistant Kira"
}'
```
-#### Use containerTags, don't over-stuff a single one
+#### Use namespaces, don't over-stuff a single one
-Use a containerTag wherever there's a hard permission boundary.
+Use a namespace 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.
+- **Don't**: ingest everything into one namespace and filter through it with metadata.
+- **Do**: give each user their own namespace (`namespace: userId`), and still filter by metadata inside it if needed.
-There's little correlation between the number of items in a container and its quality or latency. Supermemory is built for multi-tenant workloads and supports up to 1M documents and 10M memories per container.
+There's little correlation between the number of items in a namespace and its quality or latency. Supermemory is built for multi-tenant workloads and supports up to 1M documents and 10M memories per namespace.
-#### Use metadata filtering for detailed scoping inside containers
+#### Use metadata filtering for detailed scoping inside namespaces
-You'll often want to ingest and search with filtering inside a single container. Say the engineering team ingests this:
+You'll often want to ingest and search with filtering inside a single namespace. Say the engineering team ingests this:
```typescript TypeScript
-await client.add({
+await supermemory.add("org-supermemory", {
content: "The team prefers TypeScript",
metadata: { team: "Engineering" },
- containerTag: "org-supermemory",
- filterByMetadata: { team: "Engineering" }
+ group: { team: "Engineering" },
});
```
```python Python
client.add(
+ "org-supermemory",
content="The team prefers TypeScript",
metadata={"team": "Engineering"},
- container_tag="org-supermemory",
- filter_by_metadata={"team": "Engineering"}
+ group={"team": "Engineering"},
)
```
```bash curl
-curl -X POST "https://api.supermemory.ai/v3/documents" \
+curl -X POST "https://api.supermemory.ai/ns/org-supermemory/document" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "The team prefers TypeScript",
"metadata": { "team": "Engineering" },
- "containerTag": "org-supermemory",
- "filterByMetadata": { "team": "Engineering" }
+ "group": { "team": "Engineering" }
}'
```
-> Tip: `filterByMetadata` ensures a fact like "the team prefers TypeScript" is only built on top of the engineering team's knowledge.
+> Tip: `group` (called `filterByMetadata` in v3/v4) ensures a fact like "the team prefers TypeScript" is only built on top of the engineering team's knowledge.
-Later, the research team ingests this, with the same `containerTag` but different `metadata`:
+Later, the research team ingests this into the same namespace (`POST /ns/org-supermemory/document`) but with different `metadata`:
```json
{
"content": "The team prefers Python",
"metadata": { "team": "Research" },
- "containerTag": "org-supermemory",
- "filterByMetadata": { "team": "Research" }
+ "group": { "team": "Research" }
}
```
-This keeps research's and engineering's memories from mixing, even though they share a `containerTag`. When searching:
+This keeps research's and engineering's memories from mixing, even though they share a namespace. When searching:
```typescript TypeScript
-const results = await client.search({
- q: "preferred language",
- containerTag: "org-supermemory",
- searchMode: "documents",
- filters: {
- AND: [{ key: "team", value: "research" }]
- }
+const results = await supermemory.search("org-supermemory", {
+ query: "preferred language",
+ filter: { field: "team", operator: "eq", value: "Research" },
+ searchMode: "chunks",
}); // -> "python"
```
```python Python
-results = client.search.documents(
- q="preferred language",
- container_tag="org-supermemory",
- filters={
- "AND": [{"key": "team", "value": "research"}]
- }
+results = client.search(
+ "org-supermemory",
+ query="preferred language",
+ search_mode="chunks",
+ filter={"field": "team", "operator": "eq", "value": "Research"},
) # -> "python"
```
```bash curl
-curl -X POST "https://api.supermemory.ai/v3/search" \
+curl -X POST "https://api.supermemory.ai/ns/org-supermemory/search?searchMode=chunks" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
- "q": "preferred language",
- "containerTag": "org-supermemory",
- "filters": {
- "AND": [{ "key": "team", "value": "research" }]
- }
+ "query": "preferred language",
+ "filter": { "field": "team", "operator": "eq", "value": "Research" }
}'
```
diff --git a/apps/docs/concepts/super-rag.mdx b/apps/docs/concepts/super-rag.mdx
index 9e3eccaa..50c4568a 100644
--- a/apps/docs/concepts/super-rag.mdx
+++ b/apps/docs/concepts/super-rag.mdx
@@ -19,9 +19,9 @@ When you add content, Supermemory:
```typescript
// Just upload — Supermemory handles the rest
-await client.documents.uploadFile({
+await supermemory.documents.uploadFile("docs_kb", {
file: fs.createReadStream('technical-documentation.pdf'),
- metadata: JSON.stringify({ title: "Technical Documentation" })
+ metadata: JSON.stringify({ title: "Technical Documentation" }),
});
```
@@ -31,44 +31,39 @@ No chunking strategies to configure. No embedding models to choose. It just work
## 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.
+By default, every `supermemory.add()` call runs on the **memory** path (`taskType: "memory"`): Supermemory chunks and embeds the content for retrieval, *and* runs it through the memory pipeline — extracting facts, updating the profile, and linking it into the knowledge graph.
-If you're ingesting content that's purely reference material — documentation, a large PDF, a knowledge base article — and you don't need Supermemory to derive personal facts or update a profile from it, set `taskType: "superrag"`. It skips the memory pipeline entirely and only does the chunk → embed → index work needed to make the content searchable.
+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"` (a query parameter, so a top-level key in the SDK). It skips the memory pipeline entirely and only does the chunk → embed → index work needed to make the content searchable.
```typescript TypeScript
-await client.add({
- content: "...", // e.g. a long internal wiki page
- containerTag: "docs_kb",
+await supermemory.add("docs_kb", { // e.g. a long internal wiki page
+ content: "...",
taskType: "superrag",
});
```
```python Python
client.add(
- content="...",
- container_tag="docs_kb",
+ "docs_kb",
+ content="...", # e.g. a long internal wiki page
task_type="superrag",
)
```
```bash cURL
-curl -X POST "https://api.supermemory.ai/v3/documents" \
+curl -X POST "https://api.supermemory.ai/ns/docs_kb/document?taskType=superrag" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{
- "content": "...",
- "containerTag": "docs_kb",
- "taskType": "superrag"
- }'
+ -d '{ "content": "..." }'
```
| | `taskType: "memory"` (default) | `taskType: "superrag"` |
|---|---|---|
-| Chunking, embedding, indexing | ✅ | ✅ — searchable immediately via `searchMode: "documents"` |
+| Chunking, embedding, indexing | ✅ | ✅ — searchable immediately via `searchMode: "chunks"` |
| Fact extraction into memories | ✅ | ❌ skipped |
| Profile (`static`/`dynamic`/buckets) updates | ✅ | ❌ skipped |
| Graph linking (updates/extends/derives) | ✅ | ❌ skipped |
@@ -79,7 +74,7 @@ curl -X POST "https://api.supermemory.ai/v3/documents" \
-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"`.
+Content ingested as `superrag` is retrievable via chunk search (`searchMode: "chunks"`), but it will **never** surface as a memory, contribute to a user's profile, or connect into the knowledge graph. Use it for reference material you want searchable, not for anything that should shape what Supermemory knows about a user — that still needs the default `taskType: "memory"`.
When you're searching over a mix of both, `searchMode: "hybrid"` (below) is what pulls memory-path facts and superrag-path document chunks into one result set. More ingestion guidance: [Rules of supermemory → Ingest with SuperRag when you just need search](/concepts/rules#ingest-with-superrag-when-you-just-need-search).
@@ -154,10 +149,9 @@ Supermemory combines the best of both approaches in every search:
With `searchMode: "hybrid"` (the default), you get both:
```typescript
-const results = await client.search({
- q: "how do I deploy the app?",
- containerTag: "user_123",
- searchMode: "hybrid"
+const results = await supermemory.search("user_123", {
+ query: "how do I deploy the app?",
+ searchMode: "hybrid",
});
// Returns:
@@ -174,12 +168,12 @@ Two flags give you fine-grained control over result quality:
### Reranking
-Re-scores results using a cross-encoder model for better relevance:
+Re-scores results using a cross-encoder model for better relevance. `rerank` takes `"none"` (default), `"order"`, or `"aggregate"`:
```typescript
-const results = await client.search({
- q: "complex technical question",
- rerank: true // +~100ms, significantly better ranking
+const results = await supermemory.search("user_123", {
+ query: "complex technical question",
+ rerank: "order", // +~100ms, significantly better ranking
});
```
@@ -190,9 +184,9 @@ const results = await client.search({
Expands your query to capture more relevant results:
```typescript
-const results = await client.search({
- q: "how to auth",
- rewriteQuery: true // Expands to "authentication login oauth jwt..."
+const results = await supermemory.search("user_123", {
+ query: "how to auth",
+ rewriteQuery: true, // Expands to "authentication login oauth jwt..."
});
```
diff --git a/apps/docs/concepts/user-profiles.mdx b/apps/docs/concepts/user-profiles.mdx
index 980f054a..d03dede8 100644
--- a/apps/docs/concepts/user-profiles.mdx
+++ b/apps/docs/concepts/user-profiles.mdx
@@ -7,7 +7,7 @@ icon: "/icons/hugeicons/user-circle.svg"
User profiles are **automatically maintained collections of facts about your users** that Supermemory builds from all their interactions. Think of it as a persistent "about me" document that's always up-to-date.
-Each `containerTag` gets it's own profile.
+Each namespace gets its own profile.
> Note: It's called "user" profile, but in reality it can be anything - an agent, organization, etc.
@@ -48,22 +48,20 @@ The clearest example is the user's own name. If someone tells your agent "call m
```typescript
// Weeks earlier, during onboarding
-await client.add({
+await supermemory.add("user_123", {
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",
+const { results } = await supermemory.search("user_123", {
+ query: "help me plan a trip to Japan",
});
// The name preference won't be in `results` — it's not semantically
// related to trip planning, so search correctly leaves it out.
// But it's always in the profile, independent of the query:
-const { profile } = await client.profile({ containerTag: "user_123" });
-console.log(profile.static); // ["User goes by Dhravya, not their full name", ...]
+const { profile } = await supermemory.profile("user_123");
+console.log(profile.static.map((m) => m.memory)); // ["User goes by Dhravya, not their full name", ...]
```
This is the general pattern: names, pronouns, timezone, tone/format preferences, role, and other facts that should color *every* response — not just responses to a matching query — belong in the profile, not left to be caught by search. If your agent needs to "just know" something at all times, that's a strong signal it belongs in the profile rather than relying on a lucky semantic match.
@@ -96,23 +94,26 @@ Recent context and temporary states:
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.
+Every org starts with a default `preferences` bucket. Add your own in console settings at the organization level, or per namespace with `supermemory.profiles.setBuckets()` — namespace buckets are add-only, so a namespace always keeps every org-level bucket.
```typescript
-const { profile } = await client.profile({
- containerTag: "user_123",
- include: ["buckets"],
- buckets: ["preferences", "goals"], // optional — omit for all configured buckets
+// Define a namespace-level bucket (name → description)
+await supermemory.profiles.setBuckets("user_123", {
+ buckets: { goals: "Goals and targets the user is actively working toward" },
});
-console.log(profile.buckets.preferences);
+const { profile } = await supermemory.profile("user_123", { // optional — omit for all configured buckets
+ buckets: ["preferences", "goals"],
+});
+
+console.log(profile.buckets.preferences); // [{ id, memory }, ...]
console.log(profile.buckets.goals);
```
Bucket descriptions steer the classifier, so a precise description ("explicit first-person preferences only, exclude inferred traits") produces cleaner buckets than a vague one. Buckets are separate from [`filterPrompt`](/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.
+
+ Request bucketed profiles, create buckets at the org or namespace level, and see validation limits.
---
@@ -155,31 +156,24 @@ User asks: **"Can you help me debug this?"**
## Filtering profiles
-Not many people realize this, but profiles support the same [metadata filtering](/concepts/filtering) as memory and document search. A profile is synthesized from the underlying memories in a 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`.
+Not many people realize this, but profiles support the same [metadata filtering](/concepts/filtering) as memory and document search. A profile is synthesized from the underlying memories in a namespace, so any `filter` expression you'd pass to `search` also narrows which memories are eligible to contribute to `static`, `dynamic`, and `buckets`.
```typescript
// Only build the profile from memories tagged as onboarding data
-const { profile } = await client.profile({
- containerTag: "user_123",
- filters: {
- AND: [{ key: "source", value: "onboarding" }],
- },
+const { profile } = await supermemory.profile("user_123", {
+ filter: { field: "source", operator: "eq", 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:
+This is useful when a namespace mixes memories from several sources or contexts and you only want one of them reflected in the profile — for example, a support agent that should only see profile facts derived from support tickets, not from an internal wiki synced into the same namespace:
```typescript
-const { profile } = await client.profile({
- containerTag: "org_customer_442",
- filters: {
- AND: [{ key: "channel", value: "support_ticket" }],
- },
- include: ["static", "dynamic"],
+const { profile } = await supermemory.profile("org_customer_442", {
+ filter: { field: "channel", operator: "eq", value: "support_ticket" },
});
```
-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](/recall/user-profiles#filtering-profiles) for the full parameter reference.
+A v5 profile call takes no search query. If you also need query-ranked memories, make a separate `supermemory.search()` call with the same `filter`. See [Filtering Profiles](/recall/user-profiles#filtering-profiles) for the full parameter reference.
---
@@ -192,8 +186,8 @@ Profiles provide: expertise level, communication preferences, tools used, curren
```typescript
const systemPrompt = `You are assisting ${userName}.
-Background: ${profile.static.join('\n')}
-Current focus: ${profile.dynamic.join('\n')}
+Background: ${profile.static.map((m) => m.memory).join('\n')}
+Current focus: ${profile.dynamic.map((m) => m.memory).join('\n')}
Adjust responses to their expertise and preferences.`;
```
diff --git a/apps/docs/connectors/github.mdx b/apps/docs/connectors/github.mdx
index 4a227b77..25140f18 100644
--- a/apps/docs/connectors/github.mdx
+++ b/apps/docs/connectors/github.mdx
@@ -13,30 +13,24 @@ The GitHub connector requires a **Scale Plan** or **Enterprise Plan**.
## Quick setup
-### 1. Create GitHub connection
+### 1. Create GitHub Connector
```typescript
- import Supermemory from 'supermemory';
+ import { Supermemory } from "supermemory"
- const client = new Supermemory({
- apiKey: process.env.SUPERMEMORY_API_KEY!
- });
+ const supermemory = 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',
+ const connector = await supermemory.connectors.create("user-123", {
+ provider: "github",
+ redirectUrl: "https://yourapp.com/auth/github/callback"
documentLimit: 5000,
- metadata: {
- source: 'github',
- team: 'engineering'
- }
- });
+ })
- // Redirect user to GitHub OAuth
- window.location.href = connection.authLink;
- console.log('Auth expires in:', connection.expiresIn);
+ // Send the user to GitHub to authorize
+ if (connector.authorization) window.location.href = connector.authorization.url
+ console.log("Auth URL expires at:", connector.authorization?.expiresAt)
```
@@ -46,36 +40,38 @@ The GitHub connector requires a **Scale Plan** or **Enterprise Plan**.
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'
- }
+ connector = client.connectors.create(
+ "user-123",
+ request={
+ "provider": "github",
+ "redirectUrl": "https://yourapp.com/auth/github/callback",
+ "documentLimit": 5000,
+ },
)
- # Redirect user to GitHub OAuth
- print(f'Redirect to: {connection.auth_link}')
- print(f'Expires in: {connection.expires_in}')
+ # Send the user to GitHub to authorize
+ print(f"Redirect to: {connector.authorization.url}")
+ print(f"Auth URL expires at: {connector.authorization.expires_at}")
```
```bash
- curl -X POST "https://api.supermemory.ai/v3/connections/github" \
+ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
+ "provider": "github",
"redirectUrl": "https://yourapp.com/auth/github/callback",
- "containerTag": "user-123",
- "documentLimit": 5000,
- "metadata": {
- "source": "github",
- "team": "engineering"
- }
+ "documentLimit": 5000
}'
+
+ # Response: {
+ # "id": "PTzGiUYei7pgzg5buzZHgA",
+ # "authorization": {
+ # "url": "https://github.com/login/oauth/authorize?...",
+ # "expiresAt": "2024-01-15T11:30:00.000Z"
+ # }
+ # }
```
@@ -89,119 +85,87 @@ The GitHub connector requires a **Scale Plan** or **Enterprise Plan**.
### 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.
+Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants permissions, GitHub redirects to your `redirectUrl`. A pending OAuth connector is not visible in list or get until the user finishes authorization. Once it is visible, the user can select which repositories to sync.
-### 3. List and configure repositories
+### 3. Select Repositories
-Unlike other connectors, GitHub requires repository selection before syncing begins. This gives your users control over which repositories to index.
+Unlike most connectors, GitHub requires repository selection before syncing begins. There are two ways to set it:
+
+- **Hosted picker:** read the connector with `include: "picker"` and send the user to `picker.url`. The link works once and expires at `picker.expiresAt`.
+- **Direct selection:** if you already know the repository ids, pass them in `selection.repos` with `connectors.update`. A new selection replaces the old one and starts a sync.
-**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](/connectors/managing-resources) for detailed API documentation.
+See [Managing Connector Selection](/connectors/managing-resources) for the generic selection API that GitHub, Gmail and Google Drive share.
```typescript
- // List available repositories for the user
- const repositories = await client.connections.github.listRepositories(
- connectionId,
- {
- page: 1,
- perPage: 100
- }
- );
+ // Option A: send the user to the hosted picker
+ const withPicker = await supermemory.connectors.get("user-123", connectorId, {
+ include: "picker",
+ returnUrl: "https://yourapp.com/settings/integrations"
+ })
- // 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}`);
- });
+ if (withPicker.picker) window.location.href = withPicker.picker.url
- // After user selects repositories, configure them
- await client.connections.github.configure(connectionId, {
- repositories: [
- {
- id: repo.id,
- name: repo.full_name,
- defaultBranch: repo.default_branch
- }
- ]
- });
+ // Option B: set the repositories directly by id
+ await supermemory.connectors.update("user-123", connectorId, {
+ selection: {
+ repos: ["123456789", "987654321"],
+ },
+ })
- console.log('Repository sync initiated');
+ console.log("Repository sync initiated")
```
```python
- # List available repositories for the user
- repositories = client.connections.github.list_repositories(
- connection_id,
- page=1,
- per_page=100
+ # Option A: send the user to the hosted picker
+ with_picker = client.connectors.get(
+ "user-123",
+ connector_id,
+ include=["picker"],
+ return_url="https://yourapp.com/settings/integrations",
)
- # 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}')
+ if with_picker.picker:
+ print(f"Redirect to: {with_picker.picker.url}")
- # After user selects repositories, configure them
- client.connections.github.configure(
- connection_id,
- repositories=[
- {
- 'id': repo.id,
- 'name': repo.full_name,
- 'defaultBranch': repo.default_branch
- }
- ]
+ # Option B: set the repositories directly by id
+ client.connectors.update(
+ "user-123",
+ connector_id,
+ selection={"repos": ["123456789", "987654321"]},
)
- print('Repository sync initiated')
+ print("Repository sync initiated")
```
```bash
- # List available repositories
- curl -X GET "https://api.supermemory.ai/v3/connections/{connectionId}/resources?page=1&per_page=100" \
+ # Option A: get a one-time hosted picker URL
+ curl "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?include=picker&returnUrl=https%3A%2F%2Fyourapp.com%2Fsettings%2Fintegrations" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
- # Configure selected repositories
- curl -X POST "https://api.supermemory.ai/v3/connections/{connectionId}/configure" \
+ # Response includes: "picker": { "url": "https://...", "expiresAt": "2024-01-15T11:30:00.000Z" }
+
+ # Option B: set the repositories directly by id
+ curl -X PATCH "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
- "resources": [
- {
- "id": 123456789,
- "name": "your-org/documentation",
- "defaultBranch": "main"
- },
- {
- "id": 987654321,
- "name": "your-org/api-docs",
- "defaultBranch": "main"
- }
- ]
+ "selection": {
+ "repos": ["123456789", "987654321"]
+ }
}'
```
-
-**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.
-
+
+The legacy `GET /v3/connections/{id}/resources` repository listing has no v5 route. The hosted picker shows the user their repositories and saves the selection for you.
+
## Supported document types
@@ -229,7 +193,7 @@ The GitHub connector automatically sets up webhooks for real-time incremental sy
### How it works
-1. **Webhook Setup**: When you configure repositories, a webhook is automatically installed in each repository
+1. **Webhook Setup**: When you set the selection, a webhook is automatically installed in each repository
2. **Push Events**: When commits are pushed to the default branch, changed documentation files are synced
3. **Delete Events**: When documentation files are deleted, they're removed from your Supermemory knowledge base
4. **Incremental Updates**: Only changed files are processed, keeping sync fast and efficient
@@ -241,264 +205,210 @@ Webhooks are secured using HMAC-SHA256 signature validation with constant-time c
```typescript
- // Check webhook status
- const connection = await client.connections.get(connectionId);
+ // Check sync status and selection
+ const connector = await supermemory.connectors.get("user-123", connectorId, {
+ include: "syncs",
+ })
- console.log('Webhooks configured:', connection.metadata.webhooks?.length);
- console.log('Last sync:', new Date(connection.metadata.lastSyncedAt));
- console.log('Repositories:', connection.metadata.repositories);
+ console.log("Webhooks:", connector.capabilities.webhooks)
+ console.log("Last synced:", connector.system.lastSuccessfulSyncAt)
+ console.log("Last sync:", connector.latestRun?.system.status, connector.latestRun?.error)
+ console.log("Repositories:", connector.selection?.repos)
+ console.log("Recent runs:", connector.syncs)
```
```python
- # Check webhook status
- connection = client.connections.get(connection_id)
+ # Check sync status and selection
+ connector = client.connectors.get("user-123", connector_id, include=["syncs"])
- print(f'Webhooks configured: {len(connection.metadata.get("webhooks", []))}')
- print(f'Last sync: {connection.metadata.get("lastSyncedAt")}')
- print(f'Repositories: {connection.metadata.get("repositories")}')
+ print(f"Webhooks: {connector.capabilities.webhooks}")
+ print(f"Last synced: {connector.system.last_successful_sync_at}")
+ print(f"Last sync: {connector.latest_run.system.status} {connector.latest_run.error}")
+ repos = (connector.selection or {}).get("repos") or []
+ print(f"Repositories: {[r.name for r in repos]}")
+ print(f"Recent runs: {connector.syncs}")
```
```bash
- # Get 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 '{}'
+ # Get connector details including recent sync runs
+ curl "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?include=syncs" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
-## Connection management
+## Connector Management
-### List all connections
+### List All Connectors
```typescript
- // List all GitHub connections for specific container tags
- const connections = await client.connections.list({
- containerTags: ['user-123'],
- provider: 'github'
- });
+ // List GitHub connectors in a namespace
+ const { connectors } = await supermemory.connectors.list("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(`Repositories: ${conn.metadata.repositories?.length || 0}`);
- console.log('---');
- });
+ connectors
+ .filter(connector => connector.provider === "github")
+ .forEach(connector => {
+ console.log(`Provider: ${connector.provider}`)
+ console.log(`ID: ${connector.id}`)
+ console.log(`Account: ${connector.account}`)
+ console.log(`Created: ${connector.createdAt}`)
+ console.log(`Document limit: ${connector.documentLimit}`)
+ console.log(`Repositories: ${connector.selection?.repos?.length ?? 0}`)
+ console.log("---")
+ })
```
```python
- # List all GitHub connections for specific container tags
- connections = client.connections.list(
- container_tags=['user-123'],
- provider='github'
- )
+ # List GitHub connectors in a namespace
+ connectors = client.connectors.list("user-123", provider="github").connectors
- 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('---')
+ for connector in connectors:
+ print(f"Provider: {connector.provider}")
+ print(f"ID: {connector.id}")
+ print(f"Account: {connector.account}")
+ print(f"Created: {connector.created_at}")
+ print(f"Document limit: {connector.document_limit}")
+ repos = (connector.selection or {}).get("repos") or []
+ print(f"Repositories: {len(repos)}")
+ print("---")
```
```bash
- # 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"
- }'
+ # List connectors in a namespace
+ curl "https://api.supermemory.ai/ns/user-123/connectors" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
-### Update repository configuration
+### Update Repository Selection
-You can update which repositories are synced at any time:
+You can update which repositories are synced at any time. The new selection replaces the old one and starts a sync.
```typescript
// Add or remove repositories
- await 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
- }
- ]
- });
+ await supermemory.connectors.update("user-123", connectorId, {
+ selection: {
+ repos: ["123456789", "987654321"],
+ },
+ })
- console.log('Repository configuration updated');
+ console.log("Repository selection updated")
```
```python
# 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
- }
- ]
+ client.connectors.update(
+ "user-123",
+ connector_id,
+ selection={"repos": ["123456789", "987654321"]},
)
- print('Repository configuration updated')
+ print("Repository selection updated")
```
```bash
- # Update repository configuration
- curl -X POST "https://api.supermemory.ai/v3/connections/{connectionId}/configure" \
+ # Update repository selection
+ curl -X PATCH "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
- "resources": [
- {
- "id": 123456789,
- "name": "your-org/documentation",
- "defaultBranch": "main"
- },
- {
- "id": 987654321,
- "name": "your-org/new-repo",
- "defaultBranch": "develop"
- }
- ]
+ "selection": {
+ "repos": ["123456789", "987654321"]
+ }
}'
```
-When you update the repository configuration:
+When you update the repository selection:
- New repositories are added and synced immediately
- Removed repositories have their webhooks deleted
- Existing documents from removed repositories remain in Supermemory unless you delete them manually
-### Delete connection
+### Delete Connector
```typescript
- // Delete by connection ID
- const result = await client.connections.deleteByID(connectionId);
+ // Delete the connector and its imported documents (default)
+ await supermemory.connectors.delete("user-123", connectorId)
- // Or delete by provider (requires container tags)
- const result = await client.connections.deleteByProvider('github', {
- containerTags: ['user-123']
- });
-
- console.log('Deleted connection:', result.id);
+ // Delete the connector but keep the imported documents
+ await supermemory.connectors.delete("user-123", connectorId, {
+ deleteDocuments: false,
+ })
```
```python
- # Delete by connection ID
- result = client.connections.delete_by_id(connection_id)
+ # Delete the connector and its imported documents (default)
+ client.connectors.delete("user-123", connector_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}')
+ # Delete the connector but keep the imported documents
+ client.connectors.delete("user-123", connector_id, delete_documents=False)
```
```bash
- # Delete by connection ID
- curl -X DELETE "https://api.supermemory.ai/v3/connections/{connectionId}" \
+ # Delete the connector and its imported documents (default)
+ curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+ # Delete the connector but keep the imported documents
+ curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?deleteDocuments=false" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
-Deleting a GitHub connection will:
+Deleting a GitHub connector will:
- Stop all future syncs from configured repositories
- Remove all webhooks from the repositories
- Revoke the OAuth authorization
-- **Permanently delete all synced documents** from your Supermemory knowledge base (unless you pass `deleteDocuments=false` as a query parameter to keep them)
+- **Permanently delete all synced documents** from your Supermemory knowledge base (unless you pass `deleteDocuments: false` to keep them)
### Manual sync
-Trigger a manual synchronization for all configured repositories:
+Trigger a manual synchronization for all selected repositories. The call returns `409` while a sync for that connector is already running.
```typescript
- // Trigger sync for GitHub connections
- await client.connections.import('github');
+ const run = await supermemory.connectors.sync("user-123", connectorId)
- // Trigger sync for specific container tags
- await client.connections.import('github', {
- containerTags: ['user-123']
- });
-
- console.log('Manual sync initiated');
+ console.log(run.status)
+ // Output: queued
```
```python
- # Trigger sync for GitHub connections
- client.connections.import_('github')
+ run = client.connectors.sync("user-123", connector_id)
- # Trigger sync for specific container tags
- client.connections.import_(
- 'github',
- container_tags=['user-123']
- )
-
- print('Manual sync initiated')
+ print(run.status)
+ # Output: queued
```
```bash
- # Trigger sync for all GitHub connections
- curl -X POST "https://api.supermemory.ai/v3/connections/github/import" \
+ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}/sync" \
-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"}
+ # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```
@@ -507,57 +417,7 @@ Trigger a manual synchronization for all configured repositories:
### Custom OAuth application
-For white-label deployments or custom branding, configure your own GitHub OAuth app using the settings API:
-
-
-
- ```typescript
- // 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
- # 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
- # 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"
- ```
-
-
+For white-label deployments or custom branding you can connect with your own GitHub OAuth app. Custom OAuth credentials are an organization setting and are not part of the v5 connector routes. See [Custom OAuth Applications](/connectors/overview#custom-oauth-applications).
**Setting up a GitHub OAuth App:**
@@ -566,7 +426,6 @@ For white-label deployments or custom branding, configure your own GitHub OAuth
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.
+After configuration, all new GitHub connectors will use your custom OAuth app instead of Supermemory's default app.
diff --git a/apps/docs/connectors/gmail.mdx b/apps/docs/connectors/gmail.mdx
index 64ef15af..d6967e7b 100644
--- a/apps/docs/connectors/gmail.mdx
+++ b/apps/docs/connectors/gmail.mdx
@@ -13,30 +13,24 @@ Connect Gmail to automatically sync email threads into your supermemory knowledg
## Quick setup
-### 1. Create Gmail connection
+### 1. Create Gmail Connector
```typescript
- import Supermemory from 'supermemory';
+ import { Supermemory } from "supermemory"
- const client = new Supermemory({
- apiKey: process.env.SUPERMEMORY_API_KEY!
- });
+ const supermemory = 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',
+ const connector = await supermemory.connectors.create("user-123", {
+ provider: "gmail",
+ redirectUrl: "https://yourapp.com/auth/gmail/callback"
documentLimit: 5000,
- metadata: {
- source: 'gmail',
- department: 'support'
- }
- });
+ })
- // Redirect user to Google OAuth
- window.location.href = connection.authLink;
- console.log('Auth expires in:', connection.expiresIn);
+ // Send the user to Google to authorize
+ if (connector.authorization) window.location.href = connector.authorization.url
+ console.log("Auth URL expires at:", connector.authorization?.expiresAt)
```
@@ -46,107 +40,100 @@ Connect Gmail to automatically sync email threads into your supermemory knowledg
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'
- }
+ connector = client.connectors.create(
+ "user-123",
+ request={
+ "provider": "gmail",
+ "redirectUrl": "https://yourapp.com/auth/gmail/callback",
+ "documentLimit": 5000,
+ },
)
- # Redirect user to Google OAuth
- print(f'Redirect to: {connection.auth_link}')
- print(f'Expires in: {connection.expires_in}')
+ # Send the user to Google to authorize
+ print(f"Redirect to: {connector.authorization.url}")
+ print(f"Auth URL expires at: {connector.authorization.expires_at}")
```
```bash
- curl -X POST "https://api.supermemory.ai/v3/connections/gmail" \
+ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
+ "provider": "gmail",
"redirectUrl": "https://yourapp.com/auth/gmail/callback",
- "containerTag": "user-123",
- "documentLimit": 5000,
- "metadata": {
- "source": "gmail",
- "department": "support"
- }
+ "documentLimit": 5000
}'
+
+ # Response: {
+ # "id": "PTzGiUYei7pgzg5buzZHgA",
+ # "authorization": {
+ # "url": "https://accounts.google.com/o/oauth2/v2/auth?...",
+ # "expiresAt": "2024-01-15T11:30:00.000Z"
+ # }
+ # }
```
### 2. Handle OAuth callback
-After user grants permissions, Google redirects to your callback URL. The connection is automatically established and the initial sync begins.
+Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants permissions, Google redirects to your `redirectUrl` and the initial sync begins. A pending OAuth connector is not visible in list or get until the user finishes authorization.
-### 3. Check connection status
+### 3. Check Connector Status
```typescript
- // Get connection details
- const connection = await client.connections.getByTags('gmail', {
- containerTags: ['user-123']
- });
+ // Get connector details with recent sync runs
+ const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ include: "syncs",
+ })
- console.log('Connected email:', connection.email);
- console.log('Connection created:', connection.createdAt);
+ console.log("Connected email:", connector.account)
+ console.log("Last sync:", connector.latestRun?.system.status)
+ console.log("Threads synced:", connector.documentCount)
- // List synced email threads
- const documents = await client.documents.list({
- containerTags: ['user-123']
- });
+ // List synced email threads in the namespace
+ const docs = await supermemory.list("user-123", "documents")
- console.log(`Synced ${documents.memories.length} email threads`);
+ console.log(`Synced ${docs.pagination.totalItems} email threads`)
```
```python
- # Get connection details
- connection = client.connections.get_by_tags(
- 'gmail',
- container_tags=['user-123']
- )
+ # Get connector details with recent sync runs
+ connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])
- print(f'Connected email: {connection.email}')
- print(f'Connection created: {connection.created_at}')
+ print(f"Connected email: {connector.account}")
+ print(f"Last sync: {connector.latest_run.system.status}")
+ print(f"Threads synced: {connector.document_count}")
- # List synced email threads
- documents = client.documents.list(
- container_tags=['user-123']
- )
+ # List synced email threads in the namespace
+ docs = client.list("user-123", "documents")
- print(f'Synced {len(documents.memories)} email threads')
+ print(f"Synced {docs.pagination.total_items} email threads")
```
```bash
- # 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"
- }'
+ # Get connector details with recent sync runs
+ curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
- # List synced email threads
- curl -X POST "https://api.supermemory.ai/v3/documents/list" \
+ # List synced email threads in the namespace
+ curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{
- "containerTags": ["user-123"],
- "source": "gmail"
- }'
+ -d '{}'
```
+
+There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.
+
+
## What gets synced
### Email threads
@@ -178,169 +165,132 @@ Each synced thread includes searchable metadata:
You can filter searches using these metadata fields:
```typescript
-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 }
- ]
- })
-});
+const results = await supermemory.search("user-123", {
+ query: "project update",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "type", operator: "eq", value: "gmail_thread" },
+ { field: "from", operator: "eq", value: "team@company.com" },
+ ],
+ },
+ searchMode: "chunks",
+})
```
-## Connection management
+## Connector Management
-### List all connections
+### List All Connectors
```typescript
- // List all connections for specific container tags
- const connections = await client.connections.list({
- containerTags: ['user-123']
- });
+ // List all connectors in a namespace
+ const { connectors } = await supermemory.connectors.list("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('---');
- });
+ connectors.forEach(connector => {
+ console.log(`Provider: ${connector.provider}`)
+ console.log(`ID: ${connector.id}`)
+ console.log(`Account: ${connector.account}`)
+ console.log(`Created: ${connector.createdAt}`)
+ console.log(`Document limit: ${connector.documentLimit}`)
+ console.log("---")
+ })
```
```python
- # List all connections for specific container tags
- connections = client.connections.list(
- container_tags=['user-123']
- )
+ # List all connectors in a namespace
+ connectors = client.connectors.list("user-123").connectors
- 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('---')
+ for connector in connectors:
+ print(f"Provider: {connector.provider}")
+ print(f"ID: {connector.id}")
+ print(f"Account: {connector.account}")
+ print(f"Created: {connector.created_at}")
+ print(f"Document limit: {connector.document_limit}")
+ print("---")
```
```bash
- # 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"]
- }'
+ # List all connectors in a namespace
+ curl "https://api.supermemory.ai/ns/user-123/connectors" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
-### Delete connection
+### Delete Connector
```typescript
- // Delete by connection ID
- const result = await client.connections.deleteByID('connection_id_123');
- console.log('Deleted connection:', result.id);
+ // Delete the connector and its imported documents (default)
+ await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
- // Delete by provider and container tags
- const providerResult = await client.connections.deleteByProvider('gmail', {
- containerTags: ['user-123']
- });
- console.log('Deleted Gmail connection:', providerResult.id);
+ // Delete the connector but keep the imported documents
+ await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ deleteDocuments: false,
+ })
```
```python
- # Delete by connection ID
- result = client.connections.delete_by_id('connection_id_123')
- print(f'Deleted connection: {result.id}')
+ # Delete the connector and its imported documents (default)
+ client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
- # 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}')
+ # Delete the connector but keep the imported documents
+ client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
```
```bash
- # Delete by connection ID
- curl -X DELETE "https://api.supermemory.ai/v3/connections/connection_id_123" \
+ # Delete the connector and its imported documents (default)
+ curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
- # Delete 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"]
- }'
+ # Delete the connector but keep the imported documents
+ curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?deleteDocuments=false" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
-Deleting a connection will:
+Deleting a connector will:
- Stop all future syncs from Gmail
- Remove the OAuth authorization
-- Keep existing synced documents in supermemory (they won't be deleted)
+- Delete the synced documents unless you pass `deleteDocuments: false`
### Manual sync
-Trigger a manual synchronization:
+Trigger a manual synchronization. The call returns `409` while a sync for that connector is already running.
```typescript
- // Trigger sync for Gmail connections
- await client.connections.import('gmail');
+ const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
- // Trigger sync for specific container tags
- await client.connections.import('gmail', {
- containerTags: ['user-123']
- });
-
- console.log('Manual sync initiated');
+ console.log(run.status)
+ // Output: queued
```
```python
- # Trigger sync for Gmail connections
- client.connections.import_('gmail')
+ run = client.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
- # Trigger sync for specific container tags
- client.connections.import_(
- 'gmail',
- container_tags=['user-123']
- )
-
- print('Manual sync initiated')
+ print(run.status)
+ # Output: queued
```
```bash
- # Trigger sync for all Gmail connections
- curl -X POST "https://api.supermemory.ai/v3/connections/gmail/import" \
+ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
-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"]
- }'
+ # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```
@@ -358,7 +308,7 @@ Gmail connector supports multiple sync methods:
### How real-time sync works
-1. When a connection is created, supermemory registers a Gmail API "watch" subscription
+1. When a connector 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
@@ -374,7 +324,7 @@ 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 |
+| `userinfo.email` | Access to user's email address for connector identification |
**Read-only Access:** The Gmail connector only reads emails. It cannot send, delete, or modify any emails in the user's account.
@@ -387,7 +337,7 @@ The Gmail connector requests the following OAuth scopes:
- **Plan requirement**: Requires Max Plan or above
- **INBOX only** for real-time sync: Only INBOX label triggers real-time updates; other labels sync via scheduled sync
- **Watch expiration**: Gmail watch subscriptions expire after 7 days (automatically renewed by supermemory)
-- **Document limit**: Default limit is 10,000 threads per connection (configurable via `documentLimit` parameter)
+- **Document limit**: Default limit is 10,000 threads per connector (configurable via `documentLimit` parameter)
- **Attachments**: Attachment metadata is stored, but attachment content is not downloaded
- **Rate limits**: Gmail API rate limits may affect sync speed for accounts with many emails
@@ -396,25 +346,25 @@ The Gmail connector requests the following OAuth scopes:
### OAuth fails or missing refresh token
-If OAuth fails or the connection stops syncing:
+If OAuth fails or the connector stops syncing:
-1. Delete the existing connection
-2. Create a new connection
+1. Delete the existing connector
+2. Create a new connector
3. Ensure the user completes the full OAuth flow with consent
```typescript
-// Re-create connection to get fresh tokens
-await client.connections.deleteByProvider('gmail', {
- containerTags: ['user-123']
-});
+// Re-create the connector to get fresh tokens
+await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ deleteDocuments: false,
+})
-const newConnection = await client.connections.create('gmail', {
- redirectUrl: 'https://yourapp.com/auth/gmail/callback',
- containerTag: 'user-123'
-});
+const next = await supermemory.connectors.create("user-123", {
+ provider: "gmail",
+ redirectUrl: "https://yourapp.com/auth/gmail/callback"
+})
// User must re-authenticate
-window.location.href = newConnection.authLink;
+if (next.authorization) window.location.href = next.authorization.url
```
### Emails not syncing in real-time
@@ -423,8 +373,8 @@ 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
+- Check if the connector was created recently (watch registration happens on creation)
+- Trigger a manual sync to verify the connector is working
### Permission denied errors
diff --git a/apps/docs/connectors/google-drive.mdx b/apps/docs/connectors/google-drive.mdx
index 409193e4..e8a16732 100644
--- a/apps/docs/connectors/google-drive.mdx
+++ b/apps/docs/connectors/google-drive.mdx
@@ -9,43 +9,37 @@ Connect Google Drive to sync documents into your Supermemory knowledge base with
## 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).
+**Default for new connectors:** after OAuth, the user completes a **folder and file** picker (Google Docs, Sheets, Slides, and PDFs). Only items they select are synced and updated until they change the selection (for example from the Supermemory console).
-**Whole Drive:** set `metadata.syncScope` to `"full"` when creating the connection so the entire Drive syncs without the picker.
+**Whole Drive:** set `config.syncScope` to `"full"` when creating the connector 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.
+**Explicit scoped mode:** set `config.syncScope` to `"selected"` for the picker flow, or rely on the default for new connects.
-If you use scoped sync and the user has not finished the picker yet, **scheduled or manual import may skip that connection** until a selection is saved on the connection.
+If you use scoped sync and the user has not finished the picker yet, **scheduled or manual sync may skip that connector** until a selection is saved on the connector.
## Quick setup
-### 1. Create Google Drive connection
+### 1. Create Google Drive Connector
```typescript
- import Supermemory from 'supermemory';
+ import { Supermemory } from "supermemory"
- const client = new Supermemory({
- apiKey: process.env.SUPERMEMORY_API_KEY!
- });
+ const supermemory = 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',
+ const connector = await supermemory.connectors.create("user-123", {
+ provider: "google-drive",
+ redirectUrl: "https://yourapp.com/auth/google-drive/callback"
documentLimit: 3000,
- metadata: {
- source: 'google-drive',
- department: 'engineering',
- syncScope: 'selected'
- }
- });
+ config: { syncScope: "selected" },
+ })
- // Redirect user to Google OAuth
- window.location.href = connection.authLink;
- console.log('Auth expires in:', connection.expiresIn);
+ // Send the user to Google to authorize
+ if (connector.authorization) window.location.href = connector.authorization.url
+ console.log("Auth URL expires at:", connector.authorization?.expiresAt)
```
@@ -55,99 +49,155 @@ If you use scoped sync and the user has not finished the picker yet, **scheduled
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',
- }
+ connector = client.connectors.create(
+ "user-123",
+ request={
+ "provider": "google-drive",
+ "redirectUrl": "https://yourapp.com/auth/google-drive/callback",
+ "documentLimit": 3000,
+ "config": {"syncScope": "selected"},
+ },
)
- # Redirect user to Google OAuth
- print(f'Redirect to: {connection.auth_link}')
- print(f'Expires in: {connection.expires_in}')
+ # Send the user to Google to authorize
+ print(f"Redirect to: {connector.authorization.url}")
+ print(f"Auth URL expires at: {connector.authorization.expires_at}")
```
```bash
- curl -X POST "https://api.supermemory.ai/v3/connections/google-drive" \
+ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
+ "provider": "google-drive",
"redirectUrl": "https://yourapp.com/auth/google-drive/callback",
- "containerTag": "user-123",
"documentLimit": 3000,
- "metadata": {
- "source": "google-drive",
- "department": "engineering",
- "syncScope": "selected"
- }
+ "config": { "syncScope": "selected" }
}'
+
+ # Response: {
+ # "id": "PTzGiUYei7pgzg5buzZHgA",
+ # "authorization": {
+ # "url": "https://accounts.google.com/o/oauth2/v2/auth?...",
+ # "expiresAt": "2024-01-15T11:30:00.000Z"
+ # }
+ # }
```
-For **whole Drive** sync, include `"syncScope": "full"` in `metadata` on the same `POST /v3/connections/google-drive` request instead of `"selected"`.
+For **whole Drive** sync, send `"config": { "syncScope": "full" }` on the same `POST /ns/{namespace}/connectors` 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 and **must finish that step before imports run**. With `syncScope: "full"`, Supermemory skips the picker and redirects to your `redirectUrl` (or returns connection details). 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).
+Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants permissions, Google redirects through Supermemory to finish the connector. A pending OAuth connector is not visible in list or get until the user finishes authorization.
-### 3. Check connection status
+With **scoped** sync (`syncScope` omitted or `"selected"`), the user is sent to Supermemory’s **hosted file and folder picker**; they must complete that step before syncs run. With **`syncScope: "full"`**, Supermemory redirects to your `redirectUrl` **without** the picker.
+
+You can open the picker again later for an existing connector. Read the connector with `include: "picker"` and send the user to `picker.url`; the link works once and expires at `picker.expiresAt`. Pass `returnUrl` to choose where the picker sends the user afterwards.
```typescript
- // Get connection details
- const connection = await client.connections.getByTags('google-drive', {
- containerTags: ['user-123']
- });
- ```
-
-
- ```python
- # Get connection details
- connection = client.connections.get_by_tags(
- 'google-drive',
- container_tags=['user-123']
- )
+ const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ include: "picker",
+ returnUrl: "https://yourapp.com/settings/integrations"
+ })
- # List synced documents
- documents = client.connections.list_documents(
- 'google-drive',
- container_tags=['user-123']
- )
+ if (connector.picker) window.location.href = connector.picker.url
```
```bash
- # 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"
- }'
+ curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=picker&returnUrl=https%3A%2F%2Fyourapp.com%2Fsettings%2Fintegrations" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
- # List synced documents
- curl -X POST "https://api.supermemory.ai/v3/documents/list" \
+ # Response includes: "picker": { "url": "https://...", "expiresAt": "2024-01-15T11:30:00.000Z" }
+ ```
+
+
+
+If you already know the Drive file and folder ids, set them directly. A new selection replaces the old one and starts a sync.
+
+
+
+ ```typescript
+ await supermemory.connectors.update("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ selection: {
+ files: ["1AbCdEfGhIjKlMnOpQrStUvWxYz"],
+ folders: ["0BxYzAbCdEfGhIjKlMnOpQrStU"],
+ },
+ })
+ ```
+
+
+ ```bash
+ curl -X PATCH "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
- "containerTags": ["user-123"],
- "source": "google-drive"
+ "selection": {
+ "files": ["1AbCdEfGhIjKlMnOpQrStUvWxYz"],
+ "folders": ["0BxYzAbCdEfGhIjKlMnOpQrStU"]
+ }
}'
```
+### 3. Check Connector Status
+
+
+
+ ```typescript
+ // Get connector details with recent sync runs
+ const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ include: "syncs",
+ })
+
+ console.log("Account:", connector.account)
+ console.log("Selection:", connector.selection)
+ console.log("Last sync:", connector.latestRun?.system.status)
+
+ // List synced documents in the namespace
+ const { documents } = await supermemory.list("user-123", "documents")
+ ```
+
+
+ ```python
+ # Get connector details with recent sync runs
+ connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])
+
+ print(f"Account: {connector.account}")
+ print(f"Selection: {connector.selection}")
+ print(f"Last sync: {connector.latest_run.system.status}")
+
+ # List synced documents in the namespace
+ documents = client.list("user-123", "documents").documents
+ ```
+
+
+ ```bash
+ # Get connector details with recent sync runs
+ curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+ # List synced documents in the namespace
+ curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{}'
+ ```
+
+
+
+
+There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.
+
+
## Supported document types
Based on the API type definitions, Google Drive documents are identified with these types:
@@ -159,173 +209,136 @@ Based on the API type definitions, Google Drive documents are identified with th
Drive documents are converted to markdown before ingestion. This conversion is lossy — some formatting may not be preserved.
-## Connection management
+## Connector Management
-### List all connections
+### List All Connectors
```typescript
- // List all connections for specific container tags
- const connections = await client.connections.list({
- containerTags: ['user-123']
- });
+ // List all connectors in a namespace
+ const { connectors } = await supermemory.connectors.list("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('---');
- });
+ connectors.forEach(connector => {
+ console.log(`Provider: ${connector.provider}`)
+ console.log(`ID: ${connector.id}`)
+ console.log(`Account: ${connector.account}`)
+ console.log(`Created: ${connector.createdAt}`)
+ console.log(`Document limit: ${connector.documentLimit}`)
+ console.log("---")
+ })
```
```python
- # List all connections for specific container tags
- connections = client.connections.list(
- container_tags=['user-123']
- )
+ # List all connectors in a namespace
+ connectors = client.connectors.list("user-123").connectors
- 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('---')
+ for connector in connectors:
+ print(f"Provider: {connector.provider}")
+ print(f"ID: {connector.id}")
+ print(f"Account: {connector.account}")
+ print(f"Created: {connector.created_at}")
+ print(f"Document limit: {connector.document_limit}")
+ print("---")
```
```bash
- # 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"]
- }'
+ # List all connectors in a namespace
+ curl "https://api.supermemory.ai/ns/user-123/connectors" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
# Response example:
- # [
- # {
- # "id": "conn_gd123",
- # "provider": "google-drive",
- # "email": "user@example.com",
- # "createdAt": "2024-01-15T10:30:00.000Z",
- # "documentLimit": 3000
- # }
- # ]
+ # {
+ # "connectors": [
+ # {
+ # "id": "PTzGiUYei7pgzg5buzZHgA",
+ # "provider": "google-drive",
+ # "namespace": "user-123",
+ # "account": "user@example.com",
+ # "documentLimit": 3000,
+ # "documentCount": 120,
+ # "latestRun": { "system": { "status": "completed", ... }, "error": null },
+ # "system": { "status": "active", "createdAt": "2024-01-15T10:30:00.000Z", "lastSuccessfulSyncAt": "..." }
+ # }
+ # ],
+ # "pagination": { "currentPage": 1, "limit": 50, "totalItems": 1, "totalPages": 1 }
+ # }
```
-### Delete connection
+### Delete Connector
```typescript
- // Delete by connection ID
- const result = await client.connections.deleteByID('connection_id_123');
- console.log('Deleted connection:', result.id);
+ // Delete the connector and its imported documents (default)
+ await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
- // Delete by provider and container tags
- const providerResult = await client.connections.deleteByProvider('google-drive', {
- containerTags: ['user-123']
- });
- console.log('Deleted provider connection:', providerResult.id);
+ // Delete the connector but keep the imported documents
+ await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ deleteDocuments: false,
+ })
```
```python
- # Delete by connection ID
- result = client.connections.delete_by_id('connection_id_123')
- print(f'Deleted connection: {result.id}')
+ # Delete the connector and its imported documents (default)
+ client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
- # 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}')
+ # Delete the connector but keep the imported documents
+ client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
```
```bash
- # Delete by connection ID
- curl -X DELETE "https://api.supermemory.ai/v3/connections/connection_id_123" \
+ # Delete the connector and its imported documents (default)
+ curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
- # 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"}
+ # Delete the connector but keep the imported documents
+ curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?deleteDocuments=false" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
-Deleting a connection will:
+Deleting a connector will:
- Stop all future syncs from Google Drive
- Remove the OAuth authorization
-- Keep existing synced documents in Supermemory (they won't be deleted)
+- Delete the synced documents unless you pass `deleteDocuments: false`
### Manual sync
-Trigger a manual synchronization:
+Trigger a manual synchronization. The call returns `409` while a sync for that connector is already running.
```typescript
- // Trigger sync for Google Drive connections
- await client.connections.import('google-drive');
+ const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
- // Trigger sync for specific container tags
- await client.connections.import('google-drive', {
- containerTags: ['user-123']
- });
-
- console.log('Manual sync initiated');
+ console.log(run.status)
+ // Output: queued
```
```python
- # Trigger sync for Google Drive connections
- client.connections.import_('google-drive')
+ run = client.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
- # Trigger sync for specific container tags
- client.connections.import_(
- 'google-drive',
- container_tags=['user-123']
- )
-
- print('Manual sync initiated')
+ print(run.status)
+ # Output: queued
```
```bash
- # Trigger sync for all Google Drive connections
- curl -X POST "https://api.supermemory.ai/v3/connections/google-drive/import" \
+ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
-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"}
+ # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```
@@ -336,124 +349,11 @@ Trigger a manual synchronization:
### Custom OAuth application
-Configure your own Google OAuth app using the settings API:
-
-
-
- ```typescript
- // 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
- # 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
- # 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
- await client.settings.update({
- shouldLLMFilter: true,
- filterPrompt: "Only sync important business documents",
- includeItems: {
- // Your include patterns
- },
- excludeItems: {
- // Your exclude patterns
- }
- });
- ```
-
-
- ```python
- 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
- # 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": {...}
- # }
- ```
-
-
+You can connect with your own Google OAuth app. Custom OAuth credentials are an organization setting and are not part of the v5 connector routes. See [Custom OAuth Applications](/connectors/overview#custom-oauth-applications) for the setup steps and callback URL.
**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
+- Use one namespace per user or tenant
+- Check `latestRun.system.status` and `latestRun.error` for failed syncs
diff --git a/apps/docs/connectors/granola.mdx b/apps/docs/connectors/granola.mdx
index f5e33d95..a0b81029 100644
--- a/apps/docs/connectors/granola.mdx
+++ b/apps/docs/connectors/granola.mdx
@@ -19,10 +19,10 @@ The Granola connector requires a **Pro Plan** or higher in Supermemory and a Gra
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.
+5. Optionally set a document limit and namespace.
6. Click **Connect**.
-The console creates the connection and starts the initial sync automatically.
+The console creates the connector and starts the initial sync automatically.
The console limits connector setup to 500 documents. Use the API setup below for higher `documentLimit` values, up to 10,000.
@@ -33,21 +33,20 @@ The console limits connector setup to 500 documents. Use the API setup below for
```typescript
- import Supermemory from 'supermemory';
+ import { Supermemory } from "supermemory"
- const client = new Supermemory({
- apiKey: process.env.SUPERMEMORY_API_KEY!
- });
+ const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
- const connection = await client.connections.create('granola', {
- metadata: {
- apiKey: process.env.GRANOLA_API_KEY!
+ const connector = await supermemory.connectors.create("org-123", {
+ provider: "granola",
+ config: {
+ apiKey: process.env.GRANOLA_API_KEY!,
},
- containerTag: 'org-123',
- documentLimit: 1000
- });
+ documentLimit: 1000,
+ })
- console.log('Granola connection:', connection.id);
+ // Granola doesn't require OAuth; authorization is null and the first sync starts now
+ console.log("Granola connector:", connector.id)
```
@@ -57,48 +56,52 @@ The console limits connector setup to 500 documents. Use the API setup below for
client = Supermemory(api_key=os.environ["SUPERMEMORY_API_KEY"])
- connection = client.connections.create(
- 'granola',
- metadata={
- 'apiKey': os.environ["GRANOLA_API_KEY"]
+ connector = client.connectors.create(
+ "org-123",
+ request={
+ "provider": "granola",
+ "config": {
+ "apiKey": os.environ["GRANOLA_API_KEY"],
+ },
+ "documentLimit": 1000,
},
- container_tag='org-123',
- document_limit=1000
)
- print(f'Granola connection: {connection.id}')
+ # Granola doesn't require OAuth; authorization is None and the first sync starts now
+ print(f"Granola connector: {connector.id}")
```
```bash
- curl -X POST "https://api.supermemory.ai/v3/connections/granola" \
+ curl -X POST "https://api.supermemory.ai/ns/org-123/connectors" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
- "metadata": {
+ "provider": "granola",
+ "config": {
"apiKey": "'"$GRANOLA_API_KEY"'"
},
- "containerTag": "org-123",
"documentLimit": 1000
}'
+
+ # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "authorization": null}
```
-Supermemory validates the Granola API key before creating the connection. The initial sync starts automatically after the connection is created.
+Supermemory validates the Granola API key before creating the connector. The initial sync starts automatically after the connector is created.
## Configuration options
-For Granola, provider-specific fields are passed inside the top-level `metadata` object. General connection options stay top-level.
+For Granola, provider-specific fields go inside the `config` object. The namespace is in the URL, and `documentLimit` stays top-level in the body.
| Parameter | Location | Required | Description |
|-----------|----------|----------|-------------|
-| `apiKey` | `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) |
+| `apiKey` | `config.apiKey` | Yes | Granola API key from **Settings > Connectors > API keys** |
+| `documentLimit` | top-level | No | Maximum notes to sync per connector (default: 10,000) |
-In the Python SDK, use `container_tags` and `document_limit` for top-level options, but keep the Granola metadata key in camelCase: `apiKey`.
+The API key is stored encrypted and is never returned by `connectors.get` or `connectors.list`.
## What gets synced
@@ -127,70 +130,88 @@ Each synced note includes searchable metadata:
You can filter searches using these metadata fields:
```typescript
-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 }
- ]
- })
-});
+const results = await supermemory.search("org-123", {
+ query: "customer onboarding discussion",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "type", operator: "eq", value: "granola" },
+ { field: "attendees", operator: "arrayContains", value: "alex@company.com" },
+ ],
+ },
+ searchMode: "chunks",
+})
```
-## Connection management
+## Connector Management
-### Delete connection
+### Check Sync Status
```typescript
- await client.connections.deleteByID('conn_granola_abc123');
- ```
-
-
- ```python
- client.connections.delete_by_id('conn_granola_abc123')
+ const connector = await supermemory.connectors.get("org-123", "PTzGiUYei7pgzg5buzZHgA", {
+ include: "syncs",
+ })
+
+ console.log("Last sync:", connector.latestRun?.system.status, connector.latestRun?.error)
+ console.log("Notes synced:", connector.documentCount)
```
```bash
- curl -X DELETE "https://api.supermemory.ai/v3/connections/conn_granola_abc123" \
+ curl "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+ ```
+
+
+
+### Delete Connector
+
+
+
+ ```typescript
+ await supermemory.connectors.delete("org-123", "PTzGiUYei7pgzg5buzZHgA")
+ ```
+
+
+ ```python
+ client.connectors.delete("org-123", "PTzGiUYei7pgzg5buzZHgA")
+ ```
+
+
+ ```bash
+ curl -X DELETE "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
-By default, deleting a connection removes all synced documents from Supermemory. To keep documents, pass `deleteDocuments=false` as a query parameter: `DELETE /v3/connections/:id?deleteDocuments=false`
+By default, deleting a connector removes all synced documents from Supermemory. To keep documents, pass `deleteDocuments: false` (`DELETE /ns/{namespace}/connectors/{id}?deleteDocuments=false`).
### Manual sync
+The call returns `409` while a sync for that connector is already running.
+
```typescript
- await client.connections.import('granola', {
- containerTags: ['org-123']
- });
+ await supermemory.connectors.sync("org-123", "PTzGiUYei7pgzg5buzZHgA")
```
```python
- client.connections.import_(
- 'granola',
- container_tags=['org-123']
- )
+ client.connectors.sync("org-123", "PTzGiUYei7pgzg5buzZHgA")
```
```bash
- 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"]}'
+ curl -X POST "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+ # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```
@@ -202,14 +223,14 @@ By default, deleting a connection removes all synced documents from Supermemory.
| **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) |
+| **Sync schedule** | Initial sync after connector creation + manual triggers |
+| **Document limit** | 10,000 notes per connector (default) |
## Troubleshooting
| Error | Solution |
|-------|----------|
-| `Granola API key is required` | Include a non-empty `metadata.apiKey` value when creating the connection |
+| `Granola API key is required` | Include a non-empty `config.apiKey` value when creating the connector |
| `Granola API key is invalid` | Create a new key in Granola and reconnect |
| `Could not reach Granola API` | Retry after checking Granola API availability and network access |
| Missing notes | Check `documentLimit`; if the workspace has more notes than the limit, only notes up to the limit are imported |
diff --git a/apps/docs/connectors/managing-resources.mdx b/apps/docs/connectors/managing-resources.mdx
index e26c1e6a..fa6f2d26 100644
--- a/apps/docs/connectors/managing-resources.mdx
+++ b/apps/docs/connectors/managing-resources.mdx
@@ -1,201 +1,137 @@
---
-title: 'Managing connection resources'
-sidebarTitle: 'Managing resources'
-description: 'Get and configure resources for connections that support resource management'
+title: 'Managing Connector Selection'
+sidebarTitle: 'Managing Resources'
+description: 'Choose what a connector syncs with the hosted picker or a direct selection update'
icon: 'folder-sync'
---
-**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.
+**Selectable connectors:** GitHub (repositories), Gmail (labels) and Google Drive (files and folders) let you choose what to sync. Other connectors sync everything and return `selection: null`.
-Some connectors 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.
+Some connectors let you select which resources to sync. v5 exposes this through two calls on the connector itself: read it with `include: "picker"` to get a hosted picker URL, or send the ids directly with `connectors.update`.
-## Get resources
+## Open the Hosted Picker
-`GET /v3/connections/:connectionId/resources`
+`GET /ns/{namespace}/connectors/{id}?include=picker`
-Get available resources (e.g., repositories, folders) for a connection using stored credentials.
+Returns a one-time picker URL. Send the user there; Supermemory lists their resources, saves the selection and starts a sync. Pass `returnUrl` to choose where the picker sends the user when they finish.
```typescript Typescript
-import Supermemory from 'supermemory';
+import { Supermemory } from "supermemory"
-const client = new Supermemory({
- apiKey: process.env['SUPERMEMORY_API_KEY'],
-});
+const supermemory = 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 connector = await supermemory.connectors.get("user-123", connectorId, {
+ include: "picker",
+ returnUrl: "https://yourapp.com/settings/integrations"
+})
-const data = await response.json();
-console.log('Resources:', data.resources);
-console.log('Total count:', data.total_count);
+console.log("Picker URL:", connector.picker?.url)
+console.log("Expires at:", connector.picker?.expiresAt)
+console.log("Selectable:", connector.capabilities.selectable)
```
```python Python
-import requests
+from supermemory import Supermemory
+import os
-url = f"https://api.supermemory.ai/v3/connections/{connection_id}/resources"
-params = {
- "page": 1,
- "per_page": 30
-}
-headers = {
- "Authorization": f"Bearer {api_key}",
-}
+client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))
-response = requests.get(url, params=params, headers=headers)
-data = response.json()
+connector = client.connectors.get(
+ "user-123",
+ connector_id,
+ include=["picker"],
+ return_url="https://yourapp.com/settings/integrations",
+)
-print(f"Resources: {data['resources']}")
-print(f"Total count: {data.get('total_count')}")
+print(f"Picker URL: {connector.picker.url if connector.picker else None}")
+print(f"Expires at: {connector.picker.expires_at if connector.picker else None}")
+print(f"Selectable: {connector.capabilities.selectable}")
```
```bash cURL
-curl -X GET \
- "https://api.supermemory.ai/v3/connections/{connectionId}/resources?page=1&per_page=30" \
- -H "Authorization: Bearer "
+curl "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?include=picker&returnUrl=https%3A%2F%2Fyourapp.com%2Fsettings%2Fintegrations" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
### Query parameters
-- `page`: Optional. Page number for pagination. Default: `1`
-- `per_page`: Optional. Number of resources per page. Default: `30`
+- `include`: `picker` for the hosted picker URL, `syncs` for recent sync runs, or `syncs,picker` for both
+- `returnUrl`: Optional. Where the hosted picker sends the user when they finish
### Response
```json
{
- "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
+ "id": "PTzGiUYei7pgzg5buzZHgA",
+ "provider": "github",
+ "namespace": "user-123",
+ "capabilities": { "selectable": true, "webhooks": true },
+ "selection": {
+ "repos": [
+ { "id": "123456789", "name": "your-org/documentation" }
+ ]
+ },
+ "picker": {
+ "url": "https://...",
+ "expiresAt": "2024-01-15T11:30:00.000Z"
+ }
}
```
-### 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.
+The picker link works once, in any browser. `picker` is `null` for connectors that sync everything.
-## Configure connection
+## Set the Selection Directly
-`POST /v3/connections/:connectionId/configure`
+`PATCH /ns/{namespace}/connectors/{id}`
-Configure selected resources (e.g., repositories) for a connection and set up webhooks/subscriptions.
+Send the ids to sync, grouped by kind. The new selection replaces the current one and starts a sync. Webhooks are registered for the selected resources.
```typescript Typescript
-import Supermemory from 'supermemory';
+import { Supermemory } from "supermemory"
-const client = new Supermemory({
- apiKey: process.env['SUPERMEMORY_API_KEY'],
-});
+const supermemory = 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 connector = await supermemory.connectors.update("user-123", connectorId, {
+ selection: {
+ repos: ["123456789", "987654321"],
+ },
+})
-const data = await response.json();
-console.log('Success:', data.success);
-console.log('Message:', data.message);
-console.log('Webhooks registered:', data.webhooksRegistered);
+console.log("Selection:", connector.selection)
+console.log("Last sync:", connector.latestRun?.system.status)
```
```python Python
-import requests
+from supermemory import Supermemory
+import os
-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",
- },
- ]
-}
+client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))
-response = requests.post(url, json=payload, headers=headers)
-data = response.json()
+connector = client.connectors.update(
+ "user-123",
+ connector_id,
+ selection={"repos": ["123456789", "987654321"]},
+)
-print(f"Success: {data['success']}")
-print(f"Message: {data['message']}")
-print(f"Webhooks registered: {data.get('webhooksRegistered')}")
+print(f"Selection: {connector.selection}")
+print(f"Last sync: {connector.latest_run.system.status}")
```
```bash cURL
-curl -X POST \
- "https://api.supermemory.ai/v3/connections/{connectionId}/configure" \
- -H "Authorization: Bearer " \
+curl -X PATCH "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}" \
+ -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"
- }
- ]
+ "selection": {
+ "repos": ["123456789", "987654321"]
+ }
}'
```
@@ -204,140 +140,127 @@ curl -X POST \
```json
{
- "resources": [
- {
- "id": 123456789,
- "name": "your-org/documentation",
- "defaultBranch": "main"
- }
- ]
+ "selection": {
+ "repos": ["123456789"]
+ }
}
```
-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)
+The selection key depends on the provider:
+- GitHub: `repos` (repository ids)
+- Gmail: `labels` (label ids, for example `INBOX`)
+- Google Drive: `files` and `folders` (Drive ids)
+
+You can send `documentLimit` in the same body. Each call must include `selection`, `documentLimit`, or both.
### Response
+The updated connector object, including the saved `selection` as `{ id, name }` pairs.
+
```json
{
- "success": true,
- "message": "Resources configured successfully",
- "webhooksRegistered": 2
+ "id": "PTzGiUYei7pgzg5buzZHgA",
+ "provider": "github",
+ "selection": {
+ "repos": [
+ { "id": "123456789", "name": "your-org/documentation" },
+ { "id": "987654321", "name": "your-org/api-docs" }
+ ]
+ },
+ "latestRun": { "errorCode": null, "error": null, "system": { "status": "running", "startedAt": "...", "completedAt": null } }
}
```
### Error Responses
-- `400`: Connection missing refresh token
+- `400`: Connector does not support selection
- `401`: Unauthorized
-- `404`: Connection not found
-- `501`: Provider does not support resource configuration
+- `404`: Connector not found
-**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.
+**Automatic Sync:** After a selection is saved, a sync starts for the connector. You don't need to call `connectors.sync` afterwards.
-
-**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
-// 1. Create connection (see creating-connection.mdx)
-const connection = await client.connections.create('github', {
- redirectUrl: 'https://yourapp.com/callback',
-});
+// 1. Create the connector
+const created = await supermemory.connectors.create("user-123", {
+ provider: "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();
+// 2. Send the user to GitHub; the connector becomes visible once they authorize
+if (created.authorization) window.location.href = created.authorization.url
-// 3. Display repositories to user and let them select
-// (Build your UI here)
+// 3. After the callback, open the hosted picker so the user chooses repositories
+const connector = await supermemory.connectors.get("user-123", created.id, {
+ include: "picker",
+ returnUrl: "https://yourapp.com/settings/integrations"
+})
+if (connector.picker) window.location.href = connector.picker.url
-// 4. 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);
+// 4. Or, if you already know the repository ids, set them directly
+await supermemory.connectors.update("user-123", created.id, {
+ selection: { repos: selectedRepositoryIds },
+})
```
```python Python
-# 1. Create connection (see creating-connection.mdx)
-connection = client.connections.create(
- 'github',
- redirect_url='https://yourapp.com/callback'
+# 1. Create the connector
+created = client.connectors.create(
+ "user-123",
+ request={
+ "provider": "github",
+ "redirectUrl": "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"]
+# 2. Send the user to GitHub; the connector becomes visible once they authorize
+print(f"Redirect to: {created.authorization.url}")
-# 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}"}
+# 3. After the callback, open the hosted picker so the user chooses repositories
+connector = client.connectors.get(
+ "user-123",
+ created.id,
+ include=["picker"],
+ return_url="https://yourapp.com/settings/integrations",
+)
+if connector.picker:
+ print(f"Redirect to: {connector.picker.url}")
+
+# 4. Or, if you already know the repository ids, set them directly
+client.connectors.update(
+ "user-123",
+ created.id,
+ selection={"repos": selected_repository_ids},
)
-result = configure_response.json()
-print(f"Sync initiated: {result['success']}")
```
```bash cURL
-# 1. Create connection (see creating-connection.mdx)
-# ... (OAuth flow) ...
+# 1. Create the connector
+curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{"provider": "github", "redirectUrl": "https://yourapp.com/callback"}'
-# 2. Fetch available repositories
-curl -X GET \
- "https://api.supermemory.ai/v3/connections/{connectionId}/resources?page=1&per_page=100" \
+# 2. ... (user authorizes at authorization.url) ...
+
+# 3. Get a one-time hosted picker URL
+curl "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}?include=picker" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
-# 3. Configure selected repositories
-curl -X POST \
- "https://api.supermemory.ai/v3/connections/{connectionId}/configure" \
+# 4. Or set the repositories directly
+curl -X PATCH "https://api.supermemory.ai/ns/user-123/connectors/{connectorId}" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
- "resources": [
- {
- "id": 123456789,
- "name": "your-org/documentation",
- "defaultBranch": "main"
- }
- ]
+ "selection": {
+ "repos": ["123456789"]
+ }
}'
```
-
diff --git a/apps/docs/connectors/notion.mdx b/apps/docs/connectors/notion.mdx
index 09bafd94..f979bb44 100644
--- a/apps/docs/connectors/notion.mdx
+++ b/apps/docs/connectors/notion.mdx
@@ -8,30 +8,23 @@ Connect Notion workspaces to automatically sync pages, databases, and content bl
## Quick setup
-### 1. Create Notion connection
+### 1. Create Notion Connector
```typescript
- import Supermemory from 'supermemory';
+ import { Supermemory } from "supermemory"
- const client = new Supermemory({
- apiKey: process.env.SUPERMEMORY_API_KEY!
- });
+ const supermemory = 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',
+ const connector = await supermemory.connectors.create("user-123", {
+ provider: "notion",
+ redirectUrl: "https://yourapp.com/auth/notion/callback"
documentLimit: 2000,
- metadata: {
- source: 'notion',
- workspaceType: 'team',
- department: 'product'
- }
- });
+ })
- // Redirect user to Notion OAuth
- window.location.href = connection.authLink;
+ // Send the user to Notion to authorize
+ if (connector.authorization) window.location.href = connector.authorization.url
```
@@ -41,124 +34,143 @@ Connect Notion workspaces to automatically sync pages, databases, and content bl
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'
- }
+ connector = client.connectors.create(
+ "user-123",
+ request={
+ "provider": "notion",
+ "redirectUrl": "https://yourapp.com/auth/notion/callback",
+ "documentLimit": 2000,
+ },
)
- # Redirect user to Notion OAuth
- print(f'Redirect to: {connection.auth_link}')
+ # Send the user to Notion to authorize
+ print(f"Redirect to: {connector.authorization.url}")
```
```bash
- curl -X POST "https://api.supermemory.ai/v3/connections/notion" \
+ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
+ "provider": "notion",
"redirectUrl": "https://yourapp.com/auth/notion/callback",
- "containerTag": "user-123",
- "documentLimit": 2000,
- "metadata": {
- "source": "notion",
- "workspaceType": "team",
- "department": "product"
- }
+ "documentLimit": 2000
}'
+
+ # Response: {
+ # "id": "PTzGiUYei7pgzg5buzZHgA",
+ # "authorization": {
+ # "url": "https://api.notion.com/v1/oauth/authorize?...",
+ # "expiresAt": "2024-01-15T11:30:00.000Z"
+ # }
+ # }
```
### 2. Handle OAuth flow
-After user grants workspace access, Notion redirects to your callback URL. The connection is automatically established.
+Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants workspace access, Notion redirects to your `redirectUrl` and the first sync starts. A pending OAuth connector is not visible in list or get until the user finishes authorization.
### 3. Monitor sync progress
```typescript
- // Check connection details
- const connection = await client.connections.getByTags('notion', {
- containerTags: ['user-123']
- });
+ // Check connector details and recent sync runs
+ const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ include: "syncs",
+ })
- console.log('Connected workspace:', connection.email);
- console.log('Connection created:', connection.createdAt);
+ console.log("Connected workspace:", connector.account)
+ console.log("Last sync:", connector.latestRun?.system.status)
+ console.log("Documents synced:", connector.documentCount)
// List synced pages and databases
- const documents = await client.connections.listDocuments('notion', {
- containerTags: ['user-123']
- });
+ const { documents } = await supermemory.list("user-123", "documents")
```
```python
- # Check connection details
- connection = client.connections.get_by_tags(
- 'notion',
- container_tags=['user-123']
- )
+ # Check connector details and recent sync runs
+ connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])
- print(f'Connected workspace: {connection.email}')
- print(f'Connection created: {connection.created_at}')
+ print(f"Connected workspace: {connector.account}")
+ print(f"Last sync: {connector.latest_run.system.status}")
+ print(f"Documents synced: {connector.document_count}")
# List synced pages and databases
- documents = client.connections.list_documents(
- 'notion',
- container_tags=['user-123']
- )
+ documents = client.list("user-123", "documents").documents
```
```bash
- # 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"]}'
+ # Get connector details with recent sync runs
+ curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
- # Response includes connection details:
+ # Response includes connector details:
# {
- # "id": "conn_abc123",
+ # "id": "PTzGiUYei7pgzg5buzZHgA",
# "provider": "notion",
- # "email": "workspace@example.com",
- # "createdAt": "2024-01-15T10:00:00Z",
+ # "namespace": "user-123",
+ # "account": "workspace@example.com",
# "documentLimit": 2000,
- # "metadata": {...}
+ # "documentCount": 120,
+ # "latestRun": { "system": { "status": "completed", ... }, "error": null },
+ # "system": { "status": "active", "createdAt": "2024-01-15T10:00:00Z", "lastSuccessfulSyncAt": "2024-01-15T10:30:00Z" },
+ # "syncs": [...]
# }
- # List synced documents
- curl -X POST "https://api.supermemory.ai/v3/connections/notion/documents" \
+ # List synced documents in the namespace
+ curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{"containerTags": ["user-123"]}'
+ -d '{}'
- # Response: Array of document objects with sync status
- # [
- # {"title": "Product Roadmap", "type": "notion_database", "status": "done"},
- # {"title": "Meeting Notes", "type": "notion_page", "status": "done"}
- # ]
+ # Response: {"documents": [{"title": "Product Roadmap", "system": {"status": "done", ...}, ...}], "pagination": {...}}
```
+
+There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.
+
+
## Document limit
-Each connection has an optional `documentLimit` between 1 and 10,000. For Notion, each sync asks the Notion Search API for pages shared with the integration, newest edits first, and **stops after that many pages** or when Search runs out of results.
+Each connector has a **`documentLimit`** (optional when creating the connector; allowed range **1–10,000**). For Notion, each sync run asks the Notion Search API for **pages** shared with the integration, ordered by **last edited time, newest first**, and **stops after that many pages** (or when Search has no more results).
- **Full sync:** If your workspace has more shareable pages than `documentLimit`, the rest are **not** included in that run. Pages that look “missing” are often older or less recently edited relative to that ordering. Increase `documentLimit` or trigger another sync after pages change if you need broader coverage.
- **Incremental sync:** Only pages edited **after** the previous sync are candidates; each one still counts toward the same `documentLimit`. If more pages changed than the limit since last sync, only the first batch in that newest-first order is returned for that run.
Nested and child pages still count as normal pages in Search if the integration can access them—they are not skipped *because* they are nested. The limit applies to **how many pages** are fetched per sync, not to depth.
+Change the limit later with `supermemory.connectors.update(namespace, id, { documentLimit })`.
+
+## Manual Sync
+
+
+
+ ```typescript
+ const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
+
+ console.log(run.status)
+ // Output: queued
+ ```
+
+
+ ```bash
+ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+ # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
+ # 409 if a sync is already running
+ ```
+
+
+
## Supported content types
### Notion pages
@@ -189,186 +201,56 @@ Nested and child pages still count as normal pages in Search if the integration
| **Image** | Referenced with metadata | `` |
| **Embed** | Link with context | `[Embedded Content](url)` |
-## Delete connection
+## Delete Connector
-Remove a Notion connection when no longer needed:
+Remove a Notion connector when no longer needed:
```typescript
- // Delete by connection ID
- const result = await client.connections.delete('connection_id_123');
- console.log('Deleted connection:', result.id);
+ // Delete the connector and its imported documents (default)
+ await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
- // Delete by provider and container tags
- const providerResult = await client.connections.deleteByProvider('notion', {
- containerTags: ['user-123']
- });
- console.log('Deleted Notion connection for user');
+ // Delete the connector but keep the imported documents
+ await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ deleteDocuments: false,
+ })
```
```python
- # Delete by connection ID
- result = client.connections.delete('connection_id_123')
- print(f'Deleted connection: {result.id}')
+ # Delete the connector and its imported documents (default)
+ client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
- # Delete by provider and container tags
- provider_result = client.connections.delete_by_provider(
- 'notion',
- container_tags=['user-123']
- )
- print('Deleted Notion connection for user')
+ # Delete the connector but keep the imported documents
+ client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
```
```bash
- # Delete by connection ID
- curl -X DELETE "https://api.supermemory.ai/v3/connections/connection_id_123" \
+ # Delete the connector and its imported documents (default)
+ curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
- # Delete 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"]}'
+ # Delete the connector but keep the imported documents
+ curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?deleteDocuments=false" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
-Deleting a connection will:
+Deleting a connector will:
- Stop all future syncs from Notion
- Remove the OAuth authorization
-- Keep existing synced documents in Supermemory (they won't be deleted)
+- Delete the synced documents unless you pass `deleteDocuments: false`
## Advanced configuration
### Custom Notion integration
-For production deployments, create your own Notion integration:
-
-
-
- ```typescript
- // 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
- # 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
- # 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
- // 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
- # 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
- # 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"
- # }
- ```
-
-
+For production deployments you can connect with your own Notion integration. Custom OAuth credentials are an organization setting and are not part of the v5 connector routes. See [Custom OAuth Applications](/connectors/overview#custom-oauth-applications) for the setup steps and callback URL.
## Workspace permissions
@@ -389,36 +271,34 @@ Notion connector respects workspace permissions:
Notion database properties are mapped to metadata:
```typescript
-// Example: Project database with properties
-const documents = await client.connections.listDocuments('notion', {
- containerTags: ['user-123']
-});
+// List documents in the namespace (there is no per-connector document list in v5)
+const { documents } = await supermemory.list("user-123", "documents")
// Find database entries
const projectEntries = documents.filter(doc =>
- doc.metadata?.database === 'Projects'
-);
+ 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 }
- ]
- })
-});
+const projectWithStatus = await supermemory.search("user-123", {
+ query: "machine learning project",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "status", operator: "eq", value: "In Progress" },
+ { field: "priority", operator: "eq", value: "High" },
+ ],
+ },
+ searchMode: "chunks",
+})
```
### Optimization strategies
1. **Set `documentLimit` high enough** for your workspace size (see [Document limit](#document-limit))
-2. **Use targeted container tags** for efficient organization
+2. **Use one namespace per user or tenant** for efficient organization
3. **Monitor database sync performance** for large datasets
-4. **Implement content filtering** to sync only relevant pages
+4. **Share only the pages you need** with the integration so each sync stays small
5. **Handle webhook delays** gracefully in your application
diff --git a/apps/docs/connectors/onedrive.mdx b/apps/docs/connectors/onedrive.mdx
index ba0eae74..55b4d887 100644
--- a/apps/docs/connectors/onedrive.mdx
+++ b/apps/docs/connectors/onedrive.mdx
@@ -10,31 +10,23 @@ Connect OneDrive to automatically sync Word documents, Excel spreadsheets, and P
## Quick setup
-### 1. Create OneDrive connection
+### 1. Create OneDrive Connector
```typescript Typescript
-import Supermemory from 'supermemory';
+import { Supermemory } from "supermemory"
-const client = new Supermemory({
- apiKey: process.env.SUPERMEMORY_API_KEY!
-});
+const supermemory = 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',
+const connector = await supermemory.connectors.create("user-123", {
+ provider: "onedrive",
+ redirectUrl: "https://yourapp.com/auth/onedrive/callback"
documentLimit: 1500,
- metadata: {
- source: 'onedrive',
- accountType: 'business',
- department: 'marketing'
- }
-});
+})
-// Redirect user to Microsoft OAuth
-window.location.href = connection.authLink;
-// Output: Redirects to OAuth provider
+// Send the user to Microsoft to authorize
+if (connector.authorization) window.location.href = connector.authorization.url
// Output: Redirects to https://login.microsoftonline.com/oauth2/authorize?...
```
@@ -44,43 +36,36 @@ 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'
- }
+connector = client.connectors.create(
+ "user-123",
+ request={
+ "provider": "onedrive",
+ "redirectUrl": "https://yourapp.com/auth/onedrive/callback",
+ "documentLimit": 1500,
+ },
)
-# Redirect user to Microsoft OAuth
-print(f'Redirect to: {connection.auth_link}')
-# Output: Redirect to: https://oauth.provider.com/...
+# Send the user to Microsoft to authorize
+print(f"Redirect to: {connector.authorization.url}")
# Output: Redirect to: https://login.microsoftonline.com/oauth2/authorize?...
```
```bash cURL
-curl -X POST "https://api.supermemory.ai/v3/connections/onedrive" \
+curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
+ "provider": "onedrive",
"redirectUrl": "https://yourapp.com/auth/onedrive/callback",
- "containerTag": "user-123",
- "documentLimit": 1500,
- "metadata": {
- "source": "onedrive",
- "accountType": "business",
- "department": "marketing"
- }
+ "documentLimit": 1500
}'
# Response: {
-# "authLink": "https://login.microsoftonline.com/oauth2/authorize?...",
-# "expiresIn": "1 hour",
-# "id": "conn_od123"
+# "id": "PTzGiUYei7pgzg5buzZHgA",
+# "authorization": {
+# "url": "https://login.microsoftonline.com/oauth2/authorize?...",
+# "expiresAt": "2024-01-15T11:30:00.000Z"
+# }
# }
```
@@ -88,57 +73,52 @@ curl -X POST "https://api.supermemory.ai/v3/connections/onedrive" \
### 2. Handle Microsoft OAuth
-After user grants permissions, Microsoft redirects to your callback URL. The connection is automatically established and initial sync begins.
+Send the user to `authorization.url` before `authorization.expiresAt`. After the user grants permissions, Microsoft redirects to your `redirectUrl` and the initial sync begins. A pending OAuth connector is not visible in list or get until the user finishes authorization.
### 3. Monitor sync status
```typescript Typescript
- // Check connection details
- const connection = await client.connections.getByTags('onedrive', {
- containerTags: ['user-123']
- });
+ // Check connector details with recent sync runs
+ const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ include: "syncs",
+ })
+ console.log("Account:", connector.account)
+ console.log("Last sync:", connector.latestRun?.system.status)
+ console.log("Documents synced:", connector.documentCount)
- // List synced Office documents
- const documents = await client.connections.listDocuments('onedrive', {
- containerTags: ['user-123']
- });
+ // List synced Office documents in the namespace
+ const { documents } = await supermemory.list("user-123", "documents")
```
```python Python
- # Check connection details
- connection = client.connections.get_by_tags(
- 'onedrive',
- container_tags=['user-123']
- )
+ # Check connector details with recent sync runs
+ connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])
- # List synced Office documents
- documents = client.connections.list_documents(
- 'onedrive',
- container_tags=['user-123']
- )
+ print(f"Account: {connector.account}")
+ print(f"Last sync: {connector.latest_run.system.status}")
+ print(f"Documents synced: {connector.document_count}")
+
+ # List synced Office documents in the namespace
+ documents = client.list("user-123", "documents").documents
```
```bash cURL
- # 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"
- }'
+ # Get connector details with recent sync runs
+ curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
- # List synced Office documents
- curl -X POST "https://api.supermemory.ai/v3/documents/list" \
+ # List synced Office documents in the namespace
+ curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{
- "containerTags": ["user-123"],
- "source": "onedrive"
- }'
+ -d '{}'
```
+
+There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.
+
+
## Supported document types
### Microsoft Word documents
@@ -166,214 +146,84 @@ Webhooks lead to real-time syncing of changes in documents. You may also manuall
### Manual sync trigger
+The call returns `409` while a sync for that connector is already running.
+
```typescript Typescript
- // Trigger immediate sync for all OneDrive connections
- await client.connections.import('onedrive');
+ const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
- // 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');
+ console.log(run.status)
+ // Output: queued
```
```python Python
- # Trigger immediate sync for all OneDrive connections
- client.connections.import_('onedrive')
+ run = client.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
- # Trigger sync for specific user
- client.connections.import_(
- 'onedrive',
- container_tags=['user-123']
- )
-
- print('Manual sync initiated - documents will update within 5-10 minutes')
+ print(run.status)
+ # Output: queued
```
```bash cURL
- # 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"]}'
+ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+ # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```
-## Delete connection
+## Delete Connector
-Remove a OneDrive connection when no longer needed:
+Remove a OneDrive connector when no longer needed:
```typescript Typescript
-// Delete by connection ID
-const result = await client.connections.delete('connection_id_123');
-console.log('Deleted connection:', result.id);
+// Delete the connector and its imported documents (default)
+await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
-// Delete by provider and container tags
-const providerResult = await client.connections.deleteByProvider('onedrive', {
- containerTags: ['user-123']
-});
-console.log('Deleted OneDrive connection for user');
+// Delete the connector but keep the imported documents
+await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ deleteDocuments: false,
+})
```
```python Python
-# Delete by connection ID
-result = client.connections.delete('connection_id_123')
-print(f'Deleted connection: {result.id}')
+# Delete the connector and its imported documents (default)
+client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
-# Delete by provider and container tags
-provider_result = client.connections.delete_by_provider(
- 'onedrive',
- container_tags=['user-123']
-)
-print('Deleted OneDrive connection for user')
+# Delete the connector but keep the imported documents
+client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
```
```bash cURL
-# Delete by connection ID
-curl -X DELETE "https://api.supermemory.ai/v3/connections/connection_id_123" \
+# Delete the connector and its imported documents (default)
+curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
-# Delete 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"]}'
+# Delete the connector but keep the imported documents
+curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?deleteDocuments=false" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
-Deleting a connection will:
+Deleting a connector will:
- Stop all future syncs from OneDrive
- Remove the OAuth authorization
-- Keep existing synced documents in Supermemory (they won't be deleted)
+- Delete the synced documents unless you pass `deleteDocuments: false`
## Advanced configuration
### Custom Microsoft app
-For production deployments, configure your own Microsoft application:
-
-
- ```typescript Typescript
- // 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
- # 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
- # 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
- // 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
- # 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
- # 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": "..."
- # }
- ```
-
+For production deployments you can connect with your own Microsoft application. Custom OAuth credentials are an organization setting and are not part of the v5 connector routes. See [Custom OAuth Applications](/connectors/overview#custom-oauth-applications) for the setup steps and callback URL.
### Optimization tips
1. **Set realistic document limits** based on storage and usage
-2. **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
+2. **Monitor sync health** regularly with `latestRun` and `include: "syncs"`
+3. **Trigger manual syncs** when immediate updates are needed
+4. **Consider account type** when setting expectations
**OneDrive-Specific Benefits:**
diff --git a/apps/docs/connectors/overview.mdx b/apps/docs/connectors/overview.mdx
index 4320954f..a8b31e7e 100644
--- a/apps/docs/connectors/overview.mdx
+++ b/apps/docs/connectors/overview.mdx
@@ -7,6 +7,8 @@ icon: "/icons/hugeicons/layers-01.svg"
Connect external platforms to automatically sync documents into supermemory. Supported connectors include Google Drive, Gmail, Notion, OneDrive, GitHub, Granola and Web Crawler with real-time synchronization and intelligent content processing.
+Every connector belongs to one namespace. The namespace goes in the URL (`/ns/{namespace}/connectors`) and is a top-level `namespace` key in every SDK call.
+
## Supported connectors
@@ -56,29 +58,26 @@ Connect external platforms to automatically sync documents into supermemory. Sup
## Quick start
-### 1. Create connection
+### 1. Create a Connector
```typescript Typescript
-import Supermemory from 'supermemory';
+import { Supermemory } from "supermemory"
-const client = new Supermemory({
- apiKey: process.env.SUPERMEMORY_API_KEY!
-});
+const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
-const connection = await client.connections.create('notion', {
- redirectUrl: 'https://yourapp.com/callback',
- containerTag: 'user-123',
+const connector = await supermemory.connectors.create("user-123", {
+ provider: "notion",
+ redirectUrl: "https://yourapp.com/callback"
documentLimit: 5000,
- metadata: { department: 'sales' }
-});
+})
-// Redirect user to complete OAuth
-console.log('Auth URL:', connection.authLink);
-console.log('Expires in:', connection.expiresIn);
+// Send the user to authorization.url to finish OAuth
+console.log("Connector:", connector.id)
+console.log("Auth URL:", connector.authorization?.url)
+console.log("Expires at:", connector.authorization?.expiresAt)
// Output: Auth URL: https://api.notion.com/v1/oauth/authorize?...
-// Output: Expires in: 1 hour
```
```python Python
@@ -87,37 +86,38 @@ 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'}
+connector = client.connectors.create(
+ "user-123",
+ request={
+ "provider": "notion",
+ "redirectUrl": "https://yourapp.com/callback",
+ "documentLimit": 5000,
+ },
)
-# Redirect user to complete OAuth
-print(f'Auth URL: {connection.auth_link}')
-print(f'Expires in: {connection.expires_in}')
+# Send the user to authorization.url to finish OAuth
+print(f"Connector: {connector.id}")
+print(f"Auth URL: {connector.authorization.url}")
+print(f"Expires at: {connector.authorization.expires_at}")
# Output: Auth URL: https://api.notion.com/v1/oauth/authorize?...
-# Output: Expires in: 1 hour
```
```bash cURL
-curl -X POST "https://api.supermemory.ai/v3/connections/notion" \
+curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
+ "provider": "notion",
"redirectUrl": "https://yourapp.com/callback",
- "containerTag": "user-123",
- "documentLimit": 5000,
- "metadata": {"department": "sales"}
+ "documentLimit": 5000
}'
# Response: {
-# "authLink": "https://api.notion.com/v1/oauth/authorize?...",
-# "expiresIn": "1 hour",
-# "id": "conn_abc123",
-# "redirectsTo": "https://yourapp.com/callback"
+# "id": "PTzGiUYei7pgzg5buzZHgA",
+# "authorization": {
+# "url": "https://api.notion.com/v1/oauth/authorize?...",
+# "expiresAt": "2024-01-15T11:30:00.000Z"
+# }
# }
```
@@ -125,37 +125,34 @@ curl -X POST "https://api.supermemory.ai/v3/connections/notion" \
### 2. Handle OAuth callback
-After user completes OAuth, the connection is automatically established and sync begins.
+Send the user to `authorization.url` before `authorization.expiresAt`. When the user authorizes, the provider returns them to your `redirectUrl` and the first sync starts. A pending OAuth connector is not visible in list or get until the user finishes authorization.
+
+Config providers (Web Crawler, S3, Granola) return `authorization: null` and start syncing immediately.
### 3. Monitor sync status
```typescript Typescript
-import Supermemory from 'supermemory';
+import { Supermemory } from "supermemory"
-const client = new Supermemory({
- apiKey: process.env.SUPERMEMORY_API_KEY!
-});
+const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
-// List all connections using SDK
-const connections = await client.connections.list({
- containerTags: ['user-123']
-});
+// Read one connector with its recent sync runs
+const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ include: "syncs",
+})
-connections.forEach(conn => {
- console.log('Connection:', conn.id);
- console.log('Provider:', conn.provider);
- console.log('Email:', conn.email);
- console.log('Created:', conn.createdAt);
-});
+console.log("Provider:", connector.provider)
+console.log("Account:", connector.account)
+console.log("Last sync:", connector.latestRun?.system.status, connector.latestRun?.error)
+console.log("Last synced at:", connector.system.lastSuccessfulSyncAt)
+console.log("Documents:", connector.documentCount)
+console.log("Recent runs:", connector.syncs)
-// List synced documents (memories) using SDK
-const memories = await client.documents.list({
- containerTags: ['user-123']
-});
-
-console.log(`Synced ${memories.memories.length} documents`);
+// List the documents the connector synced into the namespace
+const docs = await supermemory.list("user-123", "documents")
+console.log(`Synced ${docs.pagination.totalItems} documents`)
// Output: Synced 45 documents
```
@@ -165,40 +162,46 @@ 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']
-)
+# Read one connector with its recent sync runs
+connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])
-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}')
+print(f"Provider: {connector.provider}")
+print(f"Account: {connector.account}")
+print(f"Last sync: {connector.latest_run.system.status} {connector.latest_run.error}")
+print(f"Last synced at: {connector.system.last_successful_sync_at}")
+print(f"Documents: {connector.document_count}")
+print(f"Recent runs: {connector.syncs}")
-# List synced documents (memories) using SDK
-memories = client.documents.list(container_tags=['user-123'])
-
-print(f'Synced {len(memories.memories)} documents')
+# List the documents the connector synced into the namespace
+docs = client.list("user-123", "documents")
+print(f"Synced {docs.pagination.total_items} documents")
# Output: Synced 45 documents
```
```bash cURL
-# List all connections
-curl -X POST "https://api.supermemory.ai/v3/connections/list" \
+# Read one connector with its recent sync runs
+curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+# Response: {
+# "id": "PTzGiUYei7pgzg5buzZHgA",
+# "provider": "notion",
+# "namespace": "user-123",
+# "account": "user@example.com",
+# "documentLimit": 5000,
+# "documentCount": 45,
+# "latestRun": { "errorCode": null, "error": null, "system": { "status": "completed", "startedAt": "...", "completedAt": "..." } },
+# "system": { "status": "active", "createdAt": "2024-01-15T10:30:00.000Z", "lastSuccessfulSyncAt": "2024-01-15T10:45:00.000Z" },
+# "syncs": [...]
+# }
+
+# List synced documents in the namespace
+curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{"containerTags": ["user-123"]}'
+ -d '{}'
-# 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}
+# Response: {"documents": [...], "pagination": {"totalItems": 45, ...}}
```
@@ -207,9 +210,9 @@ curl -X POST "https://api.supermemory.ai/v3/documents/list" \
### 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
+1. **Create Connector**: Call `POST /ns/{namespace}/connectors` with a `provider`. OAuth providers return an `authorization` link; config providers (Web Crawler, S3, Granola) take a `config` object and return `authUrl: null`
+2. **User Authorization**: Send the user to `authorization.url` when the provider requires it
+3. **Automatic Setup**: Connector 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
@@ -238,22 +241,28 @@ graph TD
| **Web Crawler** | ❌ Not supported | ✅ Scheduled recrawling (7+ days) | ✅ On-demand |
-## Connection management
+## Connector Management
-### List all connections
+### List Connectors
```typescript Typescript
-import Supermemory from 'supermemory';
+import { Supermemory } from "supermemory"
-const client = new Supermemory({
- apiKey: process.env.SUPERMEMORY_API_KEY!
-});
+const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
-const connections = await client.connections.list({
- containerTags: ['org-123']
-});
+// Connectors in one namespace
+const { connectors } = await supermemory.connectors.list("org-123")
+
+for (const connector of connectors) {
+ console.log(`${connector.provider}: ${connector.account} (${connector.id})`)
+ console.log(`Documents: ${connector.documentCount} of ${connector.documentLimit}`)
+ console.log(`Last sync: ${connector.latestRun?.system.status ?? "never"}`)
+}
+
+// Connectors across every namespace in the organization
+const all = await supermemory.connectors.listAll()
```
```python Python
@@ -262,43 +271,101 @@ import os
client = Supermemory(api_key=os.environ.get("SUPERMEMORY_API_KEY"))
-connections = client.connections.list(container_tags=['org-123'])
+# Connectors in one namespace
+connectors = client.connectors.list("org-123").connectors
-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
+for connector in connectors:
+ print(f"{connector.provider}: {connector.account} ({connector.id})")
+ print(f"Documents: {connector.document_count} of {connector.document_limit}")
+ print(f"Last sync: {connector.latest_run.system.status if connector.latest_run else 'never'}")
+
+# Connectors across every namespace in the organization
+all_connectors = client.connectors.list_all()
```
```bash cURL
-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"]}'
+# Connectors in one namespace
+curl "https://api.supermemory.ai/ns/org-123/connectors" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
-# Response: [
-# {
-# "id": "conn_abc123",
-# "provider": "notion",
-# "email": "user@company.com",
-# "documentLimit": 5000,
-# "createdAt": "2024-01-15T10:30:00.000Z"
-# }
-# ]
+# Response: {
+# "connectors": [
+# {
+# "id": "PTzGiUYei7pgzg5buzZHgA",
+# "provider": "notion",
+# "namespace": "org-123",
+# "account": "user@company.com",
+# "documentLimit": 5000,
+# "documentCount": 45,
+# "latestRun": { "system": { "status": "completed", ... }, "error": null },
+# "system": { "status": "active", "createdAt": "2024-01-15T10:30:00.000Z", "lastSuccessfulSyncAt": "2024-01-15T10:45:00.000Z" }
+# }
+# ],
+# "pagination": { "currentPage": 1, "limit": 50, "totalItems": 1, "totalPages": 1 }
+# }
+
+# Connectors across every namespace in the organization
+curl "https://api.supermemory.ai/connectors" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
-### Delete connections
+### Trigger a Sync
-The `DELETE /v3/connections/:connectionId` endpoint accepts an optional `deleteDocuments` query parameter:
+A connector syncs on its own schedule. Start one now with `connectors.sync`. The call returns `409` while a sync for that connector is already running.
+
+
+
+```typescript Typescript
+const run = await supermemory.connectors.sync("org-123", "PTzGiUYei7pgzg5buzZHgA")
+
+console.log(run.status)
+// Output: queued
+```
+
+```bash cURL
+curl -X POST "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+# Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
+```
+
+
+
+### Update a Connector
+
+Change the document limit, or the selection for providers that support one, with `connectors.update`. A new selection replaces the old one. See [Managing Connector Selection](/connectors/managing-resources).
+
+
+
+```typescript Typescript
+const connector = await supermemory.connectors.update("org-123", "PTzGiUYei7pgzg5buzZHgA", {
+ documentLimit: 8000,
+})
+
+console.log(connector.documentLimit)
+// Output: 8000
+```
+
+```bash cURL
+curl -X PATCH "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{"documentLimit": 8000}'
+
+# Response: the updated connector object
+```
+
+
+
+### Delete Connectors
+
+`DELETE /ns/{namespace}/connectors/{id}` accepts an optional `deleteDocuments` query parameter:
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
-| `deleteDocuments` | boolean | `true` | When `true`, all documents imported by the connection are permanently deleted. When `false`, the connection is removed but documents are kept. |
+| `deleteDocuments` | boolean | `true` | When `true`, all documents imported by the connector are permanently deleted. When `false`, the connector is removed but documents are kept. |
Setting `deleteDocuments=false` is useful when you want to disconnect an integration without losing the memories that were already imported.
@@ -307,27 +374,17 @@ Setting `deleteDocuments=false` is useful when you want to disconnect an integra
```typescript Typescript
-import Supermemory from 'supermemory';
+import { Supermemory } from "supermemory"
-const client = new Supermemory({
- apiKey: process.env.SUPERMEMORY_API_KEY!
-});
+const supermemory = 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);
+// Delete the connector and all imported documents (default)
+await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
+// Delete the connector but keep imported documents
+await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ deleteDocuments: false,
+})
```
```python Python
@@ -336,42 +393,32 @@ 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}")
+# Delete the connector and all imported documents (default)
+client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
+# Delete the connector but keep imported documents
+client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
```
```bash cURL
-# Delete connection and all imported documents (default)
-curl -X DELETE "https://api.supermemory.ai/v3/connections/conn_abc123" \
+# Delete the connector and all imported documents (default)
+curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
-# Delete connection but keep imported documents
-curl -X DELETE "https://api.supermemory.ai/v3/connections/conn_abc123?deleteDocuments=false" \
+# Delete the connector but keep imported documents
+curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?deleteDocuments=false" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
-
-# Response: {
-# "id": "conn_abc123",
-# "provider": "notion"
-# }
```
+
+There is no delete-by-provider route in v5. Find the connector with `connectors.list(namespace)` and delete it by `id`.
+
+
## Custom OAuth applications
-By default, Supermemory uses its own OAuth applications to connect to third-party providers. You can configure your own OAuth app credentials via `PATCH /v3/settings` for tighter control over data access — useful for enterprise customers.
+By default, Supermemory uses its own OAuth applications to connect to third-party providers. You can use your own OAuth app credentials for tighter control over data access, which is useful for enterprise customers. Custom OAuth credentials are an organization setting and are not part of the v5 connector routes.
1. Create the OAuth application on the provider's developer console:
- Google: [console.developers.google.com/apis/credentials/oauthclient](https://console.developers.google.com/apis/credentials/oauthclient)
@@ -381,5 +428,5 @@ By default, Supermemory uses its own OAuth applications to connect to third-part
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.
+Enabling custom keys for a provider applies to all new connectors for that provider. Existing connectors will need to be re-authorized.
diff --git a/apps/docs/connectors/s3.mdx b/apps/docs/connectors/s3.mdx
index 06032a49..79dc8bcf 100644
--- a/apps/docs/connectors/s3.mdx
+++ b/apps/docs/connectors/s3.mdx
@@ -8,7 +8,7 @@ icon: "/icons/hugeicons/database-01.svg"
Connect Amazon S3 buckets or S3-compatible storage services (MinIO, DigitalOcean Spaces, Cloudflare R2, Tigris) to sync files into your Supermemory knowledge base.
-The S3 connector requires a **Scale Plan** or higher. You can also create S3 connections directly from the [Supermemory Console](https://console.supermemory.ai).
+The S3 connector requires a **Scale Plan** or higher. You can also create S3 connectors directly from the [Supermemory Console](https://console.supermemory.ai).
## Quick setup
@@ -16,21 +16,22 @@ The S3 connector requires a **Scale Plan** or higher. You can also create S3 con
```typescript
- import Supermemory from 'supermemory';
+ import { Supermemory } from "supermemory"
- const client = new Supermemory({
- apiKey: process.env.SUPERMEMORY_API_KEY!
- });
+ const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
- const connection = await client.connections.create('s3', {
- metadata: {
+ const connector = await supermemory.connectors.create("org-123", {
+ provider: "s3",
+ config: {
+ bucket: "my-documents-bucket",
+ region: "us-east-1",
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
- bucket: 'my-documents-bucket',
- region: 'us-east-1'
},
- containerTag: 'org-123'
- });
+ })
+
+ // S3 doesn't require OAuth; authorization is null and the first sync starts now
+ console.log("Connector ID:", connector.id)
```
@@ -40,78 +41,83 @@ The S3 connector requires a **Scale Plan** or higher. You can also create S3 con
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'
+ connector = client.connectors.create(
+ "org-123",
+ request={
+ "provider": "s3",
+ "config": {
+ "bucket": "my-documents-bucket",
+ "region": "us-east-1",
+ "accessKeyId": os.environ["AWS_ACCESS_KEY_ID"],
+ "secretAccessKey": os.environ["AWS_SECRET_ACCESS_KEY"],
+ },
},
- container_tag='org-123'
)
+
+ # S3 doesn't require OAuth; authorization is None and the first sync starts now
+ print(f"Connector ID: {connector.id}")
```
```bash
- curl -X POST "https://api.supermemory.ai/v3/connections/s3" \
+ curl -X POST "https://api.supermemory.ai/ns/org-123/connectors" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
- "metadata": {
- "accessKeyId": "AKIAIOSFODNN7EXAMPLE",
- "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
+ "provider": "s3",
+ "config": {
"bucket": "my-documents-bucket",
- "region": "us-east-1"
- },
- "containerTag": "org-123"
+ "region": "us-east-1",
+ "accessKeyId": "AKIAIOSFODNN7EXAMPLE",
+ "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
+ }
}'
+
+ # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "authorization": null}
```
## Configuration options
-For S3, provider-specific connection fields are passed inside the top-level `metadata` object. General connection options stay top-level.
+For S3, provider-specific fields go inside the `config` object. The namespace is in the URL, and `documentLimit` stays top-level in the body.
| Parameter | Location | Required | Description |
|-----------|----------|----------|-------------|
-| `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 |
+| `bucket` | `config.bucket` | Yes | S3 bucket name |
+| `region` | `config.region` | Yes | AWS region (e.g., `us-east-1`). Use `auto` for S3-compatible providers that don't expose AWS-style regions (MinIO, R2, Tigris). |
+| `accessKeyId` | `config.accessKeyId` | Yes | AWS access key ID or S3-compatible service key |
+| `secretAccessKey` | `config.secretAccessKey` | Yes | AWS secret access key |
+| `endpoint` | `config.endpoint` | No | Custom endpoint for S3-compatible services |
+| `prefix` | `config.prefix` | No | Key prefix filter (e.g., `documents/`) |
| `documentLimit` | top-level | No | Maximum documents to sync (default: 10,000) |
-In the Python SDK, use `container_tags` for the top-level option, but keep S3 metadata keys in camelCase: `accessKeyId`, `secretAccessKey`, and `containerTagRegex`.
+Credentials are stored encrypted and are never returned by `connectors.get` or `connectors.list`. The `config` in responses contains only non-secret fields such as `bucket`, `region`, `endpoint` and `prefix`.
## S3-compatible services
-Use `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.
+Use `config.endpoint` to connect to S3-compatible storage. These services don't use AWS-style regions, so set `config.region` to `auto`. The value is still required for request signing but the service ignores it.
```typescript
// MinIO
-const connection = await client.connections.create('s3', {
- metadata: {
- accessKeyId: 'minio-key',
- secretAccessKey: 'minio-secret',
- bucket: 'my-bucket',
- region: 'auto',
- endpoint: 'https://minio.example.com'
+const connector = await supermemory.connectors.create("minio-sync", {
+ provider: "s3",
+ config: {
+ bucket: "my-bucket",
+ region: "auto",
+ accessKeyId: "minio-key",
+ secretAccessKey: "minio-secret",
+ endpoint: "https://minio.example.com",
},
- containerTag: 'minio-sync'
-});
+})
```
Common S3-compatible endpoint values:
-| Service | `metadata.endpoint` | `metadata.region` |
-|---------|----------------------|-------------------|
+| Service | `config.endpoint` | `config.region` |
+|---------|-------------------|-----------------|
| DigitalOcean Spaces | `https://nyc3.digitaloceanspaces.com` | `nyc3` |
| Cloudflare R2 | `https://.r2.cloudflarestorage.com` | `auto` |
| Tigris | `https://t3.storage.dev` | `auto` |
@@ -119,20 +125,20 @@ Common S3-compatible endpoint values:
Cloudflare R2 example:
```typescript
-const connection = await client.connections.create('s3', {
- metadata: {
+const connector = await supermemory.connectors.create("r2-sync", {
+ provider: "s3",
+ config: {
+ bucket: "my-bucket",
+ region: "auto",
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
- bucket: 'my-bucket',
- region: 'auto',
- endpoint: 'https://.r2.cloudflarestorage.com'
+ 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`.
+For S3-compatible services, `config.endpoint` is the base S3 endpoint. Do not include the bucket name in the endpoint URL; pass the bucket separately as `config.bucket`.
## Prefix filtering
@@ -140,80 +146,95 @@ For S3-compatible services, `metadata.endpoint` is the base S3 endpoint. Do not
Sync only files within a specific path:
```typescript
-const connection = await client.connections.create('s3', {
- metadata: {
+const connector = await supermemory.connectors.create("engineering-docs", {
+ provider: "s3",
+ config: {
+ bucket: "company-data",
+ region: "us-east-1",
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
- bucket: 'company-data',
- region: 'us-east-1',
- prefix: 'documents/engineering/' // Only syncs files under this path
+ prefix: "documents/engineering/", // Only syncs files under this path
},
- containerTag: 'engineering-docs'
-});
+})
```
-## Dynamic container tags
+## Multi-Tenant Buckets
-Extract container tags from S3 key paths for multi-tenant setups:
+A connector syncs into exactly one namespace. For a bucket shared by many tenants, create one connector per tenant namespace and point each at that tenant's path with `config.prefix`:
```typescript
-const connection = await client.connections.create('s3', {
- metadata: {
+await supermemory.connectors.create("user-123", {
+ provider: "s3",
+ config: {
+ bucket: "user-files",
+ region: "us-east-1",
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
- bucket: 'user-files',
- region: 'us-east-1',
- containerTagRegex: 'users/(?[^/]+)/'
+ prefix: "users/user-123/",
},
- 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.
-
+## Connector Management
-## Connection management
-
-### Delete connection
+### Check Sync Status
```typescript
- await client.connections.deleteByID('conn_s3_abc123');
+ const connector = await supermemory.connectors.get("org-123", "PTzGiUYei7pgzg5buzZHgA", {
+ include: "syncs",
+ })
+
+ console.log("Bucket:", connector.config?.bucket)
+ console.log("Last sync:", connector.latestRun?.system.status, connector.latestRun?.error)
+ console.log("Files synced:", connector.documentCount)
```
```bash
- curl -X DELETE "https://api.supermemory.ai/v3/connections/conn_s3_abc123" \
+ curl "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+ ```
+
+
+
+### Delete Connector
+
+
+
+ ```typescript
+ await supermemory.connectors.delete("org-123", "PTzGiUYei7pgzg5buzZHgA")
+ ```
+
+
+ ```bash
+ curl -X DELETE "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
-By default, deleting a connection removes all synced documents from Supermemory. To keep documents, pass `deleteDocuments=false` as a query parameter: `DELETE /v3/connections/:id?deleteDocuments=false`
+By default, deleting a connector removes all synced documents from Supermemory. To keep documents, pass `deleteDocuments: false` (`DELETE /ns/{namespace}/connectors/{id}?deleteDocuments=false`).
### Manual sync
+The call returns `409` while a sync for that connector is already running.
+
```typescript
- await client.connections.import('s3', {
- containerTags: ['org-123']
- });
+ await supermemory.connectors.sync("org-123", "PTzGiUYei7pgzg5buzZHgA")
```
```bash
- 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"]}'
+ curl -X POST "https://api.supermemory.ai/ns/org-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+ # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```
@@ -225,7 +246,7 @@ By default, deleting a connection removes all synced documents from Supermemory.
| **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) |
+| **Document limit** | 10,000 files per connector (default) |
## IAM permissions
diff --git a/apps/docs/connectors/troubleshooting.mdx b/apps/docs/connectors/troubleshooting.mdx
index a6d81378..146e26e2 100644
--- a/apps/docs/connectors/troubleshooting.mdx
+++ b/apps/docs/connectors/troubleshooting.mdx
@@ -14,60 +14,81 @@ Check if your connectors are working properly:
```typescript TypeScript
-const connections = await client.connections.list({
- containerTags: ['user-123']
-});
+const { connectors } = await supermemory.connectors.list("user-123")
-connections.forEach(conn => {
- console.log(`${conn.provider}: ${conn.email} - Connected ${conn.createdAt}`);
-});
+connectors.forEach(connector => {
+ console.log(`${connector.provider}: ${connector.account} - last sync ${connector.latestRun?.system.status ?? "never"}`)
+})
-// Check for stuck documents
-const documents = await client.connections.listDocuments('notion', {
- containerTags: ['user-123']
-});
+// Inspect a failing connector's recent runs and the items that failed
+const failing = connectors.filter(connector => connector.latestRun?.system.status === "failed")
+for (const connector of failing) {
+ const detail = await supermemory.connectors.get("user-123", connector.id, {
+ include: "syncs",
+ })
+ console.log(`⚠️ ${connector.provider}: ${detail.latestRun?.error}`)
+ console.log(detail.syncs)
+}
-const failed = documents.filter(doc => doc.status === 'failed');
-if (failed.length > 0) {
- console.log(`⚠️ ${failed.length} documents failed to sync`);
+// Check for stuck documents in the namespace
+const { documents } = await supermemory.list("user-123", "documents")
+const stuck = documents.filter(doc => doc.system.status === "failed")
+if (stuck.length > 0) {
+ console.log(`⚠️ ${stuck.length} documents failed to process`)
}
```
```python Python
-connections = client.connections.list(container_tags=['user-123'])
+connectors = client.connectors.list("user-123").connectors
-for conn in connections:
- print(f"{conn.provider}: {conn.email} - Connected {conn.created_at}")
+for connector in connectors:
+ status = connector.latest_run.system.status if connector.latest_run else "never"
+ print(f"{connector.provider}: {connector.account} - last sync {status}")
-# Check for stuck documents
-documents = client.connections.list_documents(
- 'notion',
- container_tags=['user-123']
-)
+# Inspect a failing connector's recent runs and the items that failed
+failing = [c for c in connectors if c.latest_run and c.latest_run.system.status == "failed"]
+for connector in failing:
+ detail = client.connectors.get("user-123", connector.id, include=["syncs"])
+ print(f"⚠️ {connector.provider}: {detail.latest_run.error}")
+ print(detail.syncs)
-failed = [doc for doc in documents if doc.status == 'failed']
-if failed:
- print(f"⚠️ {len(failed)} documents failed to sync")
+# Check for stuck documents in the namespace
+documents = client.list("user-123", "documents").documents
+stuck = [doc for doc in documents if doc.system.status == "failed"]
+if stuck:
+ print(f"⚠️ {len(stuck)} documents failed to process")
```
```bash cURL
-# List 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"]}'
+# List connectors in the namespace
+curl "https://api.supermemory.ai/ns/user-123/connectors" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
-# Check document status
-curl -X POST "https://api.supermemory.ai/v3/documents/list" \
+# Read one connector with its recent sync runs
+curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+# Check document status in the namespace
+curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{"containerTags": ["user-123"], "source": "notion"}'
+ -d '{"filter": {"field": "source", "operator": "eq", "value": "notion"}}'
```
+
+There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace; read each document's `system.status`.
+
+
## Common issues
+### Connector Missing From List
+
+**Problem:** `connectors.create` returned an `id`, but `connectors.list` and `connectors.get` do not show it
+
+**Solution:** A pending OAuth connector is not visible in list or get until the user finishes authorization. Send the user to `authorization.url` before `authorization.expiresAt`. If the link expired, create the connector again.
+
### OAuth callback fails
**Problem:** "Invalid redirect URI" error after user grants permissions
@@ -76,13 +97,13 @@ curl -X POST "https://api.supermemory.ai/v3/documents/list" \
```typescript
// correct - exact match with OAuth app settings
-const connection = await client.connections.create('notion', {
- redirectUrl: 'https://yourapp.com/auth/notion/callback',
- containerTag: 'user-123'
-});
+const connector = await supermemory.connectors.create("user-123", {
+ provider: "notion",
+ redirectUrl: "https://yourapp.com/auth/notion/callback"
+})
// Wrong - URL doesn't match
-// redirectUrl: 'https://yourapp.com/callback'
+// redirectUrl: "https://yourapp.com/callback"
```
**Prevention:**
@@ -97,13 +118,13 @@ const connection = await client.connections.create('notion', {
**Solution:** Trigger a manual sync:
```typescript
-// Force sync for stuck documents
-await client.connections.import('notion', {
- containerTags: ['user-123']
-});
+// Force a sync for the connector
+await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
+// 409 means a sync is already running; wait for latestRun.system.status to change
```
If documents consistently fail:
+- Read `latestRun.error` and `include: "syncs"` for the failed items
- Check if files are over 50MB (may timeout)
- Verify you have permission to access the documents
- Ensure the document type is supported
@@ -115,18 +136,18 @@ If documents consistently fail:
**Solution:** Re-authenticate with proper permissions:
```typescript
-// Delete and recreate connection
-await client.connections.deleteByProvider('google-drive', {
- containerTags: ['user-123']
-});
+// Delete and recreate the connector
+await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ deleteDocuments: false,
+})
-const newConnection = await client.connections.create('google-drive', {
- redirectUrl: 'https://yourapp.com/callback',
- containerTag: 'user-123'
-});
+const next = await supermemory.connectors.create("user-123", {
+ provider: "google-drive",
+ redirectUrl: "https://yourapp.com/callback"
+})
// User must re-authenticate
-window.location.href = newConnection.authLink;
+if (next.authorization) window.location.href = next.authorization.url
```
### Sync takes too long
@@ -136,11 +157,16 @@ window.location.href = newConnection.authLink;
**Solution:** Set reasonable document limits:
```typescript
-const connection = await client.connections.create('onedrive', {
- redirectUrl: 'https://yourapp.com/callback',
- containerTag: 'user-123',
- documentLimit: 500 // Start with fewer documents
-});
+const connector = await supermemory.connectors.create("user-123", {
+ provider: "onedrive",
+ redirectUrl: "https://yourapp.com/callback"
+ documentLimit: 500, // Start with fewer documents
+})
+
+// Raise it later without reconnecting
+await supermemory.connectors.update("user-123", connector.id, {
+ documentLimit: 2000,
+})
```
## Provider-specific issues
@@ -154,6 +180,10 @@ Shared drives require special permissions. Make sure:
- OAuth app has drive.readonly scope
- User is a member of the shared drive
+**Picker Not Completed**
+
+With the default scoped sync, syncs may skip the connector until the user finishes the file picker. Read the connector with `include: "picker"` and send the user to `picker.url`.
+
### Notion
**Database Not Syncing**
@@ -191,12 +221,10 @@ 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:
+4. Trigger a manual sync to verify the connector is healthy:
```typescript
-await client.connections.import('gmail', {
- containerTags: ['user-123']
-});
+await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
```
**Missing Refresh Token**
@@ -204,19 +232,19 @@ await client.connections.import('gmail', {
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
+2. Delete and recreate the connector
3. Ensure user completes full OAuth consent flow
```typescript
// Re-authenticate to get fresh tokens
-await client.connections.deleteByProvider('gmail', {
- containerTags: ['user-123']
-});
+await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ deleteDocuments: false,
+})
-const newConnection = await client.connections.create('gmail', {
- redirectUrl: 'https://yourapp.com/callback',
- containerTag: 'user-123'
-});
+const next = await supermemory.connectors.create("user-123", {
+ provider: "gmail",
+ redirectUrl: "https://yourapp.com/callback"
+})
```
**Scale Plan Required Error**
@@ -229,7 +257,7 @@ Gmail connector requires Scale Plan or Enterprise Plan. If you see access errors
## 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
+2. **Use one namespace per user or tenant** - Makes debugging easier
+3. **Monitor `latestRun`** - Check weekly for failed syncs
4. **Handle rate limits gracefully** - Implement exponential backoff
5. **Test OAuth in development** - Ensure redirect URLs work before production
diff --git a/apps/docs/connectors/web-crawler.mdx b/apps/docs/connectors/web-crawler.mdx
index 3917c429..f64838dc 100644
--- a/apps/docs/connectors/web-crawler.mdx
+++ b/apps/docs/connectors/web-crawler.mdx
@@ -13,30 +13,27 @@ The web crawler connector requires a **Scale Plan** or **Enterprise Plan**.
## Quick setup
-### 1. Create Web Crawler connection
+### 1. Create Web Crawler Connector
```typescript
- import Supermemory from 'supermemory';
+ import { Supermemory } from "supermemory"
- const client = new Supermemory({
- apiKey: process.env.SUPERMEMORY_API_KEY!
- });
+ const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
- const connection = await client.connections.create('web-crawler', {
- redirectUrl: 'https://yourapp.com/callback',
- containerTag: 'user-123',
+ const connector = await supermemory.connectors.create("user-123", {
+ provider: "web-crawler",
+ config: {
+ startUrl: "https://docs.example.com",
+ crawlDepth: 3,
+ },
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
+ // Web crawler doesn't require OAuth; crawling starts immediately
+ console.log("Connector ID:", connector.id)
+ // Note: connector.authorization is null for web-crawler
```
@@ -46,123 +43,125 @@ The web crawler connector requires a **Scale Plan** or **Enterprise Plan**.
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'
- }
+ connector = client.connectors.create(
+ "user-123",
+ request={
+ "provider": "web-crawler",
+ "config": {
+ "startUrl": "https://docs.example.com",
+ "crawlDepth": 3,
+ },
+ "documentLimit": 5000,
+ },
)
- # 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
+ # Web crawler doesn't require OAuth; crawling starts immediately
+ print(f"Connector ID: {connector.id}")
+ # Note: connector.authorization.url is None for web-crawler
```
```bash
- curl -X POST "https://api.supermemory.ai/v3/connections/web-crawler" \
+ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors" \
-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"
- }
+ "provider": "web-crawler",
+ "config": {
+ "startUrl": "https://docs.example.com",
+ "crawlDepth": 3
+ },
+ "documentLimit": 5000
}'
# Response: {
- # "id": "conn_wc123",
- # "redirectsTo": "https://yourapp.com/callback",
- # "authLink": null,
- # "expiresIn": null
+ # "id": "PTzGiUYei7pgzg5buzZHgA",
+ # "authorization": null
# }
```
-### 2. Connection established
+### Configuration options
-Unlike other connectors, the web crawler doesn't require OAuth authentication. The connection is established immediately upon creation, and crawling begins automatically.
+| Parameter | Location | Required | Description |
+|-----------|----------|----------|-------------|
+| `startUrl` | `config.startUrl` | Yes | Public URL where the crawl starts |
+| `crawlDepth` | `config.crawlDepth` | No | How many links deep to follow (1 to 5, default 3) |
+| `documentLimit` | top-level | No | Maximum pages to sync (1 to 10,000) |
+
+### 2. Connector Established
+
+Unlike OAuth connectors, the web crawler doesn't require user authorization. `authorization` is `null`, the connector is visible immediately, and crawling begins automatically.
### 3. Monitor sync progress
```typescript
- // Check connection details
- const connection = await client.connections.getByTags('web-crawler', {
- containerTags: ['user-123']
- });
+ // Check connector details with recent sync runs
+ const connector = await supermemory.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ include: "syncs",
+ })
- console.log('Start URL:', connection.metadata?.startUrl);
- console.log('Connection created:', connection.createdAt);
+ console.log("Start URL:", connector.config?.startUrl)
+ console.log("Last sync:", connector.latestRun?.system.status)
+ console.log("Pages synced:", connector.documentCount)
- // List synced web pages
- const documents = await client.connections.listDocuments('web-crawler', {
- containerTags: ['user-123']
- });
+ // List synced web pages in the namespace
+ const docs = await supermemory.list("user-123", "documents")
- console.log(`Synced ${documents.length} web pages`);
+ console.log(`Synced ${docs.pagination.totalItems} documents`)
```
```python
- # Check connection details
- connection = client.connections.get_by_tags(
- 'web-crawler',
- container_tags=['user-123']
- )
+ # Check connector details with recent sync runs
+ connector = client.connectors.get("user-123", "PTzGiUYei7pgzg5buzZHgA", include=["syncs"])
- print(f'Start URL: {connection.metadata.get("startUrl")}')
- print(f'Connection created: {connection.created_at}')
+ print(f"Start URL: {(connector.config or {}).get('startUrl')}")
+ print(f"Last sync: {connector.latest_run.system.status}")
+ print(f"Pages synced: {connector.document_count}")
- # List synced web pages
- documents = client.connections.list_documents(
- 'web-crawler',
- container_tags=['user-123']
- )
+ # List synced web pages in the namespace
+ docs = client.list("user-123", "documents")
- print(f'Synced {len(documents)} web pages')
+ print(f"Synced {docs.pagination.total_items} documents")
```
```bash
- # 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"]}'
+ # Get connector details with recent sync runs
+ curl "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?include=syncs" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
- # Response includes connection details:
+ # Response includes connector details:
# {
- # "id": "conn_wc123",
+ # "id": "PTzGiUYei7pgzg5buzZHgA",
# "provider": "web-crawler",
- # "createdAt": "2024-01-15T10:00:00Z",
+ # "namespace": "user-123",
+ # "config": {"startUrl": "https://docs.example.com", "crawlDepth": 3},
# "documentLimit": 5000,
- # "metadata": {"startUrl": "https://docs.example.com", ...}
+ # "documentCount": 240,
+ # "latestRun": { "system": { "status": "completed", ... }, "error": null },
+ # "system": { "status": "active", "createdAt": "2024-01-15T10:00:00Z", "lastSuccessfulSyncAt": "..." }
# }
- # List synced documents
- curl -X POST "https://api.supermemory.ai/v3/connections/web-crawler/documents" \
+ # List synced documents in the namespace
+ curl -X POST "https://api.supermemory.ai/ns/user-123/list/documents" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{"containerTags": ["user-123"]}'
+ -d '{}'
- # 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"}
- # ]
+ # Response: {"documents": [{"title": "Getting Started", "system": {"status": "done", ...}, ...}], "pagination": {...}}
```
+
+There is no per-connector document list in v5. `supermemory.list(namespace, "documents")` returns every document in the namespace.
+
+
## Supported content types
### Web pages
@@ -182,190 +181,137 @@ The web crawler only processes valid public URLs:
The web crawler uses **scheduled recrawling** rather than real-time webhooks:
-- **Initial Crawl**: Begins immediately after connection creation
+- **Initial Crawl**: Begins immediately after connector creation
- **Scheduled Recrawling**: Automatically recrawls sites that haven't been synced in 7+ days
- **No Real-time Updates**: Unlike other connectors, web crawler doesn't support webhook-based real-time sync
-The recrawl schedule is automatically assigned when the connection is created. Sites are recrawled periodically to keep content up to date, but updates are not instantaneous.
+The recrawl schedule is automatically assigned when the connector is created. Sites are recrawled periodically to keep content up to date, but updates are not instantaneous.
-## Connection management
+### Manual Recrawl
-### List all connections
+Start a crawl now. The call returns `409` while a sync for that connector is already running.
```typescript
- // List all web crawler connections
- const connections = await client.connections.list({
- containerTags: ['user-123']
- });
+ const run = await supermemory.connectors.sync("user-123", "PTzGiUYei7pgzg5buzZHgA")
- 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
- # 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}')
+ console.log(run.status)
+ // Output: queued
```
```bash
- # 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"]}'
+ curl -X POST "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA/sync" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
- # Response: [
- # {
- # "id": "conn_wc123",
- # "provider": "web-crawler",
- # "createdAt": "2024-01-15T10:30:00.000Z",
- # "documentLimit": 5000,
- # "metadata": {"startUrl": "https://docs.example.com", ...}
- # }
- # ]
+ # Response: {"id": "PTzGiUYei7pgzg5buzZHgA", "status": "queued"}
```
-### Delete connection
+## Connector Management
-Remove a web crawler connection when no longer needed:
+### List All Connectors
```typescript
- // Delete by connection ID
- const result = await client.connections.delete('connection_id_123');
- console.log('Deleted connection:', result.id);
+ // List all web crawler connectors in a namespace
+ const { connectors } = await supermemory.connectors.list("user-123")
- // 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');
+ const webCrawlerConnectors = connectors.filter(
+ connector => connector.provider === "web-crawler"
+ )
+
+ webCrawlerConnectors.forEach(connector => {
+ console.log(`Start URL: ${connector.config?.startUrl}`)
+ console.log(`Connector ID: ${connector.id}`)
+ console.log(`Created: ${connector.createdAt}`)
+ })
```
```python
- # Delete by connection ID
- result = client.connections.delete('connection_id_123')
- print(f'Deleted connection: {result.id}')
+ # List all web crawler connectors in a namespace
+ connectors = client.connectors.list("user-123", provider="web-crawler").connectors
- # 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')
+ for connector in connectors:
+ print(f"Start URL: {(connector.config or {}).get('startUrl')}")
+ print(f"Connector ID: {connector.id}")
+ print(f"Created: {connector.created_at}")
```
```bash
- # Delete by connection ID
- curl -X DELETE "https://api.supermemory.ai/v3/connections/connection_id_123" \
+ # List all connectors in a namespace
+ curl "https://api.supermemory.ai/ns/user-123/connectors" \
-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"]}'
+ # Response: {
+ # "connectors": [
+ # {
+ # "id": "PTzGiUYei7pgzg5buzZHgA",
+ # "provider": "web-crawler",
+ # "namespace": "user-123",
+ # "createdAt": "2024-01-15T10:30:00.000Z",
+ # "documentLimit": 5000,
+ # "config": {"startUrl": "https://docs.example.com", "crawlDepth": 3}
+ # }
+ # ],
+ # "pagination": { "currentPage": 1, "limit": 50, "totalItems": 1, "totalPages": 1 }
+ # }
+ ```
+
+
+
+### Delete Connector
+
+Remove a web crawler connector when no longer needed:
+
+
+
+ ```typescript
+ // Delete the connector and its crawled pages (default)
+ await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
+
+ // Delete the connector but keep the crawled pages
+ await supermemory.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", {
+ deleteDocuments: false,
+ })
+ ```
+
+
+ ```python
+ # Delete the connector and its crawled pages (default)
+ client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA")
+
+ # Delete the connector but keep the crawled pages
+ client.connectors.delete("user-123", "PTzGiUYei7pgzg5buzZHgA", delete_documents=False)
+ ```
+
+
+ ```bash
+ # Delete the connector and its crawled pages (default)
+ curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+ # Delete the connector but keep the crawled pages
+ curl -X DELETE "https://api.supermemory.ai/ns/user-123/connectors/PTzGiUYei7pgzg5buzZHgA?deleteDocuments=false" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
-Deleting a connection will:
-- Stop all future crawls from the website
-- Keep existing synced documents in Supermemory (they won't be deleted)
-- Remove the connection configuration
+Deleting a connector will:
+- Stop all future crawls of the website
+- Remove the connector configuration
+- Delete the crawled pages unless you pass `deleteDocuments: false`
-## Advanced configuration
-
-### Content filtering
-
-Control which web pages get synced using the settings API:
-
-
-
- ```typescript
- // 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
- # 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
- # 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
@@ -394,4 +340,3 @@ All URLs are validated before crawling:
- Robots.txt restrictions may prevent crawling some pages
- URLs must be publicly accessible (no authentication required)
-
diff --git a/apps/docs/docs.json b/apps/docs/docs.json
index 98756bad..4bb5024c 100644
--- a/apps/docs/docs.json
+++ b/apps/docs/docs.json
@@ -305,7 +305,10 @@
{
"group": "Migration guides",
"icon": "/icons/hugeicons/arrow-up-right-01.svg",
- "pages": ["migration/tools-v2-upgrade"]
+ "pages": [
+ "migration/tools-v3-upgrade",
+ "migration/tools-v2-upgrade"
+ ]
}
]
},
@@ -423,7 +426,7 @@
"authentication",
{
"group": "Ingest",
- "icon": "download",
+ "icon": "/icons/hugeicons/download-01.svg",
"pages": [
"v5/api-reference/ingest",
"POST /ns/{namespace}/document",
@@ -436,7 +439,7 @@
},
{
"group": "Content Management",
- "icon": "file-text",
+ "icon": "/icons/hugeicons/file-02.svg",
"pages": [
"v5/api-reference/content-management",
"GET /ns/{namespace}/document/{id}",
@@ -448,7 +451,7 @@
},
{
"group": "Search",
- "icon": "search",
+ "icon": "/icons/hugeicons/search-01.svg",
"pages": [
"v5/api-reference/search",
"POST /ns/{namespace}/search"
@@ -456,7 +459,7 @@
},
{
"group": "Profiles",
- "icon": "id-card",
+ "icon": "/icons/hugeicons/id.svg",
"pages": [
"v5/api-reference/profiles",
"POST /ns/{namespace}/profile",
@@ -467,7 +470,7 @@
},
{
"group": "Namespaces",
- "icon": "layers",
+ "icon": "/icons/hugeicons/layers-01.svg",
"pages": [
"v5/api-reference/namespaces",
"GET /namespaces",
@@ -476,9 +479,23 @@
"DELETE /ns/{namespace}"
]
},
+ {
+ "group": "Connectors",
+ "icon": "/icons/hugeicons/plug-01.svg",
+ "pages": [
+ "v5/api-reference/connectors",
+ "POST /ns/{namespace}/connectors",
+ "GET /ns/{namespace}/connectors",
+ "GET /ns/{namespace}/connectors/{id}",
+ "PATCH /ns/{namespace}/connectors/{id}",
+ "POST /ns/{namespace}/connectors/{id}/sync",
+ "DELETE /ns/{namespace}/connectors/{id}",
+ "GET /connectors"
+ ]
+ },
{
"group": "Organization",
- "icon": "building",
+ "icon": "/icons/hugeicons/building-03.svg",
"pages": [
"v5/api-reference/organization",
"GET /organization",
diff --git a/apps/docs/index.mdx b/apps/docs/index.mdx
index 2a61d4fb..b4f6e85c 100644
--- a/apps/docs/index.mdx
+++ b/apps/docs/index.mdx
@@ -105,7 +105,7 @@ export const ListCard = ({ title, children }) => (
Add memories
Connectors
SMFS
- Container tags
+ Namespaces
Customization
Migrate from mem0
diff --git a/apps/docs/ingestion/add-memories.mdx b/apps/docs/ingestion/add-memories.mdx
index fc0dce32..b2e01b72 100644
--- a/apps/docs/ingestion/add-memories.mdx
+++ b/apps/docs/ingestion/add-memories.mdx
@@ -5,28 +5,28 @@ description: "Add text, files, and URLs to Supermemory"
icon: "/icons/hugeicons/plus-sign.svg"
---
-Send any raw content to Supermemory — conversations, documents, files, URLs. We extract the memories automatically. Pass `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.
+Send any raw content to Supermemory — conversations, documents, files, URLs. We extract the memories automatically. Pass `id` to identify content and avoid duplicates, and `taskType: "superrag"` if you just need it searchable, not remembered — that's [5x cheaper](#memory-vs-superrag-ingestion) per token.
+
+Every write goes to one `namespace` (what v3/v4 called a container tag). The namespace sits in the URL path, never in the body.
## Quick start
```typescript
- import Supermemory from 'supermemory';
+ import { Supermemory } from "supermemory";
- const client = new Supermemory();
+ const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
// Add text content
- await client.add({
+ await supermemory.add("user_123", {
content: "Machine learning enables computers to learn from data",
- containerTag: "user_123",
- metadata: { category: "ai" }
+ metadata: { category: "ai" },
});
// Add a URL (auto-extracted)
- await client.add({
- content: "https://youtube.com/watch?v=dQw4w9WgXcQ",
- containerTag: "user_123"
+ await supermemory.add("user_123", {
+ content: "https://youtube.com/watch?v=dQw4w9WgXcQ"
});
```
@@ -38,26 +38,22 @@ Send any raw content to Supermemory — conversations, documents, files, URLs. W
# Add text content
client.add(
+ "user_123",
content="Machine learning enables computers to learn from data",
- container_tag="user_123",
- metadata={"category": "ai"}
+ metadata={"category": "ai"},
)
# Add a URL (auto-extracted)
- client.add(
- content="https://youtube.com/watch?v=dQw4w9WgXcQ",
- container_tag="user_123"
- )
+ client.add("user_123", content="https://youtube.com/watch?v=dQw4w9WgXcQ")
```
```bash
- curl -X POST "https://api.supermemory.ai/v3/documents" \
+ curl -X POST "https://api.supermemory.ai/ns/user_123/document" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "Machine learning enables computers to learn from data",
- "containerTag": "user_123",
"metadata": {"category": "ai"}
}'
```
@@ -77,34 +73,31 @@ If an irrecoverable processing error occurs, the document is automatically delet
## 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.
+Use `id` to update existing documents or conversations. When you send content with the same `id`, Supermemory intelligently processes only what's new.
### Two ways to update:
**Option 1: Send only the new content**
```typescript
// First request
-await client.add({
+await supermemory.add("user_sarah", {
content: "user: Hi, I'm Sarah.\nassistant: Nice to meet you!",
- customId: "conv_123",
- containerTag: "user_sarah"
+ id: "conv_123",
});
// Later: send only new messages
-await client.add({
+await supermemory.add("user_sarah", {
content: "user: What's the weather?\nassistant: It's sunny today.",
- customId: "conv_123", // Same ID — Supermemory links them
- containerTag: "user_sarah"
+ id: "conv_123", // Same ID — Supermemory links them
});
```
**Option 2: Send the full updated content**
```typescript
// Supermemory detects the diff and only processes new parts
-await client.add({
+await supermemory.add("user_sarah", {
content: "user: Hi, I'm Sarah.\nassistant: Nice to meet you!\nuser: What's the weather?\nassistant: It's sunny today.",
- customId: "conv_123",
- containerTag: "user_sarah"
+ id: "conv_123",
});
```
@@ -112,13 +105,13 @@ Both work — choose what fits your architecture.
### Replace entire document
-To completely replace a document's content (not append), use `memories.update()`:
+To completely replace a document's content (not append), use `documents.update()`:
```typescript
// Replace the entire document content
-await client.documents.update("doc_id_123", {
+await supermemory.documents.update("user_sarah", "doc_id_123", {
content: "Completely new content replacing everything",
- metadata: { version: 2 }
+ metadata: { version: 2 },
});
```
@@ -146,34 +139,37 @@ content: formatConversation(messages)
## Upload files
-Upload PDFs, images, and documents directly.
+Upload PDFs, images, and documents directly. This is a multipart request, so `metadata` is a JSON string here, not an object.
```typescript
- import fs from 'fs';
+ import { openAsBlob } from "node:fs";
- await client.documents.uploadFile({
- file: fs.createReadStream('document.pdf'),
- containerTag: 'user_123'
+ await supermemory.documents.uploadFile("user_123", {
+ file: await openAsBlob("document.pdf"),
+ metadata: JSON.stringify({ source: "upload" }),
});
```
```python
- with open('document.pdf', 'rb') as file:
+ import json
+
+ with open("document.pdf", "rb") as file:
client.documents.upload_file(
+ "user_123",
file=file,
- container_tag='user_123'
+ metadata=json.dumps({"source": "upload"}),
)
```
```bash
- curl -X POST "https://api.supermemory.ai/v3/documents/file" \
+ curl -X POST "https://api.supermemory.ai/ns/user_123/document/file" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-F "file=@document.pdf" \
- -F "containerTag=user_123"
+ -F 'metadata={"source":"upload"}'
```
@@ -197,14 +193,17 @@ Scale and Enterprise include [advanced document extraction for PDFs](/concepts/c
## Parameters
+`namespace`, `taskType`, and `dreaming` are top-level keys on the SDK call (`taskType` and `dreaming` are query parameters on the HTTP route). Everything else goes in `body`.
+
| Parameter | Type | Description |
|-----------|------|-------------|
+| `namespace` | string | **Required.** Group by user/project. Goes in the URL path. Required for user profiles |
| `content` | string | **Required.** Any raw content — text, conversations, URLs, HTML |
-| `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 |
+| `id` | string | **Recommended.** Your ID for the content (conversation ID, doc ID). Enables updates and deduplication. The response `id` echoes it back |
| `metadata` | object | Key-value pairs for filtering (strings, numbers, booleans) |
-| `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](/concepts/customization#entity-context) |
+| `group` | object | Filter which existing memories are used as context during ingestion. See [Filtered Writes](#filtered-writes) |
+| `supportingContext` | string | Context for memory extraction on this namespace. Max 1500 chars. See [Customization](/concepts/customization#entity-context) |
+| `date` | string | ISO 8601 date the content was originally created. Use when backfilling. See [Backfill historical data](/ingestion/batch-ingest-historical-data) |
| `dreaming` | `"dynamic" \| "instant"` | Processing mode. Default `"dynamic"`. `"instant"` processes each document on its own and bills one extra operation. See [Processing Modes](#processing-modes) |
| `taskType` | `"memory" \| "superrag"` | Pipeline to run. Default `"memory"`. `"superrag"` skips fact extraction and profile updates, doing only chunk/embed/index — at 5x cheaper per token. See [SuperRAG ingestion](/concepts/super-rag#ingesting-as-pure-superrag-tasktype-superrag) |
@@ -224,31 +223,31 @@ Scale and Enterprise include [advanced document extraction for PDFs](/concepts/c
{ content: "# Project Docs\n\n## Features\n- Real-time sync" }
```
- **Container Tags:**
+ **Namespaces:**
```typescript
// By user
- { containerTag: "user_123" }
+ { namespace: "user_123" }
// By project
- { containerTag: "project_alpha" }
+ { namespace: "project_alpha" }
// Hierarchical
- { containerTag: "org_456_team_backend" }
+ { namespace: "org_456_team_backend" }
```
- **Custom IDs (Recommended):**
+ **IDs (Recommended):**
```typescript
// 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
+ { id: "conv_abc123" } // Conversation ID
+ { id: "doc_456" } // Document ID
+ { id: "thread_789" } // Thread ID
+ { id: "meeting_2024_01_15" } // Meeting ID
- // Updates: same customId = same document
+ // Updates: same id = same document
// Supermemory only processes new/changed content
- await client.add({
+ await supermemory.add("user_123", {
content: "Updated content...",
- customId: "doc_456" // Links to existing document
+ id: "doc_456", // Links to existing document
});
```
@@ -266,18 +265,18 @@ Scale and Enterprise include [advanced document extraction for PDFs](/concepts/c
- No nested objects or arrays
- Values: string, number, or boolean only
- **Entity Context:**
+ **Supporting Context:**
```typescript
- // 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.`
- }
+ // Guide memory extraction for this namespace
+ await supermemory.add("session_abc123", {
+ content: "...",
+ supportingContext: `Design exploration conversation between john@acme.com and Brand.ai assistant.
+ Focus on John's design preferences and brand requirements.`,
+ });
```
- Max 1500 characters
- - Persists on the container tag
- - Combines with org-level filter prompts
+ - Persists on the namespace
+ - Combines with org-level organizational context
@@ -289,14 +288,14 @@ Scale and Enterprise include [advanced document extraction for PDFs](/concepts/c
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.
+- `"dynamic"` (default) — groups related documents together so memories form from coherent, logical units rather than one isolated entry at a time. A fresh namespace can show zero memories and an empty profile for several minutes.
+- `"instant"` — processes each document on its own right away, and bills one extra operation per document. Use it for quickstarts, tests, and any flow that reads memories or a profile right after `add`.
-```json
-{
- "content": "...",
- "dreaming": "instant"
-}
+```typescript
+await supermemory.add("user_123", {
+ content: "...",
+ dreaming: "instant",
+});
```
### Memory vs SuperRAG ingestion
@@ -306,11 +305,11 @@ The `taskType` parameter controls whether that content also feeds the memory pip
- `"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
-{
- "content": "...",
- "taskType": "superrag"
-}
+```typescript
+await supermemory.add("user_123", {
+ content: "...",
+ taskType: "superrag",
+});
```
Use `"superrag"` for reference material you want searchable but that shouldn't shape what Supermemory knows about a user. Full explanation: [SuperRAG → Ingesting as pure SuperRAG](/concepts/super-rag#ingesting-as-pure-superrag-tasktype-superrag).
@@ -319,9 +318,9 @@ Use `"superrag"` for reference material you want searchable but that shouldn't s
## 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.
+By default, when you add content, Supermemory uses **all** existing memories in the namespace as context for generating new memories. With **filtered writes**, you can scope this context to only memories from documents matching specific metadata.
-This is useful when you have many documents in a space but want new memories to build on top of a specific subset — for example, only memories from a particular source, category, or user.
+This is useful when you have many documents in a namespace but want new memories to build on top of a specific subset — for example, only memories from a particular source, category, or user.
The metadata itself is still written to the document, but the memories will only be built on top of what's already there matching the filter.
@@ -330,34 +329,32 @@ The metadata itself is still written to the document, but the memories will only
```typescript
- await client.add({
+ await supermemory.add("user_123", {
content: "New research findings on transformer architectures...",
- containerTag: "user_123",
metadata: { category: "ml", source: "arxiv" },
- filterByMetadata: { category: "ml" }
+ group: { category: "ml" },
});
```
```python
client.add(
+ "user_123",
content="New research findings on transformer architectures...",
- container_tag="user_123",
metadata={"category": "ml", "source": "arxiv"},
- filter_by_metadata={"category": "ml"}
+ group={"category": "ml"},
)
```
```bash
- curl -X POST "https://api.supermemory.ai/v3/documents" \
+ curl -X POST "https://api.supermemory.ai/ns/user_123/document" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "New research findings on transformer architectures...",
- "containerTag": "user_123",
"metadata": {"category": "ml", "source": "arxiv"},
- "filterByMetadata": {"category": "ml"}
+ "group": {"category": "ml"}
}'
```
@@ -365,16 +362,16 @@ The metadata itself is still written to the document, but the memories will only
### How it works
-When `filterByMetadata` is provided:
+When `group` 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
+### `group` parameter
| Key | Type | Description |
|-----|------|-------------|
-| `filterByMetadata` | `Record` | Key-value pairs to filter existing memories by their source document metadata |
+| `group` | `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)
@@ -382,13 +379,12 @@ When `filterByMetadata` is provided:
```typescript
// Match documents where category is "ml" AND source is either "arxiv" or "pubmed"
-await client.add({
+await supermemory.add("user_123", {
content: "...",
- containerTag: "user_123",
- filterByMetadata: {
+ group: {
category: "ml",
- source: ["arxiv", "pubmed"]
- }
+ source: ["arxiv", "pubmed"],
+ },
});
```
@@ -405,10 +401,10 @@ When you add content, Supermemory:
5. **Embeds** for vector search
6. **Indexes** for retrieval
-Track progress with `GET /v3/documents/{id}`:
+Track progress with `GET /ns/{namespace}/document/{id}`:
```typescript
-const doc = await client.documents.get("abc123");
-console.log(doc.status); // "queued" | "processing" | "done"
+const doc = await supermemory.documents.get("user_123", "abc123");
+console.log(doc.system.status); // "queued" | "processing" | "done"
```
@@ -421,10 +417,9 @@ console.log(doc.status); // "queued" | "processing" | "done"
for (const doc of documents) {
try {
- const result = await client.add({
+ const result = await supermemory.add("batch_import", {
content: doc.content,
- customId: doc.id,
- containerTag: "batch_import"
+ id: doc.id,
});
results.push({ id: doc.id, success: true, docId: result.id });
} catch (error) {
@@ -442,54 +437,41 @@ console.log(doc.status); // "queued" | "processing" | "done"
**Tips:**
- Batch size: 3-5 documents at once
- Delay: 1-2 seconds between requests
- - Use `customId` to track and deduplicate
+ - Use `id` to track and deduplicate
+ - For large backfills use `documents.batchAdd` instead. See [Backfill historical data](/ingestion/batch-ingest-historical-data)
| Status | Error | Cause |
|--------|-------|-------|
- | 400 | 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 |
+ | 400 | Bad request | Missing required fields, invalid parameters |
+ | 401 | Unauthorized | Invalid or missing API key |
+ | 403 | Forbidden | Insufficient permissions |
+ | 429 | Rate limited | Too many requests or quota exceeded |
+ | 500 | Internal error | Processing failure |
```typescript
- import { BadRequestError, RateLimitError } from 'supermemory';
-
try {
- await client.add({ content: "..." });
+ await supermemory.add("user_123", { 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);
- }
+ // Back off on 429, fix the request on 400, retry later on 5xx
+ console.error("add failed:", error);
}
```
-
- **Single delete:**
+
+ **Delete by IDs (one or many, Supermemory or your own ids):**
```typescript
- await client.documents.delete("doc_id_123");
- ```
-
- **Bulk delete by IDs:**
- ```typescript
- await client.documents.deleteBulk({
- ids: ["doc_1", "doc_2", "doc_3"]
+ const { count, errors } = await supermemory.documents.delete("user_123", {
+ ids: ["doc_1", "doc_2", "doc_3"],
});
```
- **Bulk delete by container tag:**
+ **Delete everything in a namespace:**
```typescript
// Delete all content for a user
- await client.documents.deleteBulk({
- containerTags: ["user_123"]
- });
+ await supermemory.namespaces.delete("user_123");
```
Deletes are permanent — no recovery.
@@ -503,4 +485,4 @@ console.log(doc.status); // "queued" | "processing" | "done"
- [How to backfill historical data](/ingestion/batch-ingest-historical-data) — Import dated content with the batch API
- [Search Memories](/recall/search) — Query your content
- [User Profiles](/recall/user-profiles) — Get user context
-- [Organizing & Filtering](/concepts/filtering) — Container tags and metadata
+- [Organizing & Filtering](/concepts/filtering) — Namespaces and metadata
diff --git a/apps/docs/ingestion/batch-ingest-historical-data.mdx b/apps/docs/ingestion/batch-ingest-historical-data.mdx
index c727fac4..534ab6c6 100644
--- a/apps/docs/ingestion/batch-ingest-historical-data.mdx
+++ b/apps/docs/ingestion/batch-ingest-historical-data.mdx
@@ -1,26 +1,26 @@
---
title: "How to backfill historical data into Supermemory"
sidebarTitle: "Backfill historical data"
-description: "Backfill historical documents into Supermemory with documentDate, stable custom IDs, and the batch ingestion API."
+description: "Backfill historical documents into Supermemory with date, stable IDs, and the batch ingestion API."
icon: "/icons/hugeicons/clock-01.svg"
---
-Use `POST /v3/documents/batch` to backfill exports, emails, messages, or other dated records.
+Use `POST /ns/{namespace}/document/batch` to backfill exports, emails, messages, or other dated records.
- Sort the source data oldest to newest, add `documentDate` to every document.
+ Sort the source data oldest to newest, add `date` 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.
+Backfill dated content by setting `date` on each document, sorting the source records oldest to newest, and sending them in batches. Each request can contain up to 600 documents.
-**Endpoint:** [`POST /v3/documents/batch`](/api-reference/ingest/batch-add-documents)
+**Endpoint:** [`POST /ns/{namespace}/document/batch`](/api-reference/ingest/batch-add-documents)
```typescript TypeScript
-import Supermemory from "supermemory";
+import { Supermemory } from "supermemory";
type SourceDocument = {
id: string;
@@ -28,22 +28,21 @@ type SourceDocument = {
createdAt: string;
};
-const client = new Supermemory();
+const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
const batchSize = 100;
async function backfillHistoricalData(sourceDocuments: SourceDocument[]) {
const documents = sourceDocuments
.map((document) => ({
content: document.content,
- customId: document.id,
- documentDate: new Date(document.createdAt).toISOString()
+ id: document.id,
+ date: new Date(document.createdAt).toISOString()
}))
- .sort((a, b) => a.documentDate.localeCompare(b.documentDate));
+ .sort((a, b) => a.date.localeCompare(b.date));
for (let offset = 0; offset < documents.length; offset += batchSize) {
- const result = await client.documents.batchAdd({
- containerTag: "historical_import",
- documents: documents.slice(offset, offset + batchSize)
+ const result = await supermemory.documents.batchAdd("historical_import", {
+ documents: documents.slice(offset, offset + batchSize),
});
if (result.failed > 0) {
@@ -71,17 +70,17 @@ def backfill_historical_data(source_documents: list[dict[str, str]]) -> None:
[
{
"content": document["content"],
- "custom_id": document["id"],
- "document_date": to_utc(document["created_at"]),
+ "id": document["id"],
+ "date": to_utc(document["created_at"]),
}
for document in source_documents
],
- key=lambda document: document["document_date"],
+ key=lambda document: document["date"],
)
for offset in range(0, len(documents), batch_size):
result = client.documents.batch_add(
- container_tag="historical_import",
+ "historical_import",
documents=documents[offset : offset + batch_size],
)
@@ -89,13 +88,27 @@ def backfill_historical_data(source_documents: list[dict[str, str]]) -> None:
raise RuntimeError(f"{result.failed} documents failed to ingest")
```
+```bash curl
+curl -X POST "https://api.supermemory.ai/ns/historical_import/document/batch" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "documents": [
+ { "content": "first message", "id": "msg_001", "date": "2023-01-05T09:00:00Z" },
+ { "content": "second message", "id": "msg_002", "date": "2023-01-06T14:30:00Z" }
+ ]
+ }'
+```
+
+Batch results preserve input order. Inspect `success`, `failed`, and every item in `results`; a batch can contain successful and failed items together.
+
## Optional: wait for processing to finish
-**Endpoint:** [`GET /v3/documents/{id}`](/api-reference/documents/get-document)
+**Endpoint:** [`GET /ns/{namespace}/document/{id}`](/api-reference/documents/get-document)
-The batch endpoint returns after accepting the documents. If a later step depends on completed memory generation, poll the returned document IDs until both `status` and `dreamingStatus` are `done`.
+The batch endpoint returns after accepting the documents. If a later step depends on the documents being indexed, poll the returned document IDs until `system.status` is `done`. If that later step also needs the extracted memories, send the batch with `dreaming: "instant"`; under the default `dynamic` mode memory extraction is batched and can lag `done` by minutes.
@@ -103,19 +116,14 @@ The batch endpoint returns after accepting the documents. If a later step depend
async function waitUntilDone(ids: string[]) {
while (true) {
const documents = await Promise.all(
- ids.map((id) => client.documents.get(id))
+ ids.map((id) => supermemory.documents.get("historical_import", id))
);
- if (documents.some((document) => document.status === "failed")) {
+ if (documents.some((document) => document.system.status === "failed")) {
throw new Error("A document failed to process");
}
- if (
- documents.every(
- (document) =>
- document.status === "done" && document.dreamingStatus === "done"
- )
- ) {
+ if (documents.every((document) => document.system.status === "done")) {
return;
}
await new Promise((resolve) => setTimeout(resolve, 10_000));
@@ -128,18 +136,23 @@ import time
def wait_until_done(ids: list[str]) -> None:
while True:
- documents = [client.documents.get(document_id) for document_id in ids]
+ documents = [
+ client.documents.get("historical_import", document_id) for document_id in ids
+ ]
- if any(document.status == "failed" for document in documents):
+ if any(document.system.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
- ):
+ if all(document.system.status == "done" for document in documents):
return
time.sleep(10)
```
+```bash curl
+curl "https://api.supermemory.ai/ns/historical_import/document/msg_001" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+# repeat until "system": { "status": "done" }
+```
+
diff --git a/apps/docs/ingestion/document-operations.mdx b/apps/docs/ingestion/document-operations.mdx
index 565aaa58..5c648f57 100644
--- a/apps/docs/ingestion/document-operations.mdx
+++ b/apps/docs/ingestion/document-operations.mdx
@@ -5,42 +5,38 @@ description: "List, get, update, and delete your ingested documents"
icon: "/icons/hugeicons/files-02.svg"
---
-Manage documents after ingestion using the SDK.
+Manage documents after ingestion using the SDK. Every call is scoped to one `namespace` (what v3/v4 called a container tag).
## List documents
-Retrieve paginated documents with filtering.
+Retrieve paginated documents with filtering. One `list` call serves documents, chunks, and memories; pick with `type`.
```typescript
- const documents = await client.documents.list({
+ const { documents } = await supermemory.list("user_123", "documents", {
limit: 10,
- containerTags: ["user_123"]
});
- documents.memories.forEach(d => {
- console.log(d.id, d.title, d.status);
+ documents.forEach(d => {
+ console.log(d.id, d.title, d.system.status);
});
```
```python
- documents = client.documents.list(
- limit=10,
- container_tags=["user_123"]
- )
+ page = client.list("user_123", "documents", limit=10)
- for doc in documents.memories:
- print(doc.id, doc.title, doc.status)
+ for doc in page.documents:
+ print(doc.id, doc.title, doc.system.status)
```
```bash
- curl -X POST "https://api.supermemory.ai/v3/documents/list" \
+ curl -X POST "https://api.supermemory.ai/ns/user_123/list/documents?limit=10" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{"limit": 10, "containerTags": ["user_123"]}'
+ -d '{}'
```
@@ -48,49 +44,58 @@ Retrieve paginated documents with filtering.
**Response:**
```json
{
- "memories": [
+ "documents": [
{
"id": "doc_abc123",
"title": "Meeting notes",
- "status": "done",
"type": "text",
- "createdAt": "2024-01-15T10:30:00Z",
- "containerTags": ["user_123"],
- "metadata": { "source": "slack" }
+ "metadata": { "source": "slack" },
+ "system": {
+ "status": "done",
+ "createdAt": "2024-01-15T10:30:00Z",
+ "updatedAt": "2024-01-15T10:30:00Z"
+ }
}
],
+ "chunks": [],
+ "memories": [],
"pagination": {
"currentPage": 1,
- "totalPages": 3,
- "totalItems": 25
+ "limit": 10,
+ "totalItems": 25,
+ "totalPages": 3
}
}
```
+Every response contains `documents`, `chunks`, `memories`, and `pagination`. Only the array for the requested `type` is filled.
+
### Parameters
+`namespace` and `type` are required. Pagination and sorting are query parameters (top-level keys in the SDK); `filter` goes in `body`.
+
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
-| `limit` | number | 50 | Items per page (max 200) |
+| `type` | string | required | `documents`, `chunks`, or `memories` |
+| `limit` | number | 10 | Items per page |
| `page` | number | 1 | Page number |
-| `containerTags` | string[] | — | Filter by tags |
-| `sort` | string | `createdAt` | Sort by `createdAt` or `updatedAt` |
+| `sort` | string | `createdAt` | Sort by `createdAt`, `updatedAt`, or `position` |
| `order` | string | `desc` | `desc` (newest) or `asc` (oldest) |
+| `body.filter` | object | — | Metadata filter. See [Organizing & Filtering](/concepts/filtering) |
```typescript
- async function getAllDocuments(containerTag: string) {
+ async function getAllDocuments(namespace: string) {
const all = [];
let page = 1;
while (true) {
- const { memories, pagination } = await client.documents.list({
- containerTags: [containerTag],
+ const { documents, pagination } = await supermemory.list(namespace, "documents", {
limit: 100,
- page
+ page,
});
- all.push(...memories);
+ all.push(...documents);
if (page >= pagination.totalPages) break;
page++;
}
@@ -102,14 +107,14 @@ Retrieve paginated documents with filtering.
```typescript
- const documents = await client.documents.list({
- containerTags: ["user_123"],
- filters: {
- AND: [
- { key: "status", value: "reviewed", negate: false },
- { key: "priority", value: "high", negate: false }
- ]
- }
+ const { documents } = await supermemory.list("user_123", "documents", {
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "status", operator: "eq", value: "reviewed" },
+ { field: "priority", operator: "eq", value: "high" },
+ ],
+ },
});
```
@@ -118,33 +123,41 @@ Retrieve paginated documents with filtering.
## Get document
-Get a specific document with its processing status.
+Get a specific document with its processing status. Pass `include` to include its chunks and memories.
```typescript
- const doc = await client.documents.get("doc_abc123");
+ const doc = await supermemory.documents.get("user_123", "doc_abc123", {
+ include: ["chunks", "memories"], // optional
+ });
- console.log(doc.status); // "queued" | "processing" | "done" | "failed"
+ console.log(doc.system.status); // "queued" | "processing" | "done" | "failed"
console.log(doc.content);
```
```python
- doc = client.documents.get("doc_abc123")
+ doc = client.documents.get(
+ "user_123",
+ "doc_abc123",
+ include=["chunks", "memories"], # optional
+ )
- print(doc.status)
+ print(doc.system.status) # "queued" | "processing" | "done" | "failed"
print(doc.content)
```
```bash
- curl "https://api.supermemory.ai/v3/documents/doc_abc123" \
+ curl "https://api.supermemory.ai/ns/user_123/document/doc_abc123?include=chunks&include=memories" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
+The `id` may be the Supermemory document ID or the `id` you supplied at ingestion. It resolves only inside the namespace in the path.
+
### Processing status
| Status | Description |
@@ -158,12 +171,12 @@ Get a specific document with its processing status.
```typescript
- async function waitForProcessing(docId: string) {
+ async function waitForProcessing(namespace: string, id: string) {
while (true) {
- const doc = await client.documents.get(docId);
+ const doc = await supermemory.documents.get(namespace, id);
- if (doc.status === "done") return doc;
- if (doc.status === "failed") throw new Error("Processing failed");
+ if (doc.system.status === "done") return doc;
+ if (doc.system.status === "failed") throw new Error("Processing failed");
await new Promise(r => setTimeout(r, 2000));
}
@@ -175,29 +188,30 @@ Get a specific document with its processing status.
## 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.
+Update a document's content or metadata. **Content changes** trigger full reprocessing; **metadata-only changes** (e.g. updating `accepted`, `version`) do not reindex. The body accepts any non-empty subset of `content`, `metadata`, `supportingContext`, `group`, or `date`.
```typescript
- await client.documents.update("doc_abc123", {
+ await supermemory.documents.update("user_123", "doc_abc123", {
content: "Updated content here",
- metadata: { version: 2, reviewed: true }
+ metadata: { version: 2, reviewed: true },
});
```
```python
client.documents.update(
+ "user_123",
"doc_abc123",
content="Updated content here",
- metadata={"version": 2, "reviewed": True}
+ metadata={"version": 2, "reviewed": True},
)
```
```bash
- curl -X PATCH "https://api.supermemory.ai/v3/documents/doc_abc123" \
+ curl -X PATCH "https://api.supermemory.ai/ns/user_123/document/doc_abc123" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content": "Updated content here", "metadata": {"version": 2}}'
@@ -205,56 +219,66 @@ Update a document's content or metadata. **Content changes** trigger full reproc
+For file-backed documents use `documents.updateFile` (PATCH: partial, `file` optional, metadata merges key by key) or `documents.replaceWithFile` (POST: full replace, `file` required, omitted metadata keys are cleared).
+
+```typescript
+await supermemory.documents.updateFile("user_123", "doc_abc123", {
+ metadata: JSON.stringify({ reviewed: true }),
+});
+
+await supermemory.documents.replaceWithFile("user_123", "doc_abc123", {
+ file,
+ metadata: JSON.stringify({ revision: 2 }),
+});
+```
+
---
## Delete documents
-Permanently remove documents.
+Permanently remove documents. One call deletes 1 to 100 documents by Supermemory or caller-defined ID.
```typescript
- // Single delete
- await client.documents.delete("doc_abc123");
-
- // Bulk delete by IDs
- await client.documents.deleteBulk({
- ids: ["doc_1", "doc_2", "doc_3"]
+ // One or many
+ const { count, errors } = await supermemory.documents.delete("user_123", {
+ ids: ["doc_1", "doc_2", "doc_3"],
});
- // Bulk delete by container tag (delete all for a user)
- await client.documents.deleteBulk({
- containerTags: ["user_123"]
- });
+ // Delete a whole namespace (all content for a user)
+ await supermemory.namespaces.delete("user_123");
```
```python
- # Single delete
- client.documents.delete("doc_abc123")
+ # One or many
+ result = client.documents.delete("user_123", ids=["doc_1", "doc_2", "doc_3"])
+ print(result.count, result.errors)
- # 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"])
+ # Delete a whole namespace (all content for a user)
+ client.namespaces.delete("user_123")
```
```bash
- # 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" \
+ # One or many
+ curl -X DELETE "https://api.supermemory.ai/ns/user_123/document" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ids": ["doc_1", "doc_2", "doc_3"]}'
+
+ # Delete a whole namespace
+ curl -X DELETE "https://api.supermemory.ai/ns/user_123" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{}'
```
+Inspect both `count` and per-ID `errors`; HTTP success can include partial failures.
+
Deletes are permanent — no recovery.
@@ -263,25 +287,31 @@ Deletes are permanent — no recovery.
## Processing queue
-Check documents currently being processed.
+There is no separate processing endpoint. List documents and read `system.status` on each item.
```typescript
- const response = await client.documents.listProcessing();
- console.log(`${response.documents.length} documents processing`);
+ const { documents } = await supermemory.list("user_123", "documents", {
+ limit: 100,
+ });
+ const processing = documents.filter(d => d.system.status !== "done" && d.system.status !== "failed");
+ console.log(`${processing.length} documents processing`);
```
```python
- response = client.documents.list_processing()
- print(f"{len(response.documents)} documents processing")
+ page = client.list("user_123", "documents", limit=100)
+ processing = [d for d in page.documents if d.system.status not in ("done", "failed")]
+ print(f"{len(processing)} documents processing")
```
```bash
- curl "https://api.supermemory.ai/v3/documents/processing" \
- -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+ curl -X POST "https://api.supermemory.ai/ns/user_123/list/documents?limit=100" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{}'
```
@@ -290,6 +320,6 @@ Check documents currently being processed.
## Next steps
-- [Memory Operations](/recall/memory-operations) — Advanced v4 memory operations
+- [Memory Operations](/recall/memory-operations) — Forget memories
- [Search](/recall/search) — Query your memories
- [Ingesting Content](/ingestion/add-memories) — Add new content
diff --git a/apps/docs/integrations/agno.mdx b/apps/docs/integrations/agno.mdx
index bd3a95fb..69342941 100644
--- a/apps/docs/integrations/agno.mdx
+++ b/apps/docs/integrations/agno.mdx
@@ -47,11 +47,11 @@ 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)
+ profile = memory.profile(user_id).profile
+ memories = memory.search(user_id, query=query, limit=5).results
- static = result.profile.static or []
- dynamic = result.profile.dynamic or []
- memories = result.search_results.results if result.search_results else []
+ static = [m.memory for m in profile.static]
+ dynamic = [m.memory for m in profile.dynamic]
return f"""
User background:
@@ -87,8 +87,9 @@ def chat(user_id: str, message: str) -> str:
# Save for next time
memory.add(
+ user_id,
content=f"User: {message}\nAssistant: {response.content}",
- container_tag=user_id
+ dreaming="instant"
)
return response.content
@@ -106,13 +107,10 @@ Supermemory keeps two buckets of user info:
- **Dynamic context**: What they're focused on lately
```python
-result = memory.profile(
- container_tag="user_123",
- q="cooking help" # Also returns relevant memories
-)
+result = memory.profile("user_123")
-print(result.profile.static) # ["Vegetarian", "Allergic to nuts"]
-print(result.profile.dynamic) # ["Learning Italian cuisine", "Meal prepping"]
+print([m.memory for m in result.profile.static]) # ["Vegetarian", "Allergic to nuts"]
+print([m.memory for m in result.profile.dynamic]) # ["Learning Italian cuisine", "Meal prepping"]
```
### Storing memories
@@ -122,9 +120,10 @@ Save interactions so future sessions have context:
```python
def store_chat(user_id: str, user_msg: str, agent_response: str):
memory.add(
+ user_id,
content=f"User asked: {user_msg}\nAgent said: {agent_response}",
- container_tag=user_id,
- metadata={"type": "conversation"}
+ metadata={"type": "conversation"},
+ dreaming="instant"
)
```
@@ -133,10 +132,10 @@ def store_chat(user_id: str, user_msg: str, agent_response: str):
Look up past interactions:
```python
-results = memory.search.memories(
- q="pasta recipes we discussed",
- container_tag="user_123",
- search_mode="hybrid",
+results = memory.search(
+ "user_123",
+ query="pasta recipes we discussed",
+ search_mode="hybrid", # Searches memories + document chunks
limit=5
)
@@ -164,16 +163,13 @@ class PersonalAssistant:
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
- )
+ profile = self.memory.profile(user_id).profile
+ memories = self.memory.search(user_id, query=query, threshold=0.5).results
return {
- "profile": result.profile.static or [],
- "recent": result.profile.dynamic or [],
- "history": [m.memory for m in (result.search_results.results or [])[:3]]
+ "profile": [m.memory for m in profile.static],
+ "recent": [m.memory for m in profile.dynamic],
+ "history": [m.memory or m.chunk for m in memories[:3]]
}
def build_description(self, context: dict) -> str:
@@ -210,9 +206,10 @@ class PersonalAssistant:
# Store for future sessions
self.memory.add(
+ user_id,
content=f"User: {message}\nAssistant: {response.content}",
- container_tag=user_id,
- metadata={"type": "chat"}
+ metadata={"type": "chat"},
+ dreaming="instant"
)
return response.content
@@ -220,9 +217,10 @@ class PersonalAssistant:
def teach(self, user_id: str, fact: str):
"""Store a preference or fact about the user."""
self.memory.add(
+ user_id,
content=fact,
- container_tag=user_id,
- metadata={"type": "preference"}
+ metadata={"type": "preference"},
+ dreaming="instant"
)
@@ -260,11 +258,7 @@ def search_memory(query: str, user_id: str) -> str:
query: What to look for
user_id: The user's ID
"""
- results = memory.search.memories(
- q=query,
- container_tag=user_id,
- limit=5
- )
+ results = memory.search(user_id, query=query, limit=5)
if not results.results:
return "Nothing relevant found in memory."
@@ -279,7 +273,7 @@ def remember(content: str, user_id: str) -> str:
content: What to remember
user_id: The user's ID
"""
- memory.add(content=content, container_tag=user_id)
+ memory.add(user_id, content=content, dreaming="instant")
return f"Remembered: {content}"
agent = Agent(
@@ -324,9 +318,10 @@ def analyze_and_remember(user_id: str, image_path: str, question: str) -> str:
# Store the interaction with image context
memory.add(
+ user_id,
content=f"User shared an image and asked: {question}\nAnalysis: {response.content}",
- container_tag=user_id,
- metadata={"type": "image_analysis", "image": image_path}
+ metadata={"type": "image_analysis", "image": image_path},
+ dreaming="instant"
)
return response.content
@@ -341,8 +336,9 @@ Tags let you narrow down searches:
```python
# Store with metadata
memory.add(
+ "user_123",
content="User prefers dark mode interfaces",
- container_tag="user_123",
+ dreaming="instant",
metadata={
"type": "preference",
"category": "ui",
@@ -351,13 +347,14 @@ memory.add(
)
# Search with filters
-results = memory.search.memories(
- q="interface preferences",
- container_tag="user_123",
- filters={
- "AND": [
- {"key": "type", "value": "preference"},
- {"key": "category", "value": "ui"}
+results = memory.search(
+ "user_123",
+ query="interface preferences",
+ filter={
+ "operator": "and",
+ "operands": [
+ {"field": "type", "operator": "eq", "value": "preference"},
+ {"field": "category", "operator": "eq", "value": "ui"}
]
}
)
diff --git a/apps/docs/integrations/ai-sdk.mdx b/apps/docs/integrations/ai-sdk.mdx
index 6f93593e..a67e248b 100644
--- a/apps/docs/integrations/ai-sdk.mdx
+++ b/apps/docs/integrations/ai-sdk.mdx
@@ -40,8 +40,8 @@ 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",
+ namespace: "user-123",
+ id: "conversation-456",
})
const result = await generateText({
@@ -52,18 +52,18 @@ const result = await generateText({
### Required fields
-Both `containerTag` and `customId` are required.
+Both `namespace` and `id` 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).
+- **`namespace`** — *who* the memories belong to. Use a stable identifier per user, workspace, or tenant (e.g. `"user-123"`, `"acme-workspace"`). Memory search and writes are scoped to this tag.
+- **`id`** — *which conversation* this turn belongs to. Use it to group messages from the same chat session into a single document (e.g. `"chat-2026-04-25"`, a thread ID, or a UUID per session).
**Memory saving is enabled by default** (`addMemory: "always"`). New conversations are persisted automatically. To opt out, set `addMemory: "never"`:
```typescript
const modelWithMemory = withSupermemory(openai("gpt-5"), {
- containerTag: "user-123",
- customId: "conversation-456",
+ namespace: "user-123",
+ id: "conversation-456",
addMemory: "never",
})
```
@@ -74,19 +74,19 @@ Both `containerTag` and `customId` are required.
**Profile Mode (Default)** - Retrieves the user's complete profile:
```typescript
-const model = withSupermemory(openai("gpt-4"), { containerTag: "user-123", customId: "conv-1", mode: "profile" })
+const model = withSupermemory(openai("gpt-4"), { namespace: "user-123", id: "conv-1", mode: "profile" })
```
**Query Mode** - Searches memories based on the user's message:
```typescript
-const model = withSupermemory(openai("gpt-4"), { containerTag: "user-123", customId: "conv-1", mode: "query" })
+const model = withSupermemory(openai("gpt-4"), { namespace: "user-123", id: "conv-1", mode: "query" })
```
**Full Mode** - Combines profile AND query-based search:
```typescript
-const model = withSupermemory(openai("gpt-4"), { containerTag: "user-123", customId: "conv-1", mode: "full" })
+const model = withSupermemory(openai("gpt-4"), { namespace: "user-123", id: "conv-1", mode: "full" })
```
### Custom prompt templates
@@ -108,8 +108,8 @@ const claudePrompt = (data: MemoryPromptData) => `
`.trim()
const model = withSupermemory(anthropic("claude-3-sonnet"), {
- containerTag: "user-123",
- customId: "conv-1",
+ namespace: "user-123",
+ id: "conv-1",
mode: "full",
promptTemplate: claudePrompt,
})
@@ -119,8 +119,8 @@ const model = withSupermemory(anthropic("claude-3-sonnet"), {
```typescript
const model = withSupermemory(openai("gpt-4"), {
- containerTag: "user-123",
- customId: "conv-1",
+ namespace: "user-123",
+ id: "conv-1",
verbose: true,
})
// Console output shows memory retrieval details
@@ -134,8 +134,8 @@ To **fail the call** when memory retrieval fails instead, set `skipMemoryOnError
```typescript
const model = withSupermemory(openai("gpt-5"), {
- containerTag: "user-123",
- customId: "conv-1",
+ namespace: "user-123",
+ id: "conv-1",
skipMemoryOnError: false,
})
```
@@ -146,8 +146,8 @@ By default, saved conversations include only user and assistant text — tool ca
```typescript
const model = withSupermemory(openai("gpt-5"), {
- containerTag: "user-123",
- customId: "conv-1",
+ namespace: "user-123",
+ id: "conv-1",
includeToolCalls: true,
})
```
@@ -210,7 +210,7 @@ const result = await streamText({
model: openai("gpt-5"),
prompt: "What do you know about me?",
tools: {
- searchMemories: searchMemoriesTool("API_KEY", { projectId: "personal" }),
+ searchMemories: searchMemoriesTool("API_KEY", { namespace: "personal" }),
createEvent: yourCustomTool,
}
})
diff --git a/apps/docs/integrations/claude-memory.mdx b/apps/docs/integrations/claude-memory.mdx
index 6afc7947..39ecacd5 100644
--- a/apps/docs/integrations/claude-memory.mdx
+++ b/apps/docs/integrations/claude-memory.mdx
@@ -26,7 +26,7 @@ import { createClaudeMemoryTool } from "@supermemory/tools/claude-memory"
const anthropic = new Anthropic()
const memoryTool = createClaudeMemoryTool(process.env.SUPERMEMORY_API_KEY!, {
- projectId: "my-app",
+ namespace: "my-app",
})
async function chatWithMemory(userMessage: string) {
@@ -85,14 +85,13 @@ 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",
+ namespace: "my-app",
// Or use container tags for more flexibility
- containerTags: ["user-123", "project-alpha"],
+ namespace: "user-123",
// Custom memory container prefix (default: "claude_memory")
- memoryContainerTag: "my_memory_prefix",
-
+
// Custom API endpoint
baseUrl: "https://custom.api.com",
})
@@ -196,7 +195,7 @@ import { createClaudeMemoryTool } from "@supermemory/tools/claude-memory"
const anthropic = new Anthropic()
const memoryTool = createClaudeMemoryTool(process.env.SUPERMEMORY_API_KEY!, {
- projectId: "assistant",
+ namespace: "assistant",
})
async function runConversation() {
diff --git a/apps/docs/integrations/convex.mdx b/apps/docs/integrations/convex.mdx
index d13aa6a3..25c027d7 100644
--- a/apps/docs/integrations/convex.mdx
+++ b/apps/docs/integrations/convex.mdx
@@ -44,18 +44,15 @@ Create simple helper functions for each Supermemory operation:
// convex/memory.ts
import { action } from "./_generated/server";
import { v } from "convex/values";
-import Supermemory from "supermemory";
+import { Supermemory } from "supermemory";
const memory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
-// Get user profile and relevant memories
+// Get user profile
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,
- });
+ args: { userId: v.string() },
+ handler: async (ctx, { userId }) => {
+ return await memory.profile(userId);
},
});
@@ -63,9 +60,9 @@ export const getProfile = action({
export const addMemory = action({
args: { userId: v.string(), content: v.string() },
handler: async (ctx, { userId, content }) => {
- return await memory.add({
+ return await memory.add(userId, {
content,
- containerTag: userId,
+ dreaming: "instant", // memories are ready as soon as status is done
});
},
});
@@ -74,9 +71,8 @@ export const addMemory = action({
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,
+ return await memory.search(userId, {
+ query,
searchMode: "hybrid",
limit: limit ?? 10,
});
@@ -103,8 +99,8 @@ export const chat = action({
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}`,
+ namespace: userId,
+ id: `convex-chat-${userId}`,
mode: "full",
addMemory: "always",
});
@@ -145,7 +141,7 @@ export default defineSchema({
import { action, mutation, query } from "./_generated/server";
import { api } from "./_generated/api";
import { v } from "convex/values";
-import Supermemory from "supermemory";
+import { Supermemory } from "supermemory";
const memory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
@@ -166,7 +162,7 @@ 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 });
+ await memory.add(userId, { content });
// Store in Convex
// Note: in production, handle partial failures — if the Convex mutation
diff --git a/apps/docs/integrations/crewai.mdx b/apps/docs/integrations/crewai.mdx
index cc6b038a..78c6e164 100644
--- a/apps/docs/integrations/crewai.mdx
+++ b/apps/docs/integrations/crewai.mdx
@@ -47,11 +47,11 @@ 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)
+ profile = memory.profile(user_id).profile
+ memories = memory.search(user_id, query=query, limit=5).results
- static = result.profile.static or []
- dynamic = result.profile.dynamic or []
- memories = result.search_results.results if result.search_results else []
+ static = [m.memory for m in profile.static]
+ dynamic = [m.memory for m in profile.dynamic]
return f"""
User Profile:
@@ -91,13 +91,10 @@ Supermemory tracks two kinds of user data:
- **Dynamic context**: What the user is working on right now
```python
-result = memory.profile(
- container_tag="user_abc",
- q="project planning" # Optional: also returns relevant memories
-)
+result = memory.profile("user_abc")
-print(result.profile.static) # ["Prefers Agile methodology", "Senior engineer"]
-print(result.profile.dynamic) # ["Working on Q2 roadmap", "Focused on API design"]
+print([m.memory for m in result.profile.static]) # ["Prefers Agile methodology", "Senior engineer"]
+print([m.memory for m in result.profile.dynamic]) # ["Working on Q2 roadmap", "Focused on API design"]
```
### Storing memories
@@ -108,9 +105,10 @@ Save crew outputs so future runs can reference them:
def store_crew_result(user_id: str, task_description: str, result: str):
"""Save crew output as a memory."""
memory.add(
+ user_id,
content=f"Task: {task_description}\nResult: {result}",
- container_tag=user_id,
- metadata={"type": "crew_execution"}
+ metadata={"type": "crew_execution"},
+ dreaming="instant"
)
```
@@ -119,10 +117,10 @@ def store_crew_result(user_id: str, task_description: str, result: str):
Pull up past interactions before running a crew:
```python
-results = memory.search.memories(
- q="previous project recommendations",
- container_tag="user_abc",
- search_mode="hybrid",
+results = memory.search(
+ "user_abc",
+ query="previous project recommendations",
+ search_mode="hybrid", # Searches memories + document chunks
limit=10
)
@@ -152,16 +150,13 @@ class ResearchCrew:
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
- )
+ profile = self.memory.profile(user_id).profile
+ memories = self.memory.search(user_id, query=topic, threshold=0.5).results
return {
- "expertise": result.profile.static or [],
- "focus": result.profile.dynamic or [],
- "history": [m.memory for m in (result.search_results.results or [])[:3]]
+ "expertise": [m.memory for m in profile.static],
+ "focus": [m.memory for m in profile.dynamic],
+ "history": [m.memory or m.chunk for m in memories[:3]]
}
def create_researcher(self, context: dict) -> Agent:
@@ -229,9 +224,10 @@ class ResearchCrew:
# Store for future sessions
self.memory.add(
+ user_id,
content=f"Research on '{topic}': {str(result)[:500]}",
- container_tag=user_id,
- metadata={"type": "research", "topic": topic}
+ metadata={"type": "research", "topic": topic},
+ dreaming="instant"
)
return str(result)
@@ -242,8 +238,9 @@ if __name__ == "__main__":
# Teach preferences
crew.memory.add(
+ "researcher_1",
content="User prefers concise summaries with bullet points",
- container_tag="researcher_1"
+ dreaming="instant"
)
# Run research
@@ -265,9 +262,9 @@ def create_collaborative_context(user_ids: list[str], topic: str) -> str:
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])}")
+ static = [m.memory for m in memory.profile(user_id).profile.static]
+ if static:
+ combined.append(f"{user_id}: {', '.join(static[:3])}")
return "\n".join(combined) if combined else "No shared context available."
```
@@ -283,9 +280,10 @@ def store_if_successful(user_id: str, task: str, result: str, success: bool):
return
memory.add(
+ user_id,
content=f"Completed: {task}\nOutcome: {result}",
- container_tag=user_id,
- metadata={"type": "success", "task": task}
+ metadata={"type": "success", "task": task},
+ dreaming="instant"
)
```
@@ -296,8 +294,9 @@ Metadata lets you filter memories by project, agent, or whatever else makes sens
```python
# Store with metadata
memory.add(
+ "user_123",
content="Research findings on distributed systems",
- container_tag="user_123",
+ dreaming="instant",
metadata={
"project": "infrastructure-review",
"agents": ["researcher", "writer"],
@@ -306,13 +305,14 @@ memory.add(
)
# 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"}
+results = memory.search(
+ "user_123",
+ query="distributed systems",
+ filter={
+ "operator": "and",
+ "operands": [
+ {"field": "project", "operator": "eq", "value": "infrastructure-review"},
+ {"field": "confidence", "operator": "eq", "value": "high"}
]
}
)
diff --git a/apps/docs/integrations/langchain.mdx b/apps/docs/integrations/langchain.mdx
index 87d4494a..09dfb928 100644
--- a/apps/docs/integrations/langchain.mdx
+++ b/apps/docs/integrations/langchain.mdx
@@ -51,13 +51,14 @@ 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)
+ # 1. Get user profile and relevant memories
+ profile_result = memory.profile(user_id)
+ search_result = memory.search(user_id, query=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 []
+ static_facts = [m.memory for m in profile_result.profile.static]
+ dynamic_context = [m.memory for m in profile_result.profile.dynamic]
+ search_results = search_result.results
context = f"""
User Background:
@@ -81,8 +82,9 @@ Relevant Memories:
# 4. Store the interaction as memory
memory.add(
+ user_id,
content=f"User: {message}\nAssistant: {response.content}",
- container_tag=user_id
+ dreaming="instant"
)
return response.content
@@ -100,14 +102,11 @@ Supermemory automatically maintains user profiles with two types of information:
- **Dynamic context**: Recent activity and current focus areas
```python
-# Fetch profile with optional search
-result = memory.profile(
- container_tag="user_123",
- q="optional search query" # Also returns relevant memories
-)
+# Fetch the profile for a namespace
+result = memory.profile("user_123")
-print(result.profile.static) # ["User is a Python developer", "Prefers dark mode"]
-print(result.profile.dynamic) # ["Currently working on API integration", "Debugging auth issues"]
+print([m.memory for m in result.profile.static]) # ["User is a Python developer", "Prefers dark mode"]
+print([m.memory for m in result.profile.dynamic]) # ["Currently working on API integration", "Debugging auth issues"]
```
### Memory storage
@@ -117,15 +116,17 @@ Content you add is automatically processed into searchable memories:
```python
# Store a conversation
memory.add(
+ "user_123",
content="User asked about async Python patterns. Explained asyncio basics.",
- container_tag="user_123",
- metadata={"topic": "python", "type": "conversation"}
+ metadata={"topic": "python", "type": "conversation"},
+ dreaming="instant"
)
# Store a document
memory.add(
+ "user_123",
content="https://docs.python.org/3/library/asyncio.html",
- container_tag="user_123"
+ dreaming="instant"
)
```
@@ -134,9 +135,9 @@ memory.add(
Search returns both extracted memories and document chunks:
```python
-results = memory.search.memories(
- q="async programming",
- container_tag="user_123",
+results = memory.search(
+ "user_123",
+ query="async programming",
search_mode="hybrid", # Searches memories + document chunks
limit=5
)
@@ -169,16 +170,16 @@ class CodeReviewAssistant:
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
+ # Get profile, then search for similar code patterns
+ profile = self.memory.profile(user_id).profile
+ memories = self.memory.search(
+ user_id,
+ query=code[:500], # Use code snippet for semantic search
threshold=0.6
- )
+ ).results
- static = result.profile.static or []
- dynamic = result.profile.dynamic or []
- memories = result.search_results.results if result.search_results else []
+ static = [m.memory for m in profile.static]
+ dynamic = [m.memory for m in profile.dynamic]
return f"""
## Developer Profile
@@ -188,7 +189,7 @@ class CodeReviewAssistant:
{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."}
+{chr(10).join(f"- {m.memory or m.chunk}" 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:
@@ -213,9 +214,10 @@ Guidelines:
# Store the review for future context
self.memory.add(
+ user_id,
content=f"Code review feedback: {response.content[:500]}",
- container_tag=user_id,
- metadata={"type": "code_review", "language": language}
+ metadata={"type": "code_review", "language": language},
+ dreaming="instant"
)
return response.content
@@ -223,9 +225,10 @@ Guidelines:
def learn_preference(self, user_id: str, preference: str):
"""Store a coding preference or style guideline."""
self.memory.add(
+ user_id,
content=f"Developer preference: {preference}",
- container_tag=user_id,
- metadata={"type": "preference"}
+ metadata={"type": "preference"},
+ dreaming="instant"
)
@@ -272,22 +275,16 @@ class ConversationalAgent:
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 []
+ profile = self.memory.profile(self.user_id).profile
+ memories = self.memory.search(self.user_id, query=query, threshold=0.5).results
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.'}
+{chr(10).join(m.memory for m in profile.static) if profile.static else 'No profile yet.'}
Current context:
-{chr(10).join(profile.dynamic) if profile.dynamic else 'No recent context.'}
+{chr(10).join(m.memory for m in 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.'}
@@ -308,8 +305,9 @@ Use this context to provide personalized, contextual responses."""
# Store interaction for long-term memory
self.memory.add(
+ self.user_id,
content=f"User: {message}\nAssistant: {response.content}",
- container_tag=self.user_id
+ dreaming="instant"
)
return response.content
@@ -326,8 +324,9 @@ Use metadata to organize and filter memories:
```python
# Store with metadata
memory.add(
+ "user_123",
content="Discussed React hooks and state management",
- container_tag="user_123",
+ dreaming="instant",
metadata={
"topic": "react",
"type": "discussion",
@@ -336,13 +335,14 @@ memory.add(
)
# 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"}
+results = memory.search(
+ "user_123",
+ query="state management",
+ filter={
+ "operator": "and",
+ "operands": [
+ {"field": "topic", "operator": "eq", "value": "react"},
+ {"field": "project", "operator": "eq", "value": "frontend-redesign"}
]
}
)
@@ -362,9 +362,10 @@ notes = [
for note in notes:
memory.add(
+ "team_standup",
content=note,
- container_tag="team_standup",
- metadata={"date": "2024-01-15", "type": "decision"}
+ metadata={"date": "2024-01-15", "type": "decision"},
+ dreaming="instant"
)
```
diff --git a/apps/docs/integrations/langgraph.mdx b/apps/docs/integrations/langgraph.mdx
index 92f611fb..bb3b603d 100644
--- a/apps/docs/integrations/langgraph.mdx
+++ b/apps/docs/integrations/langgraph.mdx
@@ -61,13 +61,14 @@ def agent(state: State):
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)
+ # Fetch user profile and relevant memories
+ profile_result = memory.profile(user_id)
+ search_result = memory.search(user_id, query=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 []
+ static_facts = [m.memory for m in profile_result.profile.static]
+ dynamic_context = [m.memory for m in profile_result.profile.dynamic]
+ search_results = search_result.results
context = f"""
User Background:
@@ -85,8 +86,9 @@ Relevant Memories:
# Store the interaction
memory.add(
+ user_id,
content=f"User: {user_query}\nAssistant: {response.content}",
- container_tag=user_id
+ dreaming="instant"
)
return {"messages": [response]}
@@ -118,13 +120,10 @@ Supermemory automatically builds user profiles from stored memories:
- **Dynamic context**: Recent activity and current focus
```python
-result = memory.profile(
- container_tag="user_123",
- q="optional search query" # Also returns relevant memories
-)
+result = memory.profile("user_123")
-print(result.profile.static) # ["User is a Python developer", "Prefers functional style"]
-print(result.profile.dynamic) # ["Working on async patterns", "Debugging rate limiting"]
+print([m.memory for m in result.profile.static]) # ["User is a Python developer", "Prefers functional style"]
+print([m.memory for m in result.profile.dynamic]) # ["Working on async patterns", "Debugging rate limiting"]
```
### Memory storage
@@ -134,15 +133,17 @@ Content you add gets processed into searchable memories:
```python
# Store a conversation
memory.add(
+ "user_123",
content="User asked about graph traversal. Explained BFS vs DFS.",
- container_tag="user_123",
- metadata={"topic": "algorithms", "type": "conversation"}
+ metadata={"topic": "algorithms", "type": "conversation"},
+ dreaming="instant"
)
# Store a document
memory.add(
+ "user_123",
content="https://langchain-ai.github.io/langgraph/",
- container_tag="user_123"
+ dreaming="instant"
)
```
@@ -151,10 +152,10 @@ memory.add(
Search returns both extracted memories and document chunks:
```python
-results = memory.search.memories(
- q="graph algorithms",
- container_tag="user_123",
- search_mode="hybrid",
+results = memory.search(
+ "user_123",
+ query="graph algorithms",
+ search_mode="hybrid", # Searches memories + document chunks
limit=5
)
@@ -198,15 +199,11 @@ class SupportAgent:
user_id = state["user_id"]
query = state["messages"][-1].content
- result = self.memory.profile(
- container_tag=user_id,
- q=query,
- threshold=0.5
- )
+ profile = self.memory.profile(user_id).profile
+ memories = self.memory.search(user_id, query=query, threshold=0.5).results
- static = result.profile.static or []
- dynamic = result.profile.dynamic or []
- memories = result.search_results.results if result.search_results else []
+ static = [m.memory for m in profile.static]
+ dynamic = [m.memory for m in profile.dynamic]
context = f"""
## User Profile
@@ -216,7 +213,7 @@ class SupportAgent:
{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."}
+{chr(10).join(f"- {m.memory or m.chunk}" for m in memories[:3]) if memories else "No similar issues found."}
"""
return {"context": context}
@@ -257,9 +254,10 @@ Guidelines:
category = state.get("category", "general")
self.memory.add(
+ state["user_id"],
content=f"Support ticket ({category}): {user_msg}\nResolution: {ai_msg[:300]}",
- container_tag=state["user_id"],
- metadata={"type": "support_ticket", "category": category}
+ metadata={"type": "support_ticket", "category": category},
+ dreaming="instant"
)
return {}
@@ -366,8 +364,9 @@ Organize memories by project, topic, or any custom field:
```python
# Store with metadata
memory.add(
+ "user_123",
content="User prefers detailed error messages with stack traces",
- container_tag="user_123",
+ dreaming="instant",
metadata={
"type": "preference",
"project": "api-v2",
@@ -376,13 +375,14 @@ memory.add(
)
# 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"}
+results = memory.search(
+ "user_123",
+ query="error handling preferences",
+ filter={
+ "operator": "and",
+ "operands": [
+ {"field": "type", "operator": "eq", "value": "preference"},
+ {"field": "project", "operator": "eq", "value": "api-v2"}
]
}
)
diff --git a/apps/docs/integrations/mastra.mdx b/apps/docs/integrations/mastra.mdx
index 968f0f78..ea192b0d 100644
--- a/apps/docs/integrations/mastra.mdx
+++ b/apps/docs/integrations/mastra.mdx
@@ -39,8 +39,8 @@ const agent = new Agent(withSupermemory(
instructions: "You are a helpful assistant.",
},
{
- containerTag: "user-123", // Required: scopes memories to this user
- customId: "conv-456", // Required: groups messages for contextual memory
+ namespace: "user-123", // Required: scopes memories to this user
+ id: "conv-456", // Required: groups messages for contextual memory
mode: "full",
}
))
@@ -55,8 +55,8 @@ const response = await agent.generate("What do you know about me?")
const agent = new Agent(withSupermemory(
{ id: "my-assistant", model: openai("gpt-4o"), ... },
{
- containerTag: "user-123",
- customId: "conv-456",
+ namespace: "user-123",
+ id: "conv-456",
addMemory: "never", // Disable automatic conversation saving
}
))
@@ -99,8 +99,8 @@ sequenceDiagram
| 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 |
+| `namespace` | `string` | **Required** | User/container tag for scoping memories |
+| `id` | `string` | **Required** | Groups messages into a single document for contextual memory |
| `apiKey` | `string` | `SUPERMEMORY_API_KEY` env | Your Supermemory API key |
| `baseUrl` | `string` | `https://api.supermemory.ai` | Custom API endpoint |
| `mode` | `"profile" \| "query" \| "full"` | `"profile"` | Memory search mode |
@@ -116,8 +116,8 @@ sequenceDiagram
```typescript
const agent = new Agent(withSupermemory(config, {
- containerTag: "user-123",
- customId: "conv-456",
+ namespace: "user-123",
+ id: "conv-456",
mode: "profile",
}))
```
@@ -126,8 +126,8 @@ const agent = new Agent(withSupermemory(config, {
```typescript
const agent = new Agent(withSupermemory(config, {
- containerTag: "user-123",
- customId: "conv-456",
+ namespace: "user-123",
+ id: "conv-456",
mode: "query",
}))
```
@@ -136,8 +136,8 @@ const agent = new Agent(withSupermemory(config, {
```typescript
const agent = new Agent(withSupermemory(config, {
- containerTag: "user-123",
- customId: "conv-456",
+ namespace: "user-123",
+ id: "conv-456",
mode: "full",
}))
@@ -153,14 +153,14 @@ const agent = new Agent(withSupermemory(config, {
## Saving Conversations
-Conversation saving is enabled by default (`addMemory: "always"`). Messages are grouped using the required `customId`:
+Conversation saving is enabled by default (`addMemory: "always"`). Messages are grouped using the required `id`:
```typescript
const agent = new Agent(withSupermemory(
{ id: "my-assistant", model: openai("gpt-4o"), instructions: "..." },
{
- containerTag: "user-123",
- customId: "conv-456", // Required: groups messages for contextual memory
+ namespace: "user-123",
+ id: "conv-456", // Required: groups messages for contextual memory
}
))
@@ -175,8 +175,8 @@ To disable automatic saving:
const agent = new Agent(withSupermemory(
{ id: "my-assistant", model: openai("gpt-4o"), instructions: "..." },
{
- containerTag: "user-123",
- customId: "conv-456",
+ namespace: "user-123",
+ id: "conv-456",
addMemory: "never", // Only retrieve memories, don't save
}
))
@@ -207,8 +207,8 @@ const claudePrompt = (data: MemoryPromptData) => `
const agent = new Agent(withSupermemory(
{ id: "my-assistant", model: openai("gpt-4o"), instructions: "..." },
{
- containerTag: "user-123",
- customId: "conv-456",
+ namespace: "user-123",
+ id: "conv-456",
mode: "full",
promptTemplate: claudePrompt,
}
@@ -236,8 +236,8 @@ const agent = new Agent({
model: openai("gpt-4o"),
inputProcessors: [
createSupermemoryProcessor({
- containerTag: "user-123",
- customId: "conv-456",
+ namespace: "user-123",
+ id: "conv-456",
mode: "full",
addMemory: "never",
verbose: true,
@@ -261,8 +261,8 @@ const agent = new Agent({
model: openai("gpt-4o"),
outputProcessors: [
createSupermemoryOutputProcessor({
- containerTag: "user-123",
- customId: "conv-456",
+ namespace: "user-123",
+ id: "conv-456",
}),
],
})
@@ -278,8 +278,8 @@ import { createSupermemoryProcessors } from "@supermemory/tools/mastra"
import { openai } from "@ai-sdk/openai"
const { input, output } = createSupermemoryProcessors({
- containerTag: "user-123",
- customId: "conv-456",
+ namespace: "user-123",
+ id: "conv-456",
mode: "full",
verbose: true,
})
@@ -297,7 +297,7 @@ const agent = new Agent({
## 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`:
+For server setups where one agent instance handles multiple concurrent conversations, use Mastra's `RequestContext` to provide per-request thread IDs. **RequestContext takes precedence** over the construction-time `id`:
```typescript
import { Agent } from "@mastra/core/agent"
@@ -308,13 +308,13 @@ 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
+ namespace: "user-123",
+ id: "fallback-conv", // Used only when RequestContext doesn't provide a threadId
mode: "full",
}
))
-// Per-request threadId takes precedence over customId
+// Per-request threadId takes precedence over id
const ctx = new RequestContext()
ctx.set(MASTRA_THREAD_ID_KEY, "user-456-session-789")
@@ -323,7 +323,7 @@ await agent.generate("Hello!", { requestContext: ctx })
```
- **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.
+ **Server-side usage**: Always use `RequestContext` to pass unique conversation IDs per request. Using a fixed `id` for all requests will merge conversations from different users.
---
@@ -336,14 +336,14 @@ Enable detailed logging for debugging:
const agent = new Agent(withSupermemory(
{ id: "my-assistant", model: openai("gpt-4o"), instructions: "..." },
{
- containerTag: "user-123",
- customId: "conv-456",
+ namespace: "user-123",
+ id: "conv-456",
verbose: true,
}
))
// Console output:
-// [supermemory] Starting memory search { containerTag: "user-123", mode: "profile" }
+// [supermemory] Starting memory search { namespace: "user-123", mode: "profile" }
// [supermemory] Found 5 memories
// [supermemory] Injected memories into system prompt { length: 1523 }
```
@@ -366,8 +366,8 @@ const agent = new Agent(withSupermemory(
outputProcessors: [myAnalyticsProcessor],
},
{
- containerTag: "user-123",
- customId: "conv-456",
+ namespace: "user-123",
+ id: "conv-456",
}
))
```
@@ -389,7 +389,7 @@ function withSupermemory(
**Parameters:**
- `config` - The Mastra agent configuration object
-- `options` - Configuration options (includes required `containerTag` and `customId`)
+- `options` - Configuration options (includes required `namespace` and `id`)
**Returns:** Enhanced config with Supermemory processors injected
@@ -430,8 +430,8 @@ function createSupermemoryProcessors(
```typescript
interface SupermemoryMastraOptions {
- containerTag: string // Required: User/container tag for scoping memories
- customId: string // Required: Groups messages for contextual memory generation
+ namespace: string // Required: User/container tag for scoping memories
+ id: string // Required: Groups messages for contextual memory generation
apiKey?: string
baseUrl?: string
mode?: "profile" | "query" | "full"
@@ -463,8 +463,8 @@ Processors gracefully handle errors without breaking the agent:
const agent = new Agent(withSupermemory(
{ id: "my-assistant", model: openai("gpt-4o"), instructions: "..." },
{
- containerTag: "user-123",
- customId: "conv-456",
+ namespace: "user-123",
+ id: "conv-456",
apiKey: undefined, // Will check SUPERMEMORY_API_KEY env
}
))
diff --git a/apps/docs/integrations/n8n.mdx b/apps/docs/integrations/n8n.mdx
index 718c5695..bc3df45c 100644
--- a/apps/docs/integrations/n8n.mdx
+++ b/apps/docs/integrations/n8n.mdx
@@ -23,8 +23,10 @@ The Supermemory integration in n8n uses the HTTP Request node to interact with t

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`
+ - Add memory: `https://api.supermemory.ai/ns/{namespace}/document`
+ - Search memories: `https://api.supermemory.ai/ns/{namespace}/search`
+
+ Replace `{namespace}` with the namespace that scopes these memories, for example a user id or `gmail`.
4. For authentication, select **Generic Credential Type** and then **Bearer Auth**
5. Click on **Create New Credential** and paste the Supermemory API Key in the Bearer Token field.

@@ -51,17 +53,16 @@ Follow these steps to build a workflow that captures and stores your Gmail messa
1. **Add an HTTP Request node** after the Gmail Trigger
2. **Method**: `POST`
-3. **URL**: `https://api.supermemory.ai/v3/documents`
+3. **URL**: `https://api.supermemory.ai/ns/gmail/document` (the namespace `gmail` is part of the URL)
4. Select your auth credentials you created with the Supermemory API Key.
#### Step 3: Format email data for Supermemory
In the HTTP Request node's **Body**, select **JSON** and **Using Fields Below**
-And create 2 fields:
+And create 1 field:
1. name: `content`, value: `{{ $json.snippet }}`
-2. name: `containerTag`, value: gmail

diff --git a/apps/docs/integrations/openai-agents-sdk.mdx b/apps/docs/integrations/openai-agents-sdk.mdx
index 6d8cc229..1259a8bb 100644
--- a/apps/docs/integrations/openai-agents-sdk.mdx
+++ b/apps/docs/integrations/openai-agents-sdk.mdx
@@ -47,11 +47,11 @@ 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)
+ profile = memory.profile(user_id).profile
+ memories = memory.search(user_id, query=query, limit=5).results
- static = result.profile.static or []
- dynamic = result.profile.dynamic or []
- memories = result.search_results.results if result.search_results else []
+ static = [m.memory for m in profile.static]
+ dynamic = [m.memory for m in profile.dynamic]
return f"""
User background:
@@ -86,8 +86,9 @@ async def run_with_memory(user_id: str, message: str) -> str:
# Save for next time
memory.add(
+ user_id,
content=f"User asked: {message}\nResponse: {result.final_output}",
- container_tag=user_id
+ dreaming="instant"
)
return result.final_output
@@ -105,13 +106,10 @@ Supermemory keeps two buckets of user info:
- **Dynamic context**: What they're working on right now
```python
-result = memory.profile(
- container_tag="user_123",
- q="travel planning" # Also searches for relevant memories
-)
+result = memory.profile("user_123")
-print(result.profile.static) # ["Prefers window seats", "Vegetarian"]
-print(result.profile.dynamic) # ["Planning trip to Japan", "Traveling in March"]
+print([m.memory for m in result.profile.static]) # ["Prefers window seats", "Vegetarian"]
+print([m.memory for m in result.profile.dynamic]) # ["Planning trip to Japan", "Traveling in March"]
```
### Storing memories
@@ -121,9 +119,10 @@ Save agent interactions so future sessions have context:
```python
def store_interaction(user_id: str, task: str, result: str):
memory.add(
+ user_id,
content=f"Task: {task}\nOutcome: {result}",
- container_tag=user_id,
- metadata={"type": "agent_run"}
+ metadata={"type": "agent_run"},
+ dreaming="instant"
)
```
@@ -132,10 +131,10 @@ def store_interaction(user_id: str, task: str, result: str):
Look up past interactions before running an agent:
```python
-results = memory.search.memories(
- q="previous travel recommendations",
- container_tag="user_123",
- search_mode="hybrid",
+results = memory.search(
+ "user_123",
+ query="previous travel recommendations",
+ search_mode="hybrid", # Searches memories + document chunks
limit=5
)
@@ -163,11 +162,7 @@ def search_memories(query: str, user_id: str) -> str:
query: What to search for
user_id: The user's identifier
"""
- results = memory.search.memories(
- q=query,
- container_tag=user_id,
- limit=5
- )
+ results = memory.search(user_id, query=query, limit=5)
if not results.results:
return "No relevant memories found."
@@ -185,10 +180,7 @@ def save_memory(content: str, user_id: str) -> str:
content: The information to remember
user_id: The user's identifier
"""
- memory.add(
- content=content,
- container_tag=user_id
- )
+ memory.add(user_id, content=content, dreaming="instant")
return f"Saved: {content}"
agent = Agent(
@@ -222,16 +214,13 @@ class SupportAgent:
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
- )
+ profile = self.memory.profile(customer_id).profile
+ memories = self.memory.search(customer_id, query=issue, threshold=0.5).results
return {
- "profile": result.profile.static or [],
- "recent": result.profile.dynamic or [],
- "history": [m.memory for m in (result.search_results.results or [])[:3]]
+ "profile": [m.memory for m in profile.static],
+ "recent": [m.memory for m in profile.dynamic],
+ "history": [m.memory or m.chunk for m in memories[:3]]
}
def build_instructions(self, context: dict) -> str:
@@ -287,9 +276,10 @@ class SupportAgent:
# Store the interaction
self.memory.add(
+ customer_id,
content=f"Support request: {message}\nResolution: {result.final_output}",
- container_tag=customer_id,
- metadata={"type": "support", "resolved": True}
+ metadata={"type": "support", "resolved": True},
+ dreaming="instant"
)
return result.final_output
@@ -300,8 +290,9 @@ async def main():
# Add some customer context
support.memory.add(
+ "customer_456",
content="Premium customer since 2021. Prefers email communication.",
- container_tag="customer_456"
+ dreaming="instant"
)
response = await support.handle(
@@ -331,13 +322,8 @@ class AgentTeam:
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]])
+ result = self.memory.search(self.user_id, query=topic, limit=5)
+ return "\n".join([m.memory or m.chunk for m in result.results])
def create_researcher(self) -> Agent:
context = self.get_shared_context("research preferences")
@@ -367,9 +353,10 @@ User context: {context}""",
# Store research for the writer
self.memory.add(
+ self.user_id,
content=f"Research on {topic}: {research.final_output[:500]}",
- container_tag=self.user_id,
- metadata={"type": "research", "topic": topic}
+ metadata={"type": "research", "topic": topic},
+ dreaming="instant"
)
# Writing phase
@@ -391,8 +378,9 @@ Tags let you narrow down searches later:
```python
# Store with metadata
memory.add(
+ "user_123",
content="User prefers detailed technical explanations",
- container_tag="user_123",
+ dreaming="instant",
metadata={
"type": "preference",
"category": "communication_style",
@@ -401,13 +389,14 @@ memory.add(
)
# 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"}
+results = memory.search(
+ "user_123",
+ query="communication preferences",
+ filter={
+ "operator": "and",
+ "operands": [
+ {"field": "type", "operator": "eq", "value": "preference"},
+ {"field": "category", "operator": "eq", "value": "communication_style"}
]
}
)
diff --git a/apps/docs/integrations/openai.mdx b/apps/docs/integrations/openai.mdx
index 5ef012b2..fe23e3fa 100644
--- a/apps/docs/integrations/openai.mdx
+++ b/apps/docs/integrations/openai.mdx
@@ -49,8 +49,8 @@ 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
+ namespace: "user-123", // Required: identifies the user/container
+ id: "conversation-456", // Required: groups messages into the same document
mode: "full", // "profile" | "query" | "full"
addMemory: "always", // "always" (default) | "never"
})
@@ -70,10 +70,10 @@ const response = await client.chat.completions.create({
```typescript
const client = withSupermemory(openai, {
// Required: identifies the user/container
- containerTag: "user-123",
+ namespace: "user-123",
// Required: Group messages into the same document
- customId: "conv-456",
+ id: "conv-456",
// Memory search mode
mode: "full", // "profile" (user profile only), "query" (search only), "full" (both)
@@ -100,7 +100,7 @@ const client = withSupermemory(openai, {
### Works with Responses API too
```typescript
-const client = withSupermemory(openai, { containerTag: "user-123", customId: "conv-456", mode: "full" })
+const client = withSupermemory(openai, { namespace: "user-123", id: "conv-456", mode: "full" })
// Memories injected into instructions
const response = await client.responses.create({
@@ -203,7 +203,7 @@ const toolDefinitions = getToolDefinitions()
// Create tool executor
const executeToolCall = createToolCallExecutor(process.env.SUPERMEMORY_API_KEY!, {
- projectId: "your-project-id",
+ namespace: "your-project-id",
})
// Use with OpenAI Chat Completions
@@ -251,7 +251,7 @@ tools = SupermemoryTools(
import { supermemoryTools } from "@supermemory/tools/openai"
const tools = supermemoryTools(process.env.SUPERMEMORY_API_KEY!, {
- containerTags: ["your-user-id"],
+ namespace: "your-user-id",
baseUrl: "https://custom-endpoint.com", // optional
})
```
@@ -422,7 +422,7 @@ import readline from 'readline'
const client = new OpenAI()
const executeToolCall = createToolCallExecutor(process.env.SUPERMEMORY_API_KEY!, {
- projectId: "chat-example",
+ namespace: "chat-example",
})
const rl = readline.createInterface({
@@ -588,7 +588,7 @@ execute_memory_tool_calls(
```typescript
supermemoryTools(
apiKey: string,
- config?: { projectId?: string; baseUrl?: string }
+ config?: { namespace?: string; baseUrl?: string }
)
```
@@ -597,7 +597,7 @@ supermemoryTools(
```typescript
createToolCallExecutor(
apiKey: string,
- config?: { projectId?: string; baseUrl?: string }
+ config?: { namespace?: string; baseUrl?: string }
) -> (toolCall: OpenAI.Chat.ChatCompletionMessageToolCall) => Promise
```
diff --git a/apps/docs/integrations/supermemory-sdk.mdx b/apps/docs/integrations/supermemory-sdk.mdx
index ae99c3f1..24caa414 100644
--- a/apps/docs/integrations/supermemory-sdk.mdx
+++ b/apps/docs/integrations/supermemory-sdk.mdx
@@ -15,7 +15,7 @@ icon: "/images/supermemory.svg"
-Both SDKs also work against [self-hosted Supermemory](/self-hosting/overview) — pass `baseURL: "http://localhost:6767"` (TypeScript) or `base_url="http://localhost:6767"` (Python) when creating the client.
+Both SDKs also work against [self-hosted Supermemory](/self-hosting/overview) running `supermemory-server` v0.0.9 or later. Pass `baseUrl: "http://localhost:6767"` (TypeScript) or `base_url="http://localhost:6767"` (Python) when creating the client.
@@ -23,81 +23,241 @@ Both SDKs also work against [self-hosted Supermemory](/self-hosting/overview)
## Install the TypeScript SDK
```bash
- npm install supermemory
+ npm i supermemory
```
## Start a TypeScript client
```typescript
- import Supermemory from 'supermemory';
+ 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);
+ const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
```
- ## Run common TypeScript operations
+ One rule for every call: URL values first as positional arguments (`namespace`, then `id` where there is one), then a single object with everything else. A namespace scopes memories to one user, workspace, or tenant, and you pass it on every call.
+
+ ## Add
```typescript
- // Add with metadata
- await client.add({
- content: "Technical design doc",
- containerTag: "user_123",
- metadata: { category: "engineering", priority: "high" }
+ const { id, status } = await supermemory.add("user_123", {
+ content: "Meeting notes from Q1 planning",
+ id: "meeting-q1", // optional, your own id; the response echoes it
+ metadata: { category: "engineering", priority: "high" },
+ dreaming: "instant",
});
-
- // 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" });
```
- ## Handle TypeScript SDK errors
+ `dreaming` defaults to `"dynamic"`, which batches memory extraction. A fresh namespace can show zero memories and an empty profile for minutes. Pass `dreaming: "instant"` in quick-start flows and anywhere the next step is a memory search or a profile read.
- | Status | Error |
- |--------|-------|
- | 400 | `BadRequestError` |
- | 401 | `AuthenticationError` |
- | 403 | `PermissionDeniedError` |
- | 404 | `NotFoundError` |
- | 409 | `ConflictError` |
- | 422 | `UnprocessableEntityError` |
- | 429 | `RateLimitError` |
- | >=500 | `InternalServerError` |
+ ## Search
- 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`.
+ ```typescript
+ const { results, searchTime } = await supermemory.search("user_123", {
+ query: "planning notes",
+ limit: 10,
+ });
- Requires TypeScript >= 4.9, Node 20+, Deno 1.28+, or Bun 1.0+.
-
+ for (const result of results) {
+ console.log(result.memory ?? result.chunk, result.similarity);
+ }
+ ```
+
+ `searchMode` is `"hybrid"` (default), `"memories"`, or `"chunks"`. Filter on metadata with a typed expression:
+
+ ```typescript
+ const { results } = await supermemory.search("user_123", {
+ query: "design document",
+ filter: { field: "category", operator: "eq", value: "engineering" },
+ searchMode: "chunks",
+ });
+ ```
+
+ Two defaults changed from the legacy SDK: `threshold` is now `0.3` (was `0.6`) and `searchMode` is now `hybrid` (was `memories`). Set both explicitly if you compare results against old code.
+
+ ## Profile
+
+ ```typescript
+ const { profile } = await supermemory.profile("user_123");
+ console.log(profile.static);
+ console.log(profile.dynamic);
+ console.log(profile.buckets);
+ ```
+
+ Buckets group profile facts under names you define:
+
+ ```typescript
+ await supermemory.profiles.setBuckets("user_123", {
+ buckets: { work: "Job, team, and current projects" },
+ });
+
+ const buckets = await supermemory.profiles.getBuckets("user_123");
+
+ await supermemory.profiles.deleteBuckets("user_123", {
+ buckets: ["work"],
+ });
+ ```
+
+ ## List
+
+ ```typescript
+ const { documents, pagination } = await supermemory.list("user_123", "documents", {
+ page: 1,
+ limit: 20,
+ sort: "createdAt",
+ order: "desc",
+ });
+
+ console.log(documents[0]?.system.status, pagination.totalPages);
+ ```
+
+ `type` is required: `"documents"`, `"chunks"`, or `"memories"`. The response always has `documents`, `chunks`, `memories`, and `pagination`, and only the requested array is filled. Pass `{ filter }` to narrow the list.
+
+ ## Documents
+
+ ```typescript
+ const doc = await supermemory.documents.get("user_123", "meeting-q1", {
+ include: ["chunks", "memories"],
+ });
+ console.log(doc.title, doc.summary, doc.system.status);
+
+ await supermemory.documents.update("user_123", "meeting-q1", {
+ metadata: { priority: "low" },
+ });
+
+ await supermemory.documents.batchAdd("user_123", {
+ documents: [{ content: "First note" }, { content: "Second note" }],
+ });
+
+ await supermemory.documents.uploadFile("user_123", { // file is a File or Blob
+ file,
+ metadata: JSON.stringify({ source: "upload" }),
+ });
+
+ const { count, errors } = await supermemory.documents.delete("user_123", {
+ ids: ["meeting-q1"],
+ });
+ ```
+
+ `uploadFile` is multipart, so `metadata` is a JSON string there. `delete` can return partial failures, so check `errors` as well as `count`.
+
+ ## Memories
+
+ ```typescript
+ const preview = await supermemory.memories.forgetMatching("user_123", {
+ query: "old office address",
+ dryRun: true,
+ });
+
+ const { count } = await supermemory.memories.forget("user_123", {
+ ids: preview.matches.map((match) => match.id),
+ });
+ ```
+
+ `dryRun` is required on `forgetMatching`. Preview with `dryRun: true`, review `matches`, then forget by id. There is no direct memory create or update: ingest or update the source document instead.
+
+ ## Namespaces
+
+ ```typescript
+ const { namespaces, pagination } = await supermemory.namespaces.list({ limit: 50 });
+
+ const ns = await supermemory.namespaces.get("user_123");
+ console.log(ns.supportingContext, namespaces[0]?.documentCount, pagination.totalPages);
+
+ await supermemory.namespaces.update("user_123", {
+ supportingContext: "A paying customer on the Pro plan",
+ });
+
+ await supermemory.namespaces.delete("user_123", { // optional, moves content instead of deleting it
+ moveTo: "archive",
+ });
+ ```
+
+ ## Organization
+
+ ```typescript
+ const { organizationalContext, namespaceCount } = await supermemory.organization.get();
+
+ await supermemory.organization.update({
+ organizationalContext: "Acme sells billing software to dental clinics",
+ });
+ ```
+
+ ## Connectors
+
+ ```typescript
+ const { id, authorization } = await supermemory.connectors.create("user_123", {
+ provider: "notion",
+ redirectUrl: "https://app.example.com/connected",
+ });
+ // send the user to authorization.url before authorization.expiresAt
+
+ const connectors = await supermemory.connectors.list("user_123");
+ const everyConnector = await supermemory.connectors.listAll();
+
+ const connector = await supermemory.connectors.get("user_123", id, {
+ include: ["syncs", "picker"],
+ });
+ console.log(connector.latestRun?.system.status, connector.documentCount);
+
+ await supermemory.connectors.update("user_123", id, {
+ documentLimit: 500,
+ });
+
+ await supermemory.connectors.sync("user_123", id);
+
+ await supermemory.connectors.delete("user_123", id, { deleteDocuments: true });
+ ```
+
+ OAuth providers (`notion`, `google-drive`, `onedrive`, `gmail`, `github`) return an `authorization` link. Config providers start syncing right away and return `authorization: null`:
+
+ ```typescript
+ await supermemory.connectors.create("user_123", {
+ provider: "web-crawler",
+ config: { startUrl: "https://docs.example.com", crawlDepth: 2 },
+ });
+ ```
+
+ `sync` returns `{ id, status: "queued" }` and responds with 409 if a sync is already running.
+
+### Error handling
+
+Every HTTP error throws a `SupermemoryError` with `statusCode`, `body`, and `rawResponse`. Common statuses have their own subclasses, all exported from the package root: `BadRequestError` (400), `UnauthorizedError` (401), `PaymentRequiredError` (402), `ForbiddenError` (403), `NotFoundError` (404), `ConflictError` (409), `InternalServerError` (500), and `ServiceUnavailableError` (503). A timeout throws `SupermemoryTimeoutError`; a network failure is a `SupermemoryError` without a `statusCode`.
+
+```typescript
+import { Supermemory, NotFoundError, SupermemoryError } from "supermemory";
+
+try {
+ await supermemory.documents.get("user_123", "missing");
+} catch (error) {
+ if (error instanceof NotFoundError) {
+ console.log("gone");
+ } else if (error instanceof SupermemoryError) {
+ console.error(error.statusCode, error.body);
+ } else {
+ throw error;
+ }
+}
+```
+
+### Timeouts and retries
+
+The client retries connection errors, 408, 429, and 5xx responses twice with backoff. Tune it on the client or per call; the per-call options object is always the optional last argument.
+
+```typescript
+const supermemory = new Supermemory({
+ apiKey: process.env.SUPERMEMORY_API_KEY,
+ timeoutInSeconds: 30, // default 60
+ maxRetries: 0, // default 2
+});
+
+await supermemory.search("user_123", { query: "planning notes" }, {
+ timeoutInSeconds: 5,
+ maxRetries: 3,
+ abortSignal: controller.signal,
+});
+```
+
+
## Install the Python SDK
@@ -116,56 +276,89 @@ Both SDKs also work against [self-hosted Supermemory](/self-hosting/overview)
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"
+ # Add a document
+ result = client.add(
+ "user_123",
+ content="Meeting notes from Q1 planning",
+ id="meeting-q1", # optional, your own id; the response echoes it
+ metadata={"category": "engineering", "priority": "high"},
+ dreaming="instant",
)
- print(response.results)
+ print(result.id, result.status)
+
+ # Search (hybrid by default: memories plus document chunks)
+ response = client.search("user_123", query="planning notes", limit=10)
+ for hit in response.results:
+ print(hit.memory or hit.chunk, hit.similarity)
# Get user profile
- profile = client.profile(container_tag="user_123")
- print(profile.profile.static)
- print(profile.profile.dynamic)
+ profile = client.profile("user_123").profile
+ print(profile.static)
+ print(profile.dynamic)
+ print(profile.buckets)
```
## Run common Python operations
```python
- # Add with metadata
- client.add(
- content="Technical design doc",
- container_tag="user_123",
- metadata={"category": "engineering", "priority": "high"}
+ import json
+
+ # Search document chunks with a filter
+ results = client.search(
+ "user_123",
+ query="design document",
+ search_mode="chunks",
+ filter={"field": "category", "operator": "eq", "value": "engineering"},
)
- # Search with filters
- results = client.search.memories(
- q="design document",
- search_mode="documents",
- container_tag="user_123",
- filters={
- "AND": [
- {"key": "category", "value": "engineering"}
- ]
- }
- )
+ # Profile buckets
+ client.profiles.set_buckets("user_123", buckets={"work": "Job, team, and current projects"})
+ buckets = client.profiles.get_buckets("user_123")
+ client.profiles.delete_buckets("user_123", buckets=["work"])
# List documents
- docs = client.documents.list(container_tags=["user_123"], limit=10)
+ page = client.list("user_123", "documents", page=1, limit=20, sort="createdAt", order="desc")
+ print(page.pagination.total_pages)
- # Delete a document
- client.documents.delete(doc_id="doc_123")
+ # Get, update, batch add, upload
+ doc = client.documents.get("user_123", "meeting-q1", include=["chunks", "memories"])
+ client.documents.update("user_123", "meeting-q1", metadata={"priority": "low"})
+ client.documents.batch_add(
+ "user_123",
+ documents=[{"content": "First note"}, {"content": "Second note"}],
+ )
+ with open("notes.pdf", "rb") as f:
+ client.documents.upload_file("user_123", file=f, metadata=json.dumps({"source": "upload"}))
+
+ # Delete documents
+ result = client.documents.delete("user_123", ids=["meeting-q1"])
+
+ # Forget memories: preview, then apply
+ preview = client.memories.forget_matching("user_123", query="old office address", dry_run=True)
+ client.memories.forget("user_123", ids=[m.id for m in preview.matches])
+
+ # Namespaces and organization
+ page = client.namespaces.list() # page.namespaces, page.pagination
+ ns = client.namespaces.get("user_123")
+ client.namespaces.update("user_123", supporting_context="A paying customer on the Pro plan")
+ client.namespaces.delete("user_123", move_to="archive") # optional, moves content instead of deleting it
+ client.organization.update(organizational_context="Acme sells billing software to dental clinics")
+
+ # Connectors
+ setup = client.connectors.create(
+ "user_123",
+ request={"provider": "notion", "redirectUrl": "https://app.example.com/connected"},
+ )
+ # send the user to setup.authorization.url before setup.authorization.expires_at
+ connector = client.connectors.get("user_123", setup.id, include=["syncs", "picker"])
+ client.connectors.sync("user_123", setup.id)
+ client.connectors.delete("user_123", setup.id, delete_documents=True)
```
## Handle Python SDK errors
- 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.
+ Failed requests raise `supermemory.APIStatusError` (`.status_code`, `.body`) or a subclass such as `NotFoundError` or `RateLimitError`. Network failures raise `supermemory.APIConnectionError`. Set `max_retries=` and `timeout=` on the client, or per call. `AsyncSupermemory` has the same methods, awaited.
- Requires Python 3.9+.
+ Requires Python 3.10+.
diff --git a/apps/docs/integrations/voltagent.mdx b/apps/docs/integrations/voltagent.mdx
index 2178c13f..796a4a47 100644
--- a/apps/docs/integrations/voltagent.mdx
+++ b/apps/docs/integrations/voltagent.mdx
@@ -45,8 +45,8 @@ const configWithMemory = withSupermemory({
instructions: "You are a helpful assistant.",
model: openai("gpt-4o"),
},
- containerTag: "user-123",
- customId: "conversation-123",
+ namespace: "user-123",
+ id: "conversation-123",
})
const agent = new Agent(configWithMemory)
@@ -65,8 +65,8 @@ const result = await agent.generateText("What's my name?")
instructions: "You are a helpful assistant.",
model: openai("gpt-4o"),
},
- containerTag: "user-123",
- customId: "conversation-123",
+ namespace: "user-123",
+ id: "conversation-123",
addMemory: "never",
})
```
@@ -80,12 +80,12 @@ When integrated with VoltAgent, Supermemory hooks into two lifecycle events:
Before each LLM call, Supermemory automatically:
- Extracts the user's latest message
-- Searches for relevant memories scoped to the `containerTag`
+- Searches for relevant memories scoped to the `namespace`
- Injects retrieved memories into the system prompt
### 2. Conversation saving (onEnd)
-After each agent response, the conversation is saved to Supermemory for future retrieval. This requires a `customId` to be set.
+After each agent response, the conversation is saved to Supermemory for future retrieval. This requires a `id` to be set.
## Memory modes
@@ -102,8 +102,8 @@ const configWithMemory = withSupermemory({
instructions: "You are a helpful assistant.",
model: openai("gpt-4o"),
},
- containerTag: "user-123",
- customId: "conversation-123",
+ namespace: "user-123",
+ id: "conversation-123",
mode: "full",
})
```
@@ -120,12 +120,12 @@ const configWithMemory = withSupermemory({
},
// Required
- containerTag: "user-123", // User/project ID for scoping memories
+ namespace: "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
+ id: "conv-456", // Groups messages into a conversation
// Search tuning
searchMode: "hybrid", // "memories" | "documents" | "hybrid"
@@ -146,10 +146,10 @@ const configWithMemory = withSupermemory({
| Parameter | Type | Default | Description |
| ----------------- | -------- | ------------ | -------------------------------------------------------- |
| `agentConfig` | object | **required** | VoltAgent agent configuration object |
-| `containerTag` | string | **required** | User/project ID for scoping memories |
+| `namespace` | string | **required** | User/project ID for scoping memories |
| `mode` | string | `"profile"` | Memory retrieval mode |
| `addMemory` | string | `"always"` | Whether to save conversations after each response |
-| `customId` | string | **required** | Custom ID to group messages into a conversation |
+| `id` | string | **required** | Custom ID to group messages into a conversation |
| `searchMode` | string | — | `"memories"`, `"documents"`, or `"hybrid"` |
| `threshold` | number | — | Similarity threshold (0 = more results, 1 = more accurate) |
| `limit` | number | — | Maximum number of memory results (integer from 1 to 100) |
diff --git a/apps/docs/integrations/zapier.mdx b/apps/docs/integrations/zapier.mdx
index dc50c9d1..c447dd9e 100644
--- a/apps/docs/integrations/zapier.mdx
+++ b/apps/docs/integrations/zapier.mdx
@@ -31,16 +31,16 @@ For this tutorial, we're building a simple flow that adds incoming emails in Gma

- Since we're ingesting data here, we'll use the add documents endpoint.
+ Since we're ingesting data here, we'll use the add document endpoint. The namespace (`gmail` here) is part of the URL.
Add the following code block:
```python
import requests
- url = "https://api.supermemory.ai/v3/documents"
+ url = "https://api.supermemory.ai/ns/gmail/document"
- payload = { "content": inputData['content'], "containerTag": "gmail" }
+ payload = { "content": inputData['content'] }
headers = {
"Authorization": "Bearer YOUR_SM_API_KEY",
"Content-Type": "application/json"
diff --git a/apps/docs/migration/from-mem0.mdx b/apps/docs/migration/from-mem0.mdx
index 24c71632..0782c082 100644
--- a/apps/docs/migration/from-mem0.mdx
+++ b/apps/docs/migration/from-mem0.mdx
@@ -36,10 +36,7 @@ data = mem0.get_memory_export(memory_export_id=export["id"])
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"]
- )
+ supermemory.add("imported_from_mem0", content=memory["content"])
print(f"✅ {memory['content'][:50]}...")
print("Migration complete!")
@@ -141,8 +138,8 @@ print("Migration complete!")
# Import to Supermemory
try:
result = client.add(
+ "imported_from_mem0",
content=content,
- container_tags=["imported_from_mem0"],
metadata={
"source": "mem0",
"created_at": memory.get("created_at"),
@@ -176,14 +173,13 @@ client.add(
)
```
-```python Supermemory
-from supermemory import Supermemory
+```typescript Supermemory
+import { Supermemory } from "supermemory";
-client = Supermemory(api_key="...")
-client.add(
- content="User prefers dark mode",
- container_tags=["user_alice"]
-)
+const client = new Supermemory({ apiKey: "..." });
+await client.add("user_alice", {
+ content: "User prefers dark mode",
+});
```
@@ -199,11 +195,11 @@ results = client.search(
)
```
-```python Supermemory
-results = client.search.memories(
- q="user preferences",
- container_tag="user_alice"
-)
+```typescript Supermemory
+const { results } = await client.search("user_alice", {
+ query: "user preferences",
+ searchMode: "memories",
+});
```
@@ -218,11 +214,10 @@ memories = client.get_all(
)
```
-```python Supermemory
-memories = client.documents.list(
- container_tags=["user_alice"],
- limit=100
-)
+```typescript Supermemory
+const { memories } = await client.list("user_alice", "memories", {
+ limit: 100,
+});
```
@@ -235,8 +230,10 @@ memories = client.documents.list(
client.delete(memory_id="mem_123")
```
-```python Supermemory
-client.documents.delete("mem_123")
+```typescript Supermemory
+await client.documents.delete("user_alice", {
+ ids: ["mem_123"],
+});
```
diff --git a/apps/docs/migration/from-zep.mdx b/apps/docs/migration/from-zep.mdx
index 2971aa1c..d8960d72 100644
--- a/apps/docs/migration/from-zep.mdx
+++ b/apps/docs/migration/from-zep.mdx
@@ -8,10 +8,10 @@ sidebarTitle: "From Zep"
| 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: "..."})` |
+| Sessions & Messages | Documents & Namespaces |
+| `session.create()` | Use the `namespace` parameter |
+| `memory.add(session_id, ...)` | `client.add(namespace, { content })` |
+| `memory.search(session_id, {text: ...})` | `client.search(namespace, { query })` |
## Installation
@@ -54,9 +54,9 @@ session = client.session.create(
)
```
-```python Supermemory
-# No explicit session creation - use containerTag
-containerTag = "user_123"
+```typescript Supermemory
+// No explicit session creation, use a namespace
+const namespace = "user_123";
```
@@ -72,11 +72,10 @@ client.memory.add(
)
```
-```python Supermemory
-client.add({
- "content": "User prefers dark mode",
- "containerTag": "user_123"
-})
+```typescript Supermemory
+await client.add("user_123", {
+ content: "User prefers dark mode",
+});
```
@@ -92,12 +91,11 @@ results = client.memory.search(
)
```
-```python Supermemory
-results = client.search.execute({
- "q": "preferences",
- "containerTag": "user_123",
- "limit": 5
-})
+```typescript Supermemory
+const { results } = await client.search("user_123", {
+ query: "preferences",
+ limit: 5,
+});
```
@@ -110,11 +108,10 @@ results = client.search.execute({
memories = client.memory.get(session_id="user_123")
```
-```python Supermemory
-documents = client.documents.list({
- "containerTags": ["user_123"],
- "limit": 100
-})
+```typescript Supermemory
+const { documents } = await client.list("user_123", "documents", {
+ limit: 100,
+});
```
@@ -122,9 +119,9 @@ documents = client.documents.list({
## 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`
+2. **Map sessions to namespaces** - Replace `session_id="user_123"` with `namespace: "user_123"`
+3. **Update method calls** - Use `add()` and `search()` instead of `memory.add()` and `memory.search()`
+4. **Change search parameter** - Use `query` instead of `text`
5. **Handle async processing** - Documents process asynchronously (status: `queued` → `done`)
## Complete example
@@ -148,33 +145,31 @@ results = client.memory.search("user_123", {
})
```
-```python Supermemory
-from supermemory import Supermemory
+```typescript Supermemory
+import { Supermemory } from "supermemory";
-client = Supermemory(api_key="...")
-containerTag = "user_123"
+const client = new Supermemory({ apiKey: "..." });
+const namespace = "user_123";
-client.add({
- "content": "I love Python",
- "containerTag": containerTag,
- "metadata": {"role": "user"}
-})
+await client.add(namespace, {
+ content: "I love Python",
+ metadata: { role: "user" },
+});
-results = client.search.execute({
- "q": "programming",
- "containerTag": containerTag,
- "limit": 3
-})
+const { results } = await client.search(namespace, {
+ query: "programming",
+ limit: 3,
+});
```
## Important notes
-- **No session creation needed** - Just use `containerTag` in requests
+- **No session creation needed** - Just pass `namespace` on each call
- **Messages are documents** - Store with `metadata.role` and `metadata.type`
-- **Async processing** - Documents may take a moment to be searchable
-- **Response structure** - Supermemory returns chunks with scores, not direct memory content
+- **Async processing** - Documents may take a moment to be searchable. Pass `dreaming: "instant"` on `add` when you need memories right away
+- **Response structure** - `search` returns `results`, each with a `memory` or `chunk` and a `similarity` score
## Migrating existing data
@@ -201,15 +196,14 @@ for (const sessionId of sessionIds) {
for (const mem of memories) {
if (mem.content) {
- await supermemory.add({
+ await supermemory.add(`session:${sessionId}:user:${memory.user_id || "unknown"}`, {
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)}...`);
}
@@ -236,16 +230,16 @@ for session_id in session_ids:
for mem in memories:
if mem.content:
- supermemory.add({
- "content": mem.content,
- "containerTag": f"session:{session_id}:user:{memory.user_id or 'unknown'}",
- "metadata": {
+ supermemory.add(
+ f"session:{session_id}:user:{memory.user_id or 'unknown'}",
+ content=mem.content,
+ metadata={
"role": mem.role,
"type": "message",
"original_uuid": mem.uuid,
- **(mem.metadata or {})
- }
- })
+ **(mem.metadata or {}),
+ },
+ )
print(f"✅ Imported: {mem.content[:50]}...")
print("Migration complete!")
@@ -315,9 +309,9 @@ async function migrateFromZep(
// Import to Supermemory
let totalMemories = 0;
for (const [sessionId, data] of Object.entries(exportedData) as any) {
- let containerTag = `imported_from_zep:session:${sessionId}`;
+ let namespace = `imported_from_zep:session:${sessionId}`;
if (data.session.user_id) {
- containerTag += `:user:${data.session.user_id}`;
+ namespace += `:user:${data.session.user_id}`;
}
for (const memory of data.memories) {
@@ -328,9 +322,8 @@ async function migrateFromZep(
continue;
}
- await supermemory.add({
+ await supermemory.add(namespace, {
content: memory.content,
- containerTag: containerTag,
metadata: {
source: "zep_migration",
role: memory.role,
diff --git a/apps/docs/migration/tools-v3-upgrade.mdx b/apps/docs/migration/tools-v3-upgrade.mdx
new file mode 100644
index 00000000..3b91fffd
--- /dev/null
+++ b/apps/docs/migration/tools-v3-upgrade.mdx
@@ -0,0 +1,64 @@
+---
+title: "Upgrading @supermemory/tools to 3.0"
+description: "Migrate from @supermemory/tools 2.x to 3.0: one namespace per config, v5 option names, conversations stored as documents."
+sidebarTitle: "Tools: 2.x → 3.0"
+keywords: ["containerTags", "projectId", "customId", "namespace", "tools"]
+---
+
+`@supermemory/tools` 3.0 moves every integration (Vercel AI SDK, OpenAI, Mastra, VoltAgent, Claude memory) to the [v5 API](/migration/api-v5) through `supermemory@5`. The names changed to match: a **namespace** is what 2.x called a container tag.
+
+```bash
+npm install @supermemory/tools@3 supermemory@5
+```
+
+## One namespace per config
+
+`containerTags` and `projectId` are gone. Pass one `namespace`, and every tool reads and writes only that namespace.
+
+```ts
+// 2.x
+supermemoryTools(apiKey, { containerTags: ["user_123"] })
+supermemoryTools(apiKey, { projectId: "personal" })
+
+// 3.0
+supermemoryTools(apiKey, { namespace: "user_123" })
+supermemoryTools(apiKey, { namespace: "sm_project_personal" }) // projectId "x" lived at sm_project_x
+```
+
+With no config, tools still use `sm_project_default`, so existing data is where it was. If you passed several tags, pick one: a v5 document lives in exactly one namespace.
+
+## v5 option names in `withSupermemory`
+
+| 2.x | 3.0 |
+| --- | --- |
+| `containerTag` | `namespace` |
+| `customId` | `id` |
+| `memoryContainerTag` (Claude memory) | removed; files are marked with `metadata.source = "claude-memory"` |
+
+```ts
+// 2.x
+withSupermemory(model, { containerTag: "user_123", customId: "conv_456" })
+
+// 3.0
+withSupermemory(model, { namespace: "user_123", id: "conv_456" })
+```
+
+Mastra, VoltAgent and the OpenAI middleware take the same two names. There are no aliases for the old ones.
+
+## Behaviour changes
+
+- **Conversations are documents.** The middlewares no longer call `/v4/conversations`. Each conversation is one document whose `id` is the `id` you pass, so the same conversation keeps updating the same document.
+- **`memoryForget`** drops `reason`. Forgetting by `memoryContent` previews matching memories with a dry run, then forgets only exact text matches.
+- **`getProfile`** returns v5 entries, `{ id, memory }` instead of strings. Those ids work with `memoryForget`.
+- **`documentList`** returns v5 documents; the status is `system.status`.
+- **No per-call scope.** `getProfile`, `documentList`, `documentDelete` and `memoryForget` no longer accept a `containerTag` argument.
+- **VoltAgent** search options use v5 shapes: `filters` becomes typed `filter`, `rerank` is `"none" | "order" | "aggregate"`, `searchMode: "documents"` is `"chunks"`, and `entityContext` is `supportingContext`.
+- **Claude memory** files written by 2.x under two tags are not migrated.
+
+## `@supermemory/ai-sdk` is retired
+
+It was a re-export of `@supermemory/tools/ai-sdk`. Import from there instead.
+
+```ts
+import { supermemoryTools } from "@supermemory/tools/ai-sdk"
+```
diff --git a/apps/docs/overview/billing.mdx b/apps/docs/overview/billing.mdx
index 936d90a9..1cb4dc0d 100644
--- a/apps/docs/overview/billing.mdx
+++ b/apps/docs/overview/billing.mdx
@@ -29,7 +29,7 @@ USD CREDIT BALANCE ──draw──► METERS
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**.
+4. **Re-ingesting the same document under the same `id` only bills the net-new token delta** — already-seen content is effectively **fully discounted**.
---
@@ -81,8 +81,8 @@ Search is primarily metered as **`sm_search_queries`** (one unit per gated searc
| 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) |
+| `POST /ns/{namespace}/search`, `supermemory.search()` | `sm_search_queries` (+1) |
+| `POST /ns/{namespace}/profile`, `supermemory.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.
@@ -94,7 +94,7 @@ 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:
+When you re-add content under the **same `id`** (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
@@ -106,24 +106,23 @@ billableTokens = max(0, fullTokenCount - previousTokenCount)
So **tokens Supermemory has already processed are not billed again**. Unchanged content costs nothing on the token meters; only the net-new delta, and any new extraction on it, 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.
+This is why long-lived agent loops stay cheap: re-sync the same conversation `id`, 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. |
+| **Stable `id`** | 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. |
+| **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 `id` for chat. |
### Practical patterns
```typescript
// Session stays one document — only new turns bill tokens
-await client.add({
+await supermemory.add(userId, {
content: "user: " + msg + "\nassistant: " + reply,
- containerTag: userId,
- customId: "chat_" + sessionId, // stable for the session
+ id: "chat_" + sessionId, // stable for the session
dreaming: "instant", // optional: +1 operation, faster memory
});
```
@@ -135,7 +134,7 @@ Connectors already use stable IDs (e.g. Drive file id, Gmail thread id, `s3://bu
- **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
+- **Brand-new `id`** = brand-new document = full tokens
---
@@ -324,11 +323,11 @@ console.log({
## Cost control checklist (production)
-1. **Always set stable `customId`** on conversations and docs so re-sync is free on old tokens.
+1. **Always set a stable `id`** on conversations and docs so re-sync is free on old tokens.
2. Prefer **session-level** conversation documents over one-line micro-adds (better memory **and** less wasted processing).
3. Use **`dreaming: "instant"`** only when you need immediate memory (extra operation); default `"dynamic"` batches extraction.
4. Cache **profiles** short-TTL in your app if you call them every turn.
-5. Scope with **`containerTag`** so you can delete/export a tenant without scanning the org.
+5. Scope with a **`namespace`** so you can delete/export a tenant without scanning the org.
6. Configure **auto top-up purchase limits** on paid plans and monitor usage separately. These limits do not cap the entire invoice.
7. Pull **`/v3/auth/billing/usage-events`** into your own FinOps dashboard for per-day Memory vs SuperRAG spend.
@@ -338,6 +337,6 @@ console.log({
- [Pricing page](https://supermemory.ai/pricing) — live plan marketing rates
- [Console billing](https://console.supermemory.ai) — plan, invoices, top-ups
-- [Add context](/ingestion/add-memories) — `customId`, `dreaming`
+- [Add context](/ingestion/add-memories) — `id`, `dreaming`
- [Analytics](/overview/analytics) — request-level observability
- [Security & compliance](/overview/security) — trust posture for enterprise review
diff --git a/apps/docs/overview/comparison.mdx b/apps/docs/overview/comparison.mdx
index e75104c7..1b2863a3 100644
--- a/apps/docs/overview/comparison.mdx
+++ b/apps/docs/overview/comparison.mdx
@@ -27,7 +27,7 @@ Non-detailed reasons to pick supermemory over the alternatives. If you're migrat
## vs pure RAG / document search products
-- **Both layers, one engine.** SuperRAG for corpus grounding, memory and profiles for people, sharing the same container tags.
+- **Both layers, one engine.** SuperRAG for corpus grounding, memory and profiles for people, sharing the same namespaces.
- **Personal state isn't just another document.** "What the policy says" and "what this customer decided last quarter" stay distinct but connected.
- **No second vendor for personalization.** You're not stitching a memory product onto your RAG stack.
@@ -111,7 +111,7 @@ If you are migrating from a specific tool, use the [migration guides](/migration
- 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 grounds answers in your documents, and memory plus profiles cover people and entities. They share container tags, so isolation stays consistent. Read [Memory vs RAG](/concepts/memory-vs-rag).
+**Supermemory instead:** **both** layers in one engine, SuperRAG for corpus grounding, memory + profiles for people and entities. They share namespaces so isolation stays coherent. Deep dive: [Memory vs RAG](/concepts/memory-vs-rag).
vs Building a full context engine in-house
@@ -128,7 +128,7 @@ If you are migrating from a specific tool, use the [migration guides](/migration
- 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
+- Authz (scoped keys, namespace boundaries), billing metering, SOC 2 / GDPR / HIPAA paths
- Sub-300ms retrieval under agent-loop load, deployability, maintaining it forever as the industry changes
**Supermemory instead:** that platform as a product, managed cloud or
@@ -140,7 +140,7 @@ self-hosted binary, so your team ships agents and apps, not a second infrastruct
|---|---|
| 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) |
+| Both personalization *and* company docs | Supermemory (memory + SuperRAG, same namespaces) |
| Full control, data never leaves your network | Supermemory [self-host](/self-hosting/overview) / Enterprise |
| Maximum control of every model weight and storage engine | Build in-house (or fork open pieces and accept the ops) |
diff --git a/apps/docs/overview/security.mdx b/apps/docs/overview/security.mdx
index 3934832b..6a97e744 100644
--- a/apps/docs/overview/security.mdx
+++ b/apps/docs/overview/security.mdx
@@ -7,7 +7,7 @@ icon: "/icons/hugeicons/shield-01.svg"
Supermemory stores long-horizon context about people and organizations. Security and compliance are part of the product surface, not a footer claim.
-This page is the product-level trust overview. For multi-tenant design details, see [Container tags](/concepts/container-tags) and authentication docs in the Developer Platform.
+This page is the product-level trust overview. For multi-tenant design details, see [Namespaces](/concepts/container-tags) and authentication docs in the Developer Platform.
## Compliance posture
@@ -28,11 +28,11 @@ Need a report, DPA, or BAA? Contact [support@supermemory.com](mailto:support@sup
### 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.
+- **Namespaces** (what v3/v4 called container tags) enforce hard boundaries between users, tenants, or projects — the primary multi-tenancy primitive.
+- **API keys** authenticate every request. Prefer **scoped keys** when a client or session must only touch one namespace.
- **Organizations** in the console manage members, keys, and billing separation.
-A malicious or buggy client with a correctly scoped key cannot read another container’s memories.
+A malicious or buggy client with a correctly scoped key cannot read another namespace’s memories.
### Data use
@@ -50,8 +50,8 @@ Supermemory is infrastructure for *your* agents. Your customer content is never
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.
+1. Scope each end-user (or tenant) to a **namespace**.
+2. When the user requests deletion, delete that namespace’s content via the API / console workflows for documents and memories under that namespace.
3. Revoke any **scoped keys** issued for that user.
Designing isolation up front makes erasure a single boundary operation instead of a forensic search.
@@ -76,7 +76,7 @@ If policy requires data never leave your network, run the [self-hosted engine](/
Which tiers include BAAs, seats, and self-host options.
-
+
How isolation works in the data model.
diff --git a/apps/docs/overview/use-cases.mdx b/apps/docs/overview/use-cases.mdx
index 53d09c1b..7297e0a5 100644
--- a/apps/docs/overview/use-cases.mdx
+++ b/apps/docs/overview/use-cases.mdx
@@ -14,7 +14,7 @@ Supermemory is one context engine. These are the shapes teams ship on top of it.
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.
+ Account history, past tickets, and product facts at answer time. Isolate each customer with a namespace.
Remember the account, stakeholders, and last commitments. Profiles keep “always know” context warm.
@@ -45,13 +45,13 @@ Supermemory is one context engine. These are the shapes teams ship on top of it.
If you are building a SaaS that needs memory **per end user** (or per workspace):
-1. Map each user/workspace to a **container tag**
+1. Map each user/workspace to a **namespace**
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
+5. On account deletion, erase that namespace’s data
-Deep dive lives in Developer Platform concepts ([container tags](/concepts/container-tags), [user profiles](/concepts/user-profiles)).
+Deep dive lives in Developer Platform concepts ([namespaces](/concepts/container-tags), [user profiles](/concepts/user-profiles)).
## Surfaces (same engine)
diff --git a/apps/docs/overview/what-is-supermemory.mdx b/apps/docs/overview/what-is-supermemory.mdx
index 85a4f298..d145c9ce 100644
--- a/apps/docs/overview/what-is-supermemory.mdx
+++ b/apps/docs/overview/what-is-supermemory.mdx
@@ -144,11 +144,9 @@ Supermemory leads the LongMemEval and LoCoMo benchmarks and independent ones suc

-- You send raw data in any format (text, files and chats), or connect a data source.
-- Supermemory indexes it and builds a graph of what it learns about each entity: a user, a document, a project or an organization. Each entity is identified by a `containerTag`.
-- Your agent searches that graph for memory or retrieval, and Supermemory keeps a profile of each entity up to date.
-
-For the full pipeline, read [how ingestion works](/concepts/how-it-works).
+- You send Supermemory raw data in any format - text, files, and chats, or connect it to the data sources
+- Supermemory [intelligently indexes them](/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 a `namespace` (v3/v4 called it a container tag)
+- 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?
@@ -167,18 +165,18 @@ Think of memory as the context a good teammate carries in their head, not a sear
## Why Supermemory?
-- **It leads long-horizon memory benchmarks.** First on LongMemEval, LoCoMo and ConvoMem, and strong on independent benchmarks like SWEContext.
-- **Memory is a graph.** Facts update, connect and expire in real time instead of sitting as nearest-neighbor chunks.
-- **User profiles are built in.** Static and dynamic facts about each user are ready to drop into the prompt.
-- **Memory and RAG share one engine.** Personalization and document search run on the same `containerTag`.
-- **Every entry point uses one store.** The API, MCP, plugins, SMFS and connectors all read and write the same memories.
-- **It is multimodal by default.** Text, chats, PDFs, images, video and code are extracted automatically, whether you upload them or sync them.
-- **It runs where you need it.** Use the managed cloud, or self-host a single binary that also works offline.
+- **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](/concepts/graph-memory) in real time; not nearest-neighbor chunks alone
+- **User profiles built in** — static + dynamic context the agent should [always know](/concepts/user-profiles), ~ready for the prompt
+- **Memory + SuperRAG in one engine** — personalize *and* ground on the same `namespace` / context pool
+- **Every door, one store** — API, [MCP](/supermemory-mcp/mcp), plugins, [SMFS](/smfs/overview), and connectors share the same memories
+- **Multimodal by default** — text, chats, PDFs, images, video, code via [extractors](/concepts/content-types) and [connectors](/connectors/overview)
+- **Run it your way** — managed cloud or [self-host](/self-hosting/overview) as a single binary (including offline)

-Memory, profiles and SuperRAG share one context pool when they use the same `containerTag`. A container can be any scope you choose: a user, a project, a team or an organization.
+Memory, profiles, and SuperRAG share the **same context pool** when you use the same isolation (`namespace`). Mix and match for your product! A namespace can be anything - a user, a project, team, organization, etc.
## Next steps
diff --git a/apps/docs/quickstart.mdx b/apps/docs/quickstart.mdx
index ad73b4f6..d3943eac 100644
--- a/apps/docs/quickstart.mdx
+++ b/apps/docs/quickstart.mdx
@@ -11,7 +11,7 @@ By the end of this page you will:
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.
+Same `namespace` for everything. One engine, three ways out. A namespace is what v3/v4 called a container tag.
## Get an API key
@@ -21,7 +21,7 @@ Grab a key from the [developer console](https://console.supermemory.ai) — **AP
```bash TypeScript
-npm install supermemory
+npm i supermemory
export SUPERMEMORY_API_KEY="sm_..."
```
@@ -37,16 +37,16 @@ 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.
+Real apps do not push four isolated one-liners as separate “memories.” They send **conversation turns** — often the full session — under a stable `id` so the pipeline can extract facts and link entities.
We’ll use one user (`user_4f8a`) and one chat session. The turns never say “Sarah *is* my VP of Product” — that connection is what the graph should resolve later.
```typescript TypeScript
-import Supermemory from "supermemory";
+import { Supermemory } from "supermemory";
-const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
-const user = "user_4f8a";
+const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
+const user = "user_4f8a"; // the namespace
const conversation = `
user: Just got back from Tokyo — the team offsite went great.
@@ -59,10 +59,9 @@ user: I need a gift idea for my VP of Product.
assistant: Happy to help brainstorm something personal.
`.trim();
-const conv = await client.add({
+const conv = await supermemory.add(user, {
content: conversation,
- containerTag: user,
- customId: "chat_offsite_2026", // one session → one document
+ id: "chat_offsite_2026", // one session → one document
metadata: { type: "conversation" },
dreaming: "instant", // process this document now — see note below
});
@@ -88,25 +87,23 @@ assistant: Happy to help brainstorm something personal.
""".strip()
conv = client.add(
+ user,
content=conversation,
- container_tag=user,
- custom_id="chat_offsite_2026",
+ id="chat_offsite_2026", # one session → one document
metadata={"type": "conversation"},
- dreaming="instant", # process this document now — see note below
+ dreaming="instant", # process this document now, see note below
)
-print(conv.id, conv.status)
+print(conv.id, conv.status) # e.g. "queued"
```
```bash curl
-curl -X POST "https://api.supermemory.ai/v3/documents" \
+curl -X POST "https://api.supermemory.ai/ns/user_4f8a/document?dreaming=instant" \
-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"
+ "id": "chat_offsite_2026",
+ "metadata": { "type": "conversation" }
}'
```
@@ -114,7 +111,7 @@ curl -X POST "https://api.supermemory.ai/v3/documents" \
`add` returns immediately with `status: "queued"`. Processing is still **async** — wait until `done` before searching.
-By default, dreaming is `"dynamic"`: Supermemory may batch related documents so memories form from coherent units, which can lag behind `status: "done"`. This quickstart passes `dreaming: "instant"` so each document is processed on its own as soon as it finishes indexing. Use it whenever you need memories or profiles right away; it bills one extra operation per document. See [processing modes](/ingestion/add-memories#processing-modes).
+**`dreaming: "instant"`** — By default, dreaming is `"dynamic"`: Supermemory batches related documents so memories form from coherent units, so a fresh namespace can show zero memories and an empty profile for several minutes. For this quickstart (and any path where you need memories/profiles right away), pass **`dreaming: "instant"`** so the document is processed on its own as soon as it finishes indexing. That bills one extra operation per document. See [Processing Modes](/ingestion/add-memories#processing-modes).
## 2. Ingest a document
@@ -134,12 +131,11 @@ 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({
+const doc = await supermemory.add(user, {
content: handbook,
- containerTag: user,
- customId: "doc_gift_policy",
+ id: "doc_gift_policy",
metadata: { type: "document", source: "handbook" },
- taskType: "superrag"
+ taskType: "superrag",
});
console.log(doc.id, doc.status);
@@ -158,32 +154,30 @@ offsite city are common picks. Tokyo offsites often inspire travel-themed gifts.
""".strip()
doc = client.add(
+ user,
content=handbook,
- container_tag=user,
- custom_id="doc_gift_policy",
+ id="doc_gift_policy",
metadata={"type": "document", "source": "handbook"},
- task_type="superrag"
+ task_type="superrag",
)
print(doc.id, doc.status)
```
```bash curl
-curl -X POST "https://api.supermemory.ai/v3/documents" \
+curl -X POST "https://api.supermemory.ai/ns/user_4f8a/document?taskType=superrag" \
-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"
+ "id": "doc_gift_policy",
+ "metadata": { "type": "document", "source": "handbook" }
}'
```
## 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`).
+Poll document status. With **`dreaming: "instant"`**, once `system.status` is `done` the document is indexed **and** memories for that document should be available for search and profiles (`queued → extracting → … → done`).
> Note that the preferred way is to have `dreaming: dynamic`. supermemory charges one extra operation for instant dreaming. Instant is good for one off tests, setup, debugging and benchmarking.
@@ -191,8 +185,8 @@ Poll document status. With **`dreaming: "instant"`**, once status is `done` the
```typescript TypeScript
async function waitUntilDone(id: string) {
for (;;) {
- const d = await client.documents.get(id);
- if (d.status === "done" || d.status === "failed") return d;
+ const d = await supermemory.documents.get(user, id);
+ if (d.system.status === "done" || d.system.status === "failed") return d;
await new Promise((r) => setTimeout(r, 1500));
}
}
@@ -207,8 +201,8 @@ import time
def wait_until_done(doc_id: str):
while True:
- d = client.documents.get(doc_id)
- if d.status in ("done", "failed"):
+ d = client.documents.get(user, doc_id)
+ if d.system.status in ("done", "failed"):
return d
time.sleep(1.5)
@@ -219,9 +213,9 @@ print("ready to search")
```bash curl
# replace DOC_ID with each document id from the add responses
-curl "https://api.supermemory.ai/v3/documents/DOC_ID" \
+curl "https://api.supermemory.ai/ns/user_4f8a/document/DOC_ID" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY"
-# repeat until "status": "done"
+# repeat until "system": { "status": "done" }
```
@@ -235,56 +229,44 @@ Chunk-level retrieval over raw knowledge — use when you need **what the docs s
```typescript TypeScript
-const rag = await client.search({
- q: "gift ideas for a VP promotion after a Tokyo offsite",
- containerTag: user,
- searchMode: "documents",
+const rag = await supermemory.search(user, {
+ query: "gift ideas for a VP promotion after a Tokyo offsite",
+ searchMode: "chunks",
limit: 3,
});
for (const hit of rag.results) {
- console.log(hit.title ?? hit.id);
- for (const chunk of hit.chunks ?? []) {
- console.log(" ", chunk.content?.slice(0, 160));
- }
+ console.log(hit.similarity, hit.chunk);
}
```
```python Python
-rag = client.search.memories(
- q="gift ideas for a VP promotion after a Tokyo offsite",
- container_tag=user,
- search_mode="documents",
+rag = client.search(
+ user,
+ query="gift ideas for a VP promotion after a Tokyo offsite",
+ search_mode="chunks",
limit=3,
)
for hit in rag.results:
- print(getattr(hit, "title", None) or hit.id)
- for chunk in hit.chunks or []:
- print(" ", (chunk.content or "")[:160])
+ print(hit.similarity, hit.chunk)
```
```bash curl
-curl -X POST "https://api.supermemory.ai/v3/search" \
+curl -X POST "https://api.supermemory.ai/ns/user_4f8a/search?searchMode=chunks&limit=3" \
-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
- }'
+ -d '{ "query": "gift ideas for a VP promotion after a Tokyo offsite" }'
```
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:
+Omit `searchMode` (or set it to `"hybrid"`, the default) to get extracted memories and document chunks together:
```typescript
-await client.search({
- q: "gift ideas for a VP promotion after a Tokyo offsite",
- containerTag: user,
+await supermemory.search(user, {
+ query: "gift ideas for a VP promotion after a Tokyo offsite",
searchMode: "hybrid",
limit: 5,
});
@@ -296,11 +278,10 @@ Search **extracted memories** with related edges. This is the entity-chain momen
```typescript TypeScript
-const memories = await client.search({
- q: "What gift should I get, and why?",
- containerTag: user,
+const memories = await supermemory.search(user, {
+ query: "What gift should I get, and why?",
+ include: { related: true },
searchMode: "memories",
- include: { relatedMemories: true },
limit: 5,
});
@@ -308,26 +289,23 @@ console.log(JSON.stringify(memories, null, 2));
```
```python Python
-memories = client.search.memories(
- q="What gift should I get, and why?",
- container_tag=user,
+memories = client.search(
+ user,
+ query="What gift should I get, and why?",
search_mode="memories",
- include={"relatedMemories": True},
+ include={"related": True},
limit=5,
)
-print(memories)
+print(memories.to_json())
```
```bash curl
-curl -X POST "https://api.supermemory.ai/v4/search" \
+curl -X POST "https://api.supermemory.ai/ns/user_4f8a/search?searchMode=memories&limit=5" \
-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
+ "query": "What gift should I get, and why?",
+ "include": { "related": true }
}'
```
@@ -338,25 +316,22 @@ Abbreviated shape:
{
"results": [
{
+ "id": "mem_8c1f",
"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"
- }
- ]
+ "included": {
+ "related": {
+ "parents": [
+ { "id": "mem_2a90", "memory": "Sarah presented the Q3 roadmap at the Tokyo offsite" }
+ ],
+ "children": [
+ { "id": "mem_b713", "memory": "User needs a gift idea for their VP of Product, Sarah" }
+ ]
+ }
}
}
],
- "timing": 287
+ "searchTime": 287
}
```
@@ -364,42 +339,31 @@ You never wrote “Sarah is my VP of Product” as one sentence. The graph conne
### 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.
+Profiles are the **always-on** summary (static + recent dynamic) of an entity (or a namespace) - what you inject every turn without re-searching the world.
```typescript TypeScript
-const { profile, searchResults } = await client.profile({
- containerTag: user,
- q: "gift for the person being promoted", // optional: also run search
-});
+const { profile } = await supermemory.profile(user);
-console.log("static:", profile.static);
-console.log("dynamic:", profile.dynamic);
-console.log("search hits:", searchResults?.results?.length ?? 0);
+console.log("static:", profile.static.map((m) => m.memory));
+console.log("dynamic:", profile.dynamic.map((m) => m.memory));
```
```python Python
-result = client.profile(
- container_tag=user,
- q="gift for the person being promoted",
-)
+profile = client.profile(user).profile
-print("static:", result.profile.static)
-print("dynamic:", result.profile.dynamic)
-print("search hits:", len(result.search_results.results) if result.search_results else 0)
+print("static:", [m.memory for m in profile.static])
+print("dynamic:", [m.memory for m in profile.dynamic])
```
```bash curl
-curl -X POST "https://api.supermemory.ai/v4/profile" \
- -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
- -H "Content-Type: application/json" \
- -d '{
- "containerTag": "user_4f8a",
- "q": "gift for the person being promoted"
- }'
+curl -X POST "https://api.supermemory.ai/ns/user_4f8a/profile" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
+The v5 profile call takes no query. When you also want query-ranked memories, run a `search` next to it (the harness below does exactly that).
+
> There is a lot more to profiles - with [Buckets](/user-profiles/buckets), for example, you can make supermemory learn and categorize incoming information for learning specific things.
@@ -409,11 +373,11 @@ curl -X POST "https://api.supermemory.ai/v4/profile" \
| **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](/concepts/memory-vs-rag).
+Same `namespace` → same context pool. See [Memory vs RAG](/concepts/memory-vs-rag).
## 5. Put it in a harness
-There is no single required harness. The pattern is the same wherever you run the model: **read** context (profile / search / docs), generate, **write** the turn back under a stable `customId` so the session stays one document.
+There is no single required harness. The pattern is the same wherever you run the model: **read** context (profile / search / docs), generate, **write** the turn back under a stable `id` so the session stays one document.
Here are two example shapes — pick whatever matches your stack.
@@ -421,12 +385,12 @@ Here are two example shapes — pick whatever matches your stack.
```typescript TypeScript
-// npm install supermemory openai
-import Supermemory from "supermemory";
+// npm i 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 supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
const llm = new OpenAI();
const user = "user_4f8a";
const sessionId = "chat_live_session";
@@ -435,33 +399,30 @@ const rl = readline.createInterface({ input: process.stdin, output: process.stdo
while (true) {
const question = await rl.question("you: ");
- const { profile, searchResults } = await memory.profile({
- containerTag: user,
- q: question,
+ const { profile } = await supermemory.profile(user);
+
+ const related = await supermemory.search(user, {
+ query: question,
+ searchMode: "memories",
+ limit: 5,
});
// optional: also pull document chunks for grounding
- const rag = await memory.search({
- q: question,
- containerTag: user,
- searchMode: "documents",
+ const rag = await supermemory.search(user, {
+ query: question,
+ searchMode: "chunks",
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.static.map((m) => m.memory),
"## Profile (dynamic)",
- ...profile.dynamic,
+ ...profile.dynamic.map((m) => m.memory),
"## Related memories",
- ...(searchResults?.results?.map((m) => m.memory).filter(Boolean) ?? []),
+ ...related.results.map((r) => r.memory).filter(Boolean),
"## Docs",
- ...docBits,
+ ...rag.results.map((r) => r.chunk).filter(Boolean),
].join("\n");
const res = await llm.chat.completions.create({
@@ -475,10 +436,9 @@ while (true) {
console.log(`assistant: ${answer}`);
// append this turn into the same conversation document
- await memory.add({
+ await supermemory.add(user, {
content: `user: ${question}\nassistant: ${answer}`,
- containerTag: user,
- customId: sessionId,
+ id: sessionId,
});
}
```
@@ -496,29 +456,23 @@ 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
+ profile = memory.profile(user).profile
+
+ related = memory.search(user, query=question, search_mode="memories", limit=5)
+
+ # optional: also pull document chunks for grounding
+ rag = memory.search(user, query=question, search_mode="chunks", limit=3)
- memories = result.search_results.results if result.search_results else []
context = "\n".join(
[
"## Profile (static)",
- *result.profile.static,
+ *[m.memory for m in profile.static],
"## Profile (dynamic)",
- *result.profile.dynamic,
+ *[m.memory for m in profile.dynamic],
"## Related memories",
- *[m.memory for m in memories if m.memory],
+ *[r.memory for r in related.results if r.memory],
"## Docs",
- *doc_bits,
+ *[r.chunk for r in rag.results if r.chunk],
]
)
@@ -532,10 +486,11 @@ while True:
answer = res.choices[0].message.content or ""
print(f"assistant: {answer}")
+ # append this turn into the same conversation document
memory.add(
+ user,
content=f"user: {question}\nassistant: {answer}",
- container_tag=user,
- custom_id=session_id,
+ id=session_id,
)
```
@@ -552,8 +507,8 @@ 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
+ namespace: "user_4f8a",
+ id: "chat_live_session", // keep stable for the whole session
mode: "full", // profile + query search
});
@@ -578,7 +533,7 @@ 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:
+Ctrl+C the process, start again with the **same** `namespace` (and optional same `id` for the live session). Ask:
```
you: who's getting promoted?
@@ -590,21 +545,21 @@ 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
+ INGEST WAIT RETRIEVE
+ ────── ──── ────────
+ Conversation (id) → system.status === done → Memory graph (+ related)
+ Document (id) → system.status === done → Document search (RAG)
+ → Profile (static + dynamic)
+ │
+ ▼
+ Chat harness
```
## Where next
- Conversations, files, URLs, customId updates, and status.
+ Conversations, files, URLs, `id` updates, and status.
Hybrid vs memories, filters, thresholds, rerank.
@@ -616,9 +571,9 @@ Nothing was reloaded from your process. Memory and docs live in supermemory.
Static vs dynamic, and when to inject a profile every turn.
- withSupermemory modes, customId, addMemory.
+ withSupermemory modes, id, addMemory.
-
+
Isolation for multi-tenant products.
diff --git a/apps/docs/recall/memory-operations.mdx b/apps/docs/recall/memory-operations.mdx
index c34ef4d8..dc4e6e8e 100644
--- a/apps/docs/recall/memory-operations.mdx
+++ b/apps/docs/recall/memory-operations.mdx
@@ -1,12 +1,12 @@
---
-title: "Memory operations"
-sidebarTitle: "CRUD & forgetting"
-description: "Advanced memory operations (v4 API)"
+title: "Memory Operations"
+sidebarTitle: "CRUD & Forgetting"
+description: "Forget extracted memories exactly or by meaning"
icon: "/icons/hugeicons/database-01.svg"
---
-These v4 endpoints operate on extracted memories (not raw documents). SDK support coming soon — use fetch or cURL for now.
+These operations act on extracted memories (not raw documents). Every call is scoped to one `namespace` (what v3/v4 called a container tag).
For document management (list, get, update, delete), see [Document Operations](/ingestion/document-operations).
For ingesting raw content (text, files, URLs) through the processing pipeline, see [Add Context](/ingestion/add-memories).
@@ -14,227 +14,127 @@ For ingesting raw content (text, files, URLs) through the processing pipeline, s
## 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.
+v5 has no direct memory-create route. Ingest a document instead and Supermemory extracts the memories from it. When you already know the exact facts, send them as short statements with `dreaming: "instant"` so they are searchable right away.
-
+
```typescript
- 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"
- })
+ await supermemory.add("user_123", {
+ content: "John prefers dark mode.\nJohn is from Seattle.",
+ id: "prefs_john",
+ metadata: { source: "user_preference" },
+ dreaming: "instant",
});
-
- 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
- curl -X POST "https://api.supermemory.ai/v4/memories" \
+ curl -X POST "https://api.supermemory.ai/ns/user_123/document?dreaming=instant" \
-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"
+ "content": "John prefers dark mode.\nJohn is from Seattle.",
+ "id": "prefs_john",
+ "metadata": { "source": "user_preference" }
}'
```
+Reusing the same `id` later appends to that document instead of creating a new one. See [Updating Content](/ingestion/add-memories#updating-content).
+
+---
+
+## Forget Memories
+
+Soft-delete memories by ID. Forgotten memories are excluded from search results but preserved. Pass 1 to 500 IDs.
+
+
+
+ ```typescript
+ const { count, matches, errors } = await supermemory.memories.forget("user_123", {
+ ids: ["mem_abc123"],
+ });
+ ```
+
+
+ ```bash
+ curl -X DELETE "https://api.supermemory.ai/ns/user_123/memories" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{ "ids": ["mem_abc123"] }'
+ ```
+
+
+
### 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 |
+| `namespace` | string | yes | Namespace the memories belong to (URL path) |
+| `ids` | string[] | yes | Memory IDs to forget (1 to 500) |
### Response
```json
{
- "documentId": "abc123",
- "memories": [
- {
- "id": "mem_1",
- "memory": "John prefers dark mode",
- "isStatic": false,
- "createdAt": "2025-01-15T10:30:00.000Z"
- }
- ]
+ "count": 1,
+ "matches": [{ "id": "mem_abc123", "memory": "John prefers dark mode" }],
+ "errors": []
}
```
| 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 |
+| `count` | number | Number of memories forgotten; always equals `matches.length` |
+| `matches` | array | The memories that were forgotten (`{ id, memory }`) |
+| `errors` | array | IDs that were missing or not eligible (`{ id, error }`) |
-
-**When should you create memories directly?** When you already know the exact facts to store, such as user preferences, traits or structured data. When you have raw content (conversations, documents, URLs) that Supermemory should process and extract memories from, [add context](/ingestion/add-memories) instead.
-
-
----
-
-## 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
- 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
- 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`).
+HTTP success does not imply every requested ID changed. Check `errors`.
---
## 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.
+Forget by meaning. Give a `query` (a prompt or topic) and the service semantically searches the namespace, decides which memories are genuinely about your target, and soft-deletes those — use this for "forget everything about X".
-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.
+This is a bulk, destructive operation. `dryRun` is required. Call with **`dryRun: true`** first to review what would be forgotten, then apply. The match is semantic, so a too-broad query can select more than you intend.
-
+
```typescript
// 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 }, ...]
+ const preview = await supermemory.memories.forgetMatching("user_123", {
+ query: "forget everything about Project Titan",
+ dryRun: true,
+ });
+ // preview.matches → [{ id, memory }, ...]
// 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
+ const result = await supermemory.memories.forget("user_123", {
+ ids: preview.matches.map((m) => m.id),
+ });
+ // result.matches → [{ id, memory }, ...]
```
```bash
# Preview
- curl -X POST "https://api.supermemory.ai/v4/memories/forget-matching" \
+ curl -X DELETE "https://api.supermemory.ai/ns/user_123/memories/semantic" \
-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" \
+ curl -X DELETE "https://api.supermemory.ai/ns/user_123/memories" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{
- "ids": ["abc123", "def456", "ghi789"],
- "containerTag": "user_123",
- "dryRun": false,
- "reason": "project cancelled"
- }'
+ -d '{ "ids": ["abc123", "def456", "ghi789"] }'
```
@@ -243,105 +143,49 @@ This is a bulk, destructive operation. Always **`dryRun` first** to review what
| 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`.
+| `namespace` | string | yes | Namespace to scope the operation to (URL path) |
+| `query` | string | yes | What to forget — a natural-language instruction ("forget everything about Project Titan") or a bare topic ("Project Titan") |
+| `dryRun` | boolean | yes | `true` returns what *would* be forgotten without changing anything. `false` forgets the memories selected when that request runs |
### Response
+Both dry-run and applied requests return the same shape as [Forget Memories](#forget-memories):
+
```json
{
- "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 }
- ]
+ "matches": [
+ { "id": "mem_1", "memory": "Project Titan ships in Q3" }
+ ],
+ "errors": []
}
```
-| 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.
-
+The payload does not say which mode was sent, so keep your own record of `dryRun` next to audit logs.
-**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.)
+**Exact, bound deletes.** Re-running `forgetMatching` with `dryRun: false` re-runs the semantic match, so the result can drift from the preview if the namespace changed in between. To forget *precisely* what you reviewed, take the `id`s from the `dryRun` preview and send them to `memories.forget` — the delete is then bound to exactly that set.
---
-## Update memory (versioned)
+## Update Memory
-Update a memory by creating a new version. The original is preserved with `isLatest=false`.
+v5 has no direct memory-update route. Update the source document instead; Supermemory reprocesses it and the memories follow.
-
-
- ```typescript
- 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",
+```typescript
+await supermemory.documents.update("user_123", "prefs_john", {
+ content: "John prefers light mode.\nJohn is from Seattle.",
+});
+```
- newContent: "Updated content goes here",
- metadata: {
- tags: ["updated"]
- }
- })
- });
- ```
-
-
- ```bash
- 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.
+See [Update Document](/ingestion/document-operations#update-document).
---
## Next steps
- [Review Inferred Memories](/recall/memory-review) — Approve or decline low-confidence memories
-- [Document Operations](/ingestion/document-operations) — Manage documents (SDK supported)
+- [Document Operations](/ingestion/document-operations) — Manage documents
- [Search](/recall/search) — Query your memories
- [Ingesting Content](/ingestion/add-memories) — Add new content
diff --git a/apps/docs/recall/memory-review.mdx b/apps/docs/recall/memory-review.mdx
index 50420c57..818cd066 100644
--- a/apps/docs/recall/memory-review.mdx
+++ b/apps/docs/recall/memory-review.mdx
@@ -14,8 +14,10 @@ These two endpoints let you build a review flow 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](/concepts/container-tags)
-(space), under `/v3/container-tags/{containerTag}`.
+These endpoints are scoped to a single [namespace](/concepts/container-tags)
+(what v3/v4 called a container tag). The review routes are still served under
+`/v3/container-tags/{containerTag}`; pass your namespace as the `containerTag`
+path parameter.
## How review affects ranking
@@ -38,7 +40,7 @@ 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
+Return the inferred memories for a namespace that are still awaiting review (the
review queue). Reviewed memories are excluded.
```
@@ -68,7 +70,7 @@ GET /v3/container-tags/{containerTag}/inferred
| Parameter | Type | Description |
|-----------|------|-------------|
-| `containerTag` | string | The container tag / space to read the review queue for |
+| `containerTag` | string | The namespace to read the review queue for |
### Response
@@ -101,7 +103,7 @@ GET /v3/container-tags/{containerTag}/inferred
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
+is forgotten, expired, or already reviewed. An unknown or empty namespace returns
`{ "memories": [], "total": 0 }`.
@@ -149,7 +151,7 @@ POST /v3/container-tags/{containerTag}/inferred/{memoryId}/review
| Parameter | Type | Description |
|-----------|------|-------------|
-| `containerTag` | string | The container tag / space the memory belongs to |
+| `containerTag` | string | The namespace the memory belongs to |
| `memoryId` | string | The memory entry ID from the list endpoint |
### Body parameters
@@ -196,7 +198,7 @@ The reject action is named **`decline`**. There is no `reject` value.
| Status | When |
|--------|------|
| `401` | Missing or invalid authentication |
-| `404` | The container tag or memory was not found in your organization |
+| `404` | The namespace 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 |
---
@@ -225,11 +227,11 @@ export type InferredMemory = {
export type ReviewAction = "approve" | "decline" | "undo";
-export function useInferredMemories(containerTag: string) {
+export function useInferredMemories(namespace: string) {
return useQuery({
- queryKey: key(containerTag),
+ queryKey: key(namespace),
queryFn: async (): Promise => {
- const res = await fetch(`${BASE}/container-tags/${containerTag}/inferred`, {
+ const res = await fetch(`${BASE}/container-tags/${namespace}/inferred`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
if (!res.ok) throw new Error("Failed to load review queue");
@@ -240,12 +242,12 @@ export function useInferredMemories(containerTag: string) {
});
}
-export function useReviewInferredMemory(containerTag: string) {
+export function useReviewInferredMemory(namespace: 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`,
+ `${BASE}/container-tags/${namespace}/inferred/${vars.memoryId}/review`,
{
method: "POST",
headers: {
@@ -261,10 +263,10 @@ export function useReviewInferredMemory(containerTag: string) {
onSuccess: (_data, { memoryId, action }) => {
// approve/decline remove the card; undo brings it back, so refetch.
if (action === "undo") {
- queryClient.invalidateQueries({ queryKey: key(containerTag) });
+ queryClient.invalidateQueries({ queryKey: key(namespace) });
return;
}
- queryClient.setQueryData(key(containerTag), (prev) =>
+ queryClient.setQueryData(key(namespace), (prev) =>
prev?.filter((m) => m.id !== memoryId),
);
},
@@ -283,5 +285,5 @@ a request and the memory simply stays in the queue for a later session.
## Next steps
- [Graph Memory](/concepts/graph-memory) — How inferred (`derive`) memories are created
-- [Memory Operations](/recall/memory-operations) — Create, forget, and update memories
+- [Memory Operations](/recall/memory-operations) — Forget memories
- [Search](/recall/search) — How inferred memories are ranked in results
diff --git a/apps/docs/recall/search.mdx b/apps/docs/recall/search.mdx
index 6e58e226..38d4d858 100644
--- a/apps/docs/recall/search.mdx
+++ b/apps/docs/recall/search.mdx
@@ -5,14 +5,14 @@ description: "Semantic search across your memories and documents"
icon: "/icons/hugeicons/search-01.svg"
---
-Search through your memories and documents with a single API call.
+Search through your memories and documents with a single API call. Each search runs inside one `namespace` (what v3/v4 called a container tag).
-**Use `searchMode: "hybrid"`** for best results. It searches both memories and document chunks, returning the most relevant content.
+**`searchMode: "hybrid"`** is the default and gives the 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.
+**TypeScript SDK:** one call, `supermemory.search(namespace, { query, searchMode })`. `searchMode` (`"memories"`, `"chunks"`, or `"hybrid"`) picks what comes back. There is no v5 Python SDK yet; the Python tab shows the legacy client.
## Quick start
@@ -20,15 +20,14 @@ Search through your memories and documents with a single API call.
```typescript
- import Supermemory from 'supermemory';
+ import { Supermemory } from "supermemory";
- const client = new Supermemory();
+ const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
- const results = await client.search({
- q: "machine learning",
- containerTag: "user_123",
+ const results = await supermemory.search("user_123", {
+ query: "machine learning",
searchMode: "hybrid",
- limit: 5
+ limit: 5,
});
results.results.forEach(result => {
@@ -42,11 +41,11 @@ Search through your memories and documents with a single API call.
client = Supermemory()
- results = client.search.memories(
- q="machine learning",
- container_tag="user_123",
+ results = client.search(
+ "user_123",
+ query="machine learning",
search_mode="hybrid",
- limit=5
+ limit=5,
)
for result in results.results:
@@ -55,15 +54,10 @@ Search through your memories and documents with a single API call.
```bash
- curl -X POST "https://api.supermemory.ai/v4/search" \
+ curl -X POST "https://api.supermemory.ai/ns/user_123/search?searchMode=hybrid&limit=5" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{
- "q": "machine learning",
- "containerTag": "user_123",
- "searchMode": "hybrid",
- "limit": 5
- }'
+ -d '{ "query": "machine learning" }'
```
@@ -77,62 +71,70 @@ Search through your memories and documents with a single API call.
"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
+ "isLatest": true,
+ "system": { "createdAt": "2024-01-15T10:30:00.000Z", "updatedAt": "2024-01-15T10:30:00.000Z" }
},
{
"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
+ "isLatest": true,
+ "system": { "createdAt": "2024-01-14T09:15:00.000Z", "updatedAt": "2024-01-14T09:15:00.000Z" }
}
],
- "timing": 92,
- "total": 5
+ "searchTime": 92
}
```
-In hybrid mode, results contain either a `memory` field (extracted facts) or a `chunk` field (document content), depending on the source.
+In hybrid mode, results contain either a `memory` field (extracted facts) or a `chunk` field (document content), depending on the source. Branch on field presence.
---
## Parameters
+`namespace` is the URL path. Every other option goes in the JSON body (the second argument in the SDKs).
+
| 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 |
+| `namespace` | string | required | Namespace to search (URL path) |
+| `query` | string | required | Search query |
+| `searchMode` | string | `"hybrid"` | `"hybrid"` (recommended), `"memories"`, or `"chunks"` |
+| `limit` | number | 10 | Max results (max 100) |
+| `threshold` | 0-1 | 0.3 | Similarity cutoff (higher = fewer, better results) |
+| `rerank` | string | `"none"` | `"none"`, `"order"` (re-score for relevance, +100ms), or `"aggregate"` |
+| `rewriteQuery` | boolean | false | Generate multiple rewrites, search all of them, and merge results. No extra cost, but adds latency |
+| `filter` | object | — | Metadata filter expression. See [Filtering](#filtering) |
+| `include` | object | — | `{ documents, related, forgotten }` — opt in to extra context per result |
+
+
+Defaults changed from v4: `searchMode` was `memories` and is now `hybrid`; `threshold` was `0.6` and is now `0.3`. Set both explicitly if you are comparing against v4 results.
+
### Search modes
-- **`hybrid`** (recommended) — Searches both memories and document chunks, and returns both in the response
+- **`hybrid`** (default, 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
+- **`chunks`** — Only searches raw document/chunk content, skipping extracted memories
```typescript
-// Hybrid: memories + document chunks (recommended)
-await client.search({
- q: "quarterly goals",
- containerTag: "user_123",
- searchMode: "hybrid"
+// Hybrid: memories + document chunks (default)
+await supermemory.search("user_123", {
+ query: "quarterly goals",
});
// Memories only: just extracted facts
-await client.search({
- q: "user preferences",
- containerTag: "user_123",
- searchMode: "memories"
+await supermemory.search("user_123", {
+ query: "user preferences",
+ searchMode: "memories",
+});
+
+// Chunks only: RAG over documents
+await supermemory.search("user_123", {
+ query: "refund policy",
+ searchMode: "chunks",
});
```
@@ -140,39 +142,29 @@ await client.search({
## Filtering
-Filter by `containerTag` to scope results to a user or project:
+The `namespace` scopes results to a user or project. Use `filter` for metadata-based filtering inside that namespace:
```typescript
-const results = await client.search({
- q: "project updates",
- containerTag: "user_123",
- searchMode: "hybrid"
+const results = await supermemory.search("user_123", {
+ query: "meeting notes",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "type", operator: "eq", value: "meeting" },
+ { field: "year", operator: "eq", value: 2024 },
+ ],
+ },
});
```
-Use `filters` for metadata-based filtering:
+
+ - **Equality:** `{ field: "status", operator: "eq", value: "active" }` (also `neq`; strings, numbers, booleans)
+ - **String contains:** `{ field: "title", operator: "contains", value: "react" }` (also `notContains`; optional `caseSensitive`)
+ - **Numeric:** `{ field: "priority", operator: "gte", value: 5 }` (`gt`, `gte`, `lt`, `lte`)
+ - **Array contains:** `{ field: "tags", operator: "arrayContains", value: "important" }` (also `arrayNotContains`)
+ - **Logic:** `{ operator: "and" | "or", operands: [...] }`, nested up to 5 levels
-```typescript
-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](/concepts/filtering) for full syntax.
+ Keys are literal: `customer.plan` is one field name, not a nested path. See [Organizing & Filtering](/concepts/filtering) for full syntax.
---
@@ -184,10 +176,9 @@ const results = await client.search({
Re-scores results for better relevance. Adds ~100ms latency.
```typescript
-const results = await client.search({
- q: "complex technical question",
- containerTag: "user_123",
- rerank: true
+const results = await supermemory.search("user_123", {
+ query: "complex technical question",
+ rerank: "order",
});
```
@@ -197,23 +188,28 @@ Control result quality vs quantity:
```typescript
// Broad search — more results
-await client.search({ q: "...", threshold: 0.3 });
+await supermemory.search("user_123", { query: "...", threshold: 0.3 });
// Precise search — fewer, better results
-await client.search({ q: "...", threshold: 0.8 });
+await supermemory.search("user_123", { query: "...", threshold: 0.8 });
```
-### Including forgotten memories
+### Attachments
-By default, search excludes memories that have been forgotten or have passed their `forgetAfter` expiration. Set `include.forgottenMemories` to `true` to recover them:
+- `attach.documents` adds the most relevant source document to each result.
+- `attach.related` adds parent, child, and sibling memories (see [graph memory](/concepts/graph-memory)).
+- `attach.forgotten` allows forgotten memories in related context. By default, search excludes memories that have been forgotten or have passed their `forgetAfter` expiration.
```typescript
-await client.search({
- q: "old project notes",
- include: { forgottenMemories: true }
+await supermemory.search("user_123", {
+ query: "old project notes",
+ include: { related: true, forgotten: true },
+ searchMode: "memories",
});
```
+Attached context arrives under `result.included` (`included.document`, `included.related.{parents,children,siblings}`).
+
---
## Chatbot example
@@ -222,12 +218,11 @@ Optimal configuration for conversational AI:
```typescript
async function getContext(userId: string, message: string) {
- const results = await client.search({
- q: message,
- containerTag: userId,
- searchMode: "hybrid",
+ const results = await supermemory.search(userId, {
+ query: message,
threshold: 0.6,
- limit: 5
+ searchMode: "hybrid",
+ limit: 5,
});
return results.results
@@ -244,14 +239,17 @@ async function getContext(userId: string, message: string) {
chunk?: string; // Present for document chunk results
similarity: number; // 0-1
metadata: object | null;
- updatedAt: string;
- version: number;
+ isLatest: boolean;
+ system: { createdAt: string; updatedAt: string };
+ included?: { // Only when you pass attach
+ document?: object;
+ related?: { parents: object[]; children: object[]; siblings: object[] };
+ };
}
interface SearchResponse {
results: SearchResult[];
- timing: number; // ms
- total: number;
+ searchTime: number; // ms
}
```
@@ -261,5 +259,5 @@ async function getContext(userId: string, message: string) {
## Next steps
- [Ingesting Content](/ingestion/add-memories) — Add content to search
-- [User Profiles](/recall/user-profiles) — Get user context with search
-- [Organizing & Filtering](/concepts/filtering) — Container tags and metadata
+- [User Profiles](/recall/user-profiles) — Get user context
+- [Organizing & Filtering](/concepts/filtering) — Namespaces and metadata
diff --git a/apps/docs/recall/user-profiles.mdx b/apps/docs/recall/user-profiles.mdx
index d1be45ae..a953fcb4 100644
--- a/apps/docs/recall/user-profiles.mdx
+++ b/apps/docs/recall/user-profiles.mdx
@@ -11,10 +11,10 @@ User profiles are extremely short summaries of context about an entity (Usually
This profile should be injected into the agent context for truly personalized experiences. To read more, visit [User profiles - Concept](/concepts/user-profiles)
-Get a user's profile — their static facts and dynamic context — with a single API call.
+Get a user's profile — their static facts and dynamic context — with a single API call. One profile per `namespace` (what v3/v4 called a container tag).
-Profiles are built automatically as you [ingest content](/ingestion/add-memories). No setup required.
+Profiles are built automatically as you [ingest content](/ingestion/add-memories). No setup required. If you read a profile right after ingesting, pass `dreaming: "instant"` on the add; otherwise a fresh namespace can show an empty profile for minutes.
## Quick start
@@ -22,16 +22,14 @@ Profiles are built automatically as you [ingest content](/ingestion/add-memories
```typescript
- import Supermemory from 'supermemory';
+ import { Supermemory } from "supermemory";
- const client = new Supermemory();
+ const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY });
- const { profile } = await client.profile({
- containerTag: "user_123"
- });
+ const { profile } = await supermemory.profile("user_123");
- console.log(profile.static); // Long-term facts
- console.log(profile.dynamic); // Recent context
+ console.log(profile.static.map((m) => m.memory)); // Long-term facts
+ console.log(profile.dynamic.map((m) => m.memory)); // Recent context
```
@@ -40,18 +38,16 @@ Profiles are built automatically as you [ingest content](/ingestion/add-memories
client = Supermemory()
- result = client.profile(container_tag="user_123")
+ profile = client.profile("user_123").profile
- print(result.profile.static) # Long-term facts
- print(result.profile.dynamic) # Recent context
+ print([m.memory for m in profile.static]) # Long-term facts
+ print([m.memory for m in profile.dynamic]) # Recent context
```
```bash
- curl -X POST "https://api.supermemory.ai/v4/profile" \
- -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
- -H "Content-Type: application/json" \
- -d '{"containerTag": "user_123"}'
+ curl -X POST "https://api.supermemory.ai/ns/user_123/profile" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
```
@@ -61,53 +57,64 @@ Profiles are built automatically as you [ingest content](/ingestion/add-memories
{
"profile": {
"static": [
- "User is a software engineer",
- "User specializes in Python and React",
- "User prefers dark mode interfaces"
+ { "id": "mem_1", "memory": "User is a software engineer" },
+ { "id": "mem_2", "memory": "User specializes in Python and React" },
+ { "id": "mem_3", "memory": "User prefers dark mode interfaces" }
],
"dynamic": [
- "User is working on Project Alpha",
- "User recently started learning Rust",
- "User is debugging authentication issues"
- ]
+ { "id": "mem_4", "memory": "User is working on Project Alpha" },
+ { "id": "mem_5", "memory": "User recently started learning Rust" },
+ { "id": "mem_6", "memory": "User is debugging authentication issues" }
+ ],
+ "buckets": {}
}
}
```
+`static` and `dynamic` are always returned. Each entry is `{ id, memory }`; the `id` is the memory ID, so you can [forget](/recall/memory-operations#forget-memories) a profile entry directly.
+
---
## Profile + search
-Get profile and search results in one call by adding the `q` parameter:
+The v5 profile call takes no query. When you also need query-ranked memories, run a [search](/recall/search) next to it:
```typescript
- const result = await client.profile({
- containerTag: "user_123",
- q: "deployment errors"
- });
+ const [{ profile }, search] = await Promise.all([
+ supermemory.profile("user_123"),
+ supermemory.search("user_123", {
+ query: "deployment errors",
+ searchMode: "memories",
+ limit: 5,
+ }),
+ ]);
// Profile data
- const { static: facts, dynamic: context } = result.profile;
+ const facts = profile.static.map((m) => m.memory);
+ const context = profile.dynamic.map((m) => m.memory);
- // Search results (only if q was provided)
- const memories = result.searchResults?.results || [];
+ // Query-specific memories
+ const memories = search.results.map((r) => r.memory).filter(Boolean);
```
```python
- result = client.profile(
- container_tag="user_123",
- q="deployment errors"
+ profile = client.profile("user_123").profile
+ search = client.search(
+ "user_123",
+ query="deployment errors",
+ search_mode="memories",
+ limit=5,
)
# Profile data
- facts = result.profile.static
- context = result.profile.dynamic
+ facts = [m.memory for m in profile.static]
+ context = [m.memory for m in profile.dynamic]
- # Search results
- memories = result.search_results.results if result.search_results else []
+ # Query-specific memories
+ memories = [r.memory for r in search.results if r.memory]
```
@@ -116,66 +123,58 @@ Get profile and search results in one call by adding the `q` parameter:
## Parameters
+`namespace` is a top-level key (URL path). `body` is optional and accepts only `filter` and `buckets`.
+
| 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](/user-profiles/buckets) |
+| `namespace` | string | Yes | User/project identifier |
+| `body.filter` | object | No | Metadata filter applied to the memories that make up the profile |
+| `body.buckets` | string[] | No | Restrict the `buckets` section to specific names (up to 50). Omit for all configured buckets. See [Profile Buckets](/user-profiles/buckets) |
---
## Filtering profiles
-Profiles support the same [metadata filters](/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.
+Profiles support the same [metadata filter](/concepts/filtering) as `search` and `list` — `filter` narrows which memories are eligible to contribute to `static`, `dynamic`, and `buckets`.
```typescript
- const { profile } = await client.profile({
- containerTag: "user_123",
- filters: {
- AND: [{ key: "source", value: "onboarding" }],
- },
+ const { profile } = await supermemory.profile("user_123", {
+ filter: { field: "source", operator: "eq", value: "onboarding" },
});
```
```python
- result = client.profile(
- container_tag="user_123",
- filters={"AND": [{"key": "source", "value": "onboarding"}]},
- )
+ profile = client.profile(
+ "user_123",
+ filter={"field": "source", "operator": "eq", "value": "onboarding"},
+ ).profile
```
```bash
- curl -X POST "https://api.supermemory.ai/v4/profile" \
+ curl -X POST "https://api.supermemory.ai/ns/user_123/profile" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
- "containerTag": "user_123",
- "filters": { "AND": [{ "key": "source", "value": "onboarding" }] }
+ "filter": { "field": "source", "operator": "eq", "value": "onboarding" }
}'
```
-Combine `filters` with `q` to scope both the profile synthesis and the accompanying search results in one call:
+Combine `filter` with `buckets` to scope both the profile synthesis and the bucket section in one call:
```typescript
-const result = await client.profile({
- containerTag: "org_customer_442",
- q: "billing issue",
- filters: {
- AND: [{ key: "channel", value: "support_ticket" }],
- },
+const { profile } = await supermemory.profile("org_customer_442", {
+ filter: { field: "channel", operator: "eq", value: "support_ticket" },
+ buckets: ["billing"],
});
```
-All filter types from [Organizing & Filtering](/concepts/filtering) are supported — string equality, `string_contains`, `numeric`, `array_contains`, nested `AND`/`OR`, and `negate`.
+All filter operators from [Organizing & Filtering](/concepts/filtering) are supported — `eq`/`neq`, `contains`/`notContains`, numeric comparisons, `arrayContains`, and nested `and`/`or`.
---
@@ -185,15 +184,15 @@ The most common pattern — inject profile into your LLM's system prompt:
```typescript
async function chat(userId: string, message: string) {
- const { profile } = await client.profile({ containerTag: userId });
+ const { profile } = await supermemory.profile(userId);
const systemPrompt = `You are assisting a user.
ABOUT THE USER:
-${profile.static?.join('\n') || 'No profile yet.'}
+${profile.static.map((m) => m.memory).join('\n') || 'No profile yet.'}
CURRENT CONTEXT:
-${profile.dynamic?.join('\n') || 'No recent activity.'}
+${profile.dynamic.map((m) => m.memory).join('\n') || 'No recent activity.'}
Personalize responses to their expertise and preferences.`;
@@ -210,25 +209,28 @@ Personalize responses to their expertise and preferences.`;
## Full context pattern
-Get profile + query-specific memories in one call:
+Get profile + query-specific memories:
```typescript
async function getContext(userId: string, query: string) {
- const result = await client.profile({
- containerTag: userId,
- q: query,
- threshold: 0.6
- });
+ const [{ profile }, search] = await Promise.all([
+ supermemory.profile(userId),
+ supermemory.search(userId, {
+ query,
+ threshold: 0.6,
+ searchMode: "memories",
+ }),
+ ]);
return `
User Background:
-${result.profile.static.join('\n')}
+${profile.static.map((m) => m.memory).join('\n')}
Current Context:
-${result.profile.dynamic.join('\n')}
+${profile.dynamic.map((m) => m.memory).join('\n')}
Relevant Memories:
-${result.searchResults?.results.map(m => m.memory).join('\n') || 'None'}
+${search.results.map((r) => r.memory).filter(Boolean).join('\n') || 'None'}
`;
}
```
@@ -241,9 +243,9 @@ Buckets are **custom topical categories** for a profile — an axis that sits al
`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.
+
+ Read and configure buckets — request bucketed profiles, create namespace buckets,
+ and see validation limits.
---
@@ -256,9 +258,7 @@ Buckets are **custom topical categories** for a profile — an axis that sits al
if (!req.user?.id) return next();
try {
- const { profile } = await client.profile({
- containerTag: req.user.id
- });
+ const { profile } = await supermemory.profile(req.user.id);
req.userProfile = profile;
} catch (e) {
req.userProfile = null;
@@ -280,9 +280,7 @@ Buckets are **custom topical categories** for a profile — an axis that sits al
export async function POST(req: NextRequest) {
const { userId, message } = await req.json();
- const { profile } = await client.profile({
- containerTag: userId
- });
+ const { profile } = await supermemory.profile(userId);
const response = await generateResponse(message, profile);
return NextResponse.json({ response });
@@ -297,8 +295,8 @@ Buckets are **custom topical categories** for a profile — an axis that sits al
// Profiles automatically injected
const model = withSupermemory(openai("gpt-4"), {
- containerTag: "user-123",
- customId: "conv-1",
+ namespace: "user-123",
+ id: "conv-1",
})
const result = await generateText({
@@ -315,16 +313,16 @@ Buckets are **custom topical categories** for a profile — an axis that sits al
## Response schema
```typescript
+interface ProfileEntry {
+ id: string; // memory ID
+ memory: string; // the fact
+}
+
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;
+ static: ProfileEntry[]; // Long-term facts
+ dynamic: ProfileEntry[]; // Recent context
+ buckets: Record; // Topical buckets, keyed by bucket name
};
}
```
diff --git a/apps/docs/self-hosting/configuration.mdx b/apps/docs/self-hosting/configuration.mdx
index 3a12164b..c58b6c9f 100644
--- a/apps/docs/self-hosting/configuration.mdx
+++ b/apps/docs/self-hosting/configuration.mdx
@@ -94,7 +94,7 @@ Local embeddings are prewarmed at startup with conservative defaults — one wor
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.
+- **Adds are accepted instantly but processed through a queue.** A `POST /ns/{namespace}/document` 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.
diff --git a/apps/docs/self-hosting/local-vs-enterprise.mdx b/apps/docs/self-hosting/local-vs-enterprise.mdx
index 59aa7f93..a3e259c8 100644
--- a/apps/docs/self-hosting/local-vs-enterprise.mdx
+++ b/apps/docs/self-hosting/local-vs-enterprise.mdx
@@ -44,7 +44,7 @@ Local is bounded by one machine — which is the point. Enterprise runs on globa
## 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.
+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.
diff --git a/apps/docs/self-hosting/overview.mdx b/apps/docs/self-hosting/overview.mdx
index 23a854a7..07d54c0c 100644
--- a/apps/docs/self-hosting/overview.mdx
+++ b/apps/docs/self-hosting/overview.mdx
@@ -26,7 +26,7 @@ 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](/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 full Memory API** — `/ns/{namespace}/document`, `/ns/{namespace}/search`, `/ns/{namespace}/profile`, namespaces, the works.
The only thing you bring is a model. In production, Supermemory runs its own proprietary models, purpose-tuned for long-horizon data understanding and memory extraction. Self-hosted, the same pipeline runs on whatever model you point it at — OpenAI, Anthropic, Gemini, Groq, or any OpenAI-compatible endpoint. Bring a key and go. Or don't bring one at all:
@@ -45,12 +45,16 @@ Local graph engine, local embeddings, local LLM. Text-memory processing can stay
## Drop-in with your existing code
+
+The v5 SDKs (`supermemory` 5.x on npm and PyPI) need `supermemory-server` v0.0.9 or later. Older servers only speak v3/v4; run `supermemory-server upgrade` first.
+
+
The self-hosted server speaks the same API as the hosted platform. Point any Supermemory SDK at it with a one-line change:
```typescript
const client = new Supermemory({
apiKey: "sm_...", // printed on first boot
- baseURL: "http://localhost:6767",
+ baseUrl: "http://localhost:6767",
})
```
@@ -71,7 +75,7 @@ Self-hosted is free within its lite license limit and useful for local developme
| 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](/self-hosting/local-vs-enterprise).
+If you outgrow a single machine — or want connectors, MCP, and the best-tuned extraction pipeline — [the platform](https://console.supermemory.ai) is one `serverURL` change away. Running this for a team or organization? See [Local vs. Enterprise](/self-hosting/local-vs-enterprise).
## Next steps
diff --git a/apps/docs/self-hosting/quickstart.mdx b/apps/docs/self-hosting/quickstart.mdx
index 2fca989b..cafd5141 100644
--- a/apps/docs/self-hosting/quickstart.mdx
+++ b/apps/docs/self-hosting/quickstart.mdx
@@ -78,19 +78,23 @@ In production, Supermemory runs proprietary models tuned for long-horizon data u
## Add your first memory
+
+The v5 SDKs (`supermemory` 5.x on npm and PyPI) need `supermemory-server` v0.0.9 or later. Older servers only speak v3/v4; run `supermemory-server upgrade` first.
+
+
```typescript
-import Supermemory from "supermemory"
+import { Supermemory } from "supermemory"
const client = new Supermemory({
apiKey: "sm_...",
- baseURL: "http://localhost:6767",
+ baseUrl: "http://localhost:6767",
})
-await client.memories.add({
+await client.add("user_dhravya", {
content: "I'm Dhravya. I love building dev tools and I'm allergic to peanuts.",
- containerTag: "user_dhravya",
+ dreaming: "instant",
})
```
@@ -103,20 +107,20 @@ client = Supermemory(
base_url="http://localhost:6767",
)
-client.memories.add(
+client.add(
+ "user_dhravya",
content="I'm Dhravya. I love building dev tools and I'm allergic to peanuts.",
- container_tag="user_dhravya",
+ dreaming="instant",
)
```
```bash
-curl http://localhost:6767/v3/documents \
+curl "http://localhost:6767/ns/user_dhravya/document?dreaming=instant" \
-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"
+ "content": "I am Dhravya. I love building dev tools and I am allergic to peanuts."
}'
```
@@ -127,28 +131,23 @@ curl http://localhost:6767/v3/documents \
```typescript
-const results = await client.search({
- q: "what food should I avoid?",
- containerTag: "user_dhravya",
+const { results } = await client.search("user_dhravya", {
+ query: "what food should I avoid?",
})
```
```python
-results = client.search(
- q="what food should I avoid?",
- container_tag="user_dhravya",
-)
+results = client.search("user_dhravya", query="what food should I avoid?")
```
```bash
-curl http://localhost:6767/v3/search \
+curl http://localhost:6767/ns/user_dhravya/search \
-H "Authorization: Bearer sm_..." \
-H "Content-Type: application/json" \
-d '{
- "q": "what food should I avoid?",
- "containerTag": "user_dhravya"
+ "query": "what food should I avoid?"
}'
```
diff --git a/apps/docs/snippets/api-v5-agent-prompt.mdx b/apps/docs/snippets/api-v5-agent-prompt.mdx
index 8f431542..348b83ff 100644
--- a/apps/docs/snippets/api-v5-agent-prompt.mdx
+++ b/apps/docs/snippets/api-v5-agent-prompt.mdx
@@ -3,5 +3,5 @@
Paste this into Claude Code, Cursor, or Codex. It fetches this guide as markdown and rewrites every legacy call.
```text
-Migrate this repository from Supermemory v3/v4 to v5. Fetch https://supermemory.ai/docs/migration/api-v5.md and follow it exactly. First write a checklist of every legacy call site, then migrate them one by one and tick each off. Stop and report any operation that has no v5 replacement instead of inventing one.
+Migrate this repository from Supermemory v3/v4 to v5. Fetch https://supermemory.ai/docs/migration/api-v5.md and follow it. First write a checklist in your reply, not as a file in the repository, of every legacy call site and of every Supermemory-specific name in this codebase: containerTag, containerTags, customId, entityContext, filterByMetadata, filters, including option names, config keys, environment variables and tests. Migrate them one by one and tick each off. Rename those names to the v5 ones (namespace, id, supportingContext, group, filter) with no aliases. A document belongs to exactly one namespace in v5, so a containerTags array becomes one namespace. If an operation has no v5 replacement, do not invent one: keep going, and list it at the end with the alternative the guide suggests.
```
diff --git a/apps/docs/snippets/api-v5-completion.mdx b/apps/docs/snippets/api-v5-completion.mdx
index 1e8895e8..a8eb0f7e 100644
--- a/apps/docs/snippets/api-v5-completion.mdx
+++ b/apps/docs/snippets/api-v5-completion.mdx
@@ -2,8 +2,6 @@
- Direct memory creation and version updates: ingest or replace a source document instead.
- Organization bucket suggestion and organization reset: not part of the v5 public surface.
-- Connector routes that still use `/v3`: unchanged; do not rewrite them.
-- Conversation ingestion (`/v4/conversations`): not part of the v5 public surface. Send the transcript as a document instead.
-- Container-tag merges: not part of the v5 public surface.
+- Connector resource listing and hosted pickers from `/v3/connections`: use `supermemory.connectors.get` with `include: ["syncs", "picker"]` instead. Connector create, list, get, update, delete, and sync all have v5 routes under `/ns/{namespace}/connectors`.
Use the [v5 reference](https://api.supermemory.ai/v5/reference) for the stable v5 API. [`/reference`](https://api.supermemory.ai/reference) always points to the latest public version.
diff --git a/apps/docs/snippets/api-v5-content-management.mdx b/apps/docs/snippets/api-v5-content-management.mdx
index 65cc398c..42f1db5a 100644
--- a/apps/docs/snippets/api-v5-content-management.mdx
+++ b/apps/docs/snippets/api-v5-content-management.mdx
@@ -68,6 +68,38 @@ DELETE /ns/{namespace}/document
The v5 array accepts 1–100 Supermemory or caller-defined IDs. Inspect both `count` and per-ID `errors`; HTTP success can include partial failures.
+### With the SDK
+
+
+```ts Legacy
+const doc = await client.documents.get("doc_1")
+const page = await client.documents.list({ containerTags: ["user_1"] })
+const processing = await client.documents.listProcessing()
+await client.documents.delete("doc_1")
+```
+
+```ts v5
+const doc = await supermemory.documents.get("user_1", "doc_1", {
+ include: ["chunks", "memories"],
+})
+
+const { documents, pagination } = await supermemory.list("user_1", "documents", {
+ page: 1,
+ limit: 100,
+ sort: "createdAt",
+ order: "desc",
+})
+
+const { memories } = await supermemory.list("user_1", "memories")
+
+const { count, errors } = await supermemory.documents.delete("user_1", {
+ ids: ["doc_1", "external_id_2"],
+})
+```
+
+
+There is no separate processing list. Read `system.status` on each item returned by `supermemory.list(namespace, "documents")`. Chunks come from `supermemory.list(namespace, "chunks")` or `documents.get` with `include: ["chunks"]`.
+
### Forget memories
| Legacy intent | v5 operation |
diff --git a/apps/docs/snippets/api-v5-document-ingestion.mdx b/apps/docs/snippets/api-v5-document-ingestion.mdx
index 661dfd74..13946239 100644
--- a/apps/docs/snippets/api-v5-document-ingestion.mdx
+++ b/apps/docs/snippets/api-v5-document-ingestion.mdx
@@ -62,9 +62,53 @@ The API acknowledges the file after durable acceptance, with `status` set as for
- `taskType: "memory"` extracts long-term memories; `taskType: "superrag"` indexes source context without memory generation.
- `dreaming: "dynamic"` (default) groups related documents into coherent memory units.
- `dreaming: "instant"` processes each document independently and bills one extra operation per document.
+- Memories appear quickly only with `dreaming: "instant"`. Under `dynamic`, a fresh namespace can show zero memories and an empty profile for several minutes. Use `instant` for quickstarts and parity tests.
These options go in the JSON body (or as form fields for file routes). Ingest routes take no query parameters; sending one returns `400`.
+### With the SDK
+
+
+```ts Legacy
+await client.add({
+ content: "new turn",
+ customId: "conv_1",
+ containerTag: "user_1",
+ metadata: { source: "chat" },
+})
+
+await client.documents.batchAdd({
+ documents: [{ content: "first" }, { content: "second" }],
+ containerTag: "user_1",
+})
+
+await client.documents.uploadFile({ file, containerTag: "user_1" })
+```
+
+```ts v5
+await client.add("user_1", {
+ content: "new turn",
+ id: "conv_1",
+ metadata: { source: "chat" },
+ dreaming: "instant",
+})
+
+await client.documents.batchAdd("user_1", {
+ documents: [
+ { content: "first", id: "doc_1" },
+ { content: "second", id: "doc_2" },
+ ],
+})
+
+await client.documents.uploadFile("user_1", {
+ file,
+ metadata: JSON.stringify({ source: "upload" }),
+})
+```
+
+
+The namespace is the first argument on every call. `taskType` and `dreaming` sit next to `content` in the same object. `uploadFile` is multipart, so `metadata` is a JSON string there, not an object.
+
### Verification
- Ingest text, a public URL, and a file, then wait for each document to finish processing.
diff --git a/apps/docs/snippets/api-v5-document-updates.mdx b/apps/docs/snippets/api-v5-document-updates.mdx
index 843ed72b..1c97e95d 100644
--- a/apps/docs/snippets/api-v5-document-updates.mdx
+++ b/apps/docs/snippets/api-v5-document-updates.mdx
@@ -55,6 +55,35 @@ PATCH changes only supplied fields. Include `file` to replace the source while r
There is no public v5 `PUT /ns/{namespace}/document/file/{id}` operation. Use POST for a complete replacement and PATCH for a partial update.
+### With the SDK
+
+
+```ts Legacy
+await client.documents.update("doc_1", {
+ content: "corrected source",
+ metadata: { revision: 2 },
+})
+```
+
+```ts v5
+await supermemory.documents.update("user_1", "doc_1", {
+ content: "corrected source",
+ metadata: { revision: 2 },
+})
+
+await supermemory.documents.replaceWithFile("user_1", "doc_1", {
+ file,
+ metadata: JSON.stringify({ revision: 2 }),
+})
+
+await supermemory.documents.updateFile("user_1", "doc_1", {
+ metadata: JSON.stringify({ reviewed: true }),
+})
+```
+
+
+`replaceWithFile` is the POST full replacement and requires `file`. `updateFile` is the PATCH partial update; `file` is optional and metadata merges key by key.
+
### IDs and scope
The path `id` may be the Supermemory document ID or your caller-defined ID. It is resolved only inside `{namespace}`; an ID from another namespace is not a cross-namespace update mechanism.
diff --git a/apps/docs/snippets/api-v5-filters.mdx b/apps/docs/snippets/api-v5-filters.mdx
index 016aee94..9e7e7ab3 100644
--- a/apps/docs/snippets/api-v5-filters.mdx
+++ b/apps/docs/snippets/api-v5-filters.mdx
@@ -52,6 +52,39 @@ Fields may contain letters, numbers, `_`, `.`, and `-`. Expressions allow up to
```
+### With the SDK
+
+
+```ts Legacy
+await client.search.execute({
+ q: "research notes",
+ containerTags: ["user_1"],
+ filters: JSON.stringify({
+ AND: [
+ { key: "category", value: "research" },
+ { key: "score", value: 0.8, filterType: "numeric", numericOperator: ">=" },
+ ],
+ }),
+})
+```
+
+```ts v5
+await supermemory.search("user_1", {
+ query: "research notes",
+ filter: {
+ operator: "and",
+ operands: [
+ { field: "category", operator: "eq", value: "research" },
+ { field: "score", operator: "gte", value: 0.8 },
+ ],
+ },
+ searchMode: "chunks",
+})
+```
+
+
+`filter` is a typed object under `body`, not a JSON string. The same shape works in `supermemory.list` and `supermemory.profile`. Keys are literal: `customer.plan` is one field name, not a nested path.
+
### Deterministic conversion
1. Rename outer `filters` to `filter`.
diff --git a/apps/docs/snippets/api-v5-memory-forgetting.mdx b/apps/docs/snippets/api-v5-memory-forgetting.mdx
index 30b266a0..2db8b57f 100644
--- a/apps/docs/snippets/api-v5-memory-forgetting.mdx
+++ b/apps/docs/snippets/api-v5-memory-forgetting.mdx
@@ -55,9 +55,31 @@ Use this workflow when a human or policy must approve the exact set. Re-running
`count` always equals `matches.length`. Both dry-run and applied semantic requests use this shape; the payload alone does not replace your record of which mode was sent.
+### With the SDK
+
+
+```ts Legacy
+await client.memories.forget({ id: "mem_1" })
+await client.memories.forgetMatching({ query: "outdated home address" })
+```
+
+```ts v5
+const preview = await supermemory.memories.forgetMatching("user_1", {
+ query: "outdated home address",
+ dryRun: true,
+})
+
+await supermemory.memories.forget("user_1", {
+ ids: preview.matches.map((m) => m.id),
+})
+```
+
+
+`dryRun` is required on `forgetMatching`. Both calls return `{ count, matches, errors }`.
+
### Removed memory-write routes
-Direct v4 memory creation and version updates have no v5 replacement. Ingest source material through document routes and update the canonical document when facts change.
+Direct v4 memory creation and version updates (`client.memories.add`, `client.memories.updateMemory`) have no v5 replacement. Ingest source material through document routes and update the canonical document when facts change.
### Verification
diff --git a/apps/docs/snippets/api-v5-namespaces.mdx b/apps/docs/snippets/api-v5-namespaces.mdx
index 97894226..cc7073d4 100644
--- a/apps/docs/snippets/api-v5-namespaces.mdx
+++ b/apps/docs/snippets/api-v5-namespaces.mdx
@@ -52,6 +52,35 @@ DELETE /ns/project_alpha?moveTo=project_archive
Legacy merge can accept multiple sources; v5 moves one source per request. Run multi-source migrations sequentially and record each operation independently.
+### With the SDK
+
+Replace calls to `/v3/container-tags/*` with `supermemory.namespaces`:
+
+
+```bash Legacy
+GET /v3/container-tags/list
+PATCH /v3/container-tags/project_alpha
+DELETE /v3/container-tags/project_alpha
+```
+
+```ts v5
+const { namespaces } = await supermemory.namespaces.list()
+const one = await supermemory.namespaces.get("project_alpha")
+
+await supermemory.namespaces.update("project_alpha", {
+ supportingContext: "Research project for distributed systems",
+})
+
+await supermemory.namespaces.delete("project_alpha")
+
+await supermemory.namespaces.delete("project_alpha", {
+ moveTo: "project_archive",
+})
+```
+
+
+`namespaces.list()` returns an array of `{ id, namespace, documentCount, memoryCount, description, system }`. A delete without `moveTo` returns final counts; with `moveTo` it returns `{ success, status: "queued", operationId, namespace, moveTo }`.
+
### Verification
- Confirm existing container-tag values resolve unchanged as namespace paths.
diff --git a/apps/docs/snippets/api-v5-organization.mdx b/apps/docs/snippets/api-v5-organization.mdx
index db220fc8..e0d34446 100644
--- a/apps/docs/snippets/api-v5-organization.mdx
+++ b/apps/docs/snippets/api-v5-organization.mdx
@@ -41,6 +41,23 @@ The field is exhaustive: the supplied value replaces the existing context. Send
Organization updates require an organization administrator. Do not silently fall back to namespace context when the caller receives `403`.
+### With the SDK
+
+
+```ts Legacy
+const settings = await client.settings.get()
+await client.settings.update({ filterPrompt: "Acme builds security tools for enterprises" })
+```
+
+```ts v5
+const { organizationalContext, namespaceCount } = await supermemory.organization.get()
+
+await supermemory.organization.update({
+ organizationalContext: "Acme builds security tools for enterprises",
+})
+```
+
+
### Removed public operations
- Organization profile-bucket mutation is not exposed through organization settings.
diff --git a/apps/docs/snippets/api-v5-overview.mdx b/apps/docs/snippets/api-v5-overview.mdx
index 931b9aeb..fbea7e63 100644
--- a/apps/docs/snippets/api-v5-overview.mdx
+++ b/apps/docs/snippets/api-v5-overview.mdx
@@ -12,7 +12,7 @@ The API base URL and bearer keys do not change.
- Search for `/v3/`, `/v4/`, `containerTag`, `containerTags`, `customId`, `entityContext`, `filterByMetadata`, `filters`, and legacy SDK methods.
+ Search for `/v3/`, `/v4/`, `containerTag`, `containerTags`, `customId`, `entityContext`, `filterByMetadata`, `filters`, and legacy SDK methods such as `client.search.execute`, `client.profile`, and `client.connections.*`.
Move the legacy `containerTag` into `/ns/{namespace}`. Never infer scope from a document or memory ID, and never send multiple namespaces to one v5 request.
@@ -31,19 +31,77 @@ The API base URL and bearer keys do not change.
+## Upgrade the SDK
+
+The v5 TypeScript SDK ships as the same `supermemory` package. Install it, then replace the legacy client:
+
+```bash
+npm i supermemory
+```
+
+
+```ts Legacy
+import Supermemory from "supermemory"
+
+const client = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
+await client.add({ content: "new turn", containerTag: "user_1" })
+```
+
+```ts v5
+import { Supermemory } from "supermemory"
+
+const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
+await supermemory.add("user_1", { content: "new turn" })
+```
+
+
+Every v5 call takes one object. `namespace`, path IDs, and query parameters are top-level keys; the JSON body goes under `body`. Legacy `containerTag` never appears in a body again. There is no v5 Python SDK yet.
+
## Endpoint map
-| Legacy | v5 |
-| --- | --- |
-| `POST /v3/documents` | `POST /ns/{namespace}/document` |
-| `POST /v3/documents/batch` | `POST /ns/{namespace}/document/batch` |
-| `POST /v3/documents/file` | `POST /ns/{namespace}/document/file` |
-| `GET/PATCH /v3/documents/{id}` | `GET/PATCH /ns/{namespace}/document/{id}` |
-| Single or bulk document delete | `DELETE /ns/{namespace}/document` |
-| Legacy document or memory lists | `POST /ns/{namespace}/list/{type}` |
-| `POST /v3/search` or `/v4/search` | `POST /ns/{namespace}/search` |
-| `POST /v4/profile` | `POST /ns/{namespace}/profile` |
-| `POST /v4/profile/buckets` | `GET /ns/{namespace}/profile/buckets` |
-| Legacy memory forget routes | `DELETE /ns/{namespace}/memories...` |
-| Container-tag settings and lifecycle | `/namespaces` and `/ns/{namespace}` |
-| `GET/PATCH /v3/settings` | `GET/PATCH /organization` |
+| Legacy | v5 | SDK method |
+| --- | --- | --- |
+| `POST /v3/documents` | `POST /ns/{namespace}/document` | `supermemory.add` |
+| `POST /v3/documents/batch` | `POST /ns/{namespace}/document/batch` | `supermemory.documents.batchAdd` |
+| `POST /v3/documents/file` | `POST /ns/{namespace}/document/file` | `supermemory.documents.uploadFile` |
+| `GET/PATCH /v3/documents/{id}` | `GET/PATCH /ns/{namespace}/document/{id}` | `supermemory.documents.get` / `supermemory.documents.update` |
+| File replace or partial file update | `POST/PATCH /ns/{namespace}/document/file/{id}` | `supermemory.documents.replaceWithFile` / `supermemory.documents.updateFile` |
+| Single or bulk document delete | `DELETE /ns/{namespace}/document` | `supermemory.documents.delete` |
+| Legacy document or memory lists | `POST /ns/{namespace}/list/{type}` | `supermemory.list` |
+| `POST /v3/search` or `/v4/search` | `POST /ns/{namespace}/search` | `supermemory.search` |
+| `POST /v4/profile` | `POST /ns/{namespace}/profile` | `supermemory.profile` |
+| `POST /v4/profile/buckets` | `GET/PUT/DELETE /ns/{namespace}/profile/buckets` | `supermemory.profiles.getBuckets` / `setBuckets` / `deleteBuckets` |
+| Legacy memory forget routes | `DELETE /ns/{namespace}/memories...` | `supermemory.memories.forget` / `supermemory.memories.forgetMatching` |
+| Container-tag settings and lifecycle | `/namespaces` and `/ns/{namespace}` | `supermemory.namespaces.list` / `get` / `update` / `delete` |
+| `GET/PATCH /v3/settings` | `GET/PATCH /organization` | `supermemory.organization.get` / `update` |
+
+### Connectors
+
+Connector routes also move under the namespace. Legacy `containerTags` arrays become one namespace per connector.
+
+| Legacy | v5 | SDK method |
+| --- | --- | --- |
+| `POST /v3/connections/list` | `GET /ns/{namespace}/connectors` or `GET /connectors` | `supermemory.connectors.list` / `supermemory.connectors.listAll` |
+| `POST /v3/connections/{provider}` | `POST /ns/{namespace}/connectors` | `supermemory.connectors.create` |
+| `GET /v3/connections/{connectionId}` | `GET /ns/{namespace}/connectors/{id}` | `supermemory.connectors.get` |
+| `POST /v3/connections/{connectionId}/configure` | `PATCH /ns/{namespace}/connectors/{id}` | `supermemory.connectors.update` |
+| `DELETE /v3/connections/{connectionId}` | `DELETE /ns/{namespace}/connectors/{id}` | `supermemory.connectors.delete` |
+| `POST /v3/connections/{provider}/import` | `POST /ns/{namespace}/connectors/{id}/sync` | `supermemory.connectors.sync` |
+
+
+```ts Legacy
+const connection = await client.connections.create("notion", {
+ containerTags: ["user_1"],
+ redirectUrl: "https://app.example.com/connected",
+})
+```
+
+```ts v5
+const { id, authorization } = await supermemory.connectors.create("user_1", {
+ provider: "notion",
+ redirectUrl: "https://app.example.com/connected"
+})
+```
+
+
+`authorization` is `null` for config providers (`web-crawler`, `s3`, `granola`), which start syncing immediately. `connectors.sync` returns `{ id, status: "queued" }` and `409` when a sync is already running.
diff --git a/apps/docs/snippets/api-v5-profiles.mdx b/apps/docs/snippets/api-v5-profiles.mdx
index 225889f7..eaa126d3 100644
--- a/apps/docs/snippets/api-v5-profiles.mdx
+++ b/apps/docs/snippets/api-v5-profiles.mdx
@@ -21,9 +21,9 @@ Remove legacy `q`, `threshold`, and `include`. If the caller needs query-ranked
```json
{
"profile": {
- "static": ["The user works in design"],
- "dynamic": ["The user is preparing a launch"],
- "buckets": { "work": ["Prefers concise project updates"] }
+ "static": [{ "id": "mem_1", "memory": "The user works in design" }],
+ "dynamic": [{ "id": "mem_2", "memory": "The user is preparing a launch" }],
+ "buckets": { "work": [{ "id": "mem_3", "memory": "Prefers concise project updates" }] }
}
}
```
@@ -67,6 +67,33 @@ DELETE /ns/user_1/profile/buckets
Names must be unique. Organization-owned buckets can appear in the effective GET response but cannot be changed or removed through namespace PUT or DELETE calls.
+### With the SDK
+
+
+```ts Legacy
+const profile = await client.profile({ containerTag: "user_1", q: "work preferences" })
+const buckets = await client.profile.buckets({ containerTag: "user_1" })
+```
+
+```ts v5
+const { profile } = await supermemory.profile("user_1", {
+ buckets: ["work"],
+})
+
+const buckets = await supermemory.profiles.getBuckets("user_1")
+
+await supermemory.profiles.setBuckets("user_1", {
+ buckets: { work: "Professional preferences and ongoing work" },
+})
+
+await supermemory.profiles.deleteBuckets("user_1", {
+ buckets: ["work"],
+})
+```
+
+
+`supermemory.profile` takes no query. `body` is optional and accepts only `filter` and `buckets`. Read `profile.static`, `profile.dynamic`, and `profile.buckets[name]` as arrays of `{ id, memory }`.
+
### Verification
- Confirm every profile response contains `static`, `dynamic`, and `buckets`.
diff --git a/apps/docs/snippets/api-v5-rollout.mdx b/apps/docs/snippets/api-v5-rollout.mdx
index e2c6cfa9..f84bcb4b 100644
--- a/apps/docs/snippets/api-v5-rollout.mdx
+++ b/apps/docs/snippets/api-v5-rollout.mdx
@@ -54,7 +54,7 @@ Do not normalize away an undocumented difference. Capture the request pair, name
### Completion checklist
- No application API call accidentally uses a `/v5` prefix.
-- Unchanged connector routes retain their documented `/v3` paths.
+- Connector calls use `/ns/{namespace}/connectors` or `supermemory.connectors.*`, not `/v3/connections`.
- No legacy field aliases or response readers remain.
- Every changed default is either accepted intentionally or passed explicitly.
- Rollback restores the previous caller without requiring data repair.
diff --git a/apps/docs/snippets/api-v5-sdks.mdx b/apps/docs/snippets/api-v5-sdks.mdx
index c8dc8a85..a22cfec3 100644
--- a/apps/docs/snippets/api-v5-sdks.mdx
+++ b/apps/docs/snippets/api-v5-sdks.mdx
@@ -1,18 +1,19 @@
-Check which client you call the API through before you translate requests. Not every client supports v5 yet.
+Check which client you call the API through before you translate requests. Both official SDKs speak v5; the integration packages do not yet.
| Client | v5 support | What to do |
| --- | --- | --- |
-| TypeScript SDK (`supermemory` on npm) | `5.0.0-rc.5` and later | Install `supermemory@rc` and follow the SDK changes below. Release candidates before `rc.5` still use the v3/v4 surface. |
-| Python SDK (`supermemory` on PyPI) | Not yet | The 3.x SDK calls v3/v4. Call v5 over HTTP as shown in this guide, or keep the SDK on v3/v4 until a v5 release ships. |
-| `@supermemory/tools` (AI SDK, OpenAI and agent integrations) | Not yet | These call v3/v4 (`/v4/profile`, `/v4/conversations`). No change needed today. |
-| CLI (`npx supermemory`) and `supermemory local` | Unchanged | The CLI calls v3/v4 directly and keeps working. No change needed. |
+| TypeScript SDK (`supermemory` on npm) | `5.0.0` and later | Run `npm i supermemory` and follow the SDK changes below. Versions before 5.0.0 use the v3/v4 surface. |
+| Python SDK (`supermemory` on PyPI) | `5.0.0` and later | Run `pip install -U supermemory`. Calls take the namespace first, then keyword arguments: `client.add("user_alex", content="...")`. Versions before 5.0.0 use the v3/v4 surface. |
+| `@supermemory/tools` (AI SDK, OpenAI, Mastra, VoltAgent, Claude memory) | `3.0.0` and later | Calls v5 and uses the v5 names. The config takes one `namespace` instead of `containerTags` or `projectId`, and `withSupermemory` takes `namespace` and `id` instead of `containerTag` and `customId`. See [Upgrading tools to 3.0](/migration/tools-v3-upgrade). `@supermemory/ai-sdk` is retired; import from `@supermemory/tools/ai-sdk`. |
+| CLI (`npx supermemory`) | `supermemory` 5.x | The CLI ships inside the npm package and now calls v5. `--tag` is `--namespace`, `SUPERMEMORY_TAG` is `SUPERMEMORY_NAMESPACE`, `supermemory tags` is `supermemory namespaces`, and `remember`, `update` and `tags merge` are gone (use `namespaces delete --move-to`). |
+| `supermemory local` | Server v0.0.9 and later | Older local servers only serve v3/v4. Run `supermemory-server upgrade` before using the 5.x SDKs or CLI against it. |
### TypeScript SDK changes
-The v5 SDK scopes every content call to one `namespace` and moves the payload under `body`:
+The v5 SDK scopes every content call to one `namespace`, passed first. URL values (`namespace`, then `id` where there is one) are positional; everything else goes in one object:
```ts
-import Supermemory from "supermemory";
+import { Supermemory } from "supermemory";
const client = new Supermemory(); // reads SUPERMEMORY_API_KEY, as before
@@ -20,23 +21,39 @@ const client = new Supermemory(); // reads SUPERMEMORY_API_KEY, as before
await client.add({ content: "Alex prefers morning meetings.", containerTag: "user_alex" });
// v5
-await client.add({
- namespace: "user_alex",
- body: { content: "Alex prefers morning meetings." },
-});
+await client.add("user_alex", { content: "Alex prefers morning meetings." });
+await client.documents.get("user_alex", "doc-1", { include: ["chunks"] });
```
+
+If your code passes a `containerTags` array, pick one namespace. A v5 document lives in exactly one namespace, so there is no multi-tag write to translate. Code that read across several tags runs one call per namespace and merges the results.
+
+
| v4 | v5 |
| --- | --- |
-| `client.search.memories({ q, containerTag })` | `client.search({ namespace, body: { query } })` |
-| `client.profile({ containerTag, q })` | `client.profile({ namespace })`, plus `client.search` in parallel if you need results |
-| `client.documents.list(...)`, `client.memories.list(...)` | `client.list({ namespace, type })` |
+| `client.search.memories({ q, containerTag })` | `client.search(namespace, { query })` |
+| `client.profile({ containerTag, q })` | `client.profile(namespace)`; profile no longer takes a query, call `client.search` separately if you need results |
+| `client.documents.list(...)`, `client.memories.list(...)` | `client.list(namespace, "documents" \| "chunks" \| "memories", { filter? })` |
| `client.containerTags.*` | `client.namespaces.*` |
+| `client.connections.*` | `client.connectors.*` (`create`, `list`, `listAll`, `get`, `update`, `delete`, `sync`) |
| `client.settings.{get, update}` | `client.organization.{get, update}` |
-| Errors per status (`RateLimitError`, …) | One `SupermemoryError`; branch on `statusCode` |
-| Retries on by default | Retries are opt-in through `retryConfig` |
-| `timeout` | `timeoutMs` |
+| `containerTag`, `customId`, `q`, `filters` | `namespace` argument, `id`, `query`, typed `filter` |
+| `APIError`, `NotFoundError`, `RateLimitError`, … | `SupermemoryError` (`statusCode`, `body`); `NotFoundError`, `UnauthorizedError`, `ConflictError`, … for common statuses; `SupermemoryTimeoutError` |
+| `timeout` (ms), `baseURL`, `defaultHeaders` | `timeoutInSeconds`, `baseUrl`, `headers`; `maxRetries` still defaults to 2 |
-The v5 SDK drops methods that have no v5 endpoint: `connections`, `conversations.add`, `settings.{reset, suggestBuckets}`, `containerTags.{merge, mergeStatus}`, `memories.{add, updateMemory}`, and `documents.{listProcessing, chunks, fileUrl, search}`. Stay on `supermemory@4` for those calls.
+Some v4 methods are gone from the v5 SDK. Most have a v5 way to do the same thing:
+
+| v4 method | v5 |
+| --- | --- |
+| `conversations.add({ containerTag, messages })` | `client.add(namespace, { content, id })`. Pass the conversation text as `content` and a stable `id` per conversation (for example the session id). Repeating the same `id` updates that document, so one conversation stays one document. |
+| `memories.add(...)` | `client.add(namespace, { content, dreaming: "instant" })`. Memories come from documents; there is no direct memory write. |
+| `memories.updateMemory(...)` | Update the source document with `client.documents.update(namespace, id, { content })`. |
+| `documents.search(...)` | `client.search(namespace, { query, searchMode: "chunks" })` |
+| `documents.chunks(id)` | `client.documents.get(namespace, id, { include: ["chunks"] })` |
+| `documents.listProcessing()` | `client.list(namespace, "documents")` and read `system.status` on each item |
+| `containerTags.merge(...)`, `mergeStatus(...)` | `client.namespaces.delete(source, { moveTo: target })`. One source per call; the response carries an `operationId`. |
+| `memories.forget({ content })` | `client.memories.forgetMatching(namespace, { query, dryRun: true })` to preview, then `memories.forget(namespace, { ids })` |
+
+Gone with no replacement: `settings.reset`, `settings.suggestBuckets`, `documents.fileUrl`, and the `reason` field on forget.
The full method map is in the SDK's [migration guide](https://github.com/supermemoryai/sdk-ts/blob/main/MIGRATION.md).
diff --git a/apps/docs/snippets/api-v5-search.mdx b/apps/docs/snippets/api-v5-search.mdx
index 98132b28..37807f04 100644
--- a/apps/docs/snippets/api-v5-search.mdx
+++ b/apps/docs/snippets/api-v5-search.mdx
@@ -27,6 +27,44 @@ POST /ns/user_1/search
```
+### With the SDK
+
+
+```ts Legacy
+const memories = await client.search.memories({
+ q: "What did the user decide?",
+ containerTag: "user_1",
+ limit: 10,
+})
+
+const docs = await client.search.execute({
+ q: "launch plan",
+ containerTags: ["user_1"],
+})
+```
+
+```ts v5
+const memories = await supermemory.search("user_1", {
+ query: "What did the user decide?",
+ threshold: 0.6,
+ rewriteQuery: false,
+ searchMode: "memories",
+ limit: 10,
+})
+
+const chunks = await supermemory.search("user_1", {
+ query: "launch plan",
+ searchMode: "chunks",
+})
+
+const hybrid = await supermemory.search("user_1", {
+ query: "launch plan",
+})
+```
+
+
+`query`, `searchMode`, `limit`, `threshold`, `filter`, `include`, `rerank`, and `rewriteQuery` all go in one object (the JSON body over HTTP). Omitting `searchMode` gives `hybrid`.
+
### Changed defaults
| Setting | v4 | v5 |
diff --git a/apps/docs/user-profiles/buckets.mdx b/apps/docs/user-profiles/buckets.mdx
index 8249758b..553a5f8b 100644
--- a/apps/docs/user-profiles/buckets.mdx
+++ b/apps/docs/user-profiles/buckets.mdx
@@ -11,7 +11,7 @@ Buckets are **custom topical categories** for a profile — an axis that sits al
New to buckets? Read the [conceptual overview](/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.
+Every org starts with a built-in `preferences` bucket. You can define your own at the organization level, and add more per namespace (what v3/v4 called a container tag) — all covered below.
---
@@ -19,39 +19,25 @@ Every org starts with a built-in `preferences` bucket. You can define your own a
### 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`.
+`supermemory.profile` returns `buckets` next to `static` and `dynamic`. Omit `buckets` in the request to get every effective bucket, or pass up to 50 names to narrow only the bucket section.
-
+
```typescript
- 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 supermemory.profile("user_123", { // optional — omit for all buckets
+ buckets: ["preferences", "goals"],
});
- const { profile } = await res.json();
- console.log(profile.buckets.preferences);
- console.log(profile.buckets.goals);
+ console.log(profile.buckets.preferences.map((m) => m.memory));
+ console.log(profile.buckets.goals.map((m) => m.memory));
```
```bash
- curl -X POST "https://api.supermemory.ai/v4/profile" \
+ curl -X POST "https://api.supermemory.ai/ns/user_123/profile" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
- -d '{
- "containerTag": "user_123",
- "include": ["buckets"],
- "buckets": ["preferences", "goals"]
- }'
+ -d '{ "buckets": ["preferences", "goals"] }'
```
@@ -60,13 +46,15 @@ Pass `include: ["buckets"]` to `/v4/profile` to return bucket-organized memories
```json
{
"profile": {
+ "static": [],
+ "dynamic": [],
"buckets": {
"preferences": [
- "[Summary] Prefers concise, technical answers and dark-mode tooling",
- "[Recent] Switched their editor to Zed"
+ { "id": "mem_1", "memory": "[Summary] Prefers concise, technical answers and dark-mode tooling" },
+ { "id": "mem_2", "memory": "[Recent] Switched their editor to Zed" }
],
"goals": [
- "[Recent] Wants to ship the billing revamp this quarter"
+ { "id": "mem_3", "memory": "[Recent] Wants to ship the billing revamp this quarter" }
]
}
}
@@ -79,205 +67,102 @@ Pass `include: ["buckets"]` to `/v4/profile` to return bucket-organized memories
### 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`:
+To see which buckets are configured for a namespace (org buckets merged with any namespace-level additions), call `profiles.getBuckets`:
-
+
```typescript
- 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: "..." }, ...]
+ const { buckets } = await supermemory.profiles.getBuckets("user_123");
+ // { preferences: "Explicit first-person preferences...", work: "..." }
```
```bash
- 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
-{
- "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
- 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
- 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
-{
- "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
- 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
- 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
-{
- "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
- 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
- curl -X POST "https://api.supermemory.ai/v3/settings/suggest-buckets" \
+ curl "https://api.supermemory.ai/ns/user_123/profile/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.
-
+**Response:**
+```json
+{
+ "buckets": {
+ "preferences": "Explicit first-person preferences the person directly stated."
+ }
+}
+```
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `buckets` | object | Map of bucket name to description. The name is a stable slug, also stored on each memory. Lowercase alphanumeric with `-`/`_`, 1–64 chars |
+| `buckets[name]` | string | What belongs in the bucket — guides the ingestion classifier |
+
+---
+
+## Creating and configuring buckets
+
+Bucket definitions live at two levels: **organization** (the default set every namespace gets) and **namespace** (per-namespace additions). Namespace buckets are managed through `profiles.setBuckets` and `profiles.deleteBuckets`.
+
+### Namespace buckets
+
+`setBuckets` sends 1 to 50 name-to-description entries. Existing namespace names are updated, new names are added, and omitted namespace buckets remain unchanged. These are **add-only** on top of org buckets — a namespace always keeps every org bucket, and if a namespace bucket's name collides with an org bucket, the org's definition wins in the merged, effective set used at ingestion and read time.
+
+
+
+ ```typescript
+ await supermemory.profiles.setBuckets("user_alex", {
+ buckets: {
+ work: "Professional role, employer, projects, and work-related decisions.",
+ trip_planning: "Upcoming trip details specific to this user.",
+ },
+ });
+ ```
+
+
+ ```bash
+ curl -X PUT "https://api.supermemory.ai/ns/user_alex/profile/buckets" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "buckets": {
+ "work": "Professional role, employer, projects, and work-related decisions.",
+ "trip_planning": "Upcoming trip details specific to this user."
+ }
+ }'
+ ```
+
+
+
+### Delete namespace buckets
+
+
+
+ ```typescript
+ await supermemory.profiles.deleteBuckets("user_alex", {
+ buckets: ["trip_planning"],
+ });
+ ```
+
+
+ ```bash
+ curl -X DELETE "https://api.supermemory.ai/ns/user_alex/profile/buckets" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{ "buckets": ["trip_planning"] }'
+ ```
+
+
+
+
+Organization-owned buckets appear in the effective `getBuckets` response but cannot be changed or removed through namespace `setBuckets` or `deleteBuckets` calls. Organization-level buckets are managed in the [console](https://console.supermemory.ai), not through the v5 API.
+
### 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 |
+| Name | 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. |
@@ -294,7 +179,7 @@ If you'd rather start from a template than write descriptions from scratch, thes
### 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.
+If neither the org nor the namespace 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.
---
@@ -302,11 +187,11 @@ If neither the org nor the space has configured any buckets, ingestion falls bac
| 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 |
+| Name format | Lowercase alphanumeric, starting with a letter/digit, may contain `-`/`_`. 1–64 chars |
+| Reserved names | `static` and `dynamic` can't be used as bucket names |
+| Max buckets | 50 per request — applies separately to an org's list and to each namespace's list |
+| Duplicate names | Rejected within a single request |
+| Description | Up to 2,000 chars |
Bucket descriptions steer classification. A precise description ("Explicit first-person preferences only — exclude inferred traits") yields cleaner buckets than a vague one.
@@ -318,32 +203,23 @@ Bucket descriptions steer classification. A precise description ("Explicit first
### 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`](/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}`:
+Bucket descriptions only steer classification *within* a bucket — they don't tell the model anything about the namespace itself. For that, set [`supportingContext`](/concepts/customization#entity-context) on the namespace: a free-text field that's appended alongside the org-level `organizationalContext` into the same prompt the extraction/classification step uses, so it shapes bucket assignment too, not just fact extraction.
-
+
```typescript
- 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."
- })
+ await supermemory.namespaces.update("user_alex", {
+ supportingContext: "This namespace belongs to a solo founder juggling sales, hiring, and product.",
});
```
```bash
- curl -X PATCH "https://api.supermemory.ai/v3/container-tags/user_alex" \
+ curl -X PATCH "https://api.supermemory.ai/ns/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."
+ "supportingContext": "This namespace belongs to a solo founder juggling sales, hiring, and product."
}'
```
@@ -351,13 +227,13 @@ Bucket `description`s only steer classification *within* a bucket — they don't
| Field | Type | Limit |
|-------|------|-------|
-| `entityContext` | string \| null | Up to 1,500 characters. Pass `null` to clear |
+| `supportingContext` | 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`](/concepts/customization) for guidance that should apply everywhere. Both are combined into the same prompt, so keep them complementary rather than redundant.
+`supportingContext` is per-namespace, so use it for context specific to that user/namespace (who they are, what the namespace is for) — use org-level [`organizationalContext`](/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`](/ingestion/add-memories) — useful if you don't want a separate settings call.
+You can also set `supportingContext` inline when adding content, via `supportingContext` in the body of [`supermemory.add`](/ingestion/add-memories#parameters) — useful if you don't want a separate namespace call.
### Model selection
@@ -369,4 +245,4 @@ The model behind extraction and bucket classification isn't configurable through
- [User Profiles](/recall/user-profiles) — Fetch and use profiles via the API
- [User Profiles Concept](/concepts/user-profiles) — Static vs dynamic vs buckets
-- [Container Tags](/concepts/container-tags) — How spaces and container tags work
+- [Namespaces](/concepts/container-tags) — How namespaces work
diff --git a/apps/docs/using-supermemory.mdx b/apps/docs/using-supermemory.mdx
index 6fc156b1..a157d3c9 100644
--- a/apps/docs/using-supermemory.mdx
+++ b/apps/docs/using-supermemory.mdx
@@ -7,7 +7,7 @@ icon: "/icons/hugeicons/compass-01.svg"
import { Journey, JourneyStep, JourneyItem } from "/snippets/journey.mdx";
-Everything in this section is one of four steps. Same loop whether you're building personal memory, RAG over docs, or both on the same `containerTag`.
+Everything in this section is one of four steps. Same loop whether you're building personal memory, RAG over docs, or both on the same `namespace`.
@@ -53,8 +53,8 @@ Raw input automatically becomes memories (the knowledge graph) and indexed docum
What happens between `add()` and a memory showing up in search.
-
- The isolation boundary every ingest and retrieve call is scoped to.
+
+ The isolation boundary every ingest and retrieve call is scoped to (v3/v4 called it a container tag).
When to reach for search, profiles, or both.
diff --git a/apps/docs/v5/api-reference/connectors.mdx b/apps/docs/v5/api-reference/connectors.mdx
new file mode 100644
index 00000000..2bbc1e37
--- /dev/null
+++ b/apps/docs/v5/api-reference/connectors.mdx
@@ -0,0 +1,54 @@
+---
+title: "Connectors"
+sidebarTitle: "Overview"
+description: "Sync Notion, Google Drive, Gmail, GitHub, websites, S3 buckets, and Granola into a namespace"
+icon: "/icons/hugeicons/book-open-01.svg"
+---
+
+| Operation | Purpose |
+| --- | --- |
+| `POST /ns/{namespace}/connectors` | Connect a source to this namespace. OAuth providers return an `authorization` link to send the user to; config providers start syncing right away |
+| `GET /ns/{namespace}/connectors` | List the connectors feeding this namespace |
+| `GET /ns/{namespace}/connectors/{id}` | Read one connector, with `include=syncs` for recent sync runs or `include=picker` for a hosted picker link |
+| `PATCH /ns/{namespace}/connectors/{id}` | Change what it syncs: `selection` and `documentLimit` |
+| `POST /ns/{namespace}/connectors/{id}/sync` | Trigger a sync now. Returns 409 while one is already running |
+| `DELETE /ns/{namespace}/connectors/{id}` | Disconnect, optionally deleting the documents it imported |
+| `GET /connectors` | List every connector in the organization, across namespaces |
+
+Providers: `notion`, `google-drive`, `onedrive`, `gmail`, `github` authenticate with OAuth. `web-crawler`, `s3`, and `granola` take their configuration in the create body and need no login.
+
+
+```typescript TypeScript
+import { Supermemory } from "supermemory"
+
+const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
+
+// OAuth provider: send the user to authorization.url, the connector appears once they finish
+const { id, authorization } = await supermemory.connectors.create("user_123", {
+ provider: "notion",
+ redirectUrl: "https://yourapp.com/connected"
+})
+
+// Config provider: starts syncing immediately, authorization is null
+const crawler = await supermemory.connectors.create("user_123", {
+ provider: "web-crawler",
+ config: { startUrl: "https://docs.example.com" }
+ documentLimit: 200,
+})
+
+const status = await supermemory.connectors.get("user_123", crawler.id, { include: "syncs" })
+```
+
+```bash cURL
+curl -X POST "https://api.supermemory.ai/ns/user_123/connectors" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{ "provider": "notion", "redirectUrl": "https://yourapp.com/connected" }'
+```
+
+
+
+A pending OAuth connector is not returned by list or get until the user completes authorization. The `authorization.url` expires after one hour (see `authorization.expiresAt`).
+
+
+See the [connector guides](/connectors/overview) for per-provider setup, selection rules, and troubleshooting.
diff --git a/apps/docs/v5/api-reference/content-management.mdx b/apps/docs/v5/api-reference/content-management.mdx
index 6a798f14..6e8fd1f5 100644
--- a/apps/docs/v5/api-reference/content-management.mdx
+++ b/apps/docs/v5/api-reference/content-management.mdx
@@ -2,7 +2,7 @@
title: "Content management"
sidebarTitle: "Overview"
description: "Retrieve, list, and remove documents, chunks, and memories"
-icon: "book-open"
+icon: "/icons/hugeicons/book-open-01.svg"
---
Inspect the context stored in a namespace and remove information that should no longer be used. Retrieve a document with its derived context, page through any resource type, or forget documents and memories precisely.
@@ -16,4 +16,29 @@ Inspect the context stored in a namespace and remove information that should no
| `DELETE /ns/{namespace}/memories` | Forget reviewed memories by exact ID |
| `DELETE /ns/{namespace}/memories/semantic` | Find and forget memories by meaning |
+
+```ts TypeScript
+const doc = await supermemory.documents.get("user_1", "conv_1", {
+ include: ["chunks", "memories"],
+})
+
+const { documents, pagination } = await supermemory.list("user_1", "documents", {
+ page: 1,
+ limit: 50,
+ sort: "createdAt",
+ order: "desc",
+})
+```
+
+```bash curl
+curl "https://api.supermemory.ai/ns/user_1/document/conv_1?include=chunks&include=memories" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+curl -X POST "https://api.supermemory.ai/ns/user_1/list/documents?page=1&limit=50&sort=createdAt&order=desc" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{}'
+```
+
+
Semantic forgetting supports `dryRun: true`, so you can review matches before deleting them. See [document read migration](/migration/api-v5-document-reads) and [memory migration](/migration/api-v5-recall).
diff --git a/apps/docs/v5/api-reference/ingest.mdx b/apps/docs/v5/api-reference/ingest.mdx
index 6c8ca8c4..6206219c 100644
--- a/apps/docs/v5/api-reference/ingest.mdx
+++ b/apps/docs/v5/api-reference/ingest.mdx
@@ -2,7 +2,7 @@
title: "Ingest"
sidebarTitle: "Overview"
description: "Turn source content into searchable memory and keep it current"
-icon: "book-open"
+icon: "/icons/hugeicons/book-open-01.svg"
---
Ingestion gives Supermemory the source material it uses to build searchable context and memories. Add new content or update an existing source without creating a disconnected copy.
@@ -16,6 +16,26 @@ Ingestion gives Supermemory the source material it uses to build searchable cont
| `POST /ns/{namespace}/document/file/{id}` | Completely replace a file-backed document |
| `PATCH /ns/{namespace}/document/file/{id}` | Refresh selected file details or its source |
+
+```ts TypeScript
+const { id, status } = await supermemory.add("user_1", {
+ content: "The user prefers concise project updates",
+ id: "conv_1",
+ metadata: { source: "chat" },
+ dreaming: "instant",
+})
+```
+
+```bash curl
+curl -X POST "https://api.supermemory.ai/ns/user_1/document?dreaming=instant" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{"content":"The user prefers concise project updates","id":"conv_1","metadata":{"source":"chat"}}'
+```
+
+
The `dynamic` processing mode (default) groups related documents together so memories form from coherent, logical units rather than one isolated entry at a time. Use `instant` to process each document on its own right away; it bills one extra operation per document.
+Memories appear quickly only with `dreaming: "instant"`. With the default `dynamic` mode, a fresh namespace can show zero memories and an empty profile for several minutes while extraction is batched. Use `instant` in quickstarts and tests.
+
Every request is scoped to a namespace, keeping each user, project, or tenant isolated. See [document write migration](/migration/api-v5-document-writes).
diff --git a/apps/docs/v5/api-reference/namespaces.mdx b/apps/docs/v5/api-reference/namespaces.mdx
index 6aa7f2aa..cfbd9313 100644
--- a/apps/docs/v5/api-reference/namespaces.mdx
+++ b/apps/docs/v5/api-reference/namespaces.mdx
@@ -2,9 +2,14 @@
title: "Namespaces"
sidebarTitle: "Overview"
description: "Keep memory isolated, understandable, and easy to reorganize"
-icon: "book-open"
+icon: "/icons/hugeicons/book-open-01.svg"
+keywords: ["container tag", "container tags", "containerTag", "namespace"]
---
+
+Namespaces were called **container tags** (`containerTag`) in v3 and v4. Same values, nothing moved.
+
+
| Operation | Purpose |
| --- | --- |
| `GET /namespaces` | Discover namespaces and see their memory footprint |
@@ -12,6 +17,26 @@ icon: "book-open"
| `PATCH /ns/{namespace}` | Give Supermemory better context for future memories |
| `DELETE /ns/{namespace}` | Retire a namespace or preserve its content elsewhere |
+
+```ts TypeScript
+const { namespaces } = await supermemory.namespaces.list()
+
+await supermemory.namespaces.update("project_alpha", {
+ supportingContext: "Research project for distributed systems",
+})
+```
+
+```bash curl
+curl "https://api.supermemory.ai/namespaces" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+curl -X PATCH "https://api.supermemory.ai/ns/project_alpha" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{"supportingContext":"Research project for distributed systems"}'
+```
+
+
`GET /ns` is an alias for `GET /namespaces`. Profile buckets are managed through `/ns/{namespace}/profile/buckets`, not namespace settings.
See [namespace and settings migration](/migration/api-v5-settings).
diff --git a/apps/docs/v5/api-reference/organization.mdx b/apps/docs/v5/api-reference/organization.mdx
index 64f28220..f87b9bb2 100644
--- a/apps/docs/v5/api-reference/organization.mdx
+++ b/apps/docs/v5/api-reference/organization.mdx
@@ -2,7 +2,7 @@
title: "Organization"
sidebarTitle: "Overview"
description: "Give every namespace a shared understanding of your organization"
-icon: "book-open"
+icon: "/icons/hugeicons/book-open-01.svg"
---
| Operation | Purpose |
@@ -10,6 +10,26 @@ icon: "book-open"
| `GET /organization` | See shared context and your namespace footprint |
| `PATCH /organization` | Improve the context guiding memory across the organization |
+
+```ts TypeScript
+const { organizationalContext, namespaceCount } = await supermemory.organization.get()
+
+await supermemory.organization.update({
+ organizationalContext: "Acme builds security tools for enterprises",
+})
+```
+
+```bash curl
+curl "https://api.supermemory.ai/organization" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY"
+
+curl -X PATCH "https://api.supermemory.ai/organization" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{"organizationalContext":"Acme builds security tools for enterprises"}'
+```
+
+
The public v5 shape intentionally excludes profile buckets, bucket suggestions, reset controls, and internal settings.
See [namespace and settings migration](/migration/api-v5-settings).
diff --git a/apps/docs/v5/api-reference/overview.mdx b/apps/docs/v5/api-reference/overview.mdx
index 7d3dfa1f..d1f2babd 100644
--- a/apps/docs/v5/api-reference/overview.mdx
+++ b/apps/docs/v5/api-reference/overview.mdx
@@ -2,7 +2,7 @@
title: "API reference"
sidebarTitle: "Overview"
description: "Documents, search, profiles, lists, memories, namespaces, and organization settings in the latest Supermemory API"
-icon: "unplug"
+icon: "/icons/hugeicons/plug-socket.svg"
---
v5 makes namespace scope explicit in the URL and consolidates overlapping legacy operations.
@@ -20,6 +20,20 @@ https://api.supermemory.ai
Use the [v5 reference](https://api.supermemory.ai/v5/reference) for the stable v5 API. [`/reference`](https://api.supermemory.ai/reference) always points to the latest public version.
+## Install the TypeScript SDK
+
+```bash
+npm i supermemory
+```
+
+```ts
+import { Supermemory } from "supermemory"
+
+const supermemory = new Supermemory({ apiKey: process.env.SUPERMEMORY_API_KEY })
+```
+
+Every SDK call takes one object. `namespace`, path IDs, and query parameters are top-level keys; the JSON body goes under `body`. Each reference page below shows the SDK call next to the equivalent `curl`. There is no v5 Python SDK yet.
+
Translate every v3/v4 request and response deterministically.
diff --git a/apps/docs/v5/api-reference/profiles.mdx b/apps/docs/v5/api-reference/profiles.mdx
index 9bb7a259..77bb560e 100644
--- a/apps/docs/v5/api-reference/profiles.mdx
+++ b/apps/docs/v5/api-reference/profiles.mdx
@@ -2,7 +2,7 @@
title: "Profiles"
sidebarTitle: "Overview"
description: "Turn accumulated memory into a ready-to-use understanding of a user"
-icon: "id-card"
+icon: "/icons/hugeicons/id.svg"
---
| Operation | Purpose |
@@ -12,6 +12,28 @@ icon: "id-card"
| `PUT /ns/{namespace}/profile/buckets` | Shape the profile with namespace-specific categories |
| `DELETE /ns/{namespace}/profile/buckets` | Remove namespace-specific categories |
+
+```ts TypeScript
+const { profile } = await supermemory.profile("user_1")
+
+await supermemory.profiles.setBuckets("user_1", {
+ buckets: { work: "Professional preferences and ongoing work" },
+})
+```
+
+```bash curl
+curl -X POST "https://api.supermemory.ai/ns/user_1/profile" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{}'
+
+curl -X PUT "https://api.supermemory.ai/ns/user_1/profile/buckets" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{"buckets":{"work":"Professional preferences and ongoing work"}}'
+```
+
+
Profiles always contain static and dynamic sections, giving applications both durable knowledge and fresh context without composing them manually. Organization-owned buckets may be inherited but cannot be changed through namespace bucket endpoints.
See [profile migration](/migration/api-v5-recall).
diff --git a/apps/docs/v5/api-reference/search.mdx b/apps/docs/v5/api-reference/search.mdx
index 92cb0121..37d57174 100644
--- a/apps/docs/v5/api-reference/search.mdx
+++ b/apps/docs/v5/api-reference/search.mdx
@@ -2,11 +2,33 @@
title: "Search"
sidebarTitle: "Overview"
description: "Recall the most relevant memories and source context"
-icon: "book-open"
+icon: "/icons/hugeicons/book-open-01.svg"
---
`POST /ns/{namespace}/search` recalls the most useful memories and source chunks for a query. Hybrid search combines both by default; set `searchMode` to `memories` or `chunks` when your experience needs one result type.
Every option goes in the JSON request body: `query`, `searchMode`, `limit`, `threshold`, `filter`, `include`, `rerank`, and `rewriteQuery`.
+
+```ts TypeScript
+const { results, searchTime } = await client.search("user_1", {
+ query: "What did the user decide about the launch?",
+ limit: 10,
+})
+```
+
+```python Python
+response = client.search("user_1", query="What did the user decide about the launch?", limit=10)
+```
+
+```bash curl
+curl -X POST "https://api.supermemory.ai/ns/user_1/search" \
+ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{"query":"What did the user decide about the launch?","limit":10}'
+```
+
+
+Two defaults differ from the legacy API: `searchMode` defaults to `hybrid` (legacy `memories`) and `threshold` defaults to `0.3` (legacy `0.6`). Pass both explicitly if you need legacy-equivalent results.
+
See [search migration](/migration/api-v5-recall) and [typed filter migration](/migration/api-v5-filters).