docs(api): move v5 search and ingest options into the request body (#1808)

Fixes v5 search and recall docs that didn't match the API.

- **Options in the URL.** Search examples passed `searchMode`/`limit` as query params, and ingest examples passed `dreaming`/`taskType` the same way. v5 rejects every query param on these routes with `400`. They now go in the JSON body (quickstart, rules, super-rag, recall/search).
- **`include`, not `attach`.** Renamed the search option. Its `forgotten` flag also admits forgotten memories into the main results, not just related context.
- **Search response.** Added the required `isInference`. `metadata` is always an object, and `system.createdAt` is optional.
- **Search add-ons are billed.** `rewriteQuery` and `rerank` each bill one extra operation; the docs said "no extra cost".
- **Removed claims that don't match current behavior:**
  - inferred memories being down-weighted in search;
  - AST code chunking via `code-chunk` (super-rag now describes how chunking actually works);
  - a 64-character metadata key limit.
- **Smaller fixes:**
  - The namespace is the SDK's first argument; the docs called it a top-level key.
  - "No v5 Python SDK" was stale.
  - Memory review: `total` semantics, the internal review stamp, and the missing 403.

Checked against the current API and the published SDKs (TS 5.0.1, Python 5.0.0). Legacy v3/v4 docs are unchanged apart from factual fixes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
sohamd22 2026-10-15 18:52:40 +00:00
parent 5323f427d0
commit ca89d161cc
7 changed files with 59 additions and 61 deletions

View file

@ -22,7 +22,7 @@ Both can be used independently or together for precise filtering.
## Namespaces
Namespaces create isolated memory spaces. Use them to separate memories by user, project, or any logical boundary. The namespace is part of the URL (`/ns/{namespace}/...`), so every SDK call takes it as a top-level key.
Namespaces create isolated memory spaces. Use them to separate memories by user, project, or any logical boundary. The namespace is part of the URL (`/ns/{namespace}/...`), so namespace-scoped SDK calls take it as the first argument.
### Adding Memories to a Namespace
@ -42,7 +42,7 @@ const results = await supermemory.search("user_123", {
```
<Note>
Each search is scoped to a single namespace. Passing `namespace: "user_123"` restricts results to memories stored in that namespace.
Each search is scoped to a single namespace. Passing `"user_123"` as the namespace restricts results to memories stored in that namespace.
</Note>
### Recommended patterns
@ -336,7 +336,6 @@ const results = await supermemory.search("user_123", { // Scopes results to this
### Metadata key rules
- 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

View file

@ -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 [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.
**"Remember" and "look up" are both supermemory, but different reads.** You [ingest documents](/ingestion/add-memories); the pipeline derives memories from them and maintains a profile per [namespace](/concepts/container-tags). `supermemory.search(namespace, { query, searchMode: "memories" })` recalls the derived facts. `supermemory.search(namespace, { query, searchMode: "chunks" })` recalls the source material itself. A support agent usually needs both: memories for "this customer runs self-hosted and already tried reinstalling", documents for the actual troubleshooting guide.
**Supermemory is not your system of record.** There's no SQL over memories, no joins, no aggregates, no querying by primary key. Keep transactional data in your database, and ingest the narrative *around* it ("the customer disputed invoice #4821 and churned over it") so your AI understands what the rows mean.
@ -56,10 +56,10 @@ client.add(
```
```bash curl
curl -X POST "https://api.supermemory.ai/ns/test/document?taskType=superrag" \
curl -X POST "https://api.supermemory.ai/ns/test/document" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "content": "testing" }'
-d '{ "content": "testing", "taskType": "superrag" }'
```
</CodeGroup>
@ -86,10 +86,10 @@ results = client.search(
```
```bash curl
curl -X POST "https://api.supermemory.ai/ns/test/search?searchMode=hybrid" \
curl -X POST "https://api.supermemory.ai/ns/test/search" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "query": "test" }'
-d '{ "query": "test", "searchMode": "hybrid" }'
```
</CodeGroup>
@ -252,11 +252,12 @@ results = client.search(
```
```bash curl
curl -X POST "https://api.supermemory.ai/ns/org-supermemory/search?searchMode=chunks" \
curl -X POST "https://api.supermemory.ai/ns/org-supermemory/search" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "preferred language",
"searchMode": "chunks",
"filter": { "field": "team", "operator": "eq", "value": "Research" }
}'
```

View file

@ -33,7 +33,7 @@ No chunking strategies to configure. No embedding models to choose. It just work
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"` (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.
If you're ingesting content that's purely reference material — documentation, a large PDF, a knowledge base article — and you don't need Supermemory to derive personal facts or update a profile from it, set `taskType: "superrag"` in the request body. It skips the memory pipeline entirely and only does the chunk → embed → index work needed to make the content searchable.
<CodeGroup>
@ -53,10 +53,10 @@ client.add(
```
```bash cURL
curl -X POST "https://api.supermemory.ai/ns/docs_kb/document?taskType=superrag" \
curl -X POST "https://api.supermemory.ai/ns/docs_kb/document" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "content": "..." }'
-d '{ "content": "...", "taskType": "superrag" }'
```
</CodeGroup>
@ -100,21 +100,9 @@ PDFs and documents are chunked by **semantic sections** — headers, paragraphs,
### Code
Code is chunked using [code-chunk](https://github.com/supermemoryai/code-chunk), our open-source library that understands AST (Abstract Syntax Tree) boundaries:
When content is recognized as Markdown, fenced code blocks are kept whole when they fit in a chunk. Larger blocks are split and re-wrapped in the original fence, including its language tag. Overlap can contain part of a previous line, so chunks are not guaranteed to be independently valid code.
- Functions and methods stay intact
- Classes are chunked by method
- Import statements grouped separately
- Comments attached to their code blocks
```typescript
// A 500-line file becomes meaningful chunks:
// - Imports + type definitions
// - Each function as a separate chunk
// - Class methods individually indexed
```
This means searching for "authentication middleware" finds the actual function, not a random slice of code.
Raw source files and content not recognized as Markdown use plain-text chunking. A lone fenced block may not be enough for automatic Markdown detection.
### Web pages

View file

@ -97,13 +97,14 @@ print(conv.id, conv.status) # e.g. "queued"
```
```bash curl
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/document?dreaming=instant" \
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/document" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "user: Just got back from Tokyo — the team offsite went great.\nassistant: Glad it went well! Anything stand out?\nuser: Sarah presented the Q3 roadmap at the offsite.\nassistant: Sounds like a big moment for her.\nuser: She is being promoted to VP of Product.\nassistant: Congrats to Sarah — that is huge.\nuser: I need a gift idea for my VP of Product.\nassistant: Happy to help brainstorm something personal.",
"id": "chat_offsite_2026",
"metadata": { "type": "conversation" }
"metadata": { "type": "conversation" },
"dreaming": "instant"
}'
```
</CodeGroup>
@ -164,13 +165,14 @@ print(doc.id, doc.status)
```
```bash curl
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/document?taskType=superrag" \
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/document" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "# Team notes — gifts & recognition\n\nWhen someone is promoted to VP or above, the company recommends a thoughtful gift in the $75–$150 range. Experiences tied to recent team milestones land better than generic swag.\n\nFor product leadership, books on platform strategy or a dinner near the last offsite city are common picks. Tokyo offsites often inspire travel-themed gifts.",
"id": "doc_gift_policy",
"metadata": { "type": "document", "source": "handbook" }
"metadata": { "type": "document", "source": "handbook" },
"taskType": "superrag"
}'
```
</CodeGroup>
@ -253,10 +255,14 @@ for hit in rag.results:
```
```bash curl
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/search?searchMode=chunks&limit=3" \
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/search" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "query": "gift ideas for a VP promotion after a Tokyo offsite" }'
-d '{
"query": "gift ideas for a VP promotion after a Tokyo offsite",
"searchMode": "chunks",
"limit": 3
}'
```
</CodeGroup>
@ -300,12 +306,14 @@ print(memories.to_json())
```
```bash curl
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/search?searchMode=memories&limit=5" \
curl -X POST "https://api.supermemory.ai/ns/user_4f8a/search" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "What gift should I get, and why?",
"include": { "related": true }
"searchMode": "memories",
"include": { "related": true },
"limit": 5
}'
```
</CodeGroup>

View file

@ -7,8 +7,8 @@ icon: "/icons/hugeicons/check-list.svg"
Supermemory's graph derives new facts from patterns across your existing memories
(see [graph memory](/concepts/graph-memory)). Because nobody stated these facts
directly, they are flagged as inferred (`isInference: true`) and **down-weighted in
search until someone confirms them**.
directly, they are flagged as inferred (`isInference: true`) until someone confirms
them.
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.
@ -20,19 +20,17 @@ These endpoints are scoped to a single [namespace](/concepts/container-tags)
path parameter.
</Info>
## How review affects ranking
## What review changes
While a memory is unreviewed and inferred it is down-weighted in search, so the
engine's guesses rank below facts you stated explicitly. Reviewing it resolves that
either way:
Reviewing an inferred memory resolves it either way:
| Action | Result | Effect on search |
|--------|--------|------------------|
| **Approve** | `isInference` cleared | Ranks like a stated fact — no longer down-weighted |
| **Approve** | `isInference` cleared | Stays in search, no longer flagged as inferred |
| **Decline** | `isForgotten` set | Removed from search entirely — a rejected guess is forgotten |
| **Undo** | back to unreviewed | Returns to the queue; inferred and down-weighted again |
| **Undo** | back to unreviewed | Returns to the queue, flagged as inferred again |
A reviewed memory is stamped with `reviewStatus` in its metadata so it drops out of the
A reviewed memory gets an internal review stamp in its metadata so it drops out of the
review queue (declined memories also leave search, since they're forgotten). **Undo**
clears that stamp — and un-forgets a declined memory — bringing it back.
@ -98,7 +96,7 @@ GET /v3/container-tags/{containerTag}/inferred
| `memories[].createdAt` | string | ISO 8601 timestamp |
| `memories[].updatedAt` | string | ISO 8601 timestamp |
| `memories[].metadata` | object \| null | Arbitrary metadata stored on the memory |
| `total` | number | Count of unreviewed inferred memories returned |
| `total` | number | Total unreviewed inferred memories in the queue; can exceed the number returned in `memories` |
<Note>
The queue returns up to **50** memories, ordered by `parentCount` descending (most
@ -166,8 +164,8 @@ The reject action is named **`decline`**. There is no `reject` value.
**Action semantics:**
- **`approve`** — Promote the memory: clears `isInference`, so it ranks like a stated
fact instead of a down-weighted guess. Stamps `reviewStatus: "approved"`.
- **`approve`** — Promote the memory: clears `isInference`, so it's treated as a
confirmed fact. Stamps `reviewStatus: "approved"`.
- **`decline`** — Reject the suggestion: the memory is **forgotten**
(`isForgotten: true`) and stamped `reviewStatus: "declined"`, so it leaves both
search and the review queue.
@ -198,6 +196,7 @@ The reject action is named **`decline`**. There is no `reject` value.
| Status | When |
|--------|------|
| `401` | Missing or invalid authentication |
| `403` | Your key can't access this namespace, or lacks write access to it |
| `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 |
@ -286,4 +285,4 @@ a request and the memory simply stays in the queue for a later session.
- [Graph Memory](/concepts/graph-memory) — How inferred (`derive`) memories are created
- [Memory Operations](/recall/memory-operations) — Forget memories
- [Search](/recall/search) — How inferred memories are ranked in results
- [Search](/recall/search) — Results flag inferred memories with `isInference`

View file

@ -12,7 +12,7 @@ Search through your memories and documents with a single API call. Each search r
</Tip>
<Note>
**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.
**SDKs:** one call, `supermemory.search(namespace, { query, searchMode })` in TypeScript or `client.search(namespace, query=..., search_mode=...)` in Python. `searchMode` (`"memories"`, `"chunks"`, or `"hybrid"`) picks what comes back.
</Note>
## Quick start
@ -54,10 +54,10 @@ Search through your memories and documents with a single API call. Each search r
</Tab>
<Tab title="cURL">
```bash
curl -X POST "https://api.supermemory.ai/ns/user_123/search?searchMode=hybrid&limit=5" \
curl -X POST "https://api.supermemory.ai/ns/user_123/search" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "query": "machine learning" }'
-d '{ "query": "machine learning", "searchMode": "hybrid", "limit": 5 }'
```
</Tab>
</Tabs>
@ -72,6 +72,7 @@ Search through your memories and documents with a single API call. Each search r
"similarity": 0.91,
"metadata": { "topic": "interests" },
"isLatest": true,
"isInference": false,
"system": { "createdAt": "2024-01-15T10:30:00.000Z", "updatedAt": "2024-01-15T10:30:00.000Z" }
},
{
@ -80,6 +81,7 @@ Search through your memories and documents with a single API call. Each search r
"similarity": 0.87,
"metadata": { "source": "onboarding_doc" },
"isLatest": true,
"isInference": false,
"system": { "createdAt": "2024-01-14T09:15:00.000Z", "updatedAt": "2024-01-14T09:15:00.000Z" }
}
],
@ -95,7 +97,7 @@ In hybrid mode, results contain either a `memory` field (extracted facts) or a `
## Parameters
`namespace` is the URL path. Every other option goes in the JSON body (the second argument in the SDKs).
`namespace` is the URL path. Every other option goes in the JSON body: the second argument in TypeScript, or keyword arguments in Python. Search takes no query-string parameters; sending any returns `400`.
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
@ -104,10 +106,10 @@ In hybrid mode, results contain either a `memory` field (extracted facts) or a `
| `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 |
| `rerank` | string | `"none"` | `"none"`, `"order"` (re-score for relevance, +100ms), or `"aggregate"`. Successful reranking or aggregation can bill one extra [operation](/overview/billing#what-is-an-operation); setting the option alone does not guarantee it runs |
| `rewriteQuery` | boolean | false | Generate multiple rewrites, search all of them, and merge results. Adds latency and can bill one extra [operation](/overview/billing#what-is-an-operation) when rewriting runs successfully |
| `filter` | object | — | Metadata filter expression. See [Filtering](#filtering) |
| `include` | object | — | `{ documents, related, forgotten }` — opt in to extra context per result |
| `include` | object | all `false` | `{ documents, related, forgotten }` — opt in to extra context per result |
<Note>
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.
@ -196,9 +198,9 @@ await supermemory.search("user_123", { query: "...", threshold: 0.8 });
### Attachments
- `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.
- `include.documents` adds the most recently updated source document to each result when one is available.
- `include.related` adds parent, child, and sibling memories (see [graph memory](/concepts/graph-memory)).
- `include.forgotten` lets forgotten and expired memories appear in results. By default, search excludes memories that have been forgotten or have passed their `forgetAfter` expiration.
```typescript
await supermemory.search("user_123", {
@ -238,10 +240,11 @@ async function getContext(userId: string, message: string) {
memory?: string; // Present for memory results
chunk?: string; // Present for document chunk results
similarity: number; // 0-1
metadata: object | null;
metadata: Record<string, unknown>; // {} when none
isLatest: boolean;
system: { createdAt: string; updatedAt: string };
included?: { // Only when you pass attach
isInference: boolean; // false for chunks
system: { createdAt?: string; updatedAt: string };
included?: { // Only when you pass include
document?: object;
related?: { parents: object[]; children: object[]; siblings: object[] };
};

View file

@ -83,7 +83,7 @@ await supermemory.search("user_1", {
```
</CodeGroup>
`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.
`filter` is a typed object in the request body (a field of the options object in the SDKs), 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