fix(n8n): drop Add fields the v3 API does not accept, correct docs

The Add operation exposed app_id, includes, and excludes in Additional
Fields and forwarded them to POST /v3/memories/add/. None of the three
are declared on that endpoint's schema (they belong to MemoryInput, used
by POST /v1/memories/), so they were silently dropped by the server.

app_id was worse than a no-op: the entity-id guard accepted it as
satisfying "at least one entity id", so filling only App ID passed local
validation and then sent a v3 request carrying no scope v3 recognises.
The guard now requires User ID, Agent ID, or Run ID.

Docs corrections alongside it:
- Add: document Custom Instructions and Custom Categories, which the
  node has always supported but the docs never mentioned
- Get Many: document Return All, which the docs omitted entirely
- README: Infer and Wait for Completion are independent controls; the
  README claimed infer=false returns synchronously, contradicting the
  node's own field description and its actual behaviour
This commit is contained in:
kartik-mem0
2026-07-29 21:41:23 +05:30
parent aa939d00d4
commit 2a61fe1a32
3 changed files with 22 additions and 30 deletions
+11 -2
View File
@@ -40,7 +40,16 @@ Create a **Mem0 API** credential in n8n:
### Add ### Add
Extracts and stores memories from one or more messages. Provide at least one entity id (**User ID**, **Agent ID**, **Run ID**, or **App ID** in Additional Fields); the node validates this before calling the API. Additional Fields also expose **Metadata (JSON)** and **Infer** (run LLM extraction, or store verbatim). Extracts and stores memories from one or more messages. Provide at least one entity id (**User ID**, or **Agent ID** / **Run ID** in Additional Fields); the node validates this before calling the API.
Additional Fields also expose:
| Field | Purpose |
| --- | --- |
| **Metadata (JSON)** | Arbitrary JSON attached to each extracted memory |
| **Infer** | On by default. Turn off to store messages verbatim instead of running LLM extraction |
| **Custom Instructions** | Free-text guidance steering what the extractor keeps or ignores, for this call |
| **Custom Categories** | JSON array of `{category: description}` objects, replacing the project-level catalog for this call |
Extraction is asynchronous. **Wait for Completion** (on by default) polls the event until it finishes and returns the resulting memories; turn it off to return immediately with the event ID. Extraction is asynchronous. **Wait for Completion** (on by default) polls the event until it finishes and returns the resulting memories; turn it off to return immediately with the event ID.
@@ -50,7 +59,7 @@ Semantic search over stored memories. Provide a **Query**, a **User ID** (requir
### Get Many ### Get Many
Lists memories for a **User ID** with **Page** and **Page Size** controls. Lists memories for a **User ID**. Turn on **Return All** to page through every memory automatically; leave it off to fetch a single **Page**. **Page Size** applies either way.
### Get / Update / Delete ### Get / Update / Delete
+9 -2
View File
@@ -20,14 +20,21 @@ The **Memory** resource supports:
| --- | --- | --- | | --- | --- | --- |
| **Add** | Extract and store memories from messages | `POST /v3/memories/add/` | | **Add** | Extract and store memories from messages | `POST /v3/memories/add/` |
| **Search** | Semantic search over stored memories | `POST /v3/memories/search/` | | **Search** | Semantic search over stored memories | `POST /v3/memories/search/` |
| **Get Many** | List memories for a user (paginated) | `POST /v3/memories/` | | **Get Many** | List memories for a user (single page, or **Return All**) | `POST /v3/memories/` |
| **Get** | Retrieve a single memory by ID | `GET /v1/memories/{id}/` | | **Get** | Retrieve a single memory by ID | `GET /v1/memories/{id}/` |
| **Update** | Update a memory's text or metadata | `PUT /v1/memories/{id}/` | | **Update** | Update a memory's text or metadata | `PUT /v1/memories/{id}/` |
| **Delete** | Delete a single memory by ID | `DELETE /v1/memories/{id}/` | | **Delete** | Delete a single memory by ID | `DELETE /v1/memories/{id}/` |
### Add & asynchronous extraction ### Add & asynchronous extraction
By default, **Add** runs LLM-based extraction asynchronously — the API returns an event ID and the node polls until extraction finishes, then returns the resulting memories. Disable **Wait for Completion** to return immediately with the event ID, or set **Infer = false** (under Additional Fields) to store messages verbatim and return synchronously. By default, **Add** runs LLM-based extraction asynchronously: the API returns an event ID and the node polls until extraction finishes, then returns the resulting memories.
Two independent controls:
- **Wait for Completion** (on by default) decides whether the node polls. Turn it off to return immediately with the event ID.
- **Infer** (on by default, under Additional Fields) decides whether the API runs LLM extraction at all. Turn it off to store the messages verbatim.
**Custom Instructions** and **Custom Categories** (also under Additional Fields) steer what extraction keeps for that call.
## Credentials ## Credentials
@@ -163,12 +163,6 @@ export class Mem0 implements INodeType {
type: 'string', type: 'string',
default: '', default: '',
}, },
{
displayName: 'App ID',
name: 'app_id',
type: 'string',
default: '',
},
{ {
displayName: 'Custom Categories', displayName: 'Custom Categories',
name: 'custom_categories', name: 'custom_categories',
@@ -186,20 +180,6 @@ export class Mem0 implements INodeType {
description: description:
'Optional instructions that steer what the extractor keeps or ignores', 'Optional instructions that steer what the extractor keeps or ignores',
}, },
{
displayName: 'Excludes',
name: 'excludes',
type: 'string',
default: '',
description: 'Optional: skip memories matching this description',
},
{
displayName: 'Includes',
name: 'includes',
type: 'string',
default: '',
description: 'Optional: only extract memories matching this description',
},
{ {
displayName: 'Infer', displayName: 'Infer',
name: 'infer', name: 'infer',
@@ -366,7 +346,6 @@ export class Mem0 implements INodeType {
const userId = this.getNodeParameter('userId', i, '') as string; const userId = this.getNodeParameter('userId', i, '') as string;
if (userId) body.user_id = userId; if (userId) body.user_id = userId;
if (addFields.agent_id) body.agent_id = addFields.agent_id; if (addFields.agent_id) body.agent_id = addFields.agent_id;
if (addFields.app_id) body.app_id = addFields.app_id;
if (addFields.run_id) body.run_id = addFields.run_id; if (addFields.run_id) body.run_id = addFields.run_id;
if (addFields.metadata) { if (addFields.metadata) {
try { try {
@@ -400,14 +379,11 @@ export class Mem0 implements INodeType {
} }
} }
if (addFields.includes) body.includes = addFields.includes;
if (addFields.excludes) body.excludes = addFields.excludes;
// API requires at least one entity id — fail clearly instead of a raw 4xx. // API requires at least one entity id — fail clearly instead of a raw 4xx.
if (!body.user_id && !body.agent_id && !body.run_id && !body.app_id) { if (!body.user_id && !body.agent_id && !body.run_id) {
throw new NodeOperationError( throw new NodeOperationError(
this.getNode(), this.getNode(),
'Add requires at least one of User ID, Agent ID, Run ID, or App ID', 'Add requires at least one of User ID, Agent ID, or Run ID',
{ itemIndex: i }, { itemIndex: i },
); );
} }