supermemory/apps/docs/recall/memory-operations.mdx
sohamd22 069b8c373c docs(memories): forget-matching accepts an id list (bound preview→apply) (#1367)
### TL;DR

Documents the new `ids` parameter for the `forget-matching` endpoint, allowing exact memory deletion without semantic search.

### What changed?

The `forget-matching` endpoint now accepts either a `query` (semantic search-based forgetting) or an explicit `ids` list (direct deletion by memory ID) — one or the other must be provided. The docs have been updated to reflect this:

- The `query` parameter is now marked as `one of*` rather than required, and `ids` is introduced as an alternative with the same mutual-exclusivity constraint.
- `threshold` is clarified as applying to `query` mode only.
- Code examples for both JavaScript and cURL now show the recommended two-step pattern: run a `dryRun` with `query`, then apply using the `ids` returned from the preview — avoiding drift if the container changes between steps.
- A new `<Tip>` block explains why passing `ids` on the apply step produces a more deterministic delete than re-running the `query`.

### How to test?

1. Call `forget-matching` with `dryRun: true` and a `query` to retrieve candidate `id`s.
2. Re-call with `dryRun: false` and the `ids` from step 1 to confirm only those exact memories are forgotten.
3. Verify that passing `ids` belonging to a different `containerTag` are ignored.
4. Confirm that providing both `query` and `ids`, or neither, returns an appropriate validation error.

### Why make this change?

Re-running a `query` on the apply step can produce different results if the container was modified between the preview and the apply. Exposing `ids` as a first-class parameter lets callers pin the delete to exactly the set they reviewed, making bulk forgetting safer and more predictable.
2026-08-06 09:32:32 +00:00

349 lines
12 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "Memory Operations"
sidebarTitle: "CRUD & Forgetting"
description: "Advanced memory operations (v4 API)"
icon: "database"
---
<Info>
These v4 endpoints operate on extracted memories (not raw documents). SDK support coming soon — use fetch or cURL for now.
For document management (list, get, update, delete), see [Document Operations](/ingestion/document-operations).
For ingesting raw content (text, files, URLs) through the processing pipeline, see [Add Context](/ingestion/add-memories).
</Info>
## 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.
<Tabs>
<Tab title="fetch">
```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"
})
});
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-..." }
// ]
// }
```
</Tab>
<Tab title="cURL">
```bash
curl -X POST "https://api.supermemory.ai/v4/memories" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"memories": [
{
"content": "John prefers dark mode",
"isStatic": false,
"metadata": { "source": "user_preference" }
},
{
"content": "John is from Seattle",
"isStatic": true
}
],
"containerTag": "user_123"
}'
```
</Tab>
</Tabs>
### Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `memories` | array | yes | Array of memory objects (1100 items) |
| `memories[].content` | string | yes | The memory text (max 10,000 chars). Should be entity-centric, e.g. "John prefers dark mode" |
| `memories[].isStatic` | boolean | no | `true` for permanent identity traits (name, hometown). Defaults to `false` |
| `memories[].metadata` | object | no | Key-value metadata (strings, numbers, booleans) |
| `containerTag` | string | yes | Space / container tag these memories belong to |
### Response
```json
{
"documentId": "abc123",
"memories": [
{
"id": "mem_1",
"memory": "John prefers dark mode",
"isStatic": false,
"createdAt": "2025-01-15T10:30:00.000Z"
}
]
}
```
| Field | Type | Description |
|-------|------|-------------|
| `documentId` | string \| null | ID of the lightweight source document created for traceability |
| `memories` | array | The created memory entries |
| `memories[].id` | string | Unique memory ID |
| `memories[].memory` | string | The memory content |
| `memories[].isStatic` | boolean | Whether this is a permanent trait |
| `memories[].createdAt` | string | ISO 8601 timestamp |
<Tip>
**When to use this vs [Add Context](/ingestion/add-memories)?**
Use **Create Memories** when you already know the exact facts to store (user preferences, traits, structured data). Use **Add Context** when you have raw content (conversations, documents, URLs) that Supermemory should process and extract memories from.
</Tip>
---
## 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`.
<Tabs>
<Tab title="fetch">
```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"
})
});
```
</Tab>
<Tab title="cURL">
```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"
}'
```
</Tab>
</Tabs>
### Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | * | Memory ID to forget |
| `content` | string | * | Exact content match to forget (alternative to ID) |
| `containerTag` | string | yes | Container tag / space the memory belongs to |
| `reason` | string | no | Optional reason recorded as `forgetReason` |
\* Either `id` or `content` must be provided.
The memory will no longer appear in search results but remains in the database (`isForgotten=true`).
---
## Forget Matching
Forget in bulk in one call, two ways. Give a **`query`** (a prompt or topic) and the service semantically searches the container, an LLM decides which memories are genuinely about your target, and those are soft-deleted — use this for "forget everything about X". Or give an explicit **`ids`** list to forget exactly those memories with no search. Provide one or the other.
<Warning>
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.
</Warning>
<Tabs>
<Tab title="fetch">
```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 }, ...]
// 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
```
</Tab>
<Tab title="cURL">
```bash
# Preview
curl -X POST "https://api.supermemory.ai/v4/memories/forget-matching" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "forget everything about Project Titan",
"containerTag": "user_123",
"dryRun": true
}'
# Apply — pass the ids from the preview to forget exactly that set
curl -X POST "https://api.supermemory.ai/v4/memories/forget-matching" \
-H "Authorization: Bearer $SUPERMEMORY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ids": ["abc123", "def456", "ghi789"],
"containerTag": "user_123",
"dryRun": false,
"reason": "project cancelled"
}'
```
</Tab>
</Tabs>
### Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | one of* | What to forget — a natural-language instruction ("forget everything about Project Titan") or a bare topic ("Project Titan") |
| `ids` | string[] | one of* | Exact memory ids to forget instead of a `query` — no semantic search. Ids are validated against `containerTag`, so unknown or out-of-scope ids are ignored |
| `containerTag` | string | yes | Container tag / space to scope the operation to |
| `dryRun` | boolean | no | When `true`, returns what *would* be forgotten without changing anything. Defaults to `false` |
| `threshold` | number | no | Similarity floor (01) 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 (1500). Defaults to `100`. Ignored in id mode, which forgets exactly the ids you pass (bounded only by the 500-item array limit) |
| `reason` | string | no | Reason recorded as `forgetReason` on each forgotten memory |
\* Provide either `query` or `ids`.
### Response
```json
{
"dryRun": false,
"count": 3,
"forgetBatchId": "VcuQoGRz4hA4ak5Xu6DRUN",
"summary": "Forgot 3 memories about \"Project Titan\".",
"forgotten": [
{ "id": "mem_1", "memory": "Project Titan ships in Q3", "score": 0.82 }
]
}
```
| Field | Type | Description |
|-------|------|-------------|
| `dryRun` | boolean | Whether this was a preview or a real forget |
| `count` | number | Number of memories selected (dryRun) or forgotten (apply) |
| `forgetBatchId` | string \| null | ID tagged on every memory forgotten in this call; `null` on dryRun |
| `summary` | string | One-line summary of the operation (e.g. `Forgot 3 memories about "Project Titan".`) |
| `candidates` | array | On `dryRun`: the memories that **would** be forgotten (`{ id, memory, score }`) |
| `forgotten` | array | On apply: the memories that **were** forgotten (`{ id, memory, score }`) |
<Note>
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.
</Note>
<Tip>
**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.)
</Tip>
---
## Update Memory (Versioned)
Update a memory by creating a new version. The original is preserved with `isLatest=false`.
<Tabs>
<Tab title="fetch">
```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",
newContent: "Updated content goes here",
metadata: {
tags: ["updated"]
}
})
});
```
</Tab>
<Tab title="cURL">
```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"]}
}'
```
</Tab>
</Tabs>
### Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | * | Memory ID to update |
| `content` | string | * | Original content to match (alternative to ID) |
| `newContent` | string | yes | New content for the memory |
| `metadata` | object | no | Updated metadata |
\* Either `id` or `content` must be provided.
---
## Next Steps
- [Review Inferred Memories](/recall/memory-review) — Approve or decline low-confidence memories
- [Document Operations](/ingestion/document-operations) — Manage documents (SDK supported)
- [Search](/recall/search) — Query your memories
- [Ingesting Content](/ingestion/add-memories) — Add new content