MemoryRouter / Documentation
Vault transfer and graft
Copy core memories between MemoryRouter vaults with a dry-run preview, either as a full transfer or a focused semantic graft.
Vault transfer and graft
Transfer and Graft copy stored core memories between Memory Keys without changing the source. Transfer copies the source core vault; Graft copies core memories matching one or more semantic searches.
Use these APIs to migrate a vault or seed a team vault with relevant, reviewed context. This is not the ChatGPT-to-Claude profile transfer and does not import an external chat archive.
Prerequisites
- A MemoryRouter account, an active source Memory Key, and a different active, writable destination Memory Key.
- Permission to share the selected source memories with everyone who can access the destination.
- Python 3 for the standard-library example below. No SDK installation is required.
- Source memories already indexed in the core vault. Session-only history and reflection tiers are outside this operation's current copy scope.
Create or choose the keys in the MemoryRouter dashboard. A read-only source is allowed; an :off source is not. A :read or :off destination is rejected.
The API authenticates the source with Authorization: Bearer ...; the JSON field destination_key identifies the writable target. Merely knowing a team's name does not authorize a copy. You need both credentials and permission to use them.
1. Prepare a safe local copy command
Save this as vault-copy.py. It prompts for keys without echoing them and defaults to preview-only. Do not put real keys in the file.
import argparse
import getpass
import json
import urllib.error
import urllib.request
parser = argparse.ArgumentParser()
parser.add_argument("operation", choices=["transfer", "graft"])
parser.add_argument("--query", action="append", default=[])
parser.add_argument("--threshold", type=float, default=0.6)
parser.add_argument("--commit", action="store_true")
args = parser.parse_args()
if args.operation == "graft" and not args.query:
parser.error("graft needs at least one --query")
if not 0 <= args.threshold <= 1:
parser.error("threshold must be between 0 and 1")
source = getpass.getpass("Source Memory Key: ")
destination = getpass.getpass("Destination Memory Key: ")
body = {"destination_key": destination, "dry_run": not args.commit}
if args.operation == "graft":
body.update(queries=args.query, threshold=args.threshold)
request = urllib.request.Request(
"https://api.memoryrouter.ai/v1/memory/" + args.operation,
data=json.dumps(body).encode(),
headers={
"Authorization": "Bearer " + source,
"Content-Type": "application/json",
},
method="POST",
)
try:
with urllib.request.urlopen(request, timeout=120) as response:
print(json.dumps(json.load(response), indent=2))
except urllib.error.HTTPError as error:
print("HTTP", error.code)
print(error.read().decode())
raise SystemExit(1)Preview output includes memory snippets. Treat it as private conversation data even though the response masks the source and destination key labels.
2. Preview the copy
Preview all source core memories:
python3 vault-copy.py transferOr preview only memories matching focused queries:
python3 vault-copy.py graft \
--query "Cedar launch review phrase" \
--query "Cedar launch deployment decisions" \
--threshold 0.6A dry run reports dry_run: true, count, estimated_tokens, and memories with snippets. Graft also reports queries, threshold, and match scores. Review the actual matches before copying, especially when a personal vault feeds a shared team vault.
Tighten the query or increase the threshold if unrelated context appears. A semantic threshold is a relevance filter, not a privacy classifier or authorization boundary.
3. Commit only the reviewed operation
After reviewing the preview, rerun the same operation with --commit:
python3 vault-copy.py graft \
--query "Cedar launch review phrase" \
--query "Cedar launch deployment decisions" \
--threshold 0.6 \
--commitFor an approved full transfer:
python3 vault-copy.py transfer --commitThe script asks for both keys again. Use the same source and destination as the preview. --commit sends dry_run: false and immediately writes matching memories. The API does not issue a preview approval digest or freeze the source: if source data changed after preview, review again before committing.
Check copied, deduped_or_skipped, and failed in the response. Inspect any returned errors. A successful HTTP status with nonzero failed is a partial copy, not full completion. Repeating the same copy deduplicates already copied content in the destination.
Cross-session proof with synthetic data
- Use two dedicated test vaults. In a source-connected ChatGPT or Claude conversation, explicitly save: For the fictional Cedar launch, the review phrase is copper-orbit-482. This is test data. Approve that exact memory.
- Search the source vault and confirm the fact is retrievable before copying.
- Preview a graft with
--query "Cedar launch review phrase". Confirm the synthetic memory appears and no unrelated personal content will be copied. - Commit the reviewed graft and check the copy counts.
- Connect a separate fresh AI conversation to the destination vault. Do not paste the original sentence or answer token. Ask: Use MemoryRouter to search this vault. What was the review phrase for the fictional Cedar launch? Quote the matching memory.
The expected retrieved content includes copper-orbit-482. Confirm the connected destination, not just the model's wording. The source memory should remain available because this operation copies rather than moves it.
API boundaries and limitations
| Property | Current behavior |
|---|---|
| Transfer endpoint | POST /v1/memory/transfer |
| Graft endpoint | POST /v1/memory/graft |
| Preview switch | dry_run: true. Omitting it means a real write. |
| Transfer maximum | 10,000 source core memories per request. Larger sources return 413. |
| Graft selection | Non-empty queries array and threshold from 0 through 1, default 0.6. |
| Graft result count | No supported limit parameter. Do not rely on one to cap sensitive output. |
| Embedding selection | Optional embeddings body field or X-Embedding-Model header. |
| Copy scope | The chosen embedding model's core vault, not an all-tier or all-session backup. |
| Provenance | Copied metadata records masked origin_key and transferred_at; it is not appended to the conversation text. |
| Undo | No automatic rollback. Manage destination memories explicitly in the dashboard. |
Copying data into a team vault changes who can retrieve it. Review team access and data policy first. A dry run estimates tokens, not a binding currency quote. See current pricing before a large copy.
Troubleshooting
| Symptom | Check |
|---|---|
400 destination error | Use an active, writable key different from the source, without :read or :off. |
401 | Re-enter the valid source key; do not confuse a model provider key with a Memory Key. |
403 | The source must permit memory reads and cannot use :off. |
413 on transfer | The source exceeds the full-transfer cap. Use focused grafts or contact support about a batched migration. |
| Graft returns zero matches | Confirm the source core vault and embedding selection, then try a more specific query or a lower threshold in a dry run. |
| Unexpected matches | Do not commit. Tighten the queries/threshold, or prepare a smaller source vault. |
Nonzero failed | Review errors and retry the same authorized copy after resolving the cause. Already copied content is deduplicated. |
| Destination chat cannot recall | Confirm it uses the destination vault and core scope, request an explicit memory search, and allow indexing to finish. |
Next steps
Agent history import
Bring selected local Claude Code, Codex, and OpenClaw conversation history into MemoryRouter with an agent-authored extract and a deterministic, approval-gated uploader.
Historical Import
Agent-driven historical import - the import nudge, the versioned extraction prompt, per-platform import commands, pricing and approval, and idempotency guarantees.