remember, so the newest statement replaces the one it contradicts. Nothing is written until you drop --dry-run.123kaibo import --dry-run memories.json # report only, no key needed, nothing sent kaibo import memories.json --from mem0 # append the events kaibo import memories.json --from mem0 # again: nothing new, same keys
--from. Pass - to read stdin. A directory is read for Markdown files.idempotency_mismatch.| Source | Input | Imported | Skipped |
mem0 | get_all / POST /v3/memories/ response ({results: [...]}), a bare array, JSON Lines, or a structured memory export object | one event per memory, with user_id, agent_id, categories, metadata; time is updated_at, else created_at | memories with replaced_by, memories past expiration_date, empty ones. Graph relations are ignored. A structured export becomes one event per field |
zep | what Zep's list calls return: thread messages, graph edges (facts), episodes. A bundle {threads, edges, episodes}, a list page {items}, an array or JSON Lines | user and assistant messages, valid facts (time is valid_at), text and json episodes | system and tool messages, facts with invalid_at or expired_at, message episodes (the thread has them), nodes and summaries |
letta | an Agent File (.af), including the older form that is JSON encoded as a string | each memory block as one event, and each agent's user and assistant messages | system prompts, tool messages and tool-call-only replies, tools and their source code, secrets and environment variables, data sources. Archival passages are not in the format yet |
jsonl | one record per line: {text}, {content}, {memory}, {message} or {body} with optional id and created_at; a plain string line; an event as the API takes it; or a line from kaibo export | the text | lines without text, lines that are not JSON, memory lines from an export (kaibo rebuilds those) |
markdown | a file, or a directory of them | one event per heading section, text starts with the heading path; date from front matter becomes the time | sections without text. Long sections are cut at paragraph breaks into parts |
--include-invalid to keep records the source marks as expired, replaced or invalidated. They are statements that stopped being true, so the default leaves them out..af files from the letta-ai/agent-file repository, including one that is double encoded. The Mem0 and Zep test fixtures are built from the documented fields, not captured from an account. If your export does not parse, the dry run tells you which record and why.1234567with-history.af format : letta agent file (.af) read : 8 would create : 4 skipped : 4 skipped #3: role "system" is not conversation 2026-02-02 We moved the release target from July 13 to July 17.
--json prints the full report: counts, up to 25 skipped records with the reason, warnings, and the first events as they would be stored.123curl -X POST "$KAIBO_URL/v1/imports?dryRun=true" \ -H "Authorization: Bearer $KAIBO_KEY" -H "Content-Type: application/json" \ -d "$(jq -n --rawfile c memories.json '{source:"mem0",content:$c}')"
POST /v1/imports takes the file text in content and needs a key that can write, for a dry run too. One request holds about 1 MB and 2,000 events; above that it returns payload_too_large and the message says so. kaibo import has no such cap: it parses locally and sends 100 events at a time.123const dry = await kaibo.imports.create({ source: "mem0", content }, { dryRun: true }); const done = await kaibo.imports.create({ source: "mem0", content }); await kaibo.jobs.wait(done.appended!.jobId!);
1dry = kaibo.imports.create("mem0", content, dry_run=True)
123kaibo export > backup.jsonl # every stream, events and memories kaibo export --stream chat --no-memories # one stream, events only kaibo import backup.jsonl --from jsonl --stream copy # into another project
POST /v1/exports streams JSON Lines: a header, then event and memory records, then an end record with counts. A file without an end line was cut off, and the SDKs raise an error for it. Past 50,000 records the server stops and sets truncated: true; the CLI exits 1 and tells you to export one stream at a time. Each key may export 10 times an hour. It needs the read scope.GET /v1/events returns, so importing them with jsonl keeps the original idempotency keys. Memory lines are derived data and are skipped on import.