{"slug": "migrating-from-mem0-to-memorysync-step-by-step-with-a-dry-run-first", "title": "Migrating from Mem0 to MemorySync, step by step (with a dry run first)", "summary": "Mohammed, founder of MemorySync, published a step-by-step guide for migrating agent memory from Mem0 to MemorySync, built around a dry-run-first workflow using memorysync-cli 1.6.1. The key difference is scoping: Mem0 passes user_id per call, while MemorySync sets the user once via an X-End-User-ID header, and the guide warns that a missing header \"does not raise — it writes into the wrong scope.\" The CLI's dry run refuses any record lacking user_id, exiting with code 2 rather than guessing, and the real import maps Mem0 fields to tags and metadata while preserving original timestamps as metadata.mem0_created_at.", "body_md": "*Disclosure: I'm Mohammed, founder of MemorySync, so read this as a vendor's page. I drafted it with AI assistance and checked every claim against each company's own docs as of Oct 25, 2026. Code status: on Oct 25, 2026 I ran the CLI dry runs in steps 2 and 3 and a real import (step 4) with memorysync-cli 1.6.1 on small test exports against the live MemorySync API, plus the MemorySync calls in step 5 (add, query, `PATCH`, forget). The Mem0 calls are quoted from Mem0's docs and were not run.*\n\nThis is the \"how\", not the \"whether\". If you're still deciding, start with [Mem0 alternative: how to compare hosted agent memory](https://memorysync.hashnode.dev/mem0-alternative-hosted-agent-memory). If you've decided to move, here's the order that keeps it reversible.\n\nIn Mem0 you pass the user on each call: `user_id` on `add`, and `filters={\"user_id\": ...}` on `search` ([Mem0 quickstart](https://docs.mem0.ai/platform/quickstart)). In MemorySync the user is a request header, `X-End-User-ID`, set once on the client as `end_user_id` ([Authentication](https://docs.memorysync.io/api/authentication?utm_source=devto&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_mem0-to-memorysync-migration)).\n\nMemorySync's guide warns that a missing `X-End-User-ID` \"does not raise — it writes into the wrong scope\" ([Migrating from Mem0](https://docs.memorysync.io/getting-started/migrate/from-mem0?utm_source=devto&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_mem0-to-memorysync-migration)). Set the header on every call. Every step below is built around getting that mapping right.\n\nUse the Mem0 dashboard export or the export API. Per MemorySync's guide, the export holds records with `id`, `content`, `metadata` and `created_at`. Make sure `user_id` is included on every record (see step 3).\n\n```\n# Reports what would be written, per user. Writes nothing.\nnpx memorysync-cli migrate mem0 --file mem0_export.json --dry-run\n```\n\nWhen I ran this on a sample export where every record had a `user_id`, it printed the record count, the number of end users, the number of requests (at 50 records per request), and a \"Would write\" count per user. It wrote nothing.\n\nWhen I ran it on an export with one record missing `user_id`, the dry run stopped with exit code 2, named the record, said \"Nothing was written\", and suggested a re-export that includes `user_id`. That's documented behaviour: the CLI refuses any record whose user can't be determined, rather than guessing.\n\nRe-export from Mem0 with `user_id` on every record, then run the dry run again.\n\nOnce the dry run looks right, drop `--dry-run` and follow the [migration guide](https://docs.memorysync.io/getting-started/migrate/from-mem0?utm_source=devto&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_mem0-to-memorysync-migration). Per the guide, the CLI:\n\n`user_id` and writes each group under that end-user scope;`client_ref`, so re-running reports records that already landed instead of storing them again;`categories` to tags and keeps `id`, `created_at`, `agent_id`, `app_id` and `run_id` in metadata;\nThe original timestamp is kept as `metadata.mem0_created_at`. MemorySync assigns its own `created_at`, so sort on the metadata field if original order matters.\n\nThese rows appear in both vendors' current docs:\n\n| Mem0 (Mem0 docs) | MemorySync (MemorySync docs) | \n|---|---|\n| `client.add(messages, user_id=\"alice\")` | `POST /memory/add` with`text` ; SDK`client.add(\"...\")` on a client created with`end_user_id=\"alice\"` | \n| `client.search(query, filters={\"user_id\": \"alice\"}, top_k=5)` | `POST /memory/query` with`query` and`k` (1 to 50, default 5); scope from the header | \n| `client.update(memory_id, ...)` (`PUT /v1/memories/{memory_id}/` ) | `PATCH /memory/{memory_id}` with`tags` ,`importance` ,`metadata` ,`source` or`event_type` | \n| `client.delete(memory_id)` (`DELETE /v1/memories/{memory_id}/` ) | `DELETE /memory/forget` with a`memory_ids` list | \n\nTwo differences to plan for. Mem0's update edits content, metadata, timestamp or expiration date. MemorySync's `PATCH` doesn't list the memory text as an editable field. And MemorySync memory ids are integers, so keep the Mem0 id in metadata (the CLI does this) rather than reusing it as an id.\n\n``` python\n# From docs.memorysync.io (run against the live API on Oct 25, 2026)\nimport os\nfrom memorysync import MemorySyncClient\n\nclient = MemorySyncClient(\n    api_key=os.environ[\"MEMORYSYNC_API_KEY\"],\n    base_url=\"https://api.memorysync.io\",\n    project_id=os.environ.get(\"MEMORYSYNC_PROJECT_ID\") or None,\n    end_user_id=\"alice\",\n)\nclient.add(\"User prefers dark mode\")\nhits = client.query(\"user preferences\", k=5)\n```\n\nRun your own real queries against both and compare the results before you cut over. Keep the Mem0 export through a full usage cycle. MemorySync's guide calls it \"the cheapest rollback available\".\n\n**Can I re-run the import if it stops halfway?** Per MemorySync's guide, yes. Each Mem0 id is sent as `client_ref`, and records that already landed are reported as already migrated. In my live test import, the second run printed \"Migrated 0 of 2 record(s)\" and \"2 already migrated on an earlier run\". The originals carried `mem0_id` and `mem0_created_at` in metadata.\n\n**Will Mem0's `agent_id` and `run_id` still be there?** Per the guide, they're kept in metadata. They don't become MemorySync scopes. Scope is project plus end user.\n\n**Do I have to change every call site?** Yes. User scope moves from call arguments to the client, and `top_k` becomes `k`.\n\nWant to see the write-then-recall loop before you migrate anything? The browser playground needs no signup: **[try MemorySync](https://memorysync.io/try?utm_source=devto&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_mem0-to-memorysync-migration)**.", "url": "https://wpnews.pro/news/migrating-from-mem0-to-memorysync-step-by-step-with-a-dry-run-first", "canonical_source": "https://dev.to/memorysync_rafay/migrating-from-mem0-to-memorysync-step-by-step-with-a-dry-run-first-p3b", "published_at": "2026-10-27 16:39:23+00:00", "updated_at": "2026-10-27 16:42:02.674871+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products"], "entities": ["MemorySync", "Mem0", "Mohammed", "memorysync-cli"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/migrating-from-mem0-to-memorysync-step-by-step-with-a-dry-run-first", "markdown": "https://wpnews.pro/news/migrating-from-mem0-to-memorysync-step-by-step-with-a-dry-run-first.md", "text": "https://wpnews.pro/news/migrating-from-mem0-to-memorysync-step-by-step-with-a-dry-run-first.txt", "jsonld": "https://wpnews.pro/news/migrating-from-mem0-to-memorysync-step-by-step-with-a-dry-run-first.jsonld"}}