{
  "name": "longbook",
  "description": "The owner's financial memory, over MCP. Two doors: the server inside the longbook app, which reads and writes the folder on their own machine, and this hosted one, which reads the copy they chose to push and takes writes as proposals their app applies.",
  "contract_revision": 40,
  "endpoint": "https://longbook.app/mcp",
  "transport": "streamable-http",
  "doors": [
    {
      "id": "local",
      "title": "On the owner’s own machine",
      "how": "The server built into the longbook app, also on npm for any computer: npx -y @longbook/mcp@latest, with --vault <folder> where the app’s iCloud folder is not. On the Mac, Set up Claude Desktop writes the client config in one click, and Copy configuration hands it to any other MCP client.",
      "auth": "none — it is their own folder",
      "reads": true,
      "writes": "straight to the folder; every batch lands in the app’s Changes screen for review",
      "documents": true
    },
    {
      "id": "hosted-oauth",
      "title": "A browser AI, signed in",
      "how": "Add https://longbook.app/mcp as an MCP connector; sign-in opens in the browser.",
      "auth": "oauth",
      "reads": true,
      "writes": "as suggestions, once the owner ticks \"Let signed-in AIs suggest changes\" on the app’s Automations screen — they wait for the app and land in its Changes screen, to keep or undo",
      "documents": false
    },
    {
      "id": "hosted-key",
      "title": "A scheduled agent, with a key",
      "how": "Create a key on the longbook app’s Automations screen (More on iPhone, Settings on the Mac) with \"Read and suggest changes\", and send it as a bearer token. On a Mac, save it to the keychain when shown and read it with `security find-generic-password -s \"longbook access key\" -w`. Clients that cannot send a header — claude.ai among them — use the signed-in door above instead.",
      "auth": "bearer lbk_… with scope memory:propose",
      "reads": true,
      "writes": "as proposals — they wait in the owner’s app and land in Changes the next time it opens",
      "documents": false
    }
  ],
  "tools": {
    "read": [
      {
        "name": "next_payment",
        "says": "The nearest day any commitment still owes, and every charge landing on it."
      },
      {
        "name": "upcoming",
        "says": "Every charge written for the next N days (default 30), soonest first, with per-currency totals — and which plans run out inside the window."
      },
      {
        "name": "cashflow",
        "says": "Monthly totals per currency, summed from the charges written: past months are the record, months ahead are the plan as written, a month with no rows is empty."
      },
      {
        "name": "spending_by_category",
        "says": "Spend per category over a window, summed from the charges written in it — the twelve months from today unless you say otherwise; a past window gives what was spent."
      },
      {
        "name": "spending_by_account",
        "says": "Spend per account over a window, summed from the charges written in it — the twelve months from today unless you say otherwise."
      },
      {
        "name": "spending_by_member",
        "says": "Spend per member over a window, summed from the charges written in it — the twelve months from today unless you say otherwise."
      },
      {
        "name": "commitments",
        "says": "Every commitment with its attributes — price now, cycle, status, category, member, account — plus its next unpaid charge, how many charges it has, and how far it is planned."
      },
      {
        "name": "charges",
        "says": "The rows themselves: every charge, or one commitment's, optionally inside a window or only paid / only unpaid — each with its amount, paid state, the day the money moved, what backs it, and its note."
      },
      {
        "name": "search",
        "says": "Finds commitments by name, category, notes, member, or account — case-insensitive, substring match."
      },
      {
        "name": "accounts",
        "says": "Every account the memory holds: id, name, type (its tag), institution and last4 when present, belongs_to (the person it belongs to), linked_to (the account it is linked to: a card to the bank account that pays it), balance with balance_date, notes — what is particular about it, one detail per line, a card's name (\"Card name: …\", the thing to look up for offers) among them — details as stored when it holds any, how many commitments and charges are billed to it, and its next unpaid charge."
      },
      {
        "name": "members",
        "says": "Every person the memory holds: id, name, tag (their role in the owner's finances — family, household help, landlord), email, the accounts that belong to them, details as stored when they hold any, how many commitments and charges are for them, and their next unpaid charge."
      },
      {
        "name": "tags",
        "says": "Every tag this vault already uses — with its icon and how many rows wear it — and then longbook's library for the rest, with the owner's own casing."
      },
      {
        "name": "whoami",
        "says": "Answers who this connection belongs to, what it may do (read, or read and propose changes), how much of its rate allowance is left, whether a hosted copy exists and when it was last pushed, how many proposals are waiting for the owner's app, which ones it could not apply and why (failed_to_apply), and where the schemas live."
      },
      {
        "name": "read_guide",
        "says": "Returns longbook's llms.txt — the contract and working method for an AI using longbook — whole, or one chapter by name."
      }
    ],
    "write": [
      {
        "name": "add_commitments",
        "says": "Adds up to 200 commitments in one call, as one reviewable batch."
      },
      {
        "name": "update_commitment",
        "says": "Changes a commitment's attributes, by id or the exact name as the memory holds it — a miss answers with the real names; everything not named stays as it was, and an explicit null clears an optional field."
      },
      {
        "name": "mark_paid",
        "says": "Records that one charge was paid, or takes the mark back with paid: false."
      },
      {
        "name": "add_charges",
        "says": "Writes rows into one commitment by date: a statement's months for a commitment that already exists (paid, basis \"statement\"), or more planned months than the app keeps."
      },
      {
        "name": "remove_charges",
        "says": "Deletes named rows from one commitment, named by id or the exact name as the memory holds it — a miss answers with the real names: a plan that was wrong."
      },
      {
        "name": "cancel_commitment",
        "says": "Cancels one commitment, by id or the exact name as the memory holds it — a miss answers with the real names."
      },
      {
        "name": "promote_to_recurring",
        "says": "Takes the ids of one-time commitments that are one thing repeating and makes one recurring commitment: their charges move under it exactly as they are — dates, amounts, paid, basis — the one-time rows go, and the plan continues from the step after the latest, never before today, for plan_months (24 by default), or stops at ends_on for a repeat that has ended."
      },
      {
        "name": "leave_insight",
        "says": "Leaves one short note on an app module; the latest note per module shows on that module's own screen."
      },
      {
        "name": "update_account",
        "says": "Changes an account's own attributes — its name, its type (`tag` — the same words as the app's Account Type), its `notes`, the person it belongs to (`member`), its balance with the day it was true, and the account it is linked to (`linked_to`) — by id or the exact name as the memory holds it — a miss answers with the real names."
      },
      {
        "name": "update_member",
        "says": "Changes a person's name, email or tag, by id or the exact name as the memory holds it — a miss answers with the real names; everything not named stays as it was, and an explicit null clears the email or the tag."
      },
      {
        "name": "update_charge",
        "says": "Changes one charge where it sits — its amount, its day, its paid mark, the day the money moved, its note."
      },
      {
        "name": "set_planned_count",
        "says": "Sets how many charges stand from today on for a recurring commitment, named by id or the exact name as the memory holds it — a miss answers with the real names — 240 at most."
      },
      {
        "name": "add_member",
        "says": "Makes a person with what is known — a `tag` for their role in the owner's finances (family, household help, landlord — the library's member tags first) and an `email`."
      },
      {
        "name": "add_account",
        "says": "Makes an account — a card, a bank account, UPI, a wallet, cash — with what is known: its type (`tag`, the app's own words: \"credit card\", \"bank account\"), the account it is linked to (`linked_to`, a card to the bank account that pays it, one level), the person it belongs to (`member`, someone the memory holds), its `notes` (one detail per line, such as a card's printed name as \"Card name: Platinum Reserve\"; numbers are refused) and a `balance` with the day it was true."
      },
      {
        "name": "rename_tag",
        "says": "Renames one tag in the vault's own vocabulary — `kind` says which list, commitment, member or account — and carries every row wearing it, the owner's tag groups too."
      },
      {
        "name": "delete_tag",
        "says": "Removes one tag from the vault's vocabulary — `kind` says which list — and the rows that wore it are left with no category; nothing else about them changes."
      },
      {
        "name": "delete_commitment",
        "says": "Deletes one commitment and every charge it ever had, by id."
      },
      {
        "name": "create_report",
        "says": "Asks longbook to draw a report from the ledger over the days and scope named."
      }
    ],
    "local_only": [
      {
        "name": "list_documents",
        "says": "Lists the files in the vault's documents/ folder — bank and card statements the owner dropped in, and source files referenced by rows."
      },
      {
        "name": "read_document",
        "says": "Returns a document's text: PDFs page by page, Excel sheets as CSV lines under sheet headings, CSV as it is."
      },
      {
        "name": "reports",
        "says": "The reports in the vault's reports/ folder, drawn by longbook from the confirmed charges it holds: those dated today or earlier whose basis is anything but \"plan\"."
      }
    ]
  },
  "contract": {
    "guide": "https://longbook.app/llms.txt",
    "schema": "https://longbook.app/schema/longbook.schema.json",
    "tag_library": "https://longbook.app/library.json",
    "prompts": "https://longbook.app/prompts.json",
    "read_guide_tool": "read_guide — the same guide, by chapter, through a tool call; a prompt’s name returns its procedure"
  },
  "prompts": [
    {
      "id": "set-up-my-longbook",
      "title": "Set up my longbook",
      "group": "Setting up",
      "phrase": "Read my 12-month bank statement and help me set up my longbook",
      "result": "Your longbook filled from your statements: every charge on record, the names you picked in setup filled in where your statements or your answers show them, each account's closing balance set, the charges that clearly repeat planned ahead, and the rest set to repeat once you confirm them. It waits in the Changes screen for you to keep.",
      "needs": []
    },
    {
      "id": "add-my-statement",
      "title": "Add my statement",
      "group": "Setting up",
      "phrase": "Add my statement in my longbook",
      "result": "Every charge on the statement recorded as paid, money in kept apart, the account's balance updated, the charges that clearly repeat set to continue, and the rest once you confirm them.",
      "needs": []
    },
    {
      "id": "categorise-my-longbook",
      "title": "Categorise my longbook",
      "group": "Setting up",
      "phrase": "Categorise my longbook",
      "result": "Every commitment without a category gets one. Your AI shows you the ones it guessed, so you can correct any.",
      "needs": []
    },
    {
      "id": "add-a-month-report-in-my-longbook",
      "title": "Add a month report in my longbook",
      "group": "Setting up",
      "phrase": "Add September month report in my longbook",
      "result": "The month's statements recorded, the charges they prove confirmed, and each account's balance set. longbook draws that month's report, with \"Not yet confirmed\" beside the total until every charge in it is confirmed.",
      "needs": []
    },
    {
      "id": "add-email-invoices-in-my-longbook",
      "title": "Add email invoices in my longbook",
      "group": "Keeping it current",
      "phrase": "Add email invoices in my longbook",
      "result": "This month's bank alerts, receipts, bills and money in from your mail, each recorded once, with nothing doubled.",
      "needs": [
        "mail"
      ]
    },
    {
      "id": "set-up-a-daily-longbook-email-round",
      "title": "Set up a daily longbook email round",
      "group": "Keeping it current",
      "phrase": "Go to longbook.app/mcp and set up a daily email round for my longbook, as longbook recommends",
      "result": "Your AI reads your mail every morning at 6 and records the money that moved, so your longbook stays current without you asking.",
      "needs": [
        "mail",
        "scheduler"
      ]
    },
    {
      "id": "add-document-invoices-in-my-longbook",
      "title": "Add document invoices in my longbook",
      "group": "Keeping it current",
      "phrase": "Add document invoices in my longbook",
      "result": "The invoices, receipts and bills you attach, or keep in the longbook documents folder, recorded as charges with the file's name in their notes, without doubling what your statements or mail already recorded.",
      "needs": []
    },
    {
      "id": "add-a-commitment-or-an-expense",
      "title": "Add a commitment or an expense",
      "group": "Keeping it current",
      "phrase": "Add a commitment in my longbook",
      "result": "A new commitment planned ahead, or a one-time expense recorded, on the account and for the person you name.",
      "needs": []
    },
    {
      "id": "what-do-i-pay-next",
      "title": "What do I pay next?",
      "group": "Reading it",
      "phrase": "What's my next commitment in my longbook",
      "result": "The next day you owe something, and what is due that day. Nothing in your longbook changes.",
      "needs": []
    },
    {
      "id": "what-is-coming-in-the-next-30-days",
      "title": "What is coming in the next 30 days?",
      "group": "Reading it",
      "phrase": "What is coming in my longbook in the next 30 days?",
      "result": "Everything due in the next 30 days, or the period you name, by day, with the total per currency. Nothing in your longbook changes.",
      "needs": []
    },
    {
      "id": "what-did-i-spend-in-a-month",
      "title": "What did I spend in a month?",
      "group": "Reading it",
      "phrase": "What did I spend in July in my longbook?",
      "result": "What you spent in that month, and what is not yet confirmed. Through the Mac connection this comes from longbook's report; otherwise from your charges, including ones nobody has confirmed yet. Nothing in your longbook changes.",
      "needs": []
    },
    {
      "id": "what-renews-this-year",
      "title": "What renews this year?",
      "group": "Reading it",
      "phrase": "What renews this year in my longbook?",
      "result": "Your yearly, half-yearly and quarterly payments due in the next 12 months, or before the day you name, in date order. Nothing in your longbook changes.",
      "needs": []
    },
    {
      "id": "make-my-weekly-longbook-page",
      "title": "Make my weekly longbook page",
      "group": "Reading it",
      "phrase": "Make my weekly longbook page",
      "result": "One page for the week: what is due in the next 7 days, what you spent last week, what renews in the next 30 days, and anything waiting for you. Nothing in your longbook changes.",
      "needs": []
    },
    {
      "id": "create-a-report-in-my-longbook",
      "title": "Create a report in my longbook",
      "group": "Reports",
      "phrase": "Create a report in my longbook",
      "result": "A report over the days, accounts, people and categories you choose, on the Reports screen the next time the app is open. It counts confirmed charges only.",
      "needs": []
    },
    {
      "id": "make-a-report-of-my-trip",
      "title": "Make a report of my trip",
      "group": "Reports",
      "phrase": "Make a report of my Goa trip in my longbook",
      "result": "A report of your trip's spending on the Reports screen, filling in as the trip's charges are confirmed.",
      "needs": []
    },
    {
      "id": "tidy-my-longbook",
      "title": "Tidy my longbook",
      "group": "Housekeeping",
      "phrase": "Tidy my longbook",
      "result": "Doubled charges, bank-style names and odd categories found, and fixed once you approve each change.",
      "needs": []
    },
    {
      "id": "set-up-a-morning-brief",
      "title": "Set up a morning brief",
      "group": "Housekeeping",
      "phrase": "Set up a morning brief for my longbook",
      "result": "A short summary every morning of what is due today and in the next 30 days. Nothing in your longbook changes.",
      "needs": [
        "scheduler"
      ]
    },
    {
      "id": "find-subscriptions-from-my-email",
      "title": "Find subscriptions from my email",
      "group": "From your email",
      "phrase": "Find subscriptions from my email and add them in my longbook",
      "result": "Your subscriptions found in the last 12 months of your mail: each one with its receipts as charges, the ones that clearly repeat planned ahead, and trials and price changes as the mails state them.",
      "needs": [
        "mail"
      ]
    },
    {
      "id": "find-expenses-from-my-email",
      "title": "Find expenses from my email",
      "group": "From your email",
      "phrase": "Find expenses from my email and add them in my longbook",
      "result": "This month's spending from your mail's bank alerts and receipts, each recorded once as a paid charge.",
      "needs": [
        "mail"
      ]
    },
    {
      "id": "find-invoices-from-my-email",
      "title": "Find invoices from my email",
      "group": "From your email",
      "phrase": "Find invoices from my email and add them in my longbook",
      "result": "The invoices and receipts in the last 12 months of your mail, each recorded once as a charge, paid or due.",
      "needs": [
        "mail"
      ]
    },
    {
      "id": "find-insurances-from-my-email",
      "title": "Find insurances from my email",
      "group": "From your email",
      "phrase": "Find insurances from my email and add them in my longbook",
      "result": "Your insurance policies found in your mail: each with its premiums as charges, its next premium planned, and its policy details in its notes.",
      "needs": [
        "mail"
      ]
    },
    {
      "id": "find-purchases-from-my-email",
      "title": "Find purchases from my email",
      "group": "From your email",
      "phrase": "Find purchases from my email and add them in my longbook",
      "result": "Your orders from the last 3 months of your mail, each recorded once as a paid charge named for what you bought.",
      "needs": [
        "mail"
      ]
    },
    {
      "id": "find-bills-from-my-email",
      "title": "Find bills from my email",
      "group": "From your email",
      "phrase": "Find bills from my email and add them in my longbook",
      "result": "Your bills from the last 3 months of your mail: what you paid recorded, what is due planned, and each card's monthly bill kept apart from spending.",
      "needs": [
        "mail"
      ]
    },
    {
      "id": "find-loans-and-emis-from-my-email",
      "title": "Find loans and EMIs from my email",
      "group": "From your email",
      "phrase": "Find loans and EMIs from my email and add them in my longbook",
      "result": "Your loans and EMIs found in your mail: each loan with its EMIs as charges, planned ahead to its end when the mails state it.",
      "needs": [
        "mail"
      ]
    },
    {
      "id": "find-travel-bookings-from-my-email",
      "title": "Find travel bookings from my email",
      "group": "From your email",
      "phrase": "Find travel bookings from my email and add them in my longbook",
      "result": "Your bookings from the last 12 months of your mail: flights, trains, hotels and cabs, each recorded as a paid charge with where and when.",
      "needs": [
        "mail"
      ]
    },
    {
      "id": "find-money-in-from-my-email",
      "title": "Find money in from my email",
      "group": "From your email",
      "phrase": "Find money in from my email and add them in my longbook",
      "result": "Your salary, refunds, interest and other money in from the last 12 months of your mail, kept apart from spending.",
      "needs": [
        "mail"
      ]
    }
  ],
  "recipes": {
    "import_statement": {
      "title": "Add my statement",
      "the_owner_says": "Add my statement in my longbook",
      "from_the_documents_folder": "Update my longbook from my statements",
      "same_text": "read_guide \"add-my-statement\""
    },
    "daily_email_round": {
      "title": "The daily longbook email round",
      "the_owner_says": "Go to longbook.app/mcp and set up a daily email round for my longbook, as longbook recommends",
      "needs": [
        "this longbook connector, with suggestions switched on",
        "a connector that reads the owner’s mail",
        "a scheduled task, daily at 06:00 in the owner’s time zone"
      ],
      "same_text": "read_guide \"daily-email-round\"",
      "instructions": "## The daily email round — a scheduled look at the owner's mail\n\nThe owner may ask you to \"set yourself up for a daily longbook email round\". It is one\nscheduled task: each morning you read the last day of their mail for money that moved, or\nis about to, and write what you find into longbook. Everything in \"Unattended runs\" holds;\nthis chapter is the method.\n\n**Setting it up, once.**\n\n1. Call `whoami`. If `can_write` is false, say its `how_to_enable` sentence and stop there.\n2. Check that you can read the owner's mail. If you cannot, say so: the round needs a mail\n   connector.\n3. Create a scheduled task that runs every day at 06:00 in the owner's time zone. Its\n   instructions: call `read_guide` with \"daily-email-round\" on the longbook connector, then\n   do \"Each run\" as that chapter says — so every morning reads the method as it stands that\n   day. If your app has no scheduled tasks, say so, and offer to run the round whenever the\n   owner asks for it.\n4. Run the first round now, so the owner sees in Changes what a morning looks like.\n\n**One month, on request.** \"Add email invoices in my longbook\" is the same reading, once,\nover the calendar month so far — no scheduled task. `whoami`, then steps 2 to 7 of \"Each\nrun\", with the search window the month's first day to today, taking the kinds of mail below\nas they stand, a bill due ahead included at its due date. Report once and stop.\n\n**Each run.**\n\n1. `whoami` first. Anything under `failed_to_apply` is earlier work that did not land: fix\n   it with the real names and file it again under the same `idempotency_key`.\n2. Search the mail since the last round — the last 24 hours, or 36 to be safe — for the\n   kinds of mail listed below.\n3. Read the memory before you write: `accounts` gives every account with its type, its\n   notes (a card's name among them), last four digits and next unpaid charge, and\n   `members` every person with their tag; `commitments` gives what still lies ahead with\n   its price, cycle, category, account and person; `charges` with `from` set to the\n   first day you are looking back over gives what is already written, card bills among\n   them. A mail whose amount and day are already there was written by an earlier round, or\n   by a bank message: skip it. This look is what stops a twin; an idempotency key only\n   protects a retry of the same call.\n4. The fields longbook takes are in each write tool's input schema and in the chapter\n   \"shape\": a name, an amount with its currency, a date, `billing_cycle`, `paid`, `basis`,\n   `direction` for money in and for a transfer, and, when the mail says so, a category, an\n   `account`, a `member` and a short note.\n5. From each mail draw exactly those. The amount and its currency. The day the money moved,\n   which is not always the day the mail arrived. Who it went to or came from, as a plain\n   name: \"Corner Grocery\", never the payment handle, never \"UPI transfer\". The account in\n   the memory whose name carries the last four digits the mail shows. A category from\n   `tags`, the library's before a new one. A person only when the memory holds them.\n6. Write each mail once, in a call of its own, so it carries its own\n   `source: \"gmail:<message id>\"` and an `idempotency_key` built from that id. `search` the\n   payee first. Money that belongs to a commitment the memory holds goes on it with\n   `add_charges`: the open charge's own date, `paid: true`, the amount, `paid_on` and\n   `basis: \"email\"` — a row already there for that day is updated in place — and a new date\n   only when no charge is open. Anything else is an `add_commitments` row of its own,\n   `basis: \"email\"`, paid or unpaid as its kind below says.\n7. End with one short report: how many rows, which mails you skipped and why in a line,\n   what needs the owner's word (a row that looks recurring, an account the memory does not\n   hold), and that it is waiting in Changes. Then stop.\n\n**The kinds of mail, and what each becomes.**\n\n- **A bank's debit alert** — UPI, card, net banking: \"Rs.250.00 is debited from your account\n  ending 1234 towards VPA shop@bank (SHOP NAME) on 05-08-26\". A charge: a one-time row at\n  its real date and amount, paid. The name is the shop or person the mail names, the words\n  in brackets after the handle. When the payee is a commitment the memory holds — a\n  subscription, a premium, a salary the owner pays — the alert is that commitment's charge,\n  never a twin beside it.\n- **A card alert** — \"Thank you for using your Debit Card ending 1234 for Rs. 1,499.00 at\n  MERCHANT on 07-08-2026\". The same.\n- **An autopay or e-mandate chain** — a consent request with an OTP, \"consent received\",\n  \"deducted from your account\", \"successfully paid\": four mails, one payment. Write it\n  once, from the mail that says the money left. A consent request, an OTP and a pre-debit\n  notice are not charges; when a pre-debit notice names a new amount for a commitment the\n  memory holds, say so in the report.\n- **A receipt or an invoice from the seller** — a subscription receipt, a service invoice,\n  a ride, a food order. Usually the same money a bank alert already reported: match on the\n  amount and the day, write it once, and let the receipt give the better name, the category\n  and the note. A receipt paid in cash has no bank alert and is a charge of its own.\n- **A bill that is due** — \"payment is due for your service, amount 1,800.00\". When the\n  memory holds the commitment, the bill is its next charge: check that charge's date and\n  amount, and add nothing. When it does not, a one-time row, `paid: false`, at its due date;\n  when its payment shows in a later round, that row is the one to mark paid.\n- **Money in** — salary, a refund, interest, cashback, a dividend, once the bank says it was\n  credited: a one-time row with `direction: \"in\"`, paid. A notice that a refund is on its\n  way is not the credit.\n- **An insurance or renewal notice** — when the memory holds the commitment, check its next\n  charge's date and amount against the notice; correct the charge only when the notice comes\n  from the payee itself, and otherwise say what differs in your report. When the memory does\n  not hold the commitment, add a one-time row, unpaid, at the due date, and propose the cycle\n  in your report.\n- **A credit card statement is ready** — ONE one-time row named for the card and the month\n  it falls due, \"<Card> bill, <month> <year>\" (\"Coral card bill, October 2026\"),\n  `direction: \"transfer\"`, `paid: false`, at the due date, the amount the total due,\n  `account` the card when the memory holds it, `basis: \"email\"`. The month is in the name\n  because a card has a bill every month: each is its own row, and a name two rows share\n  cannot address either. Never spending: every spend on that card is a charge already, and\n  the bill that pays them is a movement between the owner's own accounts — a transfer is\n  shown and counted in nothing. A bill already written for that month is corrected in\n  place, never doubled. Import the statement's own lines, by \"importing-statements\", only\n  when the owner has asked for card statements to be imported.\n- **A card bill payment** — it settles the transfer row for that month: find it with\n  `search`, and address it by id. `add_charges` on that row, its own date,\n  `paid: true`, `paid_on` the day the money moved, the amount paid, `basis: \"email\"` —\n  updated in place. Skip it only when the memory holds no such row.\n- **A wallet top-up, a transfer to the owner's own account, an investment bought** —\n  movements. Skip them, unless the memory holds the payment as a commitment of its own — a\n  monthly investment plan, a standing transfer — and then it is that commitment's charge.\n- **Skip entirely** — OTPs, sign-in alerts, offers and newsletters, brokerage and mutual\n  fund statements, margin and portfolio reports, tax intimations, forex mark-up notices\n  (the fee arrives as its own debit), and anything that names no amount.\n\nThe last four digits of a card or an account are for matching only. Never write a card\nnumber, an account number, an OTP or a tax id anywhere.\n"
    }
  },
  "first_call": "whoami — who you are, what you may do, what is waiting, what failed to apply, and where the schemas live",
  "idempotency": "Send idempotency_key on every hosted write, built from the source and the write — {source id}:{tool}:{short payload hash}. Never put a date in it."
}