Skip to content
Truenote

API reference

Every /v1 route, from the OpenAPI spec the server generates.

Base URL https://app.usetruenote.com/v1. Send Authorization: Bearer <token>: a personal access token from Connected agents, or an OAuth access token. The spec is at https://app.usetruenote.com/v1/openapi.json; this page is drawn from it at build time.

curl https://app.usetruenote.com/v1/overview -H "Authorization: Bearer tn_pat_…"
  • Money is integer cents; days are YYYY-MM-DD; transaction amounts keep Plaid's sign (positive is money out).
  • Lists page by keyset: pass the cursor an answer gives you back. Transactions take updatedSince (the last answer's syncedAt) for what changed, with deleted ids.
  • Writes are POST /v1/tools/<name>, the MCP tool's own body in dollars.
  • Errors are application/problem+json: the code, what went wrong and what fixes it (errors); 403 lists the scopes missing; 404 is also what someone else's row looks like.
  • 240 requests a minute per token. Changes are additive; deprecations are announced in the changelog.

New to it? Your first script goes from a token to a copy that keeps itself in step.

Reads

Plain JSON with amounts in integer cents. Lists page by keyset; /v1/transactions and /v1/investment-transactions take updatedSince for what changed since your last pull.

GET /v1/overview

Net worth, this month, the plan, bills, goals and the next estimated tax in one call

Needs any read scope. Accounts and entities the token excludes are invisible here, totals included.

Responses: 200, 400, 401, 403, 404

GET /v1/accounts

Accounts with latest balances (debts negative), manual accounts and net worth totals

Needs accounts:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
type query depository | credit | loan | investment | other Only accounts of this type
includeHidden query string Include closed accounts, and accounts the person hid if this token may see hidden accounts

Responses: 200, 400, 401, 403, 404

GET /v1/transactions

Transactions newest first, filtered, keyset-paged (money in positive); updatedSince for what changed since your last pull, with the ids deleted

Needs transactions:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
from query string First day (inclusive); default: all history
to query string Last day (inclusive)
accountId query uuid
categoryId query uuid | none A category id (its subcategories included), or `none` for uncategorized
entityId query uuid
merchant query string Merchant or description contains this (any case)
q query string Description, merchant or note contains this
minAmountCents query integer | null Size at least this, in cents (either direction)
maxAmountCents query integer | null
direction query in | out Money in or money out
pending query string
ownerContribution query string true: only a business's spending paid from a personal account
limit query integer Rows per page (default 50, max 200)
cursor query string `nextCursor` of the last page
updatedSince query date-time Only rows added or changed at or after this instant (the syncedAt of your last pull), and `deleted` lists the ids gone since. Keeps a copy in step without re-reading everything; apply by id (a row may repeat).

Responses: 200, 400, 401, 403, 404

GET /v1/spending

Spending grouped by category, merchant, entity or month: the household's by default (scope=all adds the businesses; entityId picks one)

Needs transactions:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
from query string Default: the first of this month
to query string Default: today
groupBy query category | merchant | entity | month
scope query household | all household (default): the personal entities, as the overview's month counts them; all: the household and every business together. Ignored with entityId
entityId query uuid Only this entity's spending (one business)
categoryId query uuid Only this category (and its subcategories)
limit query integer Groups to list (default 25); the rest are summed

Responses: 200, 400, 401, 403, 404

GET /v1/recurring

Recurring bills, subscriptions and income with next dates and monthly totals

Needs transactions:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
direction query inflow | outflow
includeInactive query string

Responses: 200, 400, 401, 403, 404

GET /v1/net-worth-history

Net worth over time by account type (day, week or month points)

Needs accounts:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
from query string Default: a year ago
to query string Default: today
interval query day | week | month

Responses: 200, 400, 401, 403, 404

GET /v1/holdings

Investment positions by account, optionally the latest investment transactions

Needs investments:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
accountId query uuid
includeTransactions query string Also the latest investment transactions (buys, sells, dividends)

Responses: 200, 400, 401, 403, 404

GET /v1/investment-transactions

Investment transactions newest first, filtered by date, account, type, subtype or symbol, keyset-paged, with totals by kind (cash in positive); updatedSince for what changed since your last pull, with the ids deleted

Needs investments:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
from query string First day (inclusive); default: all history
to query string Last day (inclusive)
accountId query uuid
type query buy | sell | cash | fee | transfer | cancel
subtype query string Plaid's subtype, any case: dividend, interest, buy, sell, deposit…
symbol query string Ticker, any case
q query string Description contains this
limit query integer Rows per page (default 50, max 200)
cursor query string `nextCursor` of the last page
updatedSince query date-time Only rows added or changed at or after this instant (the syncedAt of your last pull), and `deleted` lists the ids gone since. Keeps a copy in step without re-reading everything; apply by id (a row may repeat).

Responses: 200, 400, 401, 403, 404

GET /v1/liabilities

Cards and loans: owed, APR, minimums, due dates

Needs liabilities:read. Accounts and entities the token excludes are invisible here, totals included.

Responses: 200, 400, 401, 403, 404

GET /v1/context

Profile, entities, income sources, goals, categories, rules, notes, plans

Needs context:read. Accounts and entities the token excludes are invisible here, totals included.

Responses: 200, 400, 401, 403, 404

GET /v1/plan

The spending plan: regular take-home split into groups and lines

Needs context:read. Accounts and entities the token excludes are invisible here, totals included.

Responses: 200, 400, 401, 403, 404

GET /v1/plan/actuals

Plan vs actual for a month: household spending per plan group and category line against the plan, the trailing 3 months' average, and spending no line names

Needs context:read, transactions:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
month query string A month like 2026-10; default this month

Responses: 200, 400, 401, 403, 404

GET /v1/taxes/projection

The year's federal tax projected from today: the statement line by line, the refund or amount owed, the extra withholding per paycheck that breaks even, the safe harbor

Needs context:read, transactions:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
year query integer

Responses: 200, 400, 401, 403, 404

GET /v1/tasks

The checklist: open tasks by due date (status=done|all for the rest)

Needs context:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
status query open | done | all

Responses: 200, 400, 401, 403, 404

GET /v1/buckets

Buckets: each account's virtual split, balances from Truenote's ledger, targets and what's Unassigned

Needs accounts:read, context:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
accountId query uuid Only this account's buckets
includeArchived query string Also archived buckets
recentEntries query integer | null Latest ledger entries per bucket (default 3)

Responses: 200, 400, 401, 403, 404

GET /v1/review-items

The Review queue: deposits to split, spending that looks like a bucket's, money taken out of a bucketed account, each with a proposal

Needs accounts:read, transactions:read, context:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
status query open | resolved open (default): waiting for a decision; resolved: the latest decided

Responses: 200, 400, 401, 403, 404

GET /v1/bucket-rules

Bucket rules: spending they charge to a bucket automatically, first match wins

Needs accounts:read, context:read. Accounts and entities the token excludes are invisible here, totals included.

Responses: 200, 400, 401, 403, 404

GET /v1/documents

Documents the person uploaded (paystubs, tax forms, statements), newest first

Needs documents:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
q query string Words in the name, notes or text (Social Security and account numbers masked)
kind query paystub | w2 | form_1099 | form_1098 | tax_return | benefits | statement | receipt | other
taxYear query integer

Responses: 200, 400, 401, 403, 404

GET /v1/documents/{id}

One document's text (masked, paged by offset); an image's bytes are at /file

Needs documents:read. Accounts and entities the token excludes are invisible here, totals included.

ParameterInTypeMeaning
id * path uuid Id
offset query integer | null Where to continue a long text (the last call's nextOffset)

Responses: 200, 400, 401, 403, 404

GET /v1/documents/{id}/file

A document's file as uploaded (unmasked)

Needs documents:read.

ParameterInTypeMeaning
id * path uuid Id

Responses: 200, 401, 403, 404

GET /v1/export

Everything this token can see, every table, as JSON (truenote-export/1)

Needs accounts:read, transactions:read, investments:read, liabilities:read, context:read. Accounts and entities the token excludes are invisible here, totals included.

Responses: 200, 400, 401, 403, 404

GET /v1/export.zip

The same export as a zip of CSV files, one per table

Needs accounts:read, transactions:read, investments:read, liabilities:read, context:read.

Responses: 200, 401, 403

Writes

Every MCP write tool as POST /v1/tools/<name>, with the tool's own body: amounts in dollars (650 for $650.00), ids from the reads, the answer in cents. Truenote's own records only; nothing here moves money.

POST /v1/documents

Keep a document for the person (multipart: file + metadata, a SaveDocument as JSON)

Needs documents:write. A PDF, PNG, JPEG or WebP image, at most 10 MB; the same file twice is a 409. Its entity, income source and account must be ones this token can see. Example: curl -H 'Authorization: Bearer tn_pat_…' -F file=@paystub.pdf -F 'metadata={"name":"Paystub Oct 16","kind":"paystub","taxYear":2026}' <origin>/v1/documents

Body fieldTypeMeaning
file * binary
metadata * string

Responses: 201, 400, 401, 403, 409, 413

POST /v1/tools/update_tax_settings

Set the tax year's household figures

Needs context:write. The same operation as the MCP tool update_tax_settings; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Change the household's figures for a tax year that the projection (get_tax_projection) can't find in the accounts: priorYearTax (last year's total tax, Form 1040 line 24) and priorYearAgi (for the 100%/110% safe harbor), otherPayments (estimated payments made from accounts Truenote can't see, this year's), priorYearPayments (of the IRS payments get_tax_projection found this year, the dollars that paid last year's return, a balance due or an extension payment, so they don't count toward this year), otherIncome (this year's interest, ordinary dividends, short-term gains and other ordinary income; negative for a net capital loss, up to 3000), capitalGains (this year's long-term capital gains and qualified dividends, taxed at 0/15/20%; both count toward the 3.8% net investment income tax), itemizedDeductions (used only when larger than the standard deduction), qualifyingChildren (children under 17 at year end; null: counted from the profile's household, a baby due by December 31 included) and notes. Only the fields you pass change; null clears. Per-job and per-business figures belong on the income source (upsert_income_source). Changes the person's data: say what you set.

Body fieldTypeMeaning
year integer Default: this one
priorYearTax number | null
priorYearAgi number | null
otherPayments number
priorYearPayments number
otherIncome number
capitalGains number
itemizedDeductions number | null
qualifyingChildren integer | null
notes string

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/upsert_goal

Set a goal

Needs context:write. The same operation as the MCP tool upsert_goal; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Create or change a savings or payoff goal: by id, else by name (case-insensitive), else new (then kind and targetAmount are required). links are what counts toward it, each with the percent of it that counts: an account (accountId from list_accounts), a manual account (manualAccountId from list_accounts' manualAccounts: the house, an HSA) or a bucket (bucket: a name or id from list_buckets, for money kept in a savings bucket like an emergency fund); exactly one per link; giving links replaces them. Changes the person's data: say what you set.

Body fieldTypeMeaning
id uuid
name * string
kind emergency_fund | savings | debt_payoff | purchase | tax_reserve | other
targetAmount number Dollars
targetDate string | null
priority integer 1 is the most important
notes string
archived boolean
links object[]

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/upsert_manual_account

Add or update a manual account

Needs context:write. The same operation as the MCP tool upsert_manual_account; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Something no bank connection reports, kept by hand: the house (asset, type real_estate) and its mortgage (liability, type loan), a car loan, an HSA, IRA or pension held elsewhere (type retirement), private company stock or a startup stake (type equity), cash. By id, else by name (any case), else new: a new one needs kind and balance. balance is dollars, positive: what it's worth, or what's owed on a liability; it's recorded on balanceOn (default today, never a day still to come) and replaces that day's entry, so call it again with a new balance to update the value. These count in net worth and appear in list_accounts' manualAccounts; a goal can link them (upsert_goal links with manualAccountId). archived: true hides one that's gone (a sold car). What you leave out stays. Truenote's records only. Tell the person what you set.

Body fieldTypeMeaning
id uuid
name * string
kind asset | liability
type string real_estate, vehicle, loan, retirement, equity, cash, crypto, other
entityId uuid | null Household (null) or a business
notes string
archived boolean
balance number
balanceOn string The day the balance is for (default today)

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/upsert_income_source

Set an income source

Needs context:write. The same operation as the MCP tool upsert_income_source; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Create or change an income source (a W-2 job, a 1099 client, a business, a bonus): by id, else by name, else new (then type and cadence are required). expectedAmount is take-home per payment (a paycheck's net pay). regular sources (salaries; the default for w2) are the spending plan's income; anything else (a bonus, 1099 work) is a windfall allocated when it lands. earner is who in the household earns it. taxSetAsidePct is the share of each payment to set aside for taxes (30 for 30%); payerMatch is text its deposits carry ("NORTHSTAR") so Truenote can find them. For the yearly tax projection (get_tax_projection), a W-2 job takes grossPay (this year's salary before deductions, bonus apart), retirement (401(k)/403(b) for the year), otherPretax (health, HSA, FSA through payroll for the year), withheldYtd with withheldAsOf (federal income tax withheld so far and the date of the paystub it's from, both or neither), withholdingPerPaycheck (federal tax withheld from each regular paycheck), bonus, bonusOn (paid or expected) and bonusWithholdingPct (default 22); a 1099 client or business takes receivedElsewhere (paid this year into accounts Truenote can't see), expectedRestOfYear (still expected this year; null: from expectedAmount) and expensesEstimate (deductible expenses for the year; null: its entity's spending). Fields you leave out keep their value; null clears. Changes the person's data: say what you set.

Body fieldTypeMeaning
id uuid
name * string
type 1099 | w2 | business | other
cadence weekly | biweekly | semimonthly | monthly | quarterly | annually | irregular
expectedAmount number | null Dollars per payment
taxSetAsidePct number
payerMatch string | null
entityId uuid | null
earner string | null A household member's name
regular boolean Counts toward the spending plan's income (default: true for w2)
active boolean
notes string
grossPay number | null W-2: this year's salary, bonus apart
retirement number W-2: 401(k)/403(b) deferrals for the year
otherPretax number W-2: health, HSA, FSA through payroll, per year
withheldYtd number | null W-2: federal income tax withheld so far this year (from a paystub)
withheldAsOf string | null The paystub's date (with withheldYtd)
withholdingPerPaycheck number | null W-2: federal income tax withheld per regular paycheck
bonus number W-2: this year's bonus
bonusOn string | null When the bonus was or will be paid
bonusWithholdingPct number Default 22
receivedElsewhere number 1099/business: paid this year into accounts Truenote can't see
expectedRestOfYear number | null 1099/business: still expected this year (null: from expectedAmount)
expensesEstimate number | null 1099/business: deductible expenses for the year (null: the entity's spending)

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/update_profile

Update the household profile

Needs context:write. The same operation as the MCP tool update_profile; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Change the profile every agent reads first: the person's name, household members (each with a relationship, and a birthDate or, for a baby on the way, a dueDate), state (postal code like TX), filing status, risk tolerance, the free-form "about us" and time zone. Only the fields you pass change. householdMembers replaces the whole list: read get_context first and send everyone, including the person themself (relationship self). Changes the person's data: say what you set.

Body fieldTypeMeaning
name string What to call the person
householdMembers object[]
state AL | AK | AZ | AR | CA | CO | CT | DE | DC | FL | GA | HI | ID | IL | IN | IA | KS | KY | LA | ME | MD | MA | MI | MN | MS | MO | MT | NE | NV | NH | NJ | NM | NY | NC | ND | OH | OK | OR | PA | RI | SC | SD | TN | TX | UT | VT | VA | WA | WV | WI | WY | null
filingStatus single | married_joint | married_separate | head_of_household | qualifying_surviving_spouse | null
riskTolerance conservative | moderate | aggressive | null
about string
timeZone string IANA zone, e.g. America/Chicago

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/upsert_entity

Set an entity

Needs context:write. The same operation as the MCP tool upsert_entity; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Add or rename an entity money can belong to: the household (personal) or a business (an LLC, a side business). By id, else by name (case-insensitive), else new (then kind is required). Assign accounts and transactions to it with create_rule or categorize_transactions; each business's money is then reported on its own. Changes the person's data: say what you set.

Body fieldTypeMeaning
id uuid An existing entity to rename
name * string
kind personal | business personal or business

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/upsert_plan_line

Set a spending plan line

Needs context:write. The same operation as the MCP tool upsert_plan_line; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Add or change a line of the spending plan (read get_plan first): by id, else by name (case-insensitive), else new (then group and amount are required). group is a group's name or id; amount is dollars per payment, cadence how often (default monthly; a $139 yearly Amazon Prime is amount 139, cadence annually). endsOn is the last day it's paid (a car loan's final month); startsOn delays a line that hasn't begun (daycare from April). match is text its payment's bank description or merchant contains ("Valon" for the mortgage, "CoServ" for electricity): a line with match and no destination is a bill, paid when a payment out contains it (find the text with query_transactions or get_recurring). accountId (from list_accounts) is where saved money goes (a brokerage: its own deposits count); bucketId (from list_buckets) is the bucket it fills ("Vacation / Travel" → the Vacations bucket), which also sets the account. categoryId (from get_context) only breaks everyday spending down (Groceries, Dining). Anything you leave out stays as it is. To cancel something, delete_record with kind plan_line. Changes the person's data: say what you set.

Body fieldTypeMeaning
id uuid
name * string
group string A group's name or id
amount number Dollars per payment
cadence weekly | biweekly | semimonthly | monthly | quarterly | annually
startsOn string | null
endsOn string | null The last day it's paid
categoryId uuid | null
accountId uuid | null
bucketId uuid | null A bucket it fills (from list_buckets); the line then goes to its account
match string | null Text its payment's bank description or merchant contains; null clears it
notes string

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/upsert_plan_group

Set a spending plan group

Needs context:write. The same operation as the MCP tool upsert_plan_group; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Add, rename or retarget a group of the spending plan: by id, else by name (case-insensitive), else new (then kind is required: spending for expenses and wants, saving for savings and investing). targetPct is the share of take-home it should hold (45 for 45%); the groups' targets usually add up to 100. Changes the person's data: say what you set.

Body fieldTypeMeaning
id uuid
name * string
kind spending | saving
targetPct number
position integer Order, 0 first

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/upsert_bucket

Set a bucket

Needs context:write, accounts:read. The same operation as the MCP tool upsert_bucket; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Create or change a bucket (a virtual part of a savings or checking account, see list_buckets): by id, else by name (case-insensitive; pass accountId when two accounts have a bucket with that name), else new (then accountId is required: a checking or savings account from list_accounts). target is the bucket's own goal in dollars and targetDate when it should be reached (both optional; null clears). startingBalance (new buckets only) is what it holds today; to correct an existing bucket's balance use record_bucket_entry with kind set_balance. overflow makes it the account's overflow bucket: what a deposit has left after filling what each bucket is due goes there (one per account; setting it moves it from another). archived hides a bucket once it's empty. This changes only Truenote's records, never the bank: no money moves. Say what you set.

Body fieldTypeMeaning
id uuid
name * string
accountId uuid The account it splits (required for a new bucket)
target number | null Dollars
targetDate string | null
notes string
overflow boolean Extra from deposits goes here
archived boolean
startingBalance number Dollars it holds today (new buckets only)

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/move_between_buckets

Move money between buckets

Needs context:write, accounts:read. The same operation as the MCP tool move_between_buckets; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Move an amount from one bucket to another bucket of the same account (by name or id from list_buckets), e.g. $500 from Emergencies to Vacations. Both buckets change in one step; the account's total and Unassigned stay the same (unless it takes the from bucket below zero: what it's short comes out of Unassigned). This is Truenote's bookkeeping only: no money moves at the bank. date defaults to today (it can't be in the future). Say what you moved.

Body fieldTypeMeaning
from * string A bucket's name or id
to * string A bucket's name or id, in the same account
amount * number Dollars
date string
memo string

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/record_bucket_entry

Record a bucket entry

Needs context:write, accounts:read. The same operation as the MCP tool record_bucket_entry; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Record money going into or out of a bucket (by name or id from list_buckets) in Truenote's ledger. kind: contribution (money put into the account for this bucket, e.g. a transfer to savings), withdrawal (money taken out of the account from this bucket), charge (spending paid from another account, like a vet bill on checking, that this bucket covers: it needs the transactionId, and the bucket drops now while the account's total drops when the money is moved back), or set_balance (a correction: the bucket holds exactly amount as of date). transactionId (from query_transactions) ties the entry to the bank transaction it accounts for. date defaults to today. This never moves money at the bank. When unsure which bucket something belongs to, ask the person rather than guess. Say what you recorded.

Body fieldTypeMeaning
bucket * string A bucket's name or id
accountId uuid When two accounts have a bucket with that name
kind * contribution | withdrawal | charge | set_balance
amount * number Dollars (for set_balance: the balance it holds)
date string
memo string
transactionId uuid The transaction it accounts for (needed for charge)

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/resolve_review_item

Decide a review item

Needs context:write, accounts:read, transactions:read. The same operation as the MCP tool resolve_review_item; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Decide an item from list_review_items for the person: decision approve does what its proposal says (or, with allocations, puts other amounts in other buckets: bucket names or ids, dollars; for a withdrawal, paysBack lists the charge entry ids it pays back instead of the proposal's), and createRule (a charge only, to one bucket) also saves the proposed bucket rule so the next match is automatic; decision dismiss leaves the transaction out of buckets for good (a deposit stays in Unassigned). Together the amounts can't be more than the transaction. Only decide what the person asked you to, or what's plainly right; otherwise ask. This is Truenote's bookkeeping: no money moves at the bank. Say what you decided.

Body fieldTypeMeaning
id * uuid The item's id from list_review_items
decision * approve | dismiss
allocations object[] Instead of the proposal's buckets and amounts
paysBack uuid[] A withdrawal: the charge entry ids it pays back, instead of the proposal's
createRule boolean A charge: also save the proposed bucket rule

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/create_bucket_rule

Create a bucket rule

Needs context:write, accounts:read. The same operation as the MCP tool create_bucket_rule; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. A rule that charges matching spending to a bucket automatically ("Banfield" → Pets), for everything since the bucket's start and every sync after: textContains is matched against the merchant and the bank's description (any case); accountId (from list_accounts), categoryId (from get_context) and minAmount/maxAmount (dollars) narrow it. Money out only. The first matching rule wins; a transaction the person already decided (charged by hand, an entry deleted, a review item answered) is left alone, and nothing is ever charged twice. Returns how many it charged right away. Prefer a specific text (a merchant's name) over a broad one: a wrong automatic charge is worse than a review item. Truenote's bookkeeping only: no money moves.

Body fieldTypeMeaning
bucket * string A bucket's name or id from list_buckets
bucketAccountId uuid When two accounts have a bucket with that name
textContains * string
accountId uuid Only spending on this account
categoryId uuid Only spending in this category
minAmount number
maxAmount number

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/add_note

Remember a fact

Needs context:write. The same operation as the MCP tool add_note; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Save a dated fact or decision for every agent to know later ("Lease ends 2027-06-30", "Decided to pay the Visa in full monthly"). Short tags (lowercase words) help find it. Use for durable context, not for chat history.

Body fieldTypeMeaning
body * string
occurredOn string | null The day it's about, if any
tags string[]
pinned boolean Always shown first in get_context

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/update_note

Change a note

Needs context:write. The same operation as the MCP tool update_note; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Change a note (an id from get_context): fix or rewrite its body when a fact or decision changes, re-date it, retag it, or pin or unpin it (pinned notes always come first in get_context). What you leave out stays. Prefer this over adding a second note that contradicts the first. Changes the person's data: say what you changed.

Body fieldTypeMeaning
id * uuid
body string
occurredOn string | null The day it's about; null: none
tags string[] Replaces the note's tags
pinned boolean

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/upsert_task

Add or change a checklist task

Needs context:write. The same operation as the MCP tool upsert_task; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Add a to-do to the person's checklist, or change one: by id, else by title (any case), else new. Use it for the dated actions a plan leaves behind ("Exercise options before the 409A", due 2026-11-06), one task per action, with a due date when there's a deadline. kind event is a dated thing that happens rather than something to do ("Brooke's due date", "JJ back to work", "Closing on the house"): it needs dueOn and has no checkbox. amount (dollars) is what it costs or brings when it's about money (the $15,000 a payment needs; it reads back as sum). done: true ticks it off, false opens it again. goalId (from get_context) or bucket (a name or id from list_buckets) say what it serves; null unlinks. spotlight: true makes it the Overview's countdown, at the top of the person's home page; there is only one, so this replaces the current one (list_tasks shows which). Spotlight the one dated event that matters most to the household's money in the coming months (a birth, a move, a wedding, a job change, a big payment being saved for), not routine to-dos; the person can change it. What you leave out stays. Truenote's records only: it never acts on the task. Tell the person what you added or changed.

Body fieldTypeMeaning
id uuid
kind task | event task (default) or event
title * string
dueOn string | null The day it's due (an event's day); null: no date
notes string
done boolean
amount number | null Dollars; null clears it
spotlight boolean The Overview's countdown; replaces the current one
goalId uuid | null
bucket string | null A bucket's name or id
bucketAccountId uuid When two accounts have a bucket with that name

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/save_plan

Save a plan

Needs context:write. The same operation as the MCP tool save_plan; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Save a named scenario for the person to review: a markdown body and structured assumptions (numbers, dates, lists) such as a parental-leave budget or a tax plan. By id, else by name, else new (status draft). Saving again with the same name updates it.

Body fieldTypeMeaning
id uuid
name * string
summary string
body string Markdown
assumptions object
status draft | active | archived

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/categorize_transactions

Recategorize transactions

Needs context:write, transactions:read. The same operation as the MCP tool categorize_transactions; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Set the category, entity (household or a business) or excluded flag of up to 200 transactions at once (ids from query_transactions, category and entity ids from get_context), and/or a note. A category, entity or exclusion set here wins over rules for those transactions. All of them change or none do. To fix a whole merchant going forward, use create_rule instead.

Body fieldTypeMeaning
transactionIds * uuid[]
categoryId uuid | null null: back to the automatic category
entityId uuid | null
excluded boolean true: leave out of spending and income
note string | null

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/create_rule

Create a categorization rule

Needs context:write, transactions:read. The same operation as the MCP tool create_rule; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. A rule that sets the category, entity or excluded flag of every matching transaction, past and future (except ones edited by hand), and/or a business share: businessShare splits each match, percent of it going to a business entity as a part of its own (the rest stays the household's), for a bill partly used for business (internet 60% to the LLC). Conditions all must hold: merchant or description contains text, an account, an amount range in dollars (by size), a direction (outflow is money out). Returns how many transactions it now decides. Prefer this over categorize_transactions or split_transaction for anything recurring.

Body fieldTypeMeaning
name * string
conditions * object
setCategoryId uuid | null
setEntityId uuid | null
setExcluded boolean | null
businessShare object | null
priority integer Lower runs first

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/split_transaction

Split a transaction

Needs context:write, transactions:read. The same operation as the MCP tool split_transaction; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Split one transaction (an id from query_transactions; a part's id means its whole) into parts that add up to it exactly, each with its own category, entity and note: the business part of a mixed purchase, a bill shared with the LLC. amount is each part's size in dollars (the whole's direction is kept). The parts count in place of the whole everywhere (spending, the plan, each business's taxes), and query_transactions lists them instead (with splitOf). Replaces any split it had; rules leave it alone afterwards. unsplit: true puts it back together. For a recurring share, use create_rule's businessShare instead. Changes the person's records only: say what you split.

Body fieldTypeMeaning
transactionId * uuid
parts object[]
unsplit boolean

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/prepare_upload

Prepare a document upload

Needs documents:write. The same operation as the MCP tool prepare_upload; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Get a one-time link to keep one file (a paystub, W-2, 1099, 1098, tax return, statement, receipt; PDF or image, 10 MB at most) in the person's documents. Returns curl, a command that sends a local file straight to Truenote (replace FILE with the path; the file never passes through you), and uploadUrl, a page where the person drops the file themselves (for when you can't reach their files). Say what it is: kind, taxYear, documentOn, and the incomeSourceId a paystub is for (from get_context); name, kind and taxYear left out are guessed from the file's name. One call per file; the link lasts 15 minutes. Then read_document can read it.

Body fieldTypeMeaning
name string Default: the file's name
kind paystub | w2 | form_1099 | form_1098 | tax_return | benefits | statement | receipt | other
taxYear integer
documentOn string The date on the document (a paystub's pay date)
entityId uuid Whose it is (default: the household)
incomeSourceId uuid The income source a paystub or W-2 is for
accountId uuid The connected account a statement is of
manualAccountId uuid The manual account (list_accounts) it's about: the mortgage, the HSA
notes string

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/update_document

Edit a document

Needs documents:write. The same operation as the MCP tool update_document; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Change what a kept document is and whose (an id from list_documents): name, kind, taxYear, documentOn (the date on it, e.g. a statement's date), entityId, incomeSourceId (the job a paystub or W-2 is for), accountId (a connected account a statement is of), manualAccountId (a manual account it's about, like the mortgage), notes. Fields you leave out keep their value; null clears. The file itself never changes, and documents can't be deleted here. Changes the person's records: say what you changed.

Body fieldTypeMeaning
id * uuid
name string
kind paystub | w2 | form_1099 | form_1098 | tax_return | benefits | statement | receipt | other
taxYear integer | null
documentOn string | null
entityId uuid | null
incomeSourceId uuid | null
accountId uuid | null
manualAccountId uuid | null
notes string

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/delete_record

Delete a record

Needs context:write. The same operation as the MCP tool delete_record; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Delete one of the person's records by kind and id: goal, income_source, entity (its accounts and transactions go back to the household; rules that set it go too), category (transactions in it fall back to Plaid's mapping; rules that set it go too), rule (what it decided goes back to the other rules), manual_account (with its balance history), plan, plan_group (only once it has no lines), plan_line (something canceled or entered by mistake; for one that ends later, set its endsOn with upsert_plan_line so the plan shows what frees up), task (to finish one, upsert_task with done: true instead), note (when a decision changes, add the new note, then delete the old one so agents don't read both), bucket (its money goes back to Unassigned; no money moves), bucket_rule, bucket_entry (undoes one ledger entry: a move's both sides; an automatic one isn't redone; an archived bucket's entries stay; needs accounts:read). Ids come from get_context, get_plan, list_tasks, list_accounts, list_buckets and list_bucket_rules. Documents use delete_document. It can't be undone: confirm with the person first (a note or entry you made in this conversation excepted), then say what you deleted.

Body fieldTypeMeaning
kind * goal | income_source | entity | category | rule | manual_account | plan | plan_group | bucket | bucket_rule | plan_line | task | note | bucket_entry
id * uuid

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/delete_document

Delete a document

Needs documents:write. The same operation as the MCP tool delete_document; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Permanently delete an uploaded document (an id from list_documents): the file and its text. Use when the person asks, or for a duplicate or wrong upload. It can't be undone: confirm with the person first.

Body fieldTypeMeaning
id * uuid

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/upsert_category

Add or change a category

Needs context:write. The same operation as the MCP tool upsert_category; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Add a spending category, or rename one, change its kind (expense, income, transfer: transfers don't count as spending) or nest it under a parent (parentId, null: top level): by id, else by name (any case), else new (then kind is required). Which of the bank's categories land in it stays the person's to set in Truenote. Then categorize_transactions or create_rule to fill it. Say what you changed.

Body fieldTypeMeaning
id uuid
name * string
kind expense | income | transfer
parentId uuid | null

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/update_rule

Change a categorization rule

Needs context:write, transactions:read. The same operation as the MCP tool update_rule; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Change a rule (an id from get_context): its name, conditions (given conditions replace them all; amounts in dollars by size), what it sets (category, entity, excluded; null clears), its businessShare (null removes it), priority (lower runs first) or active (false pauses it). What you leave out stays. Saving re-decides every transaction nobody edited by hand. To remove a rule, delete_record with kind rule.

Body fieldTypeMeaning
id * uuid
name string
conditions object
setCategoryId uuid | null
setEntityId uuid | null
setExcluded boolean | null
businessShare object | null
priority integer
active boolean

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/update_account

Change a bank account's settings

Needs context:write, accounts:read. The same operation as the MCP tool update_account; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Change how Truenote treats a connected account (an id from list_accounts): nickname (null: the bank's name again), hidden (true drops it out of totals, net worth and this connection; only the person can show it again unless this connection sees hidden accounts) or entityId (the business it belongs to; null: the household). Never touches the bank. Say what you changed.

Body fieldTypeMeaning
accountId * uuid
nickname string | null
hidden boolean
entityId uuid | null

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/reset_transactions

Undo transaction edits

Needs context:write, transactions:read. The same operation as the MCP tool reset_transactions; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Undo hand edits on transactions (ids from query_transactions): their category, entity, exclusion and any hand split go back to what the rules decide. Notes stay. Use when an earlier categorize_transactions or split_transaction was wrong.

Body fieldTypeMeaning
transactionIds * uuid[]

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/refresh_bank

Refresh a bank connection

Needs context:write, accounts:read. The same operation as the MCP tool refresh_bank; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Ask the bank for what's new on the connection holding an account (an id from list_accounts): balances and transactions, in the background (a minute or two). Use when the person says something posted that Truenote doesn't show yet. A connection that needs the person to sign in again can't be refreshed: tell them to reconnect it in Truenote.

Body fieldTypeMeaning
accountId * uuid

Responses: 200, 400, 401, 403, 404, 409

POST /v1/tools/disconnect_bank

Disconnect a bank

Needs context:write, accounts:read. The same operation as the MCP tool disconnect_bank; amounts in the body are dollars (650 for $650.00), ids come from the GET routes, and the answer is the tool's data with amounts in integer cents. Remove the bank connection holding an account (an id from list_accounts): Truenote stops syncing it, and every account on that connection and its history are deleted from Truenote. Truenote is read-only to banks: nothing changes at the bank itself. Connecting it again means the person signs in to the bank in Truenote. It can't be undone: confirm with the person first, naming every account it removes.

Body fieldTypeMeaning
accountId * uuid

Responses: 200, 400, 401, 403, 404, 409

* required