Node SDK
Bulk Operations
When you have a backlog to ingest — historical messages, exported docs, a one-off migration — call bulkAdd instead of looping over add. Each call carries up to 50 items and returns a per-item result so you know exactly what landed and what was rejected.
Why bulkAdd exists
- One round-trip instead of fifty. Lower latency, fewer connections.
- The server can deduplicate across the whole batch in one pass.
- Per-item results: every input maps to
created,skipped, orrejected.
The shape of a BulkAddItem
| Field | Type | Required |
|---|---|---|
| text | string | Yes |
| source | string | No |
| eventType | string | No |
| tags | string[] | No |
| metadata | Record<string, unknown> | No |
| importance | number 0..1 | No |
| endUserId | string | No (multi-tenant only) |
A minimal call
TYPESCRIPT
const res = await client.bulkAdd([{ text: "Picked the audit log schema we discussed.", tags: ["decision"] },{ text: "Wrote the migration.", tags: ["work"] },{ text: "Reviewed the migration with the team.", tags: ["review"] },],{ deduplicate: true },);console.log(res.total, res.created, res.skipped, res.rejected);for (const r of res.results) {console.log(r.index, r.status, r.memoryIds, r.reason);}
BulkAddResponse, field by field
| Field | Type | Meaning |
|---|---|---|
| total | number | How many items were submitted. |
| created | number | How many produced new memory rows. |
| skipped | number | How many the server intentionally did not store. |
| rejected | number | How many were invalid (empty, oversized, etc.). |
| results | BulkAddItemResult[] | Per-item outcomes; index matches the input order. |
The deduplicate flag
deduplicate defaults to true. With it on, items that are semantically identical to existing memories — or to earlier items in the same batch — are returned as status: "skipped" instead of being stored a second time. Set it to false only when you have a specific reason to keep duplicates.
Chunking a backlog over 50
TYPESCRIPT
async function ingestAll(items: { text: string }[]) {const chunks: typeof items[] = [];for (let i = 0; i < items.length; i += 50) {chunks.push(items.slice(i, i + 50));}const results: number[] = [];for (const c of chunks) {const res = await client.bulkAdd(c);results.push(res.created);}return results.reduce((a, b) => a + b, 0);}
Running multiple chunks in parallel
Use a small concurrency cap (4\u20138) when you parallelise. The server will rate-limit you eventually; bound your own fan-out so you fail loud, not slow.
TYPESCRIPT
import pLimit from "p-limit"; // or any concurrency primitiveconst limit = pLimit(4);const counts = await Promise.all(chunks.map((c) => limit(() => client.bulkAdd(c).then((r) => r.created))),);
Errors specific to bulk calls
Validation fires before the network
bulkAdd([]) raises ValidationError client-side without making a request. So does an array of more than 50 items.RateLimitErroron a chunk — back off for the seconds the error reports, then retry the same chunk.ServerError— safe to retry; ingestion is idempotent on the same text.- Per-item
status: "rejected"entries inside a successful response are not exceptions; they describe why specific items did not store.