# Glacier Planner tool reference

> Every tool the hosted MCP server at https://planner.glacierfinancial.ca/mcp offers, with its permission, parameters and plan limits. Generated from the server’s own tool definitions, so it matches what your assistant sees. Guide: https://planner.glacierfinancial.ca/docs/agents.

Parameters marked **required** must be sent. `workspaceId` always comes from `find_households` or `list_workspaces`. Ordinary saved-plan `idempotencyKey` values are optional; `start_usage_decision` requires a stable explicit key.

Yearly plans and optional per-decision usage are separate. The yearly plans are described on /pricing; a basic first projection is free at /calculate. Do not treat a closed usage checkout as a closed yearly-plan checkout. Before offering the separate CAD5 tax-inclusive timing package, check usage_budget.available or /usage: it can be disabled while yearly plans remain available. Completed results stay readable when usage purchases are closed.

## Index

| Tool | Permission | Reads or writes | Plan |
| --- | --- | --- | --- |
| `find_households` | `plan:read` | read | Free. |
| `list_workspaces` | `plan:read` | read | Free. |
| `list_households` | `plan:read` | read | Free. |
| `plan_summary` | `plan:read` | read | Free: the value meter (locked: true), except an exact calculator-imported revision with a basic-result grant. Yearly plan: the full summary. |
| `value_meter` | `plan:read` | read | Free on every plan. |
| `unlock_results` | `plan:write` | write | Yearly plan with a result limit. Ask the person first. |
| `explain_change` | `plan:read` | read | Free. |
| `show_plan` | `plan:read` | read | Yearly plan (results), or the exact calculator-imported revision with a basic-result grant. The grant does not cover advanced analyses or reports. |
| `run_projection` | `plan:read` | read | Yearly plan (results), or the exact calculator-imported revision with a basic-result grant. The grant does not cover advanced analyses or reports. |
| `inspect_year` | `plan:read` | read | Yearly plan (results), or the exact calculator-imported revision with a basic-result grant. The grant does not cover advanced analyses or reports. |
| `compare_projections` | `plan:read` | read | Yearly plan (results), or the exact calculator-imported revision with a basic-result grant. The grant does not cover advanced analyses or reports. |
| `describe_fields` | `plan:read` | read | Free. |
| `get_scenario` | `plan:read` | read | Free. |
| `update_scenario` | `plan:write` | write | Free. |
| `copy_scenario` | `plan:write` | write | Free: a baseline plus one alternative per household. More need a yearly plan. |
| `create_scenario` | `plan:write` | write | Free: a baseline plus one alternative per household. More need a yearly plan. |
| `scenario_history` | `plan:read` | read | Free. |
| `restore_revision` | `plan:write` | write | Free. |
| `create_household` | `plan:write` | write | Free: one household. More need a yearly plan. |
| `update_household` | `plan:write` | write | Free. |
| `household_history` | `plan:read` | read | Free. |
| `rename_household` | `plan:write` | write | Free. |
| `rename_scenario` | `plan:write` | write | Free. |
| `archive_household` | `plan:write` | write | Free. |
| `archive_scenario` | `plan:write` | write | Free. |
| `list_archived` | `plan:read` | read | Free. |
| `restore_household` | `plan:write` | write | Free: only while you are within one live household. |
| `restore_scenario` | `plan:write` | write | Free: only while the household has fewer than two live scenarios. |
| `run_decision` | `plan:read` | read | Free: a short preview. Yearly plan: the full result. |
| `find_plan_that_works` | `plan:read` | read | Free: a short preview. Yearly plan: the full result. |
| `usage_budget` | `plan:read` | read | Owner only. Free. Billing authority is established by the person in /usage. |
| `quote_usage_decision` | `analysis:run` | write | Owner only. No charge to quote. New package: CAD5 including tax; matching completed package: free reuse. Existing annual coverage uses its entitlement. |
| `start_usage_decision` | `analysis:run` | write | Owner only. Requires browser-established payment authority, remaining cap and a valid server quote; no additional annual-covered charge. |
| `usage_decision_status` | `plan:read` | read | Owner only. Free reads of owned operations and completed results, including at the cap or payment pause. |
| `cancel_usage_decision` | `analysis:run` | write | Owner only. No charge for cancellation before final publication; published work keeps its original charge. |
| `start_analysis` | `analysis:run` | write | Yearly plan (all four kinds). |
| `analysis_status` | `plan:read` | read | Free. |
| `cancel_analysis` | `analysis:run` | write | Free. |
| `preview_annual_review` | `plan:read` | read | Free. |
| `create_annual_review` | `plan:write` | write | Free. |
| `save_recommendation` | `plan:write` | write | Free. |
| `list_recommendations` | `plan:read` | read | Free. |
| `recommendation_history` | `plan:read` | read | Free. |
| `create_report` | `reports:create` | write | Yearly plan. |
| `prepare_report_pdf` | `reports:create` | write | Yearly plan. |
| `report_pdf_status` | `plan:read` | read | Free. |
| `cancel_report_pdf` | `reports:create` | write | Free. |
| `list_clients` | `plan:read` | read | Practice. |
| `list_members` | `plan:read` | read | Free. |
| `invite_member` | `plan:write` | write | Practice. |
| `remove_member` | `plan:write` | write | Free. |
| `share_household` | `plan:write` | write | Practice. |
| `unshare_household` | `plan:write` | write | Free. |

## Find

### `find_households`

**Find a household by name.** Search households by name across your workspaces; returns workspace, household and scenario ids.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this when the user names a person or household ("the Thompsons", "Linh") and you need its workspaceId, household id (clientId) and scenario ids. Searches every workspace you can access; an empty query lists everything (up to 25). Do not use it for plan numbers: follow with plan_summary or get_scenario. Example: {"query":"thompson"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `query` | string | optional | Part of a household or scenario name, case-insensitive. Empty lists all. Default: `""`. |
| `workspaceId` | uuid | optional | Limit to one workspace. |

### `list_workspaces`

**List workspaces.** List your workspaces and your role in each.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this when you need workspace ids and the user’s role in each: owner, editor or viewer (team, every household) or client (only the households shared with them, read only; sharedBy names the advisor or firm and their contact). Most requests can start with find_households instead, which also returns workspace ids. No inputs.

No parameters.

### `list_households`

**List households in a workspace.** List every household and its scenarios in one workspace.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this to list every household in one workspace with its scenarios (ids, names, revisions) and the household revision needed by update_household. To search by name across workspaces use find_households. Example: {"workspaceId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |

## Answer

### `plan_summary`

**Summarize a plan’s outcome.** One call: does the money last, first shortfall year, estate after tax, lifetime tax, CPP/OAS start, plus rounded spending and withdrawal-order previews. On the free plan it returns the value meter instead (locked: true).

- Permission: `plan:read`. Access: read. Plan: Free: the value meter (locked: true), except an exact calculator-imported revision with a basic-result grant. Yearly plan: the full summary.
- Safe to retry: yes. Changes or removes data: no.

Use this first for questions like "will the money last?", "what is left at the end?", "how much tax over our lifetime?", "when do CPP and OAS start?" or "how much could we spend?". One call projects the saved revision and returns plain sentences plus numbers: money lasts yes/no, first shortfall year, estate after tax (nominal and start-year dollars), lifetime tax, CPP/OAS start, and rounded previews of sustainable spending and of a better withdrawal order. Includes provenance (scenarioId, revision, runId, engineVersion), benefitInputs (whether CPP/QPP and OAS come from a statement, a starting estimate or zero) and warnings to confirm with the user. A preview still calculating says so (reason "still_calculating", retryAfterSeconds); the rest of the summary is final. Do not use it for a single year’s detail (inspect_year) or to compare two scenarios (explain_change).

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** |  |
| `revision` | integer | optional | Default latest. |
| `includePreviews` | boolean | optional | Add the rounded sustainable-spending and withdrawal-order previews (latest revision only; computed together, up to a few seconds the first time, then cached). Default: `true`. |

### `value_meter`

**Show what a plan found so far.** What the plan found so far: less lifetime tax, more left to family and more to spend each year, each rounded down to $1,000 against the plan as entered. Free on every plan; never the verdict or the moves.

- Permission: `plan:read`. Access: read. Plan: Free on every plan.
- Safe to retry: yes. Changes or removes data: no.

Use this to show the value a plan could get before its results are unlocked, or when the person asks "what could this plan save us?". Returns three amounts rounded down to $1,000, each measured against the plan exactly as entered: less lifetime tax (income tax, OAS clawback and tax on the estate), more left to family after tax at the end of the plan, and more to spend each year (today's dollars). They overlap, so never add them up. It never returns the verdict, the projection or the moves. Free on every plan. Example: {"workspaceId":"…","scenarioId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** |  |

### `unlock_results`

**Open results on a yearly plan.** Opens a household’s results on a yearly plan with a result limit, using one of the year’s results. Ask the person first; repeating it on an unchanged household uses nothing.

- Permission: `plan:write`. Access: write. Plan: Yearly plan with a result limit. Ask the person first.
- Safe to retry: yes. Changes or removes data: no.

Use this only after the person agrees, when a tool returned unlock_needed: it opens the results of the scenario's household as it stands now (every scenario's latest revision) and uses one of the plan's results for the year. Repeating it on an unchanged household uses nothing. Unlimited plans never need it. Example: {"workspaceId":"…","scenarioId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `scenarioId` | uuid | **required** |  |

### `explain_change`

**Explain what changed between two plans.** What changed between two scenarios (or two revisions) in plain labels, and how the outcome moved.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this to answer "what is different and why did the result change?": compares a scenario with another scenario in the same household (againstScenarioId) or with one of its own earlier revisions (againstRevision). Returns changed inputs in plain labels (before -> after), outcome deltas (estate, lifetime tax, shortfall year) and the first year the projections diverge. Differences are this scenario minus the "against" one. Give exactly one of againstScenarioId or againstRevision. Example: {"scenarioId":"<Retire at 67>","againstScenarioId":"<Baseline>"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** |  |
| `revision` | integer | optional | Default latest. |
| `againstScenarioId` | uuid | optional | The scenario to compare against (usually the baseline). |
| `againstRevision` | integer | optional | An earlier revision of the same scenario to compare against. |
| `againstRevisionOfOther` | integer | optional | With againstScenarioId: its revision (default latest). |

### `show_plan`

**Show plan dashboard.** An in-chat dashboard (in assistants that display MCP Apps): the verdict, key numbers and a savings chart in today’s or nominal dollars, optionally overlaid with another scenario. Other assistants get the same summary as text.

- Permission: `plan:read`. Access: read. Plan: Yearly plan (results), or the exact calculator-imported revision with a basic-result grant. The grant does not cover advanced analyses or reports.
- Safe to retry: yes. Changes or removes data: no.

Use this when the user wants to see their plan rather than read about it ("show me my plan", "show the dashboard", "chart our savings", "show Baseline next to Retire at 67"). Hosts that display apps draw an in-chat dashboard: the plain-language verdict (money lasts to age N, or the first shortfall year), estate after tax, lifetime tax, CPP/OAS start ages, the rounded sustainable-spending preview and a year-end savings chart (today’s or nominal dollars), with provenance (scenario, revision, engine version). compareScenarioId overlays another scenario of the same household and lists the changed inputs and outcome differences. Read-only. Hosts without app support receive the same summary as text. For a text-only answer to "will the money last" prefer plan_summary; for input differences only, explain_change. Example: {"workspaceId":"…","scenarioId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** | Scenario to show, from find_households. |
| `revision` | integer | optional | Default latest. |
| `compareScenarioId` | uuid | optional | Optional: another scenario in the same household to overlay (differences are it minus scenarioId, latest revision). |

### `run_projection`

**Project a saved plan.** Project a saved revision and return its runId and key totals.

- Permission: `plan:read`. Access: read. Plan: Yearly plan (results), or the exact calculator-imported revision with a basic-result grant. The grant does not cover advanced analyses or reports.
- Safe to retry: yes. Changes or removes data: no.

Use this when you need a runId (for inspect_year, compare_projections, create_report or preview_annual_review) or the raw summary of a saved revision. It never changes inputs; identical requests return the same cached run. For a plain-language answer use plan_summary instead. Concise (default) returns runId, revision and key totals; detail "full" adds the full summary and the engine’s applied assumptions.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** |  |
| `revision` | integer | optional | Default latest. |
| `detail` | enum concise\|full | optional | "concise" (default) returns a short summary; "full" returns every field. Default: `"concise"`. |

### `inspect_year`

**Inspect one projection year.** One year of a run: income by source, withdrawals, contributions, tax and balances.

- Permission: `plan:read`. Access: read. Plan: Yearly plan (results), or the exact calculator-imported revision with a basic-result grant. The grant does not cover advanced analyses or reports.
- Safe to retry: yes. Changes or removes data: no.

Use this for one year’s detail from a saved run: income by source, withdrawals, contributions, taxes, balances and cash flow per person, in nominal dollars of that year. Needs a runId from run_projection. Concise (default) omits zero lines and module traces; detail "full" returns the complete row. Example: {"runId":"…","year":2035}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `runId` | uuid | **required** |  |
| `year` | integer 2000-2220 | **required** |  |
| `detail` | enum concise\|full | optional | "concise" (default) returns a short summary; "full" returns every field. Default: `"concise"`. |

### `compare_projections`

**Compare two saved runs.** Two saved runs side by side (alternative minus baseline).

- Permission: `plan:read`. Access: read. Plan: Yearly plan (results), or the exact calculator-imported revision with a basic-result grant. The grant does not cover advanced analyses or reports.
- Safe to retry: yes. Changes or removes data: no.

Use this to put two runs from the same household side by side (alternative minus baseline) when you already have both runIds. For "what changed and why" including the input differences, prefer explain_change, which takes scenario ids. Concise (default) returns key totals; detail "full" returns both full summaries.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `baselineRunId` | uuid | **required** |  |
| `alternativeRunId` | uuid | **required** |  |
| `detail` | enum concise\|full | optional | "concise" (default) returns a short summary; "full" returns every field. Default: `"concise"`. |

## Edit

### `describe_fields`

**Describe plan input fields.** The field dictionary: input paths, types, units, meanings and allowed values. Always pass a prefix.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.
- Pass a prefix such as "accounts.<n>", "people.<n>.cpp" or "meta"; an empty prefix lists every path.

Use this to find the right input path and its rules before update_scenario, or to answer "which input controls X?": returns path patterns (<n> = array index), type, units, meaning and allowed values or ranges. No prefix lists the sections; a prefix lists fields under it, e.g. "people.0.oas", "accounts", "expenses", "settings.withdrawalOrder". Does not read any plan; use get_scenario for current values.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `prefix` | string | optional | Section or path prefix, e.g. "people.0.cpp" or "accounts". |

### `get_scenario`

**Read a scenario’s inputs.** Read a scenario’s inputs and current revision.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this to read a scenario’s inputs and current revision before editing (its revision is the expectedRevision for update_scenario), or to answer "what did we assume" questions. Concise (default) shows people, accounts, incomes, spending and key settings with array indices for edit paths; detail "full" returns the complete plan. For results ("will the money last") use plan_summary instead.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** |  |
| `revision` | integer | optional | A past revision; default is the latest. |
| `detail` | enum concise\|full | optional | "concise" (default) returns a short summary; "full" returns every field. Default: `"concise"`. |

### `update_scenario`

**Change scenario inputs.** Apply validated input edits as one new revision (needs expectedRevision).

- Permission: `plan:write`. Access: write (changes inputs or state). Plan: Free.
- Safe to retry: yes. Changes or removes data: yes (new revision; nothing is deleted).

Use this to change one or more plan inputs; all edits are validated and saved together as one new revision. Requires expectedRevision (from get_scenario). Paths use dots and array indices: "expenses.base", "people.0.cpp.startAge", "accounts.1.balance", "meta.inflation". Money in CAD start-year dollars; fractions 0-1 (CPP percentOfMax 0.7 = 70%); rates as decimals (0.025). Call describe_fields to find a path. To add a record, set the whole array. To keep the original, copy_scenario first. On a stale_revision conflict, re-read and review; never retry blindly with a newer number. Not supported (do not approximate): GIS/Allowance special cases (grandfathered residence, social security agreements, absences, separation or institutional care, Ontario GAINS treaty exceptions). Example: {"scenarioId":"…","expectedRevision":3,"edits":[{"op":"set","path":"expenses.base","value":72000}],"note":"Spending review"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `scenarioId` | uuid | **required** |  |
| `expectedRevision` | integer | **required** | Current revision from get_scenario. |
| `edits` | array of {path: string, op: enum set\|remove, value?: any} | **required** |  |
| `note` | string | optional | Change note kept in scenario_history. |

### `copy_scenario`

**Copy a scenario to explore an alternative.** Copy a scenario to explore a what-if without touching the original.

- Permission: `plan:write`. Access: write. Plan: Free: a baseline plus one alternative per household. More need a yearly plan.
- Safe to retry: yes. Changes or removes data: no.

Use this to try a what-if without touching the original: copies a saved revision into a new scenario in the same household, then edit the copy with update_scenario. The source is unchanged. Example: {"scenarioId":"…","name":"Retire at 67"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `scenarioId` | uuid | **required** |  |
| `revision` | integer | optional | Source revision; default latest. |
| `name` | string | **required** | Name for the copy. |

### `create_scenario`

**Create a scenario.** Add a scenario to a household from a template or a full plan.

- Permission: `plan:write`. Access: write. Plan: Free: a baseline plus one alternative per household. More need a yearly plan.
- Safe to retry: yes. Changes or removes data: no.

Use this to add a new scenario to an existing household: from a "single" or "couple" template with zero balances (then fill it with update_scenario), or from a complete plan object. To start from an existing scenario use copy_scenario instead. Example: {"workspaceId":"…","clientId":"…","name":"Baseline","template":"couple"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `clientId` | uuid | **required** | Household id from find_households. |
| `name` | string | optional |  |
| `template` | enum single\|couple | optional | Default "single". |
| `plan` | any | optional | A complete plan (same shape as get_scenario detail "full" plan). Omit to use the template. |

### `scenario_history`

**List scenario revisions.** List saved revisions and their notes.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this to list a scenario’s saved revisions with their change notes, newest first (e.g. before restore_revision or explain_change against an older revision).

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** |  |

### `restore_revision`

**Restore an earlier revision.** Save an earlier revision as the new latest (undo; nothing is deleted).

- Permission: `plan:write`. Access: write (changes inputs or state). Plan: Free.
- Safe to retry: yes. Changes or removes data: yes (new revision; nothing is deleted).

Use this to undo changes: saves an earlier revision’s inputs as a new latest revision (nothing is deleted). Requires the current revision as expectedRevision. Example: {"scenarioId":"…","revision":2,"expectedRevision":3}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `scenarioId` | uuid | **required** |  |
| `revision` | integer | **required** | Revision to restore (from scenario_history). |
| `expectedRevision` | integer | **required** | Current latest revision. |

### `create_household`

**Create a household.** Add a new, empty household to a workspace; then add its first plan with create_scenario.

- Permission: `plan:write`. Access: write. Plan: Free: one household. More need a yearly plan.
- Safe to retry: yes. Changes or removes data: no.

Use this to add a new, empty household (client record). Then add its first plan with create_scenario (template "single" or "couple"). Do not use it to add a scenario to an existing household. Example: {"workspaceId":"…","name":"Lee family"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `name` | string | **required** | Household name, e.g. "Lee family". |
| `notes` | string | optional |  |

### `update_household`

**Update household name and notes.** Rename a household or replace its notes (needs the household revision from list_households).

- Permission: `plan:write`. Access: write (changes inputs or state). Plan: Free.
- Safe to retry: yes. Changes or removes data: yes (new revision; nothing is deleted).

Use this to rename a household or replace its notes. Requires the household revision from list_households (expectedRevision). Replaces the name and notes; earlier versions stay in household_history. Do not use it for plan inputs (use update_scenario).

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `clientId` | uuid | **required** |  |
| `expectedRevision` | integer | **required** | Household revision from list_households. |
| `name` | string | **required** |  |
| `notes` | string | **required** |  |

### `household_history`

**Read household note history.** Earlier household names and notes.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this to see earlier names and notes of one household, newest first. Not for plan revisions (use scenario_history).

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `clientId` | uuid | **required** |  |

## Rename, archive and restore

### `rename_household`

**Rename a household.** Rename a household (its notes are kept; the old name stays in its history).

- Permission: `plan:write`. Access: write. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this when the user asks to rename a household or fix its name, e.g. "rename the Smiths to Smith-Lee". Owner or editor. Writes a household revision (notes are kept; household_history shows the old name). The same name changes nothing. Do not use it to rename a scenario (use rename_scenario) or to change household notes (use update_household). Example: {"workspaceId":"…","clientId":"…","name":"Smith-Lee household"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `clientId` | uuid | **required** | Household id (clientId) from find_households, list_households or list_archived. |
| `name` | string | **required** | The new name, 1-160 characters. |
| `expectedRevision` | integer | optional | Optional household revision from list_households; a stale value fails instead of overwriting. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. |

### `rename_scenario`

**Rename a scenario.** Rename a scenario as a new revision with only the name changed.

- Permission: `plan:write`. Access: write. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this when the user asks to rename a scenario, e.g. "call the copy Retire at 62". Owner or editor. Saves a new revision with only the name changed (no number changes; scenario_history shows it). The same name changes nothing. Do not use it to rename a household (use rename_household) or to change any other input (use update_scenario). Example: {"workspaceId":"…","scenarioId":"…","name":"Retire at 62"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** | Scenario id from find_households, list_households or list_archived. |
| `name` | string | **required** | The new name, 1-160 characters. |
| `expectedRevision` | integer | optional | Optional current revision from get_scenario; a stale value fails instead of overwriting. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. |

### `archive_household`

**Archive a household (restorable for 30 days).** Archive a household and its scenarios: hidden everywhere, restorable for 30 days, then deleted.

- Permission: `plan:write`. Access: write (changes inputs or state). Plan: Free.
- Safe to retry: yes. Changes or removes data: yes (new revision; nothing is deleted).

Use this when the user asks to delete, remove or archive a whole household, e.g. "delete the test household". Owner or editor. The household and all its scenarios disappear from every list and from any client it was shared with; queued analyses stop. Saved reports stay readable. It can be restored with restore_household for 30 days; after that it is deleted for good (unless a saved report was made from it, which keeps it archived). Confirm with the user before calling. Do not use it to remove one scenario (use archive_scenario) or to stop sharing with a client (use unshare_household). Example: {"workspaceId":"…","clientId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `clientId` | uuid | **required** | Household id (clientId) from find_households, list_households or list_archived. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. |

### `archive_scenario`

**Archive a scenario (restorable for 30 days).** Archive one scenario (a household always keeps one); restorable for 30 days.

- Permission: `plan:write`. Access: write (changes inputs or state). Plan: Free.
- Safe to retry: yes. Changes or removes data: yes (new revision; nothing is deleted).

Use this when the user asks to delete, remove or archive one scenario, e.g. "get rid of the copy we made". Owner or editor. It disappears from lists and from shared clients; queued analyses on it stop; saved reports stay readable. A household always keeps one live scenario, so the last one cannot be archived (archive the household instead). Restorable with restore_scenario for 30 days, then deleted (unless a saved report was made from it). Confirm with the user before calling. On the free plan, archiving a scenario frees room for a new one. Do not use it to remove a whole household (use archive_household). Example: {"workspaceId":"…","scenarioId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** | Scenario id from find_households, list_households or list_archived. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. |

### `list_archived`

**List archived households and scenarios.** Archived households and scenarios, with the date each can be restored until.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this when the user wants back something they deleted or archived, to find its id before restore_household or restore_scenario: archived households (with their scenario count) and scenarios archived on their own, newest first, with the date until which each can be restored. Team members only. Do not use it for live households (use find_households). Example: {"workspaceId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |

### `restore_household`

**Restore an archived household.** Bring back an archived household with its scenarios.

- Permission: `plan:write`. Access: write. Plan: Free: only while you are within one live household.
- Safe to retry: yes. Changes or removes data: no.

Use this when the user asks to bring back, undo the deletion of, or restore a household archived in the last 30 days. Owner or editor. Its scenarios come back with it (a scenario archived on its own stays archived). On the free plan this fails if the live household limit is reached. Find the clientId with list_archived. Do not use it for a scenario (use restore_scenario). Example: {"workspaceId":"…","clientId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `clientId` | uuid | **required** | Household id (clientId) from find_households, list_households or list_archived. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. |

### `restore_scenario`

**Restore an archived scenario.** Bring back an archived scenario.

- Permission: `plan:write`. Access: write. Plan: Free: only while the household has fewer than two live scenarios.
- Safe to retry: yes. Changes or removes data: no.

Use this when the user asks to bring back or undo the deletion of a scenario archived in the last 30 days. Owner or editor. Its household must be live (restore_household first if not). On the free plan this fails if the household already has its baseline and one alternative. Find the scenarioId with list_archived. Do not use it for a household (use restore_household). Example: {"workspaceId":"…","scenarioId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** | Scenario id from find_households, list_households or list_archived. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. |

## Decide

### `run_decision`

**Run a guided retirement decision.** Guided Canadian decisions in one call: where to save (RRSP vs TFSA), when to start CPP and OAS, drawing RRSP early, pension splitting, withdrawal order. Returns the best option, a compact table and the edits to try it as an alternative.

- Permission: `plan:read`. Access: read. Plan: Free: a short preview. Yearly plan: the full result.
- Safe to retry: yes. Changes or removes data: no.

Use this when the user asks one of these Canadian decisions about a saved scenario: where to put savings while working, RRSP versus TFSA (decision "where_to_save"); when to start CPP and OAS ("benefit_timing"); whether to draw extra from the RRSP early, before 72 ("rrsp_meltdown"); whether a couple should split pension income ("pension_splitting"); which accounts to draw from first in retirement ("withdrawal_order"); or whether the money still lasts if markets go badly right after retirement ("bad_start": stocks -20% then -10% in the first two retirement years against the plan as given and the same years ten years later, a deterministic stress test, not a forecast). The engine runs a bounded set of projections on a saved revision and returns a plain headline answer, the best option, a compact table (estate after tax and lifetime tax in future dollars, sustainable spending in today's dollars) and, for each option, the update_scenario edits that turn the plan into it. Read-only: it never changes a plan. A couple's plan can take a minute: the result is then {"status":"calculating","retryAfterSeconds":N}; call again with the same arguments after N seconds (the work continues; no extra cost). To try an option, with the user's approval, copy_scenario (same revision) and then update_scenario on the copy with expectedRevision 1 and that option's edits. objective: "estateAfterTax" (default), "lifetimeTax" or "sustainableSpend"; pension_splitting and bad_start take no objective. Do not use it for "what would it take to make the plan work" (use find_plan_that_works), for a plain outcome summary (plan_summary) or for a range of random markets (start_analysis "montecarlo"; bad_start is the one specific bad sequence). Example: {"workspaceId":"…","scenarioId":"…","decision":"benefit_timing"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** | Scenario id from find_households. |
| `revision` | integer | optional | A saved revision; default the latest. Options and edits apply to this revision. |
| `detail` | enum concise\|full | optional | "concise" (default): headline, best option, compact table and each option's edits. "full" adds the explanation, assumptions and notes. Default: `"concise"`. |
| `decision` | enum where_to_save\|benefit_timing\|rrsp_meltdown\|pension_splitting\|withdrawal_order\|bad_start | **required** | Which decision to run. |
| `objective` | enum estateAfterTax\|lifetimeTax\|sustainableSpend | optional | What "best" means: largest estate after tax (default), lowest lifetime tax, or highest sustainable spending. Not for pension_splitting or bad_start. |

### `find_plan_that_works`

**Find a plan that works.** Goal solver: choose the levers (retirement age, spending, CPP/OAS timing, withdrawal order) and a goal; returns up to three options that meet it, each with its tradeoff and edits.

- Permission: `plan:read`. Access: read. Plan: Free: a short preview. Yearly plan: the full result.
- Safe to retry: yes. Changes or removes data: no.

Use this when a plan runs short (or misses an estate or spending goal) and the user asks what it would take to make it work. Name the levers the user is willing to pull (retirementAge per person, spending, benefits = CPP/OAS start ages, withdrawalOrder) and the goal (money lasts to noShortfallToAge, default the plan's end age; optionally minEstate and minSpending). A bounded engine search (at most 60 projections) returns the 2-3 gentlest options that meet the goal, or the closest attempts, each with the tradeoff in words, the outcome and its update_scenario edits. Read-only; a "calculating" result means call again with the same arguments after retryAfterSeconds. Apply an option as with run_decision (copy_scenario, then update_scenario on the copy). Do not use it for a single named decision such as CPP timing (use run_decision) or to test one input across values (start_analysis "sweep"). Example: {"workspaceId":"…","scenarioId":"…","levers":{"spending":true,"retirementAge":{"p1":true},"benefits":true},"goal":{"noShortfallToAge":95}}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** | Scenario id from find_households. |
| `revision` | integer | optional | A saved revision; default the latest. Options and edits apply to this revision. |
| `detail` | enum concise\|full | optional | "concise" (default): headline, best option, compact table and each option's edits. "full" adds the explanation, assumptions and notes. Default: `"concise"`. |
| `levers` | {retirementAge?: {p1?: true or object, p2?: true or object}, spending?: true or object, benefits?: boolean or object, withdrawalOrder?: boolean} | **required** | At least one lever. |
| `goal` | {noShortfallToAge?: integer 18-120, minEstate?: {amount: number 0-1000000000, dollars: enum today\|future}, minSpending?: number <= 1000000000} | optional |  |

## Usage decisions

### `usage_budget`

**Read your usage spending cap.** Read the owner’s UTC-month gross CAD spending cap, commitments and outstanding exposure. Does not enable payment or change authority.

- Permission: `plan:read`. Access: read. Plan: Owner only. Free. Billing authority is established by the person in /usage.
- Safe to retry: yes. Changes or removes data: no.

Use this to read the payer’s gross CAD cap, reserved/settled use and remaining amount. Free. Only the person can enable billing or raise the cap in the browser at /usage. New paid decisions may not be open yet: read usage_budget.available before offering a purchase. Existing completed results remain readable when purchases are closed.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** |  |

### `quote_usage_decision`

**Price a CPP and OAS timing decision.** Quote a bounded CPP/OAS timing package for a saved revision: CAD5 including tax, or cached:true with an operationId for free completed-result reuse.

- Permission: `analysis:run`. Access: write. Plan: Owner only. No charge to quote. New package: CAD5 including tax; matching completed package: free reuse. Existing annual coverage uses its entitlement.
- Safe to retry: no. Changes or removes data: no.

Use this to get a fixed CAD$5 tax-inclusive quote for a completed bounded CPP/OAS timing decision on this saved revision. No charge for a quote. A matching completed decision returns cached:true, grossCents:0 and operationId: read it with usage_decision_status instead of starting again. An uncached quote creates a new expiring quote, so avoid repeated quote requests. Annual-covered work must use its existing entitlement. Paid use requires the person’s browser-established mandate and cap. New paid decisions may not be open yet: read usage_budget.available before offering a purchase. Existing completed results remain readable when purchases are closed.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** |  |
| `scenarioId` | uuid | **required** |  |
| `revision` | integer | **required** |  |
| `kind` | "benefit_timing" | **required** |  |

### `start_usage_decision`

**Run a quoted timing decision.** Accept a server-issued quote within the human-established cap; requires a stable idempotencyKey. One charge only after a complete result is published.

- Permission: `analysis:run`. Access: write. Plan: Owner only. Requires browser-established payment authority, remaining cap and a valid server quote; no additional annual-covered charge.
- Safe to retry: yes. Changes or removes data: no.

Use this to accept the server-issued quote within the person’s existing authorized cap. Reserves its gross price; charges once only when the final result is durably published. Retries with the same key are free. Never increase payment authority or guess a quote price. New paid decisions may not be open yet: read usage_budget.available before offering a purchase. Existing completed results remain readable when purchases are closed.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** |  |
| `quoteId` | uuid | **required** |  |
| `idempotencyKey` | string | **required** |  |

### `usage_decision_status`

**Read a paid decision.** Read status and completed decision evidence for free, including after the cap or payment pause.

- Permission: `plan:read`. Access: read. Plan: Owner only. Free reads of owned operations and completed results, including at the cap or payment pause.
- Safe to retry: yes. Changes or removes data: no.

Use this to read a quoted decision’s status or immutable final evidence, alternatives and edits. Polling and reopening a completed result are free, including after the cap or payment pause. New paid decisions may not be open yet: read usage_budget.available before offering a purchase. Existing completed results remain readable when purchases are closed.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** |  |
| `operationId` | uuid | **required** |  |

### `cancel_usage_decision`

**Cancel unfinished usage work.** Cancel unpublished work without charge; if already published, return the existing result and original charge.

- Permission: `analysis:run`. Access: write. Plan: Owner only. No charge for cancellation before final publication; published work keeps its original charge.
- Safe to retry: yes. Changes or removes data: no.

Use this to cancel and fence unpublished work without a customer charge. If the result was already published, return the existing result and one charge. This never changes the spending cap. New paid decisions may not be open yet: read usage_budget.available before offering a purchase. Existing completed results remain readable when purchases are closed.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** |  |
| `operationId` | uuid | **required** |  |

## Analyse

### `start_analysis`

**Start an analysis.** Monte Carlo, sustainable spending, withdrawal orders or a one-input sweep, run in the background.

- Permission: `analysis:run`. Access: write. Plan: Yearly plan (all four kinds).
- Safe to retry: yes. Changes or removes data: no.

Use this for questions a single projection cannot answer: "montecarlo" (how often the plan works across simulated market returns), "spending" (the highest sustainable spending), "withdrawals" (tested withdrawal orders vs the current one) or "sweep" (one input across up to 10 values, e.g. CPP start age 60/65/70). Runs in the background on a saved revision and returns estimatedSeconds: then call analysis_status with the returned id, which waits for the result (a 200-run Monte Carlo on a couple takes about 15 seconds, 1,000 runs about 70; spending, withdrawals and sweeps usually under 30). It does not change inputs. An identical request without a key returns the earlier job; pass a new idempotencyKey to re-run after a cancel or failure. Monte Carlo uses seeded normal returns, not historical sequences. For a quick rounded spending or withdrawal answer, plan_summary’s previews may be enough. Example: {"scenarioId":"…","revision":2,"kind":"sweep","options":{"path":"people.0.cpp.startAge","values":[60,65,70]}}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `scenarioId` | uuid | **required** |  |
| `revision` | integer | **required** | Saved revision to analyse (from get_scenario or plan_summary). |
| `kind` | enum montecarlo\|spending\|withdrawals\|sweep | **required** |  |
| `options` | {runs?: integer 20-1000, seed?: integer 0-4294967295} or {objective?: enum estateAfterTax\|lifetimeTax\|sustainableSpend} or {path: enum expenses.base\|meta.inflation\|people.0.cpp.startAge\|people.0.retirementAge, values: array of number} or {} | optional | Options for the chosen kind. montecarlo: {runs?, seed?}; withdrawals: {objective?}; sweep: {path, values}; spending: {}. |

### `analysis_status`

**Read analysis status and results.** Poll an analysis and read its result.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this after start_analysis to get the result. It waits up to waitSeconds (default 20, max 25) for the analysis to finish and returns as soon as it does, so call it again only while status is still queued or running (estimatedSecondsRemaining says how long is left); there is no need to poll quickly. Failed and cancelled jobs have no result (start it again with a new idempotencyKey). Concise (default) returns status, progress and the result; detail "full" adds options, provenance and model assumptions. Example: {"workspaceId":"…","jobId":"…","waitSeconds":25}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `jobId` | uuid | **required** |  |
| `detail` | enum concise\|full | optional | "concise" (default) returns a short summary; "full" returns every field. Default: `"concise"`. |
| `waitSeconds` | integer 0-25 | optional | Seconds to wait for the job to finish before answering, 0-25 (default 20). The call returns as soon as the job ends. Default: `20`. |

### `cancel_analysis`

**Cancel an analysis.** Stop a queued or running analysis.

- Permission: `analysis:run`. Access: write (changes inputs or state). Plan: Free.
- Safe to retry: yes. Changes or removes data: yes (new revision; nothing is deleted).

Use this to stop a queued or running analysis (e.g. started with the wrong options). Repeating it is harmless; a finished job keeps its status.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `jobId` | uuid | **required** |  |

## Year-end review

### `preview_annual_review`

**Preview next year’s opening inputs.** Propose next year’s opening inputs from a run’s closing year, without saving.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this at year end to propose next-year inputs from a saved run’s closing year (balances, cost bases, room) without saving anything. Projected values must be checked against actual statements. Then create_annual_review saves it as a new scenario.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `runId` | uuid | **required** | runId from run_projection. |
| `asOfYear` | integer 2000-2220 | **required** | Closing year inside the projection, usually the first year. |
| `name` | string | **required** |  |

### `create_annual_review`

**Create an annual review scenario.** Save that roll-forward as a new scenario.

- Permission: `plan:write`. Access: write. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this after preview_annual_review to save the rolled-forward plan as a new scenario; the source scenario is preserved. Requires the source scenario’s current revision. Pass actuals (year-end statement balances by account id from the preview, and optionally the actual yearly spending) to roll forward with real numbers; otherwise replace the projected openings in the new scenario before relying on it. To compare plan vs actual, use the preview’s projected (spending, income, savings, balances for the closing year).

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `runId` | uuid | **required** |  |
| `asOfYear` | integer 2000-2220 | **required** |  |
| `name` | string | **required** |  |
| `expectedRevision` | integer | **required** | Current revision of the source scenario. |
| `actuals` | {balances?: object, annualSpending?: number >= 0} | optional | Optional actual year-end figures that replace projected openings. |

## Record and report

### `save_recommendation`

**Record a planning recommendation.** Record or revise a planning recommendation tied to a revision.

- Permission: `plan:write`. Access: write (changes inputs or state). Plan: Free.
- Safe to retry: yes. Changes or removes data: yes (new revision; nothing is deleted).

Use this to record or revise a user-authored recommendation tied to a scenario revision (e.g. after comparing scenarios). To revise, pass its id and expectedRevision. status: proposed | agreed | done | dismissed; it tracks discussion, it does not certify advice. Do not use it to change plan inputs.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `id` | uuid | optional | Existing recommendation id to revise; omit to create. |
| `expectedRevision` | integer | optional | Required with id. |
| `scenarioId` | uuid | **required** |  |
| `scenarioRevision` | integer | **required** | Scenario revision the recommendation refers to. |
| `title` | string | **required** |  |
| `body` | string | **required** |  |
| `status` | enum proposed\|agreed\|done\|dismissed | **required** |  |

### `list_recommendations`

**List planning recommendations.** Read recommendations for a scenario.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this to read the recommendations recorded for a scenario, with status (proposed, agreed, done, dismissed) and the scenario revision each refers to. These are user-authored notes, not engine output.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `scenarioId` | uuid | **required** |  |

### `recommendation_history`

**Read recommendation history.** Earlier versions of a recommendation.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this to read earlier text, status and source revisions of one recommendation, newest first.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `recommendationId` | uuid | **required** |  |

### `create_report`

**Create a saved report.** Create a saved report from a projection run, with the sections you choose.

- Permission: `reports:create`. Access: write. Plan: Yearly plan.
- Safe to retry: yes. Changes or removes data: no.

Use this to produce a client-ready report from a saved run (runId from run_projection). sections: page-value (what the plan found, against the plan’s price), page-verdict, page-income, page-tax, page-estate, page-moves (money put into or taken from each account in the first years, and benefit starts), page-compare (needs compareRunId from the same household), page-assumptions, page-education, page-insurance, page-charity. Draft is the default; "reviewed" records that inputs were reviewed, it does not certify outcomes. Recommendations saved for the run’s revision are included by default (pass recommendationIds [] to leave them out), and the household’s other calculated scenarios appear as "Options considered". Then prepare_report_pdf for a PDF. Example: {"runId":"…","title":"Thompson retirement plan","sections":["page-verdict","page-income","page-tax","page-estate"]}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `runId` | uuid | **required** |  |
| `title` | string | **required** |  |
| `comment` | string | optional |  |
| `sections` | array of enum page-value\|page-verdict\|page-income\|page-tax\|page-estate\|page-moves\|page-compare\|page-assumptions\|page-education\|page-insurance\|page-charity | **required** |  |
| `includeAnnualTable` | boolean | optional |  |
| `includeCalculationDetails` | boolean | optional | Include the full benefit and tuition assessment records. Defaults to concise results; saved inputs and projection provenance are preserved. Default: `false`. |
| `compareRunId` | uuid | optional |  |
| `recommendationIds` | array of uuid | optional |  |
| `state` | enum draft\|reviewed | optional |  Default: `"draft"`. |

### `prepare_report_pdf`

**Prepare a report PDF.** Queue the PDF for a saved report; poll report_pdf_status for the link.

- Permission: `reports:create`. Access: write. Plan: Yearly plan.
- Safe to retry: yes. Changes or removes data: no.

Use this after create_report to queue the PDF (usually ready in 5-10 seconds, up to about 40 when analyses are running); then call report_pdf_status, which waits for it and returns a download link you can give the user (no sign-in needed, valid 15 minutes). After a cancelled or failed PDF, call it again with a new idempotencyKey.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `reportId` | uuid | **required** |  |

### `report_pdf_status`

**Read PDF status and get the download link.** Poll the PDF and get its link.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this after prepare_report_pdf. It waits up to waitSeconds (default 20, max 25) for the PDF and returns as soon as it is ready. When succeeded it returns downloadUrl: a link that downloads the PDF without signing in, valid for 15 minutes; give it to the user as is (call this tool again for a fresh link after it expires). pdfUrl opens the same PDF in a signed-in browser. Example: {"workspaceId":"…","jobId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `jobId` | uuid | **required** |  |
| `waitSeconds` | integer 0-25 | optional | Seconds to wait for the job to finish before answering, 0-25 (default 20). The call returns as soon as the job ends. Default: `20`. |

### `cancel_report_pdf`

**Cancel PDF preparation.** Stop a queued or running PDF job before it finishes.

- Permission: `reports:create`. Access: write (changes inputs or state). Plan: Free.
- Safe to retry: yes. Changes or removes data: yes (new revision; nothing is deleted).

Use this to stop a queued or running PDF job; saved reports and finished PDFs are kept.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from find_households or list_workspaces. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key only to deliberately create a second identical record. |
| `jobId` | uuid | **required** |  |

## Advisor practice

### `list_clients`

**List clients with review status.** Practice dashboard: every household with last review, review due, shortfall flag and recommendation counts.

- Permission: `plan:read`. Access: read. Plan: Practice.
- Safe to retry: yes. Changes or removes data: no.

Use this when the user asks about their book of clients, e.g. "who is due for a review?", "which households have a shortfall?" or "how many open recommendations do we have?". One row per household: last saved revision, last review (latest annual review or recommendation update), review due 12 months later (overdue, due_soon within 30 days, current or never), status of the most recently saved scenario’s latest revision (shortfall with first shortfall year and age, or covered; a small cash gap that savings cover is a timing gap, not a shortfall; latest revisions never projected are calculated on the spot, and any still calculating show as not_projected or stale with calculating true: call list_clients again after summary.retryAfterSeconds), recommendation counts by status, and sharedWith (clients who can view that household; pendingShares = open invitations). Do not use it for a household’s numbers (use plan_summary) or to search other workspaces. Example: {"workspaceId":"…","sort":"review_due"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from list_workspaces or find_households. |
| `query` | string | optional | Part of a household name; empty lists all. Default: `""`. |
| `sort` | enum name\|review_due\|updated | optional | "review_due" puts overdue and never-reviewed households first. Default: `"name"`. |
| `detail` | enum concise\|full | optional | "concise" (default) returns the key columns; "full" returns every field. Default: `"concise"`. |

### `list_members`

**List workspace members and roles.** People with access to a workspace and their roles.

- Permission: `plan:read`. Access: read. Plan: Free.
- Safe to retry: yes. Changes or removes data: no.

Use this when you need who has access to a workspace: team members and their roles (owner, editor, viewer; they see every household), a member’s userId before remove_member, and the clients who can view single shared households. Owners also see pending and expired invitations. Do not use it for household numbers or review status (use list_clients or find_households). Example: {"workspaceId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from list_workspaces or find_households. |

### `invite_member`

**Invite someone to a workspace.** Invite someone by email as an editor or a view-only client (owner only).

- Permission: `plan:write`. Access: write. Plan: Practice.
- Safe to retry: yes. Changes or removes data: no.

Use this when the user asks to add a colleague to their TEAM, e.g. "give my assistant edit access" or "add Sam as a viewer". Owner only. Team access covers the whole workspace: every household in it becomes visible to the invited person. Emails a single-use link valid for 7 days; the person must sign in with that exact (verified) address to accept. role "editor" can change plans; "viewer" can only read. The link is never returned to you. Do not use it to give a client access to their own plan (use share_household, which shares one household only), to change an existing member’s role or to remove access (use remove_member). Example: {"workspaceId":"…","email":"alex@example.com","role":"editor"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from list_workspaces or find_households. |
| `email` | string | **required** | The invited person’s email address. |
| `role` | enum editor\|viewer | **required** | "editor" changes plans; "viewer" reads only. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key to deliberately repeat the action (e.g. invite the same address again after a revoke or expiry). |

### `remove_member`

**Remove a member from a workspace.** Remove someone’s access to a workspace (owner only).

- Permission: `plan:write`. Access: write (changes inputs or state). Plan: Free.
- Safe to retry: yes. Changes or removes data: yes (new revision; nothing is deleted).

Use this when the user asks to take away someone’s access to a workspace. Owner only; needs the member’s userId from list_members. They lose access to every household in the workspace at once; their earlier edits stay in the history. Owners cannot be removed (change their role in the web app first), and you cannot remove yourself. Do not use it to withdraw an invitation that was not accepted yet (the owner revokes it in the web app). Example: {"workspaceId":"…","userId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from list_workspaces or find_households. |
| `userId` | string | **required** | Member userId from list_members. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key to deliberately repeat the action (e.g. invite the same address again after a revoke or expiry). |

### `share_household`

**Share one household with a client.** Share one household with a client by email, read only.

- Permission: `plan:write`. Access: write. Plan: Practice.
- Safe to retry: yes. Changes or removes data: no.

Use this when the user asks to let a client see their own plan, e.g. "share the Lee plan with jan@example.com" or "give the Thompsons access to their household". Owner or editor. Emails the client a single-use link valid for 7 days (they can create an account from it with that exact address). After accepting, the client sees ONLY this household, read only, from the current revision onward (earlier revisions stay private): its scenarios, projections, reports and recommendations; never the Planning notes (household notes and change notes), other households, the team list, billing or branding. The link is never returned to you. Do not use it for colleagues (use invite_member: team members see every household) or to remove access (use unshare_household). Example: {"workspaceId":"…","clientId":"…","email":"jan@example.com"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from list_workspaces or find_households. |
| `clientId` | uuid | **required** | Household id (clientId) from find_households or list_clients. |
| `email` | string | **required** | The client’s email address. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key to deliberately repeat the action (e.g. invite the same address again after a revoke or expiry). |

### `unshare_household`

**Stop sharing a household with a client.** Stop sharing a household with a client, or cancel a pending invitation.

- Permission: `plan:write`. Access: write (changes inputs or state). Plan: Free.
- Safe to retry: yes. Changes or removes data: yes (new revision; nothing is deleted).

Use this when the user asks to remove a client’s access to a shared household, or to withdraw a share invitation that was not accepted yet. Owner or editor. Give exactly one of userId (a client in list_clients sharedWith) or invitationId (an open invitation from list_members). Access ends on the client’s next request; the household and its history are unchanged. Do not use it for team members (use remove_member). Example: {"workspaceId":"…","clientId":"…","userId":"…"}.

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `workspaceId` | uuid | **required** | Workspace id from list_workspaces or find_households. |
| `clientId` | uuid | **required** | Household id (clientId). |
| `userId` | string | optional | The client’s userId from list_clients sharedWith. |
| `invitationId` | uuid | optional | An open share invitation id from list_members (owners) to withdraw. |
| `idempotencyKey` | string | optional | Optional. Omit it: a key is derived from the arguments so an identical resend is not applied twice. Pass a new unique key to deliberately repeat the action (e.g. invite the same address again after a revoke or expiry). |
