Blog·Learning

Migrating from Mem0 to supermemory: a production playbook for the v5 API

By Dhravya Shah·13 min read

A blurred photo of a vase of flowers and a row of books on a blue shelf, with the supermemory logo and the headline Move from Mem0 to Supermemory.

To move from Mem0 Platform to supermemory, map each Mem0 entity scope to one supermemory namespace, import your Mem0 memories as documents with stable IDs, point your read path at POST /ns/{namespace}/search, and run both systems side by side until the new one passes your tests. This playbook uses supermemory's v5 API (namespace, id, filter), the only version to build on now: v3 and v4 shut down on December 31, 2026, and the v5 migration guide covers the differences.

If you followed an earlier version of this guide, your code uses v3/v4 names such as containerTag, customId and q, and the direct memory endpoint that v5 removed. Run npx supermemory@latest migrate in that repo: it scans for v3/v4 calls and hands your coding agent a migration prompt built for the project.

This guide covers Mem0 Platform. If you self-host Mem0 Open Source, document the vector store, graph store, model providers and persistence layers in your deployment first, because a platform export can't represent that whole system. If you're still deciding whether to switch, read why Scira moved to supermemory and our supermemory and Mem0 comparison first. For the longer write-up, read how supermemory and Mem0 compare on features. The short version of the import scripts is in the docs; this article is about moving production data and traffic without making the move irreversible.

What changes in a Mem0 migration?

The central change is identity. Mem0 can scope a memory by user_id, agent_id, app_id and run_id at once. supermemory scopes everything by one namespace, which goes in the URL of every v5 request, and uses metadata for anything else you need to filter on or trace back.

Mem0 Platform supermemory v5 What you have to decide
user_id, agent_id, app_id, run_id One namespace in the path (/ns/{namespace}/...), plus metadata A deterministic namespace rule, before importing anything
add(messages, user_id=...) POST /ns/{namespace}/document with content and a stable id One document per conversation. Repeating the id adds to that document
Extracted memory record A small document per fact, since v5 has no direct memory write Stable IDs, so a retry isn't a second copy
search(query, filters, top_k) and its score POST /ns/{namespace}/search with query, limit, searchMode, threshold and filter. Results carry similarity A rewritten adapter and retuned thresholds
get_all(filters) POST /ns/{namespace}/list/memories One namespace per call, paged up to 100 at a time
update(memory_id, text) PATCH /ns/{namespace}/document/{id} The new content replaces the source and is reprocessed
delete(memory_id) Forget memories (DELETE /ns/{namespace}/memories) or delete the source (DELETE /ns/{namespace}/document) Whether your app means hide or erase
expiration_date No field sets an expiry when you write A scheduled job, described below

Don't start the backfill until that mapping is written down. A migration script can move every record successfully and still break tenant isolation if the namespace rule is wrong.

Is this guide right for your setup?

Go ahead when every memory read and write can be mapped to a stable user, tenant, project or agent boundary.

Pause for architecture work when:

  • queries depend on several Mem0 entity dimensions at once
  • the application expects to search across arbitrary tenants
  • nested metadata drives authorization or retrieval
  • exact graph topology must be preserved
  • deletes must be permanent the moment the application requests them

A v5 request reads or writes one namespace. That's useful isolation, but the namespace has to match the boundary your authorization model already enforces, and code that searched across several scopes needs one call per namespace with the results merged. Graph edges and provider-specific scores aren't portable data formats.

What should you inventory first?

Write an inventory before touching production:

  1. Record the Mem0 SDK and API versions in use.
  2. List every user, agent, app and run scope that owns memories.
  3. Choose one scope order for the export, and deduplicate globally by Mem0 memory ID before counting records.
  4. Separate raw conversations and documents from already-extracted memory facts.
  5. Find metadata with nested objects that will need flattening or serializing.
  6. Capture a golden set of real queries, expected facts, current ranks, empty-result rate, error rate, and p50 and p95 latency.

That last step is the migration's acceptance test. Without a baseline, "the import completed" quietly replaces "the product still remembers correctly."

Mem0's v3 list endpoint takes entity IDs inside a filters object, needs at least one entity filter, and returns pages of up to 200 records. Overlapping user, agent, app and run queries can return the same memory more than once, so write each source ID to the snapshot once. Check the response fields from your own API version before mapping identifiers: the list endpoint returns the run as session_id, while search returns the same value as run_id.

How should Mem0 IDs map to namespaces?

Pick one deterministic rule and use it everywhere. Common patterns:

  • user:{user_id} for a single-tenant consumer app
  • org:{org_id}:user:{user_id} for user memory inside an enterprise tenant
  • agent:{agent_id} for an agent-owned knowledge base
  • app:{app_id}:user:{user_id} when the same person must stay isolated across products

Store the dimensions that don't define access as flat metadata, such as mem0_agent_id, mem0_app_id and mem0_run_id. A v5 namespace is up to 100 characters of letters, numbers, underscores, colons and hyphens (API reference). If a source ID falls outside that, encode or hash it without creating collisions, and keep the mapping. Never derive the namespace one way in the importer and another way in the live request path.

A namespace scopes what's stored and searched. It doesn't authenticate the caller. Derive it from the authenticated identity on the server, check access before each operation, and test cross-tenant requests before cutover. The namespaces docs cover the model in more detail.

How do you export from Mem0 without losing anything?

For the backfill, page through Mem0's memory list endpoint in the scope order you chose, deduplicate globally by memory ID, and write the raw records to an immutable JSONL file. Expired memories are hidden by default, so pass show_expired if the migration needs them. Memories that Mem0's Dream feature merged into another one are hidden from reads by default too (its docs describe an include_merged flag that returns them), while superseded memories come back by default with replaced_by set. Reconcile your counts before calling the snapshot complete. Keep for each record:

  • the memory text and source ID
  • user, agent, app and run identifiers
  • categories and metadata
  • creation, update and expiration timestamps
  • the lifecycle state your application depends on

Mem0 also has structured memory exports. Those are useful when you want to reshape memories into a schema, such as a profile, but they aren't a one-to-one dump, because the schema and export instructions change the result. If you use one, poll until the job completes and download it promptly.

Keep the raw snapshot even after supermemory is live. It's the only reliable starting point for a rollback or a corrected re-import.

How do you import Mem0 memories into supermemory?

v5 has no endpoint that writes a memory directly. Memories come from documents, so each Mem0 fact becomes a small document of its own (v5 guide). This script reads the snapshot, groups it by namespace (each v5 request writes to one) and imports it in batches of up to 600:

import { readFileSync } from "node:fs"
import { Supermemory } from "supermemory" // npm i supermemory (v5 SDK, 5.0.0 or later)

type Mem0Record = {
  id: string
  memory: string
  user_id?: string | null
  agent_id?: string | null
  app_id?: string | null
  session_id?: string | null // the list endpoint's name for run_id
  categories?: string[] | null
  created_at: string
  expiration_date?: string | null
  replaced_by?: string | null
}

const supermemory = new Supermemory() // reads SUPERMEMORY_API_KEY

// One rule for the namespace, shared with the live request path
const namespaceFor = (userId: string) => `user:${userId}`

const snapshot: Mem0Record[] = readFileSync("mem0-snapshot.jsonl", "utf8")
  .split("\n")
  .filter(Boolean)
  .map((line) => JSON.parse(line))

// A v5 request writes to one namespace, so group the snapshot first
const byNamespace = new Map<string, Mem0Record[]>()
for (const r of snapshot) {
  if (!r.user_id || !r.memory || r.replaced_by) continue // superseded facts stay in the snapshot; log records without a user_id, they need their own namespace rule
  const ns = namespaceFor(r.user_id)
  const list = byNamespace.get(ns) ?? []
  list.push(r)
  byNamespace.set(ns, list)
}

for (const [namespace, records] of byNamespace) {
  for (let i = 0; i < records.length; i += 600) {
    const res = await supermemory.documents.batchAdd(namespace, {
      documents: records.slice(i, i + 600).map((r) => ({
        id: `mem0_${r.id}`, // stable, so a retry updates this document instead of adding a copy
        content: r.memory,
        date: r.created_at, // when the fact is from
        metadata: {
          source: "mem0",
          mem0_id: r.id,
          mem0_agent_id: r.agent_id ?? "",
          mem0_app_id: r.app_id ?? "",
          mem0_run_id: r.session_id ?? "",
          mem0_categories: r.categories ?? [],
          mem0_expires_on: r.expiration_date ?? "",
        },
      })),
    })
    for (const item of res.results) {
      if (item.error) console.error(namespace, item.id, item.error, item.details) // send to a rejects file
    }
  }
}

A few things the script relies on:

  • The id makes retries safe. Sending the same id again adds to or diffs that document instead of creating a copy, and unchanged content isn't billed twice (billing docs). Because the Mem0 ID is in the document ID, your migration ledger is mostly the ID rule itself.
  • date keeps when the fact is from, so recent context stays recent after the import.
  • Metadata values must be flat: strings, numbers, booleans or arrays of strings. Flatten or serialize nested Mem0 metadata, and write records you can't convert to a rejects file instead of truncating them.
  • Read every result. A batch can partly succeed, and results list accepted documents first, then failed ones, so match them by id, not by position.
  • Memories form after the request returns. With the default dreaming: "dynamic", memories form in the background, so wait until each document's system.dreamingStatus is done before you test recall on it. dreaming: "instant" processes each document straight away for one extra operation per document, which helps when you're running parity tests on a small cohort (v5 guide, pricing).
  • Each fact is extracted again. supermemory reads the imported text and forms its own memories from it, so compare a sample of them with the Mem0 originals before you import everyone.

How should the live write path change?

Use two write paths on purpose.

For new conversations and source documents, add the content to the user's namespace with a stable id, usually the conversation ID. Repeating that id appends to the same document, so one conversation stays one document. Decide whether each write sends the whole conversation or only the new turns, and keep that the same for every write to an id.

For exact facts your application already knows, add each one as its own small document with a stable id, the same way the import does. Use dreaming: "instant" when the next request needs it.

Processing is asynchronous. A successful write means the work was accepted. A document's system.status reaches done when it's processed, and system.dreamingStatus tracks memory formation separately: under the default dynamic mode, memories can still be forming in the background after status reaches done, so wait on dreamingStatus when the next step reads memories or the profile.

During the migration, dual-write new events to both providers, and keep one of them authoritative for reads until the new path passes the full evaluation.

How should the read path change?

The adapter needs more than renamed fields. Mem0's query stays query, entity filters become the namespace plus a typed filter on metadata, top_k becomes limit, and Mem0's score has no direct equivalent: v5 results carry a similarity from 0 to 1.

// Mem0: search(query, filters={"user_id": ...}, top_k=5)
export async function recall(userId: string, query: string) {
  const { results } = await supermemory.search(namespaceFor(userId), {
    query,
    searchMode: "memories", // v5 defaults to hybrid; pin the mode while you compare
    limit: 5,
    threshold: 0.3, // the v5 default, written out so you can tune it
  })
  return results.map((r) => ({ text: r.memory, similarity: r.similarity })).filter((r) => r.text)
}

v5 search defaults to hybrid, which returns memories and source chunks in one ranked list. Pin searchMode: "memories" while you compare against Mem0, then test hybrid on its own. Don't copy a Mem0 score cutoff into threshold: the two are calibrated differently, so tune it against the golden set you captured.

For validation, score the retrieved context before the answer model sees it. A fluent model can hide a bad retrieval by guessing convincingly.

How do you validate before cutover?

Run both systems in parallel on the same production-shaped queries, and measure:

  • exact-fact recall and top-k relevance
  • stale or contradictory memories
  • empty results and unexpected extra results
  • tenant isolation and metadata filters
  • expiry behavior
  • ingestion success and time until a fact is searchable
  • latency and error rates as your application sees them
  • duplicate documents after retries

Add a small lifecycle fixture to the golden set: a saved preference, a correction, a temporary exception and a deletion request, with the same questions asked after each step. When an answer is wrong, find out where: a connector, the namespace, processing that hadn't finished, retrieval, or the final prompt. Fixing the boundary that failed is part of the migration, whichever provider you end up on.

Backfill a small group of namespaces first. Shadow reads without showing the new response to users. When the acceptance criteria pass, move a low-risk cohort behind a feature flag and keep rollback to Mem0 available. Switching memory infrastructure covers the organizational side. The technical rule is simpler: don't move all reads and writes on the same day.

Which lifecycle differences need explicit handling?

Operation Mem0 supermemory v5 Decision required
Update Updates a memory record, and Dream can mark older facts superseded PATCH the source document: the new content replaces it and is reprocessed. Memories keep version history (isLatest, earlier and later versions) Whether your application shows history
Forget delete removes the memory memories.forget removes memories from normal recall and keeps their audit history. Search can still return them with include.forgotten Don't treat forgetting as an erasure request
Erase Deletes the record documents.delete permanently removes the document and everything derived from it Use this for erasure requests
Expire expiration_date hides a memory from the day after that date, in UTC. Nothing is deleted No field sets an expiry when you write Keep the date in metadata, run a daily job that forgets the memories of expired documents, and test it

Mem0 memory IDs, your mem0_ document IDs and supermemory memory IDs are three different things. Keep the type next to every ID in your ledger, or deletion code will eventually target the wrong one. Forgetting the memories that came from one imported fact looks like this:

// Hide a fact from recall but keep its history (Mem0's expiry behavior)
export async function forgetFact(userId: string, mem0Id: string) {
  const doc = await supermemory.documents.get(namespaceFor(userId), `mem0_${mem0Id}`, { include: ["memories"] })
  const ids = (doc.memories ?? []).map((m) => m.id)
  if (ids.length) await supermemory.memories.forget(namespaceFor(userId), { ids })
}

To erase it instead, call supermemory.documents.delete(namespace, { ids: ["mem0_..."] }). Deletion works once a document has finished processing, and the response lists any IDs that failed, so check it.

What should the rollback plan contain?

A rollback plan needs four concrete assets:

  1. the immutable source snapshot
  2. the source-to-destination ID rule and ledger
  3. a reversible read and write feature flag
  4. an append-only log of writes created after the snapshot

During dual-write, declare which provider is the source of truth. If you roll back, switch reads first, restore the original write path, then replay only the events recorded after the snapshot. Don't delete Mem0 data until the rollback window closes and the team has verified counts, retrieval quality, lifecycle behavior and tenant isolation.

What's the safest next step?

Migrate one representative tenant: not the easiest one, and not the largest. Import its facts, replay its real queries, test updates and forgetting, and measure the engineering work it takes to keep the system healthy. Then decide from the evidence, and keep the first cutover small enough to reverse.

Common questions

Can I migrate from Mem0 to supermemory without downtime?

A dual-write and shadow-read rollout can avoid a planned outage, but no migration is zero-downtime by default. Keep Mem0 serving reads while you backfill and validate supermemory, then move a small cohort behind a reversible feature flag.

How do Mem0 entity IDs map to supermemory namespaces?

Choose one namespace as the isolation boundary, such as org:{org_id}:user:{user_id}. Keep the other Mem0 dimensions, such as agent_id, app_id and run_id, as flat metadata, or put them in the namespace when they affect who can read the memory.

Will supermemory keep Mem0 memory IDs and timestamps?

Put the Mem0 ID in the document ID (for example mem0_<id>) and in metadata, and pass the original created_at as the document's date. supermemory gives the memories it forms their own IDs.

Is there a tool that migrates for me?

npx supermemory@latest migrate moves code from supermemory's v3/v4 API to v5. It doesn't read Mem0, so for the data itself use the import script above. The docs scripts are a shorter start, but they read a structured export and put every memory in one namespace, so adapt them to your namespace rule first.

Does this guide apply to Mem0 Open Source?

Not directly. It covers Mem0 Platform's APIs. Mem0 Open Source can use different vector stores, models, embedders and persistence layers, so its export plan has to match the deployed stack.

  1. How AI memory actually works. A beginner's guideHow AI apps remember things between chats, why a bigger context window doesn't solve it, and the parts every memory system shares.
  2. We're open sourcing the company brain. Here's how we designed the multiplayer harnessCompany Brain is now open source. A walkthrough of the multiplayer harness behind its Slack experience, from proactivity and memory boundaries to approvals and recovery.
  3. Jev changes a lot in memory & context engineering. Here's exactly how.We tested Jev across reranking, chunking, observation, and harness decisions. Here is where fast decision models help memory systems, where they cost more, and where they still fall short.
  4. I reverse-engineered Instinct's memory. Here's exactly how it worksInstinct keeps its memory as git-tracked markdown files, found with grep rather than vectors. Here is the whole system as far as black-box probing can reconstruct it, and how to rebuild it on supermemory in about 60 lines.
  5. An update to supermemoryWe've discontinued the supermemory company brain and Nova. Everyone who was charged has been refunded, our MCP and plugins continue to run, and we're going all in on the memory engine.
  6. Scaling Conversations: How Adapta Grew Usage Without Losing ContextAdapta added Supermemory as a persistent memory layer so every conversation keeps its context — letting the team scale usage without losing the thread.
  7. How Chatarmin Ditched RAG and Went Memory-Only with SupermemoryChatarmin replaced a heavy RAG pipeline with Supermemory's memory layer — cutting average AI response time from 40s to 12s and token usage by 40–50%.
  8. SMFS: making agentic retrieval 55% cheaper AND more accurateWe launched SMFS.ai (Supermemory Filesystem) a few weeks ago, with a simple bet: We can redesign the filesystem specifically for agents, with special files, structures, and commands that it can use for it's tasks. Today, SMFS is used by hundreds of companies to power their agents.
  9. Introducing Dynamic Dreaming: supermemory now connects the dots, for you.Dreaming is magical. TLDR: We're launching Dynamic Dreaming in supermemory today, which automatically works if you're using supermemory in any way - API, OpenClaw, Hermes agent, etc.
  10. Dear reader, we just made supermemory insanely cheap... the Context CloudWhen I first started building supermemory, I had one goal: To build the best memory system for AI. I would talk to customers, and find out that memory was not the only thing they needed - They were all setting up 7-8 different vendors at the same time.
  11. Introducing @supermemory/tools v2.0.0Today we're releasing v2.0.0. This release unifies the API across all agents sdk integrations from AI SDK to Mastra, makes conversation identity a first-class concept, and ships with memory saving on by default.
  12. Solving the Precision-Recall Tradeoff: Search Result AggregationWhen you're building memory for AI, search is your foundational layer. The way search generally works is straightforward: the user defines a query, and then sets a limit (top-K) on how many search results they want returned. Usually, this is set to 10 or 20.
  13. OpenClaw Memory Problems: Why It Forgets and How to Fix It (2026)OpenClaw remembers by writing Markdown files in its workspace, so it forgets anything the agent didn't save. Why that breaks down, how the supermemory plugin recalls and saves on every turn instead, and how to install it, free on every plan.
  14. Stateful Coding Agents with Memory: Build Long-Running Agents (2026)We built a plugin for Claude Code and OpenCode that gives your coding agent persistent memory. It remembers your preferences, learns your codebase, and never loses context mid-conversation. The result is an agent you can run for months without starting over.
  15. Clawd / Molt bot's memory SUCKS. We gave it supermemory.I'm the founder of supermemory. Clawd/Molt bot is blowing up right now, with many, many use cases. I set it up, too, and have been using it through telegram. TLDR: just go to https://supermemory.ai/docs/integrations/clawdbot to set up supermemory for your clawd bot.
  16. Catch up with our UNFORGETTABLE Launch WeekOver the last year, one belief has guided almost everything we’ve built at Supermemory AI becomes meaningfully useful only when it remembers. Memory shouldn’t be something developers rebuild from scratch. It shouldn’t be fragile, expensive, or trapped inside a single tool.
  17. Empowering the Next Generation of Founders: Supermemory Startup ProgramIf there’s one thing we’ve learned while building Supermemory, it’s that most startups don’t fail because they didn't build features; they fail when infrastructure slows them down, or they built too slow.
  18. Building code-chunk: AST Aware Code ChunkingAt Supermemory, we're building context engineering infrastructure for AI. A huge part of that is dealing with code: ingesting repos, understanding structure, and making it searchable. The problem is that most code chunking solutions are terrible. We built code-chunk to fix this.
  19. Supermemory raises $3 million with the best memory engine for LLMsToday, I am excited to announce our first funding round to accelerate our mission of building an interoperable, scalable and reliable memory for LLMs and agents. Memory is one of the hardest challenges in AI right now.
  20. Mem0 vs Supermemory: Why Scira SwitchedScira AI moved its production memory layer from Mem0 to Supermemory. This is what failed, what improved, and how the team evaluated the two systems.
  21. Never Record Again: How Montra Uses Supermemory to Rethink Video CreationCampbell Baron, the founder of Montra, has been making videos since he was twelve. By thirteen, he was already doing brand work. Today, he’s betting on a very different future for creators: a world where recording is the exception, and most videos are generated from scratch.
  22. Unified Memory That Works Where You Work: Your Second Brain With SupermemoryHi everyone, I’m Dhravya, the founder of Supermemory. I want to start with a little story behind why this product means so much to me. You can also skip straight to what it is and how it works below.
  23. Supermemory just got faster on PlanetScaleWhat is Supermemory? Supermemory completes the missing part of the LLM puzzle: memory. Just as memory is crucial for human intelligence, it's essential for truly intelligent AI systems.
  24. Faster, smarter, reliable infinite chat: Supermemory IS context engineering.People are obsessed with prompts and prompt engineering. Sure, what you say is important, but what the model knows when you say it is the difference between a stateless text generator and an intelligent AI system. In short, context is the most crucial component.
  25. We solved AI API interoperabilityOne API to rule them all, One spec to find them, One library to bring them all and in the TypeScript, bind them. When we were building the Infinite Chat API, initially, we only supported the OpenAI format. This was fine, until a lot of our customers started asking for more.
  26. The Wow Factor of Memory - How Flow Used Supermemory To Build Smarter, Stickier ProductsOverview: Flow is a note-taking app built around a bold vision: to create a more personal, context-aware writing experience powered by AI. At the heart of this mission is memory.
  27. The UX and technicalities of awesome MCPsLast month, we launched the Supermemory MCP, mostly to test our own infrastructure and get some initial traction. It blew up. To my absolute surprise, the initial launch itself got half a million impressions (!!!). Then, we launched and got #2 on ProductHunt too.
  28. Architecting a memory engine inspired by the human brainLanguage is at the heart of intelligence, but what truly powers meaningful interaction is memory — the ability to accumulate, recall, and contextualize information over time. Large Language Models (LLMs) have mastered language, but memory remains their Achilles’ heel.
YOUR PRIVACY

Change or withdraw any time via Cookie settings in the footer. Read our cookie notice.