Platform

Memory API

Store facts and preferences about each user, or about an agent or an org, and recall the relevant ones by meaning, with deduplication, expiry, forget and GDPR erasure.

The Memory API gives your app a long-term memory per user. Store short facts ("Prefers vegetarian recipes"), then search them by meaning when the user asks something. Near-duplicates update the existing memory instead of piling up, memories can expire, and one call forgets everything about a user. Threads can add a user's memories to a model's context automatically.

This is memory for your app's users, stored in your project database. It is separate from Project Memory, which is where AI coding agents keep notes about your project.

Subjects

Every memory belongs to a subject, written type:id:

  • A signed-in user is always user:<their id>. Their calls never name a subject, and naming someone else's is refused with 403.
  • The service-role key must name the subject, and can use any type: user:<id>, agent:support, org:42. The type is a lowercase word of up to 32 characters; the id is up to 200 characters.

Quick start

TypeScript
import { createClient } from '@nestcore/js';

const client = createClient({ apiKey: import.meta.env.VITE_NESTCORE_ANON_KEY });
await client.auth.signIn({ email, password });

await client.memory.add('Prefers vegetarian recipes', { kind: 'preference', importance: 4 });
await client.memory.add('Has a dog called Pixel');

const { data } = await client.memory.search('what should I cook tonight?', { k: 5 });
// data.results: memories sorted by relevance, each with similarity and score
// data.degraded: true if the provider was down and keyword search answered

On a server, with the service-role key, name the subject:

server.ts
await admin.memory.add('Escalate refunds over $500 to a human', { subject: 'agent:support' });
await admin.memory.search('large refund', { subject: 'agent:support' });

client.memory is in the Node.js SDK from version 0.14.0 and the web SDK (@nestcore/js) from version 0.8.0.

Flutter:

Dart
await nestcore.memory.add('Prefers vegetarian recipes', kind: 'preference');
final found = await nestcore.memory.search('what should I cook tonight?');

Add

memory.add(content, options) returns { memory, deduped }.

OptionDefaultDescription
subjectthe signed-in userService key only, and then required
kindfactA lowercase word: fact, preference, episode, summary or your own
importance31 to 5. Used to break near-ties in search.
expiresAtnoneISO date. The memory stops appearing in search after it and is deleted within the hour.
metadata{}Any JSON
sourceThreadIdnoneThe thread the memory came from, for your records
dedupetrueUpdate a near-identical memory instead of adding one

Content is 1 to 8,000 characters. With dedupe on, NestCore compares the new memory with the subject's closest existing memory. If their cosine similarity is 0.92 or more, it updates that memory instead: the content and kind are replaced, importance becomes the higher of the two, metadata is merged, and a new expiresAt replaces the old one. The result then has deduped: true and the existing id.

memory.search(query, options) returns { results, degraded, model }.

OptionDefaultDescription
subjectthe signed-in userService key only, and then required
k5Results, 1 to 50
kindallOnly memories of this kind
minSimilarity0Drop results below this cosine similarity

Results are ranked by score: the cosine similarity plus small nudges for importance (0.02 × (importance − 3)) and recency (up to 0.04, halving every 30 days since the memory was last updated). Similarity dominates; the nudges only reorder memories that are about equally relevant. Expired memories are never returned.

If the AI provider is down (a server error or an unavailable model), search falls back to keyword matching instead of failing. The response then has degraded: true, model: null, and each result has similarity: null. Configuration errors, such as a missing key, still fail.

List, update and delete

MethodWhat it does
memory.list({ subject, kind, limit, offset })The subject's memories, most recently updated first. limit 1 to 200 (default 50). Returns { memories, total }.
memory.get(id)One memory
memory.update(id, { content, kind, importance, expiresAt, metadata })Change a memory. New content is embedded again.
memory.remove(id)Delete one memory
memory.forget(subject?)Delete every memory of a subject. Users always forget themselves. Returns { deleted }.
memory.subjects({ type, limit })Service key: the subjects that have memories, with counts. limit up to 500 (default 100).
memory.reembed({ subject, limit })Service key: re-embed up to 64 memories made with another model. Returns { reembedded, remaining, model }.

Signed-in users only ever reach their own memories: get and update of another id return 404, and remove returns deleted: false.

Forget and GDPR

forget hard-deletes the subject's memories, vectors included. It leaves a tombstone (subject, number of rows deleted, time) for 7 days as a record that the data was erased.

When you delete one of your app's users (in Studio or with the auth admin API), NestCore also forgets user:<their id> and deletes the threads they own.

Changing the embedding model

Memories are embedded with the project's default embedding model (see Add a provider key). Each memory remembers the model that embedded it, and search only compares memories made with the current model. If you switch provider or default embedding model, call memory.reembed() from a server with the service key until remaining is 0.

Access rules

CallerCan
Signed-in user (anon key + access token)Add, search, list, update, delete and forget their own memories
Service-role keyEverything, for any subject, plus subjects and reembed
Anon key aloneNothing: 401

Memories live in the ai_memories table of your project database, created on first use. Its row-level security policy also limits users to their own rows if they query it with SQL.

Limits and quotas

Every Memory API call that embeds text counts as a memory operation: an add, a search, an update with new content, and a re-embed. Thread runs that use memory count too. The workspace allowance per month:

PlanMemory operations per month
Free1,000
Starter25,000
Pro250,000

Past it, adds, searches and content updates return 402 with code: "QUOTA_EXCEEDED" and quota: "ai_memory_ops" until the next month. Listing, deleting and forgetting always work. Memory calls are also AI gateway calls on your own key: they count toward your per-minute AI limit and spend caps. See Plans & billing.

In Studio

Project Settings → AI → Memory lists subjects with their memory counts. Pick a subject to see its memories, search them, delete one, or forget the subject. Deleting and forgetting need the workspace admin role.

The MCP tool nestcore_memory_subjects lists subjects, and nestcore ai overview (CLI) or nestcore_ai_overview (MCP) count memories and subjects.

HTTP API

Under /api/project/{slug}/ai/v1. Send the anon key in apikey and the user's access token in Authorization: Bearer, or the service-role key.

MethodPathBody or query
POST/memory{ content, subject?, kind?, importance?, expiresAt?, metadata?, sourceThreadId?, dedupe? }
POST/memory/search{ query, subject?, k?, kind?, minSimilarity? }
GET/memory?subject&kind&limit&offset
GET/memory/subjects?type&limit (service key)
POST/memory/forget{ subject? }
POST/memory/reembed{ subject?, limit? } (service key)
GET/memory/{id}—
PATCH/memory/{id}{ content?, kind?, importance?, expiresAt?, metadata? }
DELETE/memory/{id}—

Memories come back as stored: id, subject_type, subject_id, kind, content, embed_model, importance, metadata, source_thread_id, expires_at, created_at, updated_at.

Errors

StatusWhen
400Empty or too long content, a bad kind, importance or expiresAt, or a service-key call without subject
401The anon key was sent without a user token
403A user named another subject, or called subjects or reembed
404The memory doesn't exist or isn't the user's
402QUOTA_EXCEEDED (ai_memory_ops) or AI_BUDGET_EXCEEDED

Embedding can also fail with the AI gateway errors.