GeniffyDocs
Changelog Log In Get a key

Files kept exactly

Some text has to come back exactly as it was written, such as the notes an agent keeps for itself. Put it under a path and Geniffy keeps it character for character, spaces and line endings included. It also learns from it like a note titled by its path, so context() and ask() recall what it says.

notes = "# Lumen\n- Priya Nair signs the renewal.\n"
mem.files.put("/memories/notes.md", notes)    # creates or replaces it
mem.files.get("/memories/notes.md").text      # notes, exactly

A write answers with the file and the source it is learned from. size is in characters:

{
  "path": "/memories/notes.md",
  "size": 40,
  "updated_at": "2026-10-06T11:00:01.204913+00:00",
  "created": true,
  "source": {
    "id": "6f1c0a3e9b2d4c8e8a7f5b3d2e1c0f9a",
    "kind": "note",
    "title": "/memories/notes.md",
    "labels": {}
  }
}

This is what Claude's memory tool keeps its notes in. Any agent that writes its own notes as files can do the same, with any model.

What a write does

  • A new path gets a file, and what it says is learned as a note titled by its path. created is true.
  • A path that has a file gets the new text whole. Only the paragraphs that changed are learned, and what was removed is taken back, as when a record is sent again under your own id.
  • Empty text is a file too: it is kept, with nothing learned from it, and emptying a file takes back what it taught.
  • Labels work as on anything you add, such as labels={"channel": "claude-memory"}. Written again, new labels replace the old ones; left out, the file keeps its own; {} clears them. See Labels.
  • A write is whole or not at all. If the memory service refuses it, a new file leaves nothing behind, and a file written before keeps its old text and what that taught. In the rare case the memory service can't be reached to take back what it may have kept of a new file, the error names the failed source that holds it: the file still reads as missing, and writing it again or deleting it clears that too.

Read and list

f = mem.files.get("/memories/notes.md")   # .text, .size, .updated_at
page = mem.files.list("/memories/")       # .files, .total and .next
  • A file's size is in characters. A list holds each file's path, size and updated_at, without the text, in path order.
  • A prefix is a plain prefix of paths, not a pattern: /memories/ is every file beneath that directory, / is every file, and /memories/pre finds /memories/preferences.md. Paths are compared character by character, so /Memories/ is another directory.
  • A page holds up to 200 files, 100 unless you ask for more with limit, and next is the cursor of the page after, to pass as cursor; it is null (None in Python) at the end.
  • A path with no file is a 404 with the code not_found; the SDKs raise NotFoundError.

Move

mem.files.move("/memories/notes.md", "/memories/lumen.md")   # a file
mem.files.move("/memories/drafts", "/memories/archive")      # a directory

When there is a file at from, it moves to to. When there isn't, every file beneath from/ moves to the same place beneath to/. Each keeps its text and what it taught, and nothing is learned again. A move answers with how many files moved. When a file is already where one of them would go, nothing moves and the call is a 409 with the code conflict; when there is nothing at from, a 404.

Delete

mem.files.delete("/memories/lumen.md")          # a file
mem.files.delete_prefix("/memories/archive/")   # every file under a prefix

Each file goes with its text and every memory only it taught. A memory that another source also taught stays, because something still says it. Over HTTP, a prefix deletes up to 100 files a call and more: true says to call again; the SDKs call again for you, and return how many files were deleted.

The rules for a path

  • It starts with / and is up to 255 characters.
  • None of its parts is empty, . or .., and it holds no backslash and no control character.
  • A file's path doesn't end with /; a prefix may.

A path that breaks a rule is refused with bad_path, and nothing is stored.

Files and the rest of a memory

  • Each file is a source, a note titled by its path under the external_id file: and its path: sources.list() shows it, and DELETE /v1/sources?external_id=file:/memories/lumen.md deletes it as deleting the file does. Only PUT /v1/files writes one, so adding a note, a page or a file under an external_id that starts with file: is refused with bad_external_id.
  • Recall reads them with everything else, and each line cites the file's path: - Priya Nair signs the renewal. [/memories/notes.md, 2026-10-06].
  • They go with the user. Forgetting a user takes their files, and their copy, export(), lists every file by its path, size and when it was written. files.get(path) hands back each one's text as written, so the copy stays small however much the files hold.
  • A file holds up to 2,000,000 characters, as a note does. Text with a NUL character (\u0000) in it, or half of a surrogate pair, can't be kept exactly as sent, so it is refused with invalid_request.
Last updated October 6, 2026