List account balances
well_list_account_balancesList every account on the workspace with its stored balance. Rows only — this tool holds no definition of cash, and returns no figure the app renders.
Use it when you are computing a cash figure whose RULES you are stating yourself: which accounts belong to the business, which account types count as cash, which stored field is "the balance", what each currency converts at. The server derives no cash position of its own from this call, so a cash figure starts here: state the rules, keep exactly the rows they admit, then put the result on a card with well_render_cash_position — or, when the answer is cash month by month rather than one total, with well_render_cash_forecast.
It applies no scope. Every active account comes back, including ones you will almost certainly exclude. ownership is workspace, counterparty or unknown, and it decides membership together with company_id:
workspace— the connector marks the row as workspace-owned. The server admits it to proven cash only when the company is in the proven own set, or when the row has no company id and no pairing can disprove the tag.counterparty— not the business's, unconditionally. A counterparty row's balance, or its absence, is never reported: anullthere is expected, not a missing reading, and itscash_exclusion_reasonsays nothing to report. Never report its balance gap or its state: do not call it missing, waiting, unclassified or syncing. At most count it.unknown— unsettled unless its company is in the proven own set.
own_company_id is null when the workspace has not set one. Nothing is settled against it then — no unknown row, and no workspace row's company pairing either — so say so rather than counting or dropping on a guess. This is the same three-way rule the app's own canvas account scope applies, and a figure that departs from it disagrees with the number the product shows.
owned_by_workspace is the server's proven verdict. Keep exactly the true rows as proven business accounts and do not re-derive the answer from ownership and company_id. cash_scope_certainty explains the other rows: estimated means the company is a self-candidate, so the row is an estimate and must stay out of proven cash; unproven means the own set did not prove it. A candidate estimate can be described separately, never added to the proven total.
It applies no type filter. account_type is one of deposit, credit, loan, investment, payroll, other. A credit or loan account is a liability, so its balance normally nets out of cash rather than adding to it — but that is your decision to state, not a fact about the row, and the sign stored is the sign the provider sent.
It returns both amounts. closing_booked is SETTLED cash; closing_value includes pending and uncleared movements. The two differ by every initiated-but-unsettled payment, so which one you total is the single most consequential choice a cash figure makes: state it. opening_booked is the fallback for a freshly-opened balance with no settled activity yet. Any of them is null when the stored value was absent or not a finite number, which is not a zero balance.
One field for every account. A balance per account is closing_booked, or opening_booked when closing_booked is null. Read that same field on every row, deposit, credit and loan alike. Never quote closing_value as one account's balance: on a credit or loan account it can hold the provider's available figure (the unused credit line), which is not a balance. Quote closing_value only when the person asks for available funds, and then for every account.
It converts nothing. Each reading carries its own currency, which can differ from the account's own account_currency. Convert per row at the stored rate for that currency as of the reading, read from exchange_rates and never estimated, then total — a sum across currencies is denominated in nothing and no field here would say it happened.
It lists each copy of an account. One physical account can arrive once per connector that syncs it. duplicate_of_account_id names the account a row is a second copy of, and is null on every other row. Leave a marked row out of every total and every count: the balance that counts is the named account's, which is the one the app's own figure reads, and the transactions of both copies are counted once. Total both copies and the figure holds that money twice. When the named account has no readable balance, report it as having none rather than taking the copy's reading in its place, or the figure departs from the app's. Marking follows the ownership rule above: only the business's own accounts are marked, and an unknown row only once own_company_id settles it.
institution_name is the bank that holds the account: the bank company Well identified, else the bank an accounting tool names for an account it syncs, else the connector that syncs the account. When the person names a bank ("how much at Chase"), keep the rows whose institution_name is that bank; account_name is the label the provider sends and often does not name the bank.
source_workspace_connector_id is the workspace_connector_id of the connection that syncs the row, or null when no connection does. A live read on that same connection (well_invoke_connector_tool, well_get_investment_holdings) reads the same money as the row: never add both.
synced_at is the source connection's last successful sync time, or null when the row has no live synced source. fresh_enough is true only when that sync was within the last 24 hours. When a row has a source connection and fresh_enough is false, do not present its balance as current: say it may be old or lack a successful sync, name synced_at when available, and offer a fresh sync when one is available. When source_workspace_connector_id and synced_at are both null, say that sync age cannot be assessed rather than calling the manual balance stale.
unlinked_accounts: true marks a row that carries ZERO weight in cash, and cash_exclusion_reason says why. It is present only on such a row, and its value is one of four:
unbound_mirror— a legacy state: a row the old name rule zeroed before any account evidence. Its weight returns when the one-time re-judge decides it; say it is waiting, not a duplicate.bound— an alias of another account in the list; that account, the keeper, carries the weight.pending— a row a bank account at the same institution could mirror, not yet decided by the pair judge. It counts for nothing until that is settled, so keep it out and, for a row the ownership rule admits, say it is waiting rather than calling it a duplicate.removed_connector— the source connection was removed and no other live connector links the account. Its history remains available, but it no longer counts in workspace cash.
A row with no cash_exclusion_reason is counted. Like a duplicate_of_account_id row, an excluded row is listed and flagged, never dropped: keep it out of every total and every count, and when you tell the reader what was left out, give its reason in plain words. Its balance still reads as stored.
balance is null when no row was selected for that account. Two different situations produce it and they must not be reported the same way: verification_rejected: true means the newest balance failed verification and the bounded walk back found no verified one, so the data is repudiated; false means the account simply has no history yet. This applies to the business's own accounts only: a counterparty row's null balance is expected, never a reading to report.
months_back adds month_ends to every row: one reading per complete month end, oldest first, keyed YYYY-MM, ending on the last COMPLETE month. This series is where a cash forecast starts — "what will our cash look like", "project our cash forward", "when do we hit zero" — and it is the settled half of well_render_cash_forecast; the projected half is that series' last settled month minus the burn you measure with well_sum_transactions. A null reading is a month no stored row covered — not a zero balance, so never plot it as one and never interpolate between two real points. Omit months_back for the current reading alone; the series is a second query and is not free.
partial: true means the read was cut short BEFORE RETURNING ANYTHING, so it always arrives with an empty rows — stop and offer to try again. own_set_truncated: true is different: rows did return, but the workspace's own-company set reached its row cap, so a company missing from that set may still be ours. Continue with the ownership verdicts the server gave, and mark any cash total partial: it is a floor over the known owned accounts. Pass own_set_truncated to well_render_cash_position; its returned partial must be stated. unreadable_rows is another separate case that continues: those rows have no usable balance and make any total a floor, by up to their count. An estimated row remains outside proven cash even when the own-set read is complete.
When the token authorizes one workspace, call this directly — no workspace call is needed first. When it authorizes several, this read will not guess which one you mean: pass workspace_id on the call.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| months_back | No | How many complete month ends to carry per account, oldest first. Omit for the current reading alone. | |
| workspace_id | No | Target workspace. Omit when the token authorizes one workspace. Required when it authorizes several: this read reports one workspace's own figures and will not choose which. | |
| conversation_id | No | The conversation id returned by the previous Well result, in its meta under well/conversation_id, in its structuredContent, or in its JSON text block. Pass it back on every call in the same conversation, including a call a card makes, so the chosen workspace and the earlier answers still apply. It decides the conversation on its own: nothing the host states about the session replaces it. Omit it only on the first call of a conversation. | |
| workspace_scope | No | Restrict this call to the family: the token's primary workspace plus the company workspaces under it that you are a member of. Unrelated authorized workspaces drop out of the read; row reads merge across the family, while a per-workspace figure still reports exactly one workspace. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| rows | Yes | ||
| error | No | ||
| partial | Yes | ||
| success | Yes | ||
| base_currency | Yes | ||
| own_company_id | Yes | ||
| conversation_id | No | The conversation this result belongs to. Pass it back as the conversation_id argument on every later Well call in the same conversation. | |
| newly_connected | No | Tools that connected to this workspace since the last result and that no session was told about yet, oldest first. Present only when at least one tool is new. The connected-line rule says when to tell an entry; when it does, one line per entry, in the person's language: "<name> is connected.". Never fold several entries into one sentence. Each entry comes back once, on one result only: never repeat its line later. | |
| unreadable_rows | Yes | ||
| own_set_truncated | Yes | ||
| resolved_workspace | No | The workspace that answered, when the caller named none and the token authorizes several. | |
| conversation_id_note | No | Present only when the server opened a fresh lane, stating that no choice recorded earlier was read. | |
| conversation_id_source | No | Where the conversation id came from: the host's own request meta, the caller's argument, or a fresh lane the server opened. |