Recovery
Nothing an agent does to a note in a brain is a one-way door. Every write keeps the content it replaced; every delete is a soft delete. This page is what to do when something needs undoing.
Fixing a wrong edit
Ask your AI to walk this three-tool sequence. Every version of a note, including the one just overwritten, stays in history.
get_historylists every past version, newest first, with who changed it and when:get_history({ brain: "<your-brain>", path: "overview.md" }) [ { "version": 3, "edited_by_name": "Max", "created_at": "2026-09-06T16:44:05Z" }, { "version": 2, "edited_by_name": "Max", "created_at": "2026-09-06T16:43:43Z" }, { "version": 1, "edited_by_name": "Max", "created_at": "2026-09-06T16:43:35Z" } ]read_versionshows exactly what a past version said, before you commit to restoring it:read_version({ brain: "<your-brain>", path: "overview.md", version: 2 }) { "path": "overview.md", "version": 2, "content": "…the content as it was at v2…", "edited_by_name": "Max", "created_at": "2026-09-06T16:43:43Z" }restore_versionrolls the note back to that content. This does not erase what was there a moment ago: the current content is saved to history first, as a new version, so a restore can itself be undone with another restore.restore_version({ brain: "<your-brain>", path: "overview.md", version: 2 }) { "path": "overview.md", "version": 5, "restored_from": 2 }Notice the new version number keeps climbing (here, v5) even though the content now matches v2. Restoring is a write, not a rewind of the version counter.
Recovering a deleted note
delete_note is a soft delete: the note stops showing up in list_notes and search, but its content and history are untouched. undelete_note brings it back exactly as it was, at the same version number it had when deleted, since nothing about its content ever changed.
undelete_note({ brain: "<your-brain>", path: "overview.md" })
{ "path": "overview.md", "version": 5 }What a stale-write warning means
Pass expected_version (the version read_note last gave you) to write_note, and if someone else changed the note since you read it, you get a conflict_warning back instead of a rejected write. Be clear-eyed about what that means: the warning is informational, and your write still lands, on top of theirs.
write_note({ brain: "<your-brain>", path: "overview.md", content: "…", expected_version: 1 })
{
"path": "overview.md",
"version": 4,
"conflict_warning": "This note was changed by Max at 2026-09-06T16:43:43Z (you had v1, current is v3). Your change was saved on top."
}Nothing here is lost. Whatever was in the note between your read and your write is still in history: get_history, then read_version on the version the warning names, recovers it, and restore_version brings it back if that is what should have happened instead.
expected_version warns, it does not reject
A stale expected_version never blocks a write. If two sessions are editing the same note at once and that matters to you, say so in your brain’s guide (for example, “check get_history before big edits to session_log.md”) rather than relying on this to stop a collision.
A note too big to read in one call
read_note never fails on a large note; a note over roughly 39,000 characters is truncated automatically, and the response tells you so: total_chars is the note’s real size, returned_range is what you got, and next_offset is where to continue. Pass outline: true instead to see the note’s heading structure, with offsets and section sizes, without reading its content at all. It is the cheap way to decide what to read before reading it.
To change a large note without rewriting the whole thing, use edit_note, which replaces one exact string. It fails safely rather than half-writing: if old_string is not found, or is not unique when you expected exactly one match, the call returns an error and the note is untouched, not partially edited.
edit_note({ brain: "<your-brain>", path: "overview.md", old_string: "text that is not in the note", new_string: "…" })
Error: edit_note failed: notes.edit failed: edit_note: old_string not found in overview.mdUse find_in_note first if you are not sure of the exact text to pass as old_string: it does literal substring matching and returns the offsets a precise edit needs, without you having to read the whole note to find them.