# Agent instructions
Source: https://docs.cfo.ai/agent-instructions
For an AI agent helping a user connect the cfo.ai MCP server: confirm access, register the server, get browser approval, verify the connection and workspace, understand the Model, shape calls, work safely, and troubleshoot.
You are helping a user connect cfo.ai to an AI client and work in their workspace. The cfo.ai MCP server exposes the same workspace tools Ari, cfo.ai's AI finance coworker, uses. Those tools can read and change a live financial Model.
You configure the MCP client. The user creates their account, signs in, sets up their plan, and approves the connection in a browser. A connection is complete only when the client lists the server and an authorized cfo.ai tool call succeeds.
Every page linked here also has a Markdown version: add `.md` to its address, such as `https://docs.cfo.ai/agents/mcp-tools.md`.
## Confirm account access
1. Ask whether the user can sign in to [app.cfo.ai](https://app.cfo.ai). If they cannot, direct them to create or open their account there and finish setup in the browser.
2. The workspace must have started its free trial or a plan. Until it has, every tool call answers **Start the free trial in cfo.ai before using its MCP tools**.
3. Wait for the user to finish browser-only steps. Never ask for their password, verification code, payment details, or an authorization token. cfo.ai has no API keys, so there is no key to ask for.
## Register the MCP server
The production server URL is:
```text theme={null}
https://api.cfo.ai/mcp
```
For Claude Code:
```bash theme={null}
claude mcp add --transport http cfoai https://api.cfo.ai/mcp
```
For Codex:
```bash theme={null}
codex mcp add cfoai --url https://api.cfo.ai/mcp
```
For another client, add `https://api.cfo.ai/mcp` as a remote HTTP MCP server named `cfoai`. Claude's desktop and web apps add it as a custom connector, and ChatGPT and Cursor have their own forms; [Connect an AI tool](/guides/connect-an-ai-client) gives the steps for each.
If you cannot run a setup command, give the matching command to the user and wait for them to run it.
## Have the user approve the connection
The user approves the connection in a browser while signed in to cfo.ai.
* In Claude Code, restart the session if needed, open `/mcp`, select `cfoai`, and choose **Authenticate**.
* In Codex, run `codex mcp login cfoai`.
* In another client, follow its normal OAuth or remote-MCP sign-in flow.
The browser shows a cfo.ai page headed **Connect CFO.ai**. Tell the user to check that they are signed in to the cfo.ai account the client should use, then select **Connect CFO.ai**. The page is valid for 60 minutes. If it reports that the approval was not recorded, start the sign-in again; [If sign-in fails](/guides/connect-an-ai-client#if-sign-in-fails) explains each message.
## Verify the connection and the workspace
1. Confirm that the client lists `cfoai` and exposes its tools. If it does not, restart the session and check its MCP server list again.
2. Call `list_orgs`. It returns the user's workspaces, each with `org_id`, `name`, `slug`, and `role`.
3. If the user has one workspace, tools select it automatically. If they have several, ask which one to use, and pass its ID or slug as `org_id` on every later call.
4. Make one read-only call before you report success, such as `inspect_pages` with:
```json theme={null}
{ "list": {} }
```
## Understand the Model before changing it
The server sends cfo.ai's modeling instructions when the client connects. Read them first. Then read the built-in skill that covers the work, with `inspect_skills`:
* `build-model` before you create or change model logic, Variables, Dimensions, or saved tables.
* `build-visualizations` before you add a Canvas block.
* `import-model` before you rebuild a model from a workbook or an export.
List the skills with `{"list": {}}`, and read one with `{"instructions": {"target": "build-model"}}`. The signed-in person's own skills hold the procedures they saved; read the relevant ones before substantial work.
Then inspect the existing workspace with `inspect_pages`, `inspect_table_blocks`, `inspect_dimension_mappings`, and `inspect_scenarios`. The mental model:
* A workspace contains a financial Model, Pages, Scenarios, Variables, Dimensions, and connected data.
* Main is the primary working Scenario. Use a different Scenario when the user wants a saved alternative.
* `inspect_pages`, `inspect_table_blocks`, `inspect_dimension_mappings`, and `inspect_scenarios` read the existing Model.
* `edit_table_blocks` creates and changes saved tables, including their Variables, Dimensions, and formulas. Every model formula is written through it.
* `edit_pages`, `edit_dimension_mappings`, and `edit_scenarios` change Pages, Dimension mappings, and Scenarios.
* `inspect_canvas_library` and `edit_canvas_blocks` read the Canvas component library and create or update Canvas blocks, the charts and custom visuals on a Page.
* Write operations apply to the user's live Model and must match the user's request and workspace access.
## Shape each call as one action block
A tool with several actions takes exactly one block named for the action, with that action's fields inside it. There is no flat `action` field. For example, read a saved table with `inspect_table_blocks`:
```json theme={null}
{ "read": { "target": "Operating Plan" } }
```
and set a row's formula with `edit_table_blocks`:
```json theme={null}
{
"update": {
"target": "Operating Plan",
"row_updates": [
{
"rows": ["Gross Margin"],
"default_formulas": { "forecast": { "formula": "iferror(`Gross Profit` / Revenue, 0)" } }
}
]
}
}
```
Copy names and references, such as `Gross Profit#3f2`, from inspection results instead of inventing IDs. [MCP tools and call shapes](/agents/mcp-tools) lists every tool, its blocks, the fields beside them, and the refusals a wrong shape gets. [Create and update formulas](/agents/write-formulas) has the full formula shapes, and [Canvas blocks for agents](/agents/canvas-blocks) the Canvas ones.
## Work safely
* Writes apply to the live Model immediately and are recorded in its changelog under the user's name. `edit_history` undoes, redoes, or reverts them, so reverse a wrong change rather than patching over it.
* Use a Scenario other than Main when the user wants to try a change without altering the current plan.
* Connections, access changes, invitations, share links, and emails have effects outside model undo. Confirm those with the user before you make them.
* Honor read-only and review-before-write requests. [Analyze saved values without changing the Model](/agents/analyze-without-saving) covers answering from what is saved.
* When a call creates or changes something, end your reply with the link the tool returned for it, exactly as printed. A link you build yourself will not open.
## Troubleshoot
* **`cfoai` is missing from the client.** Restart the client and check whether the server registration succeeded.
* **Browser approval fails.** Confirm that the user is signed in to the intended cfo.ai account and can open the intended workspace, then start the sign-in again. In cfo.ai, Ari can list the user's own recent sign-in attempts and why each one failed.
* **Tools report authorization errors after a previous success.** Repeat the client's sign-in flow.
* **A call answers `org_id is required`.** The user belongs to several workspaces. Call `list_orgs` and pass the intended workspace as `org_id`.
* **A call answers `you do not have access to this workspace`.** The signed-in account is not a member of that workspace. Check `list_orgs` and the account the user approved with.
* **A call answers `Start the free trial in cfo.ai before using its MCP tools`.** The user must start the workspace's free trial or a plan in cfo.ai.
* **A call answers `rate limit exceeded for` a tool.** Wait and send it again. Each tool accepts up to 60 calls a minute per user and workspace.
* **Calls built from the client's tool list are refused as unknown or retired shapes.** The client's tool definitions are stale. Refresh its tool list or start a new conversation, then call again.
# Analyze saved values without changing the Model
Source: https://docs.cfo.ai/agents/analyze-without-saving
For agents: answer financial questions from saved tables, compare and rank existing values, read history, check a proposed formula without saving it, and know when a new result needs a saved row and the user's authorization.
A read-only analysis starts from saved Model content. Inspect the existing Table blocks, Variables, Scenarios, and source information, and answer from them without creating a new calculation.
## Read a saved table
Use `inspect_table_blocks` with its `read` block and the table that holds the values:
```json theme={null}
{
"scenario": "Main",
"read": {
"target": "Revenue by Customer",
"formula_origins": "all"
}
}
```
To narrow a follow-up to a visible row, cell, or subtree, copy row and column references from the response into `rows` and `columns`, and use `depth` to include the items beneath a selected row. [Read and explain formulas](/agents/read-formulas) covers everything a read returns.
## Compare or rank existing values
* Use the saved table's existing Scenario or time comparison when it already answers the question.
* To compare Scenarios the table does not save, give the read a `comparison` object, as [Read a table that compares Scenarios or periods](/agents/read-formulas#read-a-table-that-compares-scenarios-or-periods) shows. The saved table stays as it is.
* For a top-items question, read the item rows and rank them from the returned values yourself; the read does not sort.
## Read the Model as it was
* A historical read uses `as_of_point` against the same saved table and the same target, which keeps a before-and-after comparison tied to one stable artifact. [Read the Model at an earlier point](/agents/mcp-tools#read-the-model-at-an-earlier-point) lists the point types.
* The `compare` block shows how a table changed since a saved change, named by `before_change_log_id`. It lists the rows removed or added since then under `row_membership`.
## Check a proposed formula without saving it
Only a new table can be checked without saving. `edit_table_blocks` `create` with `dry_run: true` validates the proposed table's structure, references, and formula syntax without writing, and returns `valid`, `warnings`, and `model_effects`.
* A dry run checks the write. It returns no values, because calculation does not run, so it does not create a calculated series to inspect.
* `update`, `reshape`, and `delete` take no dry run. Checking a change to an existing table means making the change, which needs the user's authorization. When the user wants to see a change before adopting it, make it in a Scenario other than Main.
[Validate and verify formula writes](/agents/validate-and-verify#validate-before-you-save) has the details.
## Create a new result only when authorized
A new ratio, signal, or comparison that does not exist yet needs a saved, visible Model artifact. If the user authorizes that work, use `edit_table_blocks` to add a row and its first formula, then inspect the saved result.
If the user asked for no Model changes, explain what you can answer from the existing information, and name the saved change a new calculation would require.
# Canvas blocks for agents
Source: https://docs.cfo.ai/agents/canvas-blocks
Reference for agents: find Canvas components with inspect_canvas_library, create and patch Canvas blocks with edit_canvas_blocks, shape their datasets, follow the theme, check renders with validate_code_blocks, read saved blocks, and name blocks in edit_pages calls.
A Canvas block is a chart or custom visual on a Page: a sandboxed React component that reads Model values through named datasets. Four tools work with them:
* `inspect_canvas_library` finds the components and templates a block can use.
* `edit_canvas_blocks` creates a block or changes a saved one.
* `validate_code_blocks` renders saved blocks in a browser and reports what it saw.
* `inspect_canvas_blocks` reads saved blocks.
Read the `build-visualizations` skill through `inspect_skills` before you add a Canvas block; it owns design guidance. Page order and block removal belong to `edit_pages`, and editable model tables to `edit_table_blocks`.
## Find components and templates
Call `inspect_canvas_library` with a `list` block whose `query` is a few search words:
```json theme={null}
{ "list": { "query": "slider chart kpi" } }
```
* An entry matches when any one word appears in its title, description, tags, aliases, or decision outcomes.
* `kind` narrows the list to `component` or `template`. Without `query`, the list holds every entry of that kind.
Then `read` the entries you plan to use. One `read` takes up to 16 targets and resolves templates and components together, so a template and the components it needs come back in one call:
```json theme={null}
{ "read": { "target": ["canvas-library:template:hiring-plan-decision", "canvas-library:component:slider", "canvas-library:component:kpi-card"] } }
```
A `read` returns each entry's instructions, its props skeleton, and its runtime requirements. A name that matches nothing, or more than one entry, comes back as an unresolved entry. Library entries are building guidance; they are not saved blocks and prove nothing about how a block renders.
## Create a Canvas block
`edit_canvas_blocks` `create` requires `page`, `title`, and `source`:
```json theme={null}
{
"scenario": "Main",
"create": {
"page": "Board summary",
"title": "Pipeline by stage",
"source": "function Block({ data }) { return {data.pipeline.rows.length} months of pipeline; }",
"datasets": [
{
"name": "pipeline",
"layout": {
"rows": [{ "variable": "Pipeline value", "breakdown": ["Stage"] }],
"columns": [{ "time": "MONTH" }]
}
}
]
}
}
```
* `page` is the Page that receives the block, by name or as `inspect_pages` prints it. `after_block` places the block under another one; without it the block goes last.
* `title` is the block's name. Later updates and page edits name the block by it.
* The receipt names the new block two ways. `target` is the block's name-form selector, which the page and Canvas tools accept wherever they take a block as `target`. `ref` is the block's pasteable reference, such as `Pipeline by stage#a74`, which they accept wherever a result's block reference is accepted, such as `reorder_blocks` `block_order`. The reference keeps naming the block in later `edit_pages` calls even after a rename.
* The receipt also carries `dataset_evidence`, one series and one row per dataset as the block receives them, and `source_notes`.
### What the source must be
`source` is one complete component, `Block({ data, timeSettings, theme, state, action, emit })`, run in a sandbox:
* React is injected. Reach hooks through it, such as `React.useState(...)`; a bare `useState(...)` is refused, and `window.React` is unavailable.
* Imports, network access, and direct AgCharts are forbidden. Custom font families are refused.
* `print()` prints only that block's frame.
* The injected components, their props, and the rest of the runtime contract are in the `source` field's description in the tool schema, and each component's props in `inspect_canvas_library` `read`.
## Datasets
A dataset is a live, read-only table read that the block receives as `data.`. A block holds up to 8.
| Field | Meaning |
| - | - |
| `name` | The identifier the code uses, `data.`. It must match `^[a-zA-Z][a-zA-Z0-9_]*$`. |
| `layout.rows`, `layout.columns` | The visible shape, as a table declares it. Variables, with any breakdown, go on rows; the shared dates or categories go on columns. [Table view grammar](/agents/table-view-grammar#canvas-dataset-layouts) has the layout fields. |
| `scenario` | Optional Scenario for a comparison dataset. Without it, the dataset reads the current Scenario. A block reads at most three distinct Scenarios, the selected one included. |
| `window` | Optional `start`, `end` (as `YYYY-MM-DD`), and `granularity`. Without it, the workspace default applies. |
The block receives the read inverted: each row of the layout becomes a series, and each column becomes one entry of `data..rows`, one plotted point. A layout with `columns: []` and `window.granularity: "NO_GRANULARITY"` declares timeless values: one row holding each Variable's value.
On `update`, each `datasets` entry adds a dataset or replaces the saved one with the same name, and the others stay. Leaving `datasets` out keeps them all; `[]` clears them all; `remove_datasets` removes the ones it names.
### Values a dataset row carries
Each row carries each Variable's own value under its series key:
| Saved value | What the row holds |
| - | - |
| Number | A finite number |
| Text | The text |
| Date | An ISO day, such as `2026-01-30` |
| Boolean | `"true"` or `"false"` |
| Empty cell or calculation error | `null` |
`row.date` is a display label, and `periodStart`, when present, is the ISO start of the period. A dataset that failed has empty `rows` and `series` and an `error` string. A comparison dataset also carries `scenario.label`, `scenario.isCurrent`, and `scenario.lastClose`, and `timeSettings.lastClose` is the current Scenario's close date or `null`.
## Colors and the theme
* Colors and type from `theme.tokens`, `theme.design`, or `--cb-*` CSS variables follow the viewer's light or dark mode.
* A literal or named color, including one in a palette, keeps its value in both modes. The write saves it, and the receipt's `source_notes` names the first one: "`` on line `` is the first fixed color in this source: it keeps this value in light and dark mode. Colors from theme.tokens, theme.design, or --cb-\* CSS vars follow the viewer's mode."
* A workspace brand can set the heading typeface, the accent color, and the corner shape. A block follows the brand through `theme.tokens.fontHeading` or `--cb-font-heading`, `theme.tokens.accent` or `--cb-accent`, and `theme.tokens.radius` or `--cb-radius`, in every workspace it is copied to.
* `source_notes` also flags a component declared inside `Block`. React remounts such a component every time `Block` renders, so a focused input loses focus and its local state resets. Declare components at the top level of the source and pass values through props.
## Change a saved block
`edit_canvas_blocks` `update` names the block with `target`, by name, ID, or as `inspect_pages` prints it. Fields you leave out keep their saved values, and the block keeps its identity and position.
* `source` replaces the whole component.
* `source_patches` changes part of it: a list of up to 50 exact `old` to `new` text replacements, applied in order. Each `old` must occur exactly once in the source as the earlier patches left it. The block saves only when every patch applies.
* `title` renames the block; an empty string clears the title.
* `page`, when given, confirms the block is still on that Page.
```json theme={null}
{
"update": {
"target": "Pipeline by stage#a74",
"source_patches": [
{ "old": "height={240}", "new": "height={320}" },
{ "old": "title=\"Pipeline\"", "new": "title=\"Pipeline by stage\"" }
]
}
}
```
## Check that a block renders
A saved block compiles, but only a browser shows whether it renders.
* A Canvas write that no browser rendered returns a `render_unverified` warning: **The block is saved and compiles; whether it renders is proven only when validate\_code\_blocks opens it in a browser.**
* `validate: true` beside the write returns the browser's result for the saved revision under `validation` instead. A render check that fails or is unavailable leaves the edit saved. If validation has not finished when the result is due, the receipt says **The Canvas edit saved. Browser validation had not finished when this result was due, so it was stopped and the render was not observed.**
* `validate_code_blocks` checks saved blocks on their own:
```json theme={null}
{ "target": ["Pipeline by stage#a74", "Cash runway gauge#1c0"], "visual_review": { "target": "Pipeline by stage#a74", "theme": "dark" } }
```
### What `validate_code_blocks` reports
* It renders up to 8 blocks per call in Chromium, with live calculated datasets, once per theme (light and dark). Each render uses the block's saved state, Scenario, number formats, and Last close, in a frame 1200 px wide. Each result names the revision and source hash it rendered.
* The result's `observed` sentence states what the check saw, such as: "Each block was rendered once per theme, with its saved state, in a 1200 px wide frame. Errors, warnings, guide anchors, and any visual review come from those renders. Clicks, typing, links, and guide launches did not run, so what they do is unobserved, and the layout at other widths is unseen."
* Errors are failures the render reported: runtime errors, widget errors, a stalled render, console errors, and failed element loads. Warnings may be intended: console warnings, blank or error-code cells, empty or zero-only data, missing series keys, hidden series, and content past the frame's edge.
* A clean render does not check that the values are financially correct.
* `guide_targets` lists the copyable guide anchors visible in at least one theme, and `guideAnchors` keeps each theme's visibility and ambiguity. `guideAnchors` lists at most the first 64 distinct anchor IDs per theme, in document order. When the render had more, `guide_anchors_cut` says how many, and `guide_targets` draws only on the listed ones.
* `visual_review` returns one block in one theme as an image at the frame's width.
### When a render does not happen
| Situation | Result |
| - | - |
| The blocks in one call read a lot of calculated data | They render in several consecutive requests. Each request carries at most 20 MiB of calculated data. |
| One block alone reads more than 20 MiB | That block is not rendered. Its result is `valid: false`, with one message per theme that states its size and that nothing about how it renders was observed. The other blocks still render, and `observed` counts how many did. |
| Browser validation is switched off or unreachable | `enabled: false`, with a note that the saved block was not rendered or validated. |
| The render run failed | `ok: false`, `error_kind: browser_validation_unavailable`, `transient: true`, and a message that the browser render check failed before it reported on these blocks, with the error behind it. Send the same call again. |
### Errors a person's browser reported
When a person's browser hits a runtime failure while rendering a saved block, it reports the error for that exact Scenario, block, and source revision. `inspect_pages` `read` then returns it as the block's `render_error`, and a later successful render clears it. An MCP call has no browser of its own, so a block nobody has rendered has no such report. A missing `render_error` does not mean the block renders.
## Read saved Canvas blocks
`inspect_canvas_blocks` takes `target`, a block name, ID, or reference, or a list of related blocks, and optionally `page` to constrain them. It returns each block's exact saved source, dataset layouts and Scenario bindings, display state, position, revision, and references. All blocks are read at one snapshot, the latest unless `as_of_point` names an earlier one. It does not calculate datasets, and saved code is no proof of a successful render.
A selection larger than one result is cut short:
* Each run of 1,000 or more encoded characters in a source, such as base64 or hex, reads as `[[N encoded characters not shown]]`.
* A source still too large comes back in part. `source_part` gives the byte range it covers, and the next call passes `next.source_offset` as `source_offset`.
* `not_returned` lists the selected blocks left out, with their sizes, and `next` holds the arguments that read the rest.
## Links a block opens
On a user click, `emit({type: 'navigate', url})` opens a page or block link that a tool result returned, or another app address in the current workspace, through normal browser history. It keeps Scenario query parameters and `#block-anchor-`. External addresses and public share views are not supported. The `source` field's description covers the block's other actions, such as saving presentation `state` and starting an Ari action.
## Name blocks in page edits
`edit_pages` arranges and removes blocks, including Canvas blocks.
* A block action names its Page with `page`.
* `add_text` may omit `page`. It then uses the Page of an `after_block` reference it was given, else the Page in view, else the Scenario's only Page. When none of these settles it, the call is refused: with several Pages, with **no target page** and up to 20 of them as candidates, and its hint gives the Scenario's page count and how many are listed; with no Pages, with **this scenario holds no pages**.
* `update_text` and `delete_block` may omit `page`; they then use the Page in view, and a returned block target finds its own Page.
* `reorder_blocks` names the Page it arranges with `page`, or with `target`. `block_order` is the complete top-to-bottom order, with each visible block once, by name or by the block references other results print. `place` moves one block instead: `block`, `after_block`, and `to_page` to move it to another Page.
* A `delete_block` aimed at a block that another saved change already deleted answers `not_found` and changes nothing. Its message names that change and, when the block went with its Page, the Page: `Block is already deleted in scenario "Main": saved change deleted it together with its page "". This call changed nothing.` The result carries `block_id`, `layer_id`, and `deleted_by_change_log_id`.
* `delete_block` refuses to delete objects that lose their last table occurrence while formulas, other blocks, settings, or hand-added items depend on them. `force: true` deletes them anyway, and those references break. Objects still shown on other tables are kept.
For paging through `inspect_pages`, see [Pagination and truncation](/agents/mcp-tools#pagination-and-truncation).
# Formula scope and formula ranges
Source: https://docs.cfo.ai/agents/formula-scope
For agents: how a formula's condition sets which cells it controls, from Model-wide defaults to one segment or one cell; exact versus inheriting conditions; how results describe a formula's reach; and how Actuals and Forecast ranges follow Last close.
A formula's scope is the set of Model values it controls. Its condition says where it applies, and its formula range says whether it covers Actuals, Forecast, or both. A visible Table block target is the preferred authority for both, because the server derives the actual cell address and formula range from it. For the customer-facing explanation, see [How formulas work](/concepts/formulas) and [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast).
## Start from the visible target
Inspect the saved table, and copy the returned row or column reference into `edit_table_blocks`:
* Use a row or a column for a formula that applies along that axis.
* Use a cell for one exact visible result.
The server derives the scope and the formula range from the target. You do not invent Dimension IDs, array positions, formula-range IDs, or conditions for a visible edit.
## Write a condition
When you write a condition yourself, use the readable Dimension and item names that inspection returns. Put backticks around names with spaces, and add a returned reference's short suffix only when needed.
| Scope | Condition | Use it when |
| - | - | - |
| Model-wide default | `[]` | The same formula should apply across the Model, with no narrower business condition. |
| Every item of a Dimension | `[Department in any]` | The formula applies to each Department. It still applies when more Dimensions are present. |
| One segment | `[Department = "Sales"]` | The formula applies to one item. |
| A combination of segments | `[Department = "Sales", Region = "West"]` | The formula applies where both hold. |
| A segment across time | `[Department = "Sales", Date.Month in any]` | The formula is dated, in a monthly Model. |
A condition with no Date term, such as `[]`, can supply inherited defaults in both Actuals and Forecast.
## Exact and inheriting conditions
A condition without `$` can still match a richer cell address. A condition that begins with `$` matches exactly the set of Dimensions it names:
```text theme={null}
[Department = "Sales"]
$[Department = "Sales"]
```
In a saved table, the exact form is what a cell write produces:
* A `cell_updates` entry saves one exact formula per selected cell, so one row across 60 period columns saves 60 formulas, and `clear_formula` removes them.
* A `row_updates` entry's `default_formulas` saves one inheriting formula that covers every period in its range.
* On a timeless row with one value column, the cell's coordinates are the row's own, so the edit writes or removes the row's formula.
The leading `$` belongs to the condition grammar. In an expression, an absolute reference puts `$` after the Variable's name instead: `Revenue$[Department = "Sales"]`.
Use exact scope only when the user intends that narrower behavior. A formula that leaves out Date cannot fill dated cells when it matches only its exact set of Dimensions.
## How results describe a formula's reach
Formula reads and write receipts describe each saved default's scope the same way:
* its condition and its range;
* its Date grain, when the formula is dated: `time_grain` in a read, `Date grain: ` in a receipt;
* its reach in words. `includes additional dimensions` means an inheriting `[...]` condition that also covers cells carrying more business Dimensions, at the same Date grain. `this grain only` means an exact `$[...]` condition.
Reach describes Dimension coverage only; time coverage is the separate Date grain. A Month formula never covers Day cells, however far its reach extends, so a daily forecast needs a Day formula of its own.
## Actuals and Forecast ranges
Formula ranges separate parts of the timeline that use different calculations. Actuals and Forecast are defined relative to a Scenario's Last close, and each Scenario has its own. Interpret a formula range against the boundary of the Scenario you read or write: the one `scenario` names, or the Scenario in view when the call leaves `scenario` out.
### Let the visible target choose the range
When the formula belongs to a visible row, column, or cell, inspect the saved table and use `edit_table_blocks` to target the actual result. The server derives the formula range from the target, so you do not invent or fetch a formula-range ID.
### Dated formulas need Date at the base grain
A formula for Actuals or Forecast needs Date in its condition, at the Model's base grain. In a monthly Model:
```text theme={null}
[Date.Month in any]
```
```text theme={null}
[Department = "Sales", Date.Month in any]
```
The range is chosen by where the formula sits: `default_formulas.actuals` or `default_formulas.forecast` on a row or a column, or in a `variables` entry of `edit_table_blocks`. A formula saved through a `variables` entry, with no table named, gets one Date term at the Model's base grain automatically.
### Read Last close when it matters
Every table `read` returns the Scenario's `calendar`: `last_close`, its default `granularity`, and its default range. Read it when you need to confirm Last close, because the boundary decides which dates count as Actuals and which belong to Forecast. `edit_table_blocks` `update_calendar` changes these settings for the Scenario; every table in the Scenario shares them.
If the Scenario has no Last close, explain the missing setting before you build a result that depends on the range.
### Keep the Model's time grain
Use the workspace's actual Date grain rather than assuming every Model is monthly. Date conditions, the table's selected periods, and the user's requested range must agree.
### Check both sides of the boundary
After a change, inspect one period in Actuals and one in Forecast. Confirm that the formula applies only where it should, and that the Scenario you meant holds the saved change.
# Formula authoring for agents
Source: https://docs.cfo.ai/agents/formulas-overview
For agents working on cfo.ai formulas: which tool answers which formula question, why writes go through visible saved tables, and the rules every formula read and write follows.
Formulas belong to saved Variables, and their calculated values appear in saved Table blocks. The Model the user sees is the source of truth for human and agent work alike, so an agent reads and writes formulas through the same tables a person uses. For how cfo.ai chooses a formula for each cell, see [How formulas work](/concepts/formulas); for the expression grammar and functions, see [Formula syntax](/reference/formula-syntax) and [Formula functions](/reference/functions).
Tool messages and field descriptions often call a saved formula a rule. These pages say formula, except when they quote the product.
## Match the tool to the question
Two tools cover formula work. Each takes one action block named for the action.
| The user wants to | Call | Read more |
| - | - | - |
| Know how a Variable or Dimension is calculated | `inspect_table_blocks` with a `variable` or `dimension` block | [Read and explain formulas](/agents/read-formulas) |
| See the values a table shows, with row and column references, formula origins, and sources | `inspect_table_blocks` with a `read` block | [Read and explain formulas](/agents/read-formulas) |
| Create or change a formula on a visible row, column, or cell | `edit_table_blocks` with an `update` block | [Create and update formulas](/agents/write-formulas) |
| Build a new table and its formulas | `edit_table_blocks` with a `create` block | [Create and update formulas](/agents/write-formulas) |
| Save a Variable's own formula with no table | `edit_table_blocks` `update` with a `variables` entry and no table `target` | [Create and update formulas](/agents/write-formulas#save-a-variables-formula-without-a-table) |
| Check a new table's formulas without saving | `edit_table_blocks` `create` with `dry_run: true` | [Validate and verify formula writes](/agents/validate-and-verify) |
| Get an answer from what is already saved | `inspect_table_blocks` reads only | [Analyze saved values without changing the Model](/agents/analyze-without-saving) |
A question that existing values can answer stays read-only. A new calculated result needs a visible, saved formula and the user's authorization to create it.
## Start from the visible table
Read the relevant Table block once, reuse the row and column references it returns, and send related edits together. The server derives the Variable, the formula's scope, its time range, and the write permissions from that visible target, so you never build them yourself.
When the work has no table yet, read the existing Variables and Dimensions first, and reuse their returned references, such as `Revenue#3f2`, when you compose a formula.
Write expressions with readable Variable names. Put backticks around a name that contains spaces, punctuation, an operator, or a reserved word, such as `` `Gross Profit` ``. Add a returned reference's short suffix only when two names collide.
## Respect the Scenario
Formulas are Scenario-scoped. Use the Scenario the user is working in, or the one they asked for, and pass it as `scenario` beside the action block. If a write could reasonably go to more than one Scenario, settle which one before you change the Model.
## Keep the condition and the expression apart
A formula has two parts. The condition decides where it applies; the expression decides what it calculates. For a visible table edit, the server derives the condition from the target you choose:
* a row's own formula,
* a `child_defaults` formula that the items beneath a row inherit, or
* one cell.
[Formula scope and formula ranges](/agents/formula-scope) covers conditions and Actuals and Forecast ranges.
## Verify the outcome
A formula that parses can still express the wrong business rule. After a material write, read the receipt's warnings and the saved Table block, and compare representative periods and segments with what the user asked for. [Validate and verify formula writes](/agents/validate-and-verify) lists what a receipt reports and how to check the numbers.
# cfo.ai for AI agents
Source: https://docs.cfo.ai/agents/index
For AI agents connected to cfo.ai through its MCP server, and the developers who set them up: what the server offers, how an agent shares the Model with Ari and the user, how to start, and every agent page.
The cfo.ai MCP server, at `https://api.cfo.ai/mcp`, gives an AI agent the same product tools Ari uses. With them an agent can read a company's financial Model and, when the user asks, change it: Pages, Table blocks, Canvas blocks, formulas, Variables, Dimensions, and Scenarios, along with the workspace's connections, Skills, Triggers, and memory. These pages describe those tools for the agent and for the developer who connects it.
## One Model for the user, Ari, and the agent
The agent works in the user's live workspace, signed in as that user. There is no separate copy of the Model for agents. Whatever the agent builds appears in the workspace right away, where the user can inspect the assumptions, change the result, or continue the work with Ari. Every change to the Model, such as a formula, a Table block, or a Page edit, is recorded in the Model's changelog under the user's name and can be undone. Actions outside the Model are not in that changelog, and undo cannot reverse them: changes to connections, Skills, Triggers, memory, and access, such as invitations and share links, and emails sent. See [Work safely](/agent-instructions#work-safely).
## How to start
1. Connect the client and verify access with the [Agent instructions](/agent-instructions). A person setting up their own tool can follow [Connect an AI tool](/guides/connect-an-ai-client) instead.
2. Read the `build-model` skill through `inspect_skills` before you change model logic, as the server's instructions ask.
3. Inspect the existing workspace before you create anything, and reuse its Variables, Dimensions, and Pages.
## Pages for agents
Connect a client, get the user's browser approval, choose the workspace, and work safely.
Every published tool, the one-action-block call shape, workspace selection, results, and pagination.
Create, patch, and render-check Canvas blocks, and name blocks in page edits.
The breakdown notation for a table's rows and columns, and the typed layout fields that carry it.
### Formulas
Which tool answers which formula question, and the rules every formula write follows.
Read a Variable's saved formulas and the table values they produce, then explain them.
Write formulas through saved tables or on a Variable, with every request and receipt shape.
Conditions that say where a formula applies, and Actuals and Forecast ranges.
Answer from saved tables, and know when a new result needs a saved row.
Check a write before and after it saves, read its receipt, and confirm the numbers.
## Reading these pages
These pages are left out of the site search, so they do not crowd the answers customers look for. Every page has a Markdown version: add `.md` to its address, such as `https://docs.cfo.ai/agent-instructions.md`. [llms.txt](https://docs.cfo.ai/llms.txt) lists every page on the site.
# MCP tools and call shapes
Source: https://docs.cfo.ai/agents/mcp-tools
Reference for agents and developers: the cfo.ai MCP server's address, sign-in, and limits; every published tool and its actions; the one-action-block call shape and its refusals; workspace selection; results; pagination and truncation; and what is not published.
The cfo.ai MCP server publishes 34 tools: Ari's public product tools plus `list_orgs`. This page describes the server, each tool, and the conventions every call follows. To connect a client, follow the [Agent instructions](/agent-instructions).
## Server
| Fact | Value |
| - | - |
| Address | `https://api.cfo.ai/mcp`. `https://api.cfo.ai/mcp/v2` serves the same tools; use `/mcp` for new connections. |
| Transport | Streamable HTTP, stateless. Each request authenticates and selects its workspace on its own, and the server neither issues nor requires an `Mcp-Session-Id`. |
| Sign-in | OAuth. A person approves the client in a browser, and the client sends the token it receives in the `Authorization` header. The protected resource metadata is at `https://api.cfo.ai/.well-known/oauth-protected-resource/mcp`, and clients register themselves through dynamic client registration or a client ID metadata document. |
| API keys | None. cfo.ai issues no API keys or access tokens to copy; every client signs in as a person and acts as that person. |
| Instructions | The server sends cfo.ai's modeling instructions when a client connects. |
| Tool list | Fixed for each server process. The tool list carries a five-minute private cache hint, and the server advertises `listChanged: false`. When a client's definitions are stale, refresh its tool list or start a new conversation. |
| Request size | A request body holds at most 1 MiB. A larger one is refused with **The MCP request is too large.** |
| Batches | JSON-RPC batch requests are refused with **JSON-RPC batches are not supported.** |
| Rate limit | 60 calls a minute for each tool, per user and workspace. A call past the limit fails with **rate limit exceeded for** followed by the tool name. |
## Published tools
A tool marked read-only never changes saved state. The **Actions or fields** column lists a multi-action tool's blocks; for a single-purpose tool it lists the fields it takes directly.
### Tables, formulas, and mappings
| Tool | What it does | Actions or fields |
| - | - | - |
| `inspect_table_blocks` | Read-only. Lists saved tables, reads cells with their formulas and sources, compares a table with an earlier saved state, and reads Variables, Dimensions, and their items. | `list`, `read`, `compare`, `variable`, `dimension`, `items`, `bounds` |
| `edit_table_blocks` | Creates, updates, reshapes, copies, or deletes saved tables in one atomic change each. Writes every model formula, and sets time rollups and the Scenario's calendar. | `create`, `update`, `reshape`, `copy`, `delete`, `set_time_rollup`, `update_calendar` |
| `inspect_dimension_mappings` | Read-only. Lists the mappings a reporting Dimension owns, or reads one lookup chosen by its exact source keys. | `target`, `keys`, `limit`, `after`, `scenario`, `as_of_point` |
| `edit_dimension_mappings` | Maps items of source Dimensions to a reporting Dimension's items, sets or clears the catch-all, removes rules or lookups, and combines or splits compatible source Dimensions. | `map`, `unmap`, `catch_all`, `clear_catch_all`, `drop_mapping`, `combine_dimension`, `split_dimension` |
### Pages and Canvas blocks
| Tool | What it does | Actions or fields |
| - | - | - |
| `inspect_pages` | Read-only. Reads the page tree, each Page's blocks in order, saved table settings, and reusable references. | `list`, `read` |
| `edit_pages` | Creates, renames, moves, shares, and deletes Pages; sets page icons and date ranges; adds and edits Text blocks; reorders and removes blocks; creates share links. | `create`, `update`, `move`, `share`, `create_share_link`, `delete`, `add_text`, `update_text`, `delete_block`, `reorder_blocks` |
| `inspect_canvas_blocks` | Read-only. Reads selected Canvas blocks: saved source, dataset layouts and Scenario bindings, display state, position, revision, and references. | `target`, `page`, `scenario`, `as_of_point`, `source_offset` |
| `edit_canvas_blocks` | Creates a Canvas block on a Page, or changes one. | `create`, `update` |
| `inspect_canvas_library` | Read-only. Finds Canvas components and templates, and reads their instructions and props. | `list`, `read` |
| `validate_code_blocks` | Read-only. Renders up to 8 saved Canvas blocks in a browser and reports errors, warnings, guide anchors, and, on request, a screenshot. | `target`, `scenario`, `visual_review` |
[Canvas blocks for agents](/agents/canvas-blocks) covers these tools in detail.
### Scenarios and history
| Tool | What it does | Actions or fields |
| - | - | - |
| `inspect_scenarios` | Read-only. Reads Scenarios, their owners, Snapshots, and descendant trees. | `list`, `read`, `subtree` |
| `edit_scenarios` | Creates, renames, snapshots, merges, or deletes Scenarios. | `create`, `rename`, `snapshot`, `merge`, `delete` |
| `inspect_history` | Read-only. Reads the changelog, the net difference between two points in time, and what a Scenario would merge into its parent. | `changelog`, `changes_between_points`, `scenario_summary`, `read` |
| `edit_history` | Undoes or redoes changes, or reverts a Scenario to a past changelog entry. Each operation returns its new changelog entry and the action that reverses it. | `undo`, `redo`, `revert` |
### Connected data
| Tool | What it does | Actions or fields |
| - | - | - |
| `inspect_integrations` | Read-only. Reads saved connections with their readiness and safe next action, the integration catalog, permitted API endpoints, connection-link progress, ingestion status, and sync setups. `outcomes` also lists the user's own recent sign-ins to this MCP server. | `catalog`, `connections`, `outcomes`, `endpoints`, `connect_progress`, `ingestion`, `sync_setups`, `query_deletion_preview`, `read` |
| `edit_integrations` | Opens connection setup for the user, connects, re-links, and deletes connections and saved queries, starts ingestion, ingests a CSV, saves API and MCP sync setups, chooses synced tables, and sets daily refresh times. | `request`, `connect`, `relink`, `delete`, `delete_query`, `start_ingestion`, `ingest_csv`, `save_setup`, `delete_setup`, `select_source_tables`, `set_refresh_time`, `clear_refresh_time` |
| `call_connected_api` | Calls one endpoint of a connected third-party API. The server supplies the credentials and enforces permissions. | `connection`, `endpoint`, `grantId`, `pathParams`, `query`, `body` |
### Workspace, people, and automation
| Tool | What it does | Actions or fields |
| - | - | - |
| `list_orgs` | Read-only, MCP only. Lists the user's workspaces with each one's ID, name, slug, and role. | none |
| `inspect_skills` | Read-only. Lists the built-in Skills and the signed-in user's own Skills that apply in the selected workspace, and reads their instructions and supporting files. Each of the user's Skills says where it applies, `all` or `this`. Reading a skill does not run it. | `list`, `instructions`, `file`, `read` |
| `edit_skills` | Creates, updates, or deletes the signed-in user's own Skills. `workspaces` sets where a Skill applies: `all`, the default, for every workspace where the user is a member, or `this` for the selected workspace only. Built-in skills cannot be edited. | `create`, `update`, `delete`, `workspaces` |
| `inspect_triggers` | Read-only. Reads the user's own scheduled Triggers, their schedules, and their last runs. | `query` |
| `edit_triggers` | Creates, updates, pauses, resumes, runs, or deletes the user's own Triggers. | `create`, `update`, `run`, `delete` |
| `inspect_guides` | Read-only. Reads the workspace's saved Guides. | `query`, `include_content` |
| `edit_guides` | Creates, replaces, or deletes saved Guides. | `create`, `update`, `delete` |
| `inspect_memory` | Read-only. Searches saved memories: the user's private facts and the workspace's shared facts. | `query`, `scope`, `tags`, `batch`, `include_superseded`, `limit` |
| `edit_memory` | Saves 1 to 12 facts, saves a document, replaces facts, or forgets them. | `retain`, `retain_document`, `forget` |
| `inspect_team` | Read-only. Reads the people and pending invitations in the workspace. | `query`, `target` |
| `edit_team` | Invites admins, promotes members, removes members, or cancels invitations. Every change needs the user's explicit confirmation. | `invite`, `update_member`, `remove_member`, `cancel_invite` |
| `edit_org` | Renames the workspace, or sets, clears, or copies its brand. | `rename`, `brand` |
| `inspect_billing` | Read-only. Reads the plan, AI usage, spending settings, integration allowances, and available plans. It cannot change billing. | `read` |
| `inspect_profile` | Read-only. Reads the user's profile, personal theme, and the workspace brand. | none |
| `edit_profile` | Updates the user's display name, personal theme, or both. | `name`, `personal_theme` |
| `notify_user` | Emails the user at their verified address or sends them a Slack DM. Email to anyone else waits as a preview until the user presses Send. | `email`, `slack` |
| `inspect_documentation` | Read-only. Searches the cfo.ai documentation and reads whole pages. | `search`, `read` |
## Fields every tool accepts
Every tool except `list_orgs` also accepts these two fields at the root. The server removes them before the tool runs.
| Field | Meaning |
| - | - |
| `org_id` | The workspace, by ID or slug. Optional when the user belongs to one workspace. See [Choose the workspace](#choose-the-workspace). |
| `_context` | The in-app editing context, all optional: `layerId` (the active Scenario's ID), `pageId` (the active Page's ID), `timeZone` (such as `America/New_York`), and `utcOffset` (such as `-04:00`). Send `timeZone` or `utcOffset` when relative dates matter. Any other key is refused with **\_context must contain only layerId, pageId, timeZone, and utcOffset**. |
### The Scenario a call uses
Most model tools take `scenario`, by name or returned reference. When a call omits it, the tool uses the Scenario in view: the one `_context.layerId` names, or Main when `_context` names none. Pass `scenario` on every write so the target does not depend on context. [Create and update formulas](/agents/write-formulas#write-in-a-scenario) quotes the sentence a table write adds when `scenario` was omitted.
## Shape each call as one action block
A tool with several actions takes exactly one block named for the action, with that action's fields inside it. There is no flat `action` field.
```json theme={null}
{ "read": { "target": "Operating Plan", "rows": ["Revenue"] }, "scenario": "Main" }
```
* Each tool's input schema says, in its description, which blocks exist and which fields sit beside them: "Fill exactly one action block: list, read. Fields beside the block apply to it: as\_of\_point, scenario."
* An empty block selects an action that needs no fields, such as `{"list": {}}`. A block set to `null` counts as not filled.
* Fields beside the block apply to it: `scenario` on most model tools, `as_of_point` on `inspect_pages` and `inspect_scenarios`, `validate` on `edit_canvas_blocks`, `target` on `edit_dimension_mappings`, and `idempotency_key` on `edit_memory`. A block that takes such a field also accepts it inside; when both places carry it, the two values must match.
* Single-purpose tools take their fields directly at the root, as the table above shows. `notify_user` takes an `email` block, a `slack` block, or both.
* The schema root is a plain object, so every client can see the blocks. The rule that exactly one block is filled is enforced when the call runs.
* Unknown fields and retired request shapes are refused before anything runs. Nothing is translated from an older shape.
### Refusals for a wrong shape
A refused call changes nothing. Its message names the problem without repeating any value you sent.
| Mistake | Message |
| - | - |
| No block filled | `no action block is filled; the call takes exactly one of [...]` |
| Two blocks filled | `the call takes exactly one action block; [...] are filled` |
| A flat `action` field | `action "read" is the read block; the fields of that action belong inside it: {"read": {"target": ...}}` |
| `action` inside a block | `the read block takes no action field; the block name selects the action` |
| A block that is not an object | `read must be an object holding that action's fields` |
| A root field that is not a block | ` is not an action block; the blocks are [...]` |
| A field the block does not take | ` is not a field of the block` |
| A beside-field inside a block that does not take it | ` belongs beside the block, not inside it` |
| The same field beside and inside, with different values | ` is beside the block and inside it; a call carries one , in either place` |
| A value of the wrong type or shape | ` received ; it accepts `, such as `/sections/0 received a string of 9 characters; it accepts an object whose section is one of …`. At an action's root, the path reads `the input`. |
A shape is described by type: a string by its length, an array by its item count, an object by up to 8 of its key names, and a number, boolean, or null by its type.
The refusal arrives with `isError: true` and this structure:
```json theme={null}
{
"error": {
"code": "invalid_input",
"message": "the call takes exactly one action block; [\"list\" \"read\"] are filled",
"transient": false,
"validation": {
"issues": [{ "path": "", "code": "invalid_value", "message": "..." }],
"accepted_fields": ["..."],
"required_fields": ["..."],
"truncated": false
}
},
"state_changed": "no"
}
```
`validation` lists at most 20 issues; `truncated` is true when there were more. `accepted_fields` lists the chosen block's own fields plus the fields beside it.
## Choose the workspace
`list_orgs` takes no input and returns the workspaces the user can use through MCP:
```json theme={null}
{ "orgs": [{ "org_id": "4127", "name": "Example Co", "slug": "example-co", "role": "admin" }] }
```
Pass a returned ID or slug as `org_id` on later calls. A number, or a string of digits, always names a workspace ID; anything else is read as a slug.
| Situation | Answer |
| - | - |
| `org_id` omitted, one workspace | That workspace is used. |
| `org_id` omitted, several workspaces | `org_id is required: you belong to cfo.ai workspaces — pass one of [ (), …] (or call list_orgs)` |
| `org_id` omitted, no workspace | `no org_id supplied and you are not a member of any cfo.ai workspace` |
| A workspace the user is not a member of, or a slug that matches none | `you do not have access to this workspace`. Unknown and inaccessible workspaces get the same answer. |
| A numeric `org_id` that is not a positive whole ID | `a numeric org_id must be a positive whole cfo.ai workspace ID; pass slugs as strings`, or for zero `org_id must be a positive numeric cfo.ai workspace ID` |
| `org_id` neither a string nor a number | `org_id must be a string or number` |
| A workspace that has not started its free trial or a plan | `Start the free trial in cfo.ai before using its MCP tools`. `list_orgs` leaves such workspaces out, and answers with this message when none remain. |
| A deactivated workspace | `This workspace has been deactivated. Contact help@cfo.ai for assistance.` |
| A workspace paused while cfo.ai investigates an issue | `This workspace is temporarily unavailable while we investigate an issue. We'll report back soon.` |
## References and links
* Selectors accept readable names, returned references, or IDs, unless a field says it takes less. Copy references from inspection results instead of building IDs.
* A returned reference has the form `Name#hex`, such as `Revenue#a1b`. The suffix is a prefix of the object's identity, at least three hex digits, and unique among objects of its kind when the result was written. Later creates can lengthen it; an older reference keeps resolving while it stays unique, and a fresh inspection carries the current spelling.
* A name that is ambiguous or matches nothing is refused with `error.candidates` when alternatives are known: each with `display_name`, `ref` to pass back, `kind`, and, when ranked, `score` and `match`.
* A result names each object with `ref`, the selector to pass back, and `url`, the address that opens it in cfo.ai.
* A write also returns a resource link for each thing it created or changed, described "Open in cfo.ai" and meant for the user. End your reply with the link a tool returned, exactly as printed; a link you assemble yourself will not open.
## Results
Every result is JSON text. A result that is a JSON object also arrives as `structuredContent`. A `validate_code_blocks` call with `visual_review` also returns the render as image content.
### Write receipts
A write receipt opens with a shared head. Tool-specific sections sit beside it.
| Field | Values |
| - | - |
| `status` | `applied`: everything asked for is saved. `partially_applied`: some items are saved; `items` says which. `rejected`: nothing is saved because the request or every item was refused. `no_op`: nothing is saved because saved state already matched. `blocked`: nothing is saved because a dependency or a rule refused the write. |
| `state_changed` | `yes`, `no`, or `unknown` |
| `counts` | `requested`, and when nonzero `created`, `reused`, `applied`, `skipped`, `rejected`, `unchanged`, and `unknown` |
| `items[]` | One entry per requested item, in request order: `ref`, `display_name`, `status` (`created`, `reused`, `applied`, `skipped`, `rejected`, `unchanged`, or `unknown`), and `reason` when it did not land |
| `readback` | `status`: `complete`, `partial`, `unavailable`, or `not_needed`, with `reason`. A write whose readback is unavailable still saved. |
| `warnings[]` | `code`, `message`, and `ref`: things the saved write will not do as asked |
| `error` | Present exactly when `status` is `rejected` or `blocked` |
An item whose status is `unknown` may or may not have landed. Read current state before you repeat it.
### Refusals and failures
* A refusal carries `error` with `code`, `message`, `transient`, and, when known, `candidates` and `hint`, beside `state_changed`. Codes include `ambiguous`, `not_found`, `invalid`, `blocked`, `conflict`, `stale_target`, `selection_too_wide`, `file_too_large`, `result_unavailable`, and `browser_validation_unavailable`.
* `result_unavailable` reads **The call's outcome is unavailable. Read current state before repeating a write; repeating it could apply it again.**
* A result with `ok: false` and an `error_kind` is an answer, not a crash. Use its message and `hint` to choose the next call.
* Refusals and fully rejected writes arrive with `isError: true`. A write that saved some items stays a success, with each item's status.
* `transient: true` means the same call can succeed later without changes. A failure that knows whether it is retryable adds `Transient: true.` or `Transient: false.` to its text and `{"error": {"message", "transient"}}` to its structured content. The server never retries for you.
## Pagination and truncation
A list cut short carries `truncation` and, when it can continue, `next`:
* `truncation.by` says what cut it: `response_budget` (the next item did not fit one result), `count_limit` (the page reached the `limit` you set), `row_limit`, or `depth`.
* `truncation.shown` counts what came back, and `truncation.total`, when counted, what the selection matched. No `truncation` means the result is complete.
* `next` holds the values that continue the read. Pass a cursor back unchanged, with the same tool, action, target, filters, and snapshot.
| Tool and action | Page size | Continue with |
| - | - | - |
| `inspect_pages` `list` | `limit`. Without it, every page summary that fits one result; `truncation.total` counts all pages. | `next.after` as `after`, with the same `scenario` and `target` |
| `inspect_pages` `read` | A page whose block list was cut carries its own `truncation`. | — |
| `inspect_scenarios` `list` | `limit`. Without it, every visible Scenario that fits one result. | `next.after` as `after` |
| `inspect_history` `changelog` | At most 100 entries per read; `limit` takes up to 100. Without it, as many of those as fit one result. | `next.before` as `before` |
| `inspect_history` `changelog` with `search` | Each read searches up to 1,000 entries, newest first, and reports how many it read in `entries_searched`. | The `before` value the result carries |
| `inspect_table_blocks` `list` | `limit` up to 500. Without it, every table that fits one result. | `next.after` as `after` |
| `inspect_table_blocks` `read` and `compare` | `limit` up to 500 grid rows; default 500. Parents and descendants count separately. | `next.after` as `after`; it binds the table, selection, Scenario, and snapshot |
| `inspect_table_blocks` `items` | `limit` up to 500 items; default 500. | The last item's spelling as `after`, including an empty string |
| `inspect_table_blocks` `variable` and `dimension` | `limit` up to 100 saved formula definitions per page; default 100. | `next.after` as `after`, with the same `target` |
| `inspect_dimension_mappings` | `limit` rows per lookup. Without it, every row of each lookup, or as many per lookup as fit one result. Each lookup's `row_count` is its true total. | That lookup's `next` as `after`, with the same `keys` |
| `inspect_canvas_blocks` | A selection larger than one result is cut short. See [Canvas blocks for agents](/agents/canvas-blocks#read-saved-canvas-blocks). | `next` holds the arguments that read the rest |
A table `read` that selects rows, columns, or periods, limits `depth`, continues from `after`, or stops early returns `partial: true` at its root. `coverage.delivery` says whether the response reached the end of that selection, and `coverage.calculation` whether its values finished calculating.
A changelog entry larger than one page keeps its place in the list. Without `include_details`, it comes back with an empty `changes` list and no `blocks` list, and `changes_truncation` and `blocks_truncation` beside them count what each list held. With `include_details`, it keeps every change, and a result larger than the allowance is replaced whole by the allowance marker.
## Read the Model at an earlier point
`inspect_pages`, `inspect_table_blocks` (every action except `bounds`), `inspect_canvas_blocks`, `inspect_dimension_mappings`, and `inspect_scenarios` take `as_of_point`. Without it they read the current state. It holds exactly one point type:
| Point type | Meaning |
| - | - |
| `change_log_id` | One of the workspace's changes, as a write receipt or `inspect_history` reports it. The snapshot is the Model right after that change. |
| `timestamp` | An exact instant in RFC 3339 with a time zone, supplied by the user, the context, or a tool result. |
| `calendar_boundary` | `unit` (`day`, `week`, `month`, `quarter`, or `year`), `offset` (0 this period, -1 the previous, 1 the next), and `boundary` (`start` or `end`), in the user's time zone from `_context`, or UTC without one. Weeks start on Monday. Use it for "yesterday" or "last month". |
| `relative_duration` | `amount`, `unit` (`minute`, `hour`, `day`, or `week`), and `direction` (`ago` or `from_now`), from server time. A day is 24 hours and a week seven days. |
`inspect_history` `changes_between_points` takes a `start` point and an optional `end` point, which may also be `latest: true`. A `change_log_id` there resolves to the workspace's latest change at or before it.
## Safety annotations
Each tool carries standard MCP annotations, which clients use to decide which calls need the user's approval:
* `readOnlyHint: true` on every `inspect_*` tool, `validate_code_blocks`, and `list_orgs`.
* `openWorldHint: true` on tools that can reach outside cfo.ai, notify a person, send an invitation, or change an outside integration: `edit_pages`, `edit_triggers`, `edit_profile`, `edit_integrations`, `edit_team`, `edit_org`, `call_connected_api`, `notify_user`, and `validate_code_blocks`.
* `destructiveHint: false` only on `notify_user`. Every other write keeps the MCP default, which treats it as possibly destructive.
## What the server does not publish
Ari has tools that an outside agent does not get, because they need the cfo.ai app, belong to cfo.ai's own agents, or are switched on per workspace by a release setting that the server's single tool list cannot follow.
| Kind | Not published | Instead |
| - | - | - |
| Cards and views shown in the cfo.ai chat | Asking the user questions in a card, playing a Guide, showing a Scenario or table rows on screen, scenario confirmation cards, and sign-up steps | Ask the user in your own conversation, and describe what to open. |
| cfo.ai's own agents | Delegating to background agents, task tools, the ingestion agent's query, source, and job tools, and reports to cfo.ai engineering | Request the outcome, such as `edit_integrations` `start_ingestion`, and read its progress with `inspect_integrations` `ingestion`. cfo.ai runs the workers and jobs. |
| Ari's own runtime tools | Its code sandbox and files, web search, chat attachments and spreadsheets, calling other MCP servers, and reading past chats | Use your own client's tools. |
| Tools switched on per workspace | Downloading a Page as a spreadsheet, image generation, and the publication library | None through MCP. The user can download a Page as a spreadsheet in cfo.ai, or ask Ari for one. |
# Read and explain formulas
Source: https://docs.cfo.ai/agents/read-formulas
For agents: read a Variable's or Dimension's saved formulas with inspect_table_blocks, connect them to the values a table shows, read at another grain or an earlier point, explain one cell's inputs, and handle a table that cannot calculate.
Use `inspect_table_blocks` with its `variable` block when a user asks how a Variable is calculated, or why it behaves differently by segment or period. Use its `read` block to see which formula actually produced the numbers in a table.
## Read a Variable's saved formulas
```json theme={null}
{
"scenario": "Main",
"variable": { "target": "Gross Margin" }
}
```
* The result lists every formula definition saved on the Variable, each with its condition and its Actuals or Forecast range.
* Add `conditions` only when the question needs one exact saved scope, such as `["[Department = \"Sales\"]"]`. Leave it out when the user wants every formula.
* A Variable with many formulas returns them in pages. To read the next page, copy the response's `next.after` into `after` in the same `variable` block, with the same `target`.
* Leave out `target` to list every readable Variable in the Scenario, as the same entries, in saved order. `next` continues the listing.
Use the `dimension` block the same way to read a Dimension's saved definitions, and the `items` block to list its items.
### What reads each object
Each `variable` or `dimension` entry carries `dependents`, what reads the object at that snapshot:
| Field | Meaning |
| - | - |
| `total` | Every dependent |
| `formulas`, `blocks`, `settings`, `manual_items` | Counts by kind |
| `inaccessible` | Dependents this user cannot read |
| `items` | Descriptions of up to 20 dependents |
A `delete` without `force` is refused exactly when `total` is above 0, and deletes at once when it is 0.
### How each definition's scope is described
Each definition states its `time_grain` when it is dated, and its reach in words. [How results describe a formula's reach](/agents/formula-scope#how-results-describe-a-formulas-reach) explains the wording.
### Imported Variables and Dimensions
For a Variable or Dimension read from an imported source, the entry's `source_default` also states `order`, such as `last by MONTH` or `last by source order`. It says how a column that shows one row per record picks that row when a record has several: the latest row by the source's date column when the source has one, or the last row in source order when it has none. A column that sums its rows has no `order`.
### Read several at once
`target` on the `variable` and `dimension` blocks also takes a list of references:
```json theme={null}
{
"scenario": "Main",
"variable": { "target": ["Amount#a4d", "Probability#058"] }
}
```
The response holds one entry per reference, in the order given, all read at one snapshot. Each imported source's query is stated once under `sources`, and an entry's `source.query_ref` names its key there. A reference that matches nothing is refused in its own entry while the others still answer.
## Connect formulas to the values a table shows
Use the `read` block on the table that actually shows the result:
```json theme={null}
{
"scenario": "Main",
"read": {
"target": "Operating Plan",
"rows": ["Gross Margin"],
"formula_origins": "all"
}
}
```
* Each row and column already carries its formula per range under `formulas`, and a cell that breaks its row's formula says so in the cell.
* `formula_origins: "all"` adds, for every row, each formula the engine ranked for it, best first, with the ones that computed a cell marked selected. `"zeros"` adds them only to rows that show a zero, blank, or error. The default is `"off"`, except that a selection of exactly one cell defaults to `"all"`.
* A row computed by a formula saved outside this table's rows and columns names that formula's owner and condition in `inherited_from`.
* `include_edit_rules: true` adds each returned row's and column's editing rules: the actions a write accepts there, its own Actuals and Forecast defaults with ownership and writability, and its formula roles.
Use the same Scenario for the formula read and the table read. The saved formula explains the intended calculation; the table confirms which formula applies to the selected period, segment, and Scenario.
### Select rows and columns
* Select rows and columns by the references a read prints, by unambiguous names or full paths, or by coordinate predicates. A name in backticks selects the same row as the bare name.
* An item row is selected by its path from its Variable or axis Dimension, one bracket per breakdown level, as a readback prints it: `Revenue[West]`, `Revenue[West][Enterprise]`, `Employee ID[E-207]`.
* A nested item written without the items above it, such as `Revenue[Enterprise]` for `Revenue[West][Enterprise]`, selects the one saved coordinate that ends in it. When several do, the read is refused as ambiguous and names each.
* The item that holds rows with no value displays as **None** and is spelled `empty:None` in a selector, such as `Region[empty:None]`.
* A text item is printed as written, even when it contains a colon. The read's `member` field, a receipt's `members` list, and a row reference such as `Cash Step[Expected in: open invoice #483]#c00` all spell it `Expected in: open invoice #483`, and a formula matches that exact spelling.
* A read that names rows or columns holds every item of the layout, zero and empty items included, whatever the saved display hides. A read that names none shows what the app displays.
### Which targets take a write
Each rendered target's formulas say whether `formula` and `clear_formula` can be written there. A target whose cells a direct write would refuse, such as cells read from an integration or cells whose origin the read cannot establish, does not offer those actions. Its formula carries a `readOnlyReason` instead, so a write is not attempted where it would fail.
The spelling differs by level: a rendered target's formula spells it `readOnlyReason`, while a row or column that cannot be written spells its own reason `read_only_reason` beside `writable: false`. Dated Actuals and Forecast defaults keep their own writability.
### Facts the read computed
A `read` result also carries `figures`, facts about the returned numbers that no row or column states. Each key appears only when its fact holds:
| Key | Present when |
| - | - |
| `signs_as_stored: "mixed"` | The outermost returned rows differ in sign, because every value keeps the sign it is stored with. |
| `currency` | The currency-formatted figures share no single saved code: `no saved code`, `some figures have no saved code`, or `saved codes differ`. |
| `window` | The table saves no valid date range and returns no period columns or rows, so every value aggregates over a defaulted range. It holds `start`, `end`, `granularity`, and `defaulted: true`. |
| `source_grain` | A source query holds several rows for one item of the Dimension that keys the rows. Each entry names the `query`, `dimension`, `rows`, `members`, and `max_rows_per_member`. |
Receipts from `edit_table_blocks` carry no `figures`.
## Read a table that compares Scenarios or periods
* A read of a table that compares Scenarios returns every compared Scenario's value when `columns` names periods: one column reference per Scenario and period, each labeled with its Scenario.
* When a compared Scenario returns no values, the result carries a `comparison_scenario_missing` issue, and coverage reads `incomplete`.
* `comparison` defaults to `"saved"`, the table's saved comparison. `"none"` reads the selected Scenario's own values and formulas, and `formula_origins: "all"` annotates such a read with the formula each cell used.
* A `comparison` object compares the selected Scenario with each Scenario it names in this one read, whatever the table saves, and leaves the saved table unchanged: `{"scenarios": ["Budget"], "measures": ["VALUE", "DELTA"]}`. `measures` takes up to three of `VALUE`, `DELTA` (the compared Scenario minus the selected one), and `PERCENT` (`DELTA` over the selected Scenario's value); it defaults to `VALUE`.
* A column or row that is not the selected Scenario's own reads `writable: false`, with a `read_only_reason` that says what it shows: another Scenario's values, saved in that Scenario; another period's values, saved in that period; or the difference between two Scenarios or two periods, which is calculated and has no saved value.
* The comparison qualifier alone makes nothing read-only. The selected Scenario's own column or row is as writable under either reference the read prints, with or without the qualifier, as the same cells are in a read with no comparison.
## List tables and their layouts
* `list` without `limit` returns every table in the Scenario, and pages with `next.after` when the result ends early.
* With `target`, it returns each named table's full saved layout. A table whose layout is larger than one result comes back as the summary entry a list without `target` returns, with a `layout_problem` note that states why; a `read` of that table pages through its rows and columns.
## Read a monthly table by quarter or year
`read` takes `grain: "QUARTER"` or `"YEAR"` to read a table's Date periods at a coarser grain than the saved table's own, without changing the table:
```json theme={null}
{
"scenario": "Main",
"read": { "target": "Operating Plan", "rows": ["Bookings"], "grain": "YEAR" }
}
```
* Each period holds what the Model calculates at that grain: the Variable's time rollup (its base periods summed, closed, or averaged), or a timeless formula recomputed there.
* A Variable with neither reads `#NO_ROLLUP` in those cells, explained as no formula computing it at that grain, with its values in the table's own periods. The app shows such a cell as 0, and a Scenario or time comparison read at a coarser grain also shows 0 there.
* The period columns come back `writable: false`, because their references spell the date each period starts and would name the saved month column in a write. A read at the grain the table already shows is the table's plain read, with writable period columns.
* A finer grain, or a table with no Date axis, comes back as an `invalid` result rather than a tool error, with its reason: `grain QUARTER is finer than this table's YEAR periods; a read's grain shows the periods at their own grain or rolled up to a coarser one`, or `grain QUARTER sets the grain of a table's Date periods, and this table has no Date axis`.
* Continuation cursors carry the grain.
A period selector can be spelled the way people write it, and it is read under each column's own grain: `2027` or `FY2027` selects a year column, `Q3 2026` or `2026 Q3` a quarter column, and `Mar 2026` a month column. `2027` never selects January of a monthly axis. The stored spelling, such as `2027-01`, still works everywhere.
## When the table cannot calculate
When the calculation service cannot compute a table, `read` still returns the saved configuration: the rows, their default formulas and formatting, and the saved column axes. No cells come back.
* Each axis entry is marked `axis: true` and carries its Dimension, grain, and writability, such as `{"ref": "Date#10b", "axis": true, "dimension": "Date", "grain": "MONTH", "writable": true}`, so an empty `columns` list never stands in for a Date axis that exists.
* The result carries a `calculation_unavailable` issue. When the calculation service explained the failure, the message includes `The calculation service reported : `, such as `table_config/external_column_not_mapped: external column Class L1 is not mapped in table …`, so you can name the cause instead of guessing.
* The issue also carries `category` (`capacity`, `model`, `request`, `dependency`, or `internal`), a `transient` flag, and a sentence stating what the category means. Retry only a transient failure: a calculation that did not finish within its time limit is `capacity` and not transient, while a service that did not answer is `dependency` and transient.
* A failure the calculation service did not classify carries the error as raised, clipped to 500 characters, with no `category` or `transient` flag, so assume nothing about a retry.
A table whose saved comparison names a Scenario that no longer exists still reads. The comparison columns are dropped for the calculation only, and a `table_problem` issue names the former Scenario. An `edit_scenarios` `delete` lists the readable tables that compared against the deleted Scenario as `tables_comparing_against_it`.
## Explain one cell's inputs
For a single cell, select one row and one column and add `include_inputs: true`:
* The result shows one level of the formula's inputs and where each value came from: lookup coordinates, aggregate units, the selected formula, and source dates. Read an input by its own reference to go deeper.
* `sources` lists the imported tables whose fetched values fed the cell, or fed the cells that the cell's formula directly reads, one whole table per entry and never a record.
* `sources_status` says how complete that list is: `available`, `truncated` when one of the engine's bounds cut the search, or `not_retained` when the calculation recorded no such table within that reach, which says nothing about whether imported data feeds the cell.
* `include_inputs` needs `comparison: "none"`. On a time or Scenario comparison, the read answers with an invalid-input refusal that names the option, not a tool error.
* The inputs come from a second calculation after the table's own. When it is still running at the read's answer deadline, the read answers with the calculated values and this note: "include\_inputs: the calculated values on this page are complete. Its input rows are unavailable: the second calculation, which adds each row's formula inputs, was still running at the read's answer deadline, and the read answers within 119 seconds."
Every `read` also reports `calculation.source`. When a value is in doubt, [Verify the calculated values](/agents/validate-and-verify#verify-the-calculated-values) says when to read again with `recalculate: true`.
## Read the Model at an earlier point
Use `as_of_point` when the user asks about the Model as it was at a previous saved change; leave it out for the current state. [Read the Model at an earlier point](/agents/mcp-tools#read-the-model-at-an-earlier-point) lists the point types.
* `inspect_canvas_blocks` takes it too, and returns each block's `dataset_layouts` at that point.
* `inspect_table_blocks` `items` reads the source run the Model used at that point.
* `bounds` reads current source state and refuses `as_of_point` with `bounds reads current source state; as_of_point applies to items`.
## Explain the business meaning
Lead with what the formula does. Then explain its inputs, the Scenario, the Dimensions involved, and whether Actuals or Forecast changes the result. Include assumptions only when they matter to the user's question.
# Table view grammar
Source: https://docs.cfo.ai/agents/table-view-grammar
For agents: the breakdown notation that describes a Table block's rows and columns, and the typed layout fields that carry it in edit_table_blocks and Canvas datasets, with orientation rules and why a table comes back empty.
A table's view is its rows and columns. Tools take them as typed lists: each entry names one Variable, one Dimension, the Date axis at a grain, a blank row, or a fork into side-by-side branches, and a Variable entry can carry its own breakdown of Dimension levels. The same shapes have a compact written form, the breakdown notation, which the `build-model` skill uses and which reads well in an explanation. Requests always use the typed fields, never the bracket string.
## The written notation
| Form | Meaning |
| - | - |
| `[Department]` | Split by one Dimension: one row per Department item. |
| ``[`Spend Type`]`` | Backticks quote a Dimension name that is not a single word. Written bare, the parse stops at the second word. |
| `[Department, Level]` | A comma nests: Levels sit under each Department. |
| `[Department, {Level, Owner}]` | Braces fork: under each Department, a Level breakdown and an Owner breakdown sit side by side. Whatever follows the brace applies to every branch. Each path from the top of a breakdown to its end can fork once. |
| `[Department, {Level[Department = "Engineering"], Owner}]` | A branch condition: Level sits under Engineering only, while Owner applies under every Department. A branch condition names one item of an earlier Dimension on the same path; a condition over several items is not accepted. |
| `{Level, [Owner, Date.Month]}` | A branch that nests is bracketed: Owner splits by month, while Level does not. |
| `[Region in {East, West}]` | An item filter keeps only the listed items instead of every item in the Dimension. |
| `[Date.Month]` | A grain buckets a date Dimension: Day, Week, Month, Quarter, Half, or Year. A table has one Date axis. |
In a layout, an item filter is always a list of items to keep. Ranges, exclusions, and `where` conditions have no layout field. To narrow an existing axis by a condition, such as `Region not in {"West"}`, set the axis's `members` condition with `row_updates` or `column_updates`, as [Filter the items an axis shows](/agents/write-formulas#filter-the-items-an-axis-shows) describes.
## Table layout fields
`edit_table_blocks` `create` and `reshape`, and `structure.insert` on `update`, take rows and columns as lists of nodes. `inspect_table_blocks` returns the same saved shape under `layout.rows` and `layout.columns`.
| Node field | Meaning |
| - | - |
| `variable` | A Variable row or column, by name or reference. |
| `breakdown` | The levels nested beneath that Variable, outermost first, up to 10. Each level is a Dimension name, a level object, or a `branches` fork. |
| `dimension` | A Dimension node: a breakdown level, or the value axis of a record table. A name that matches one Dimension reuses it; an unmatched name creates it, with `items` and `data_type` when given. |
| `time` | The Date axis at `DAY`, `WEEK`, `MONTH`, `QUARTER`, `HALF`, or `YEAR`. It creates the Date Dimension when the Model has none. |
| `filter` | `{"include": [...]}`: the items of a Dimension this node declares or places that stay visible. List order does not set display order. A retained node takes `members` instead. |
| `when` | `{"dimension": ..., "equals": ...}`: a branch condition that restricts this level to one item of an ancestor Dimension on the same path. |
| `branches` | A fork: 2 to 10 sibling paths, each a list of up to 10 levels. A path from the top to the end may fork once. |
| `space` | `true`: a blank separator row on create, or a saved empty slot on reshape. |
| `summary` | `NONE` keeps the parent's own modeled value. `SUM`, `AVG`, `MIN`, `MAX`, `COUNT`, or `LAST` combine the children instead, independently of the time rollup. |
| `role` | `DIMENSION` lists a Dimension's items; `VARIABLE` holds one value of the Dimension for each item on the other axis. |
A breakdown in the written notation and in the typed fields:
```text theme={null}
[Department, {Level[Department = "Engineering"], Owner}]
```
```json theme={null}
{
"variable": "Headcount",
"breakdown": [
"Department",
{
"branches": [
[{ "dimension": "Level", "when": { "dimension": "Department", "equals": "Engineering" } }],
[{ "dimension": "Owner" }]
]
}
]
}
```
Levels that a mapping relates pair each key item with its mapped item. Levels that one source table's rows carry together pair as in those rows. Levels that neither relates pair in every combination.
## Canvas dataset layouts
A Canvas dataset declares the same visible shape in a flatter form, under its `layout.rows` and `layout.columns`:
| Entry field | Meaning |
| - | - |
| `variable` | An existing Variable to read. |
| `breakdown` | The shared levels beneath it, outermost first, up to 10. A level is a Dimension name, or an object with `dimension` or `time`, and optional `include` and `when`. |
| `branches` | Sibling paths as one list of levels. Each level names its path with `branch`, a number from 1 to 10; levels with the same number form one path in list order. A fork takes 2 to 10 paths, each of up to 10 levels. |
| `after_branches` | Levels that follow every branch. Each complete path is `breakdown`, then one branch, then `after_branches`. |
| `include` | The items to keep on that level, up to 50, written directly on the level. |
| `dimension`, `time` | A standalone grouping axis entry, such as the shared dates on `columns`. |
An entry with `branches` and no `variable` forms a shared grouping axis. [Canvas blocks for agents](/agents/canvas-blocks#datasets) covers the rest of a dataset.
## Variables and Dimensions as rows
Each row in a table's view is either:
* a **Variable**, which measures something, such as Revenue, Headcount, or Gross Margin; or
* a **Dimension used as a mapping**, where each cell shows that Dimension's item for its row and column instead of a number. This suits a database-style list of entities and their attributes.
A Variable row can carry its own breakdown, splitting only that row differently from the rest of the table. A mapping row takes no breakdown; a `breakdown` on a Dimension is refused before anything saves, with an explanation of the record-key and mapped-field roles.
## Orientation
By default, a view with a measured Variable keeps values on rows, and its Dimensions, including time, on columns. A flipped view puts the Variables on columns instead. A mapping-only view already reads down the rows: source keys on rows, and their mapped attributes across the columns.
There is no separate transpose action. To flip a table, reshape it by moving the Variables between rows and columns. Moving an axis changes the presentation, not the grain of any cell.
## When a table comes back empty
An empty result on a table with dates often means the table's window, the date range and grain it is set to show, does not cover the dates that hold values. Writing dated values does not move the window, and a wrong window is fixed on the table itself, with `window`. Two other causes are common:
* An item whose cells are all empty is hidden when the table hides empty items.
* An item outside a formula's condition reads empty.
Check the table's window before you rework its breakdown.
# Validate and verify formula writes
Source: https://docs.cfo.ai/agents/validate-and-verify
For agents: how cfo.ai validates a formula write before it saves, dry-run a new table, read everything a write receipt reports, handle deadlines and readbacks, and verify that the calculated values match the business rule.
Validation checks a formula's syntax and references before a write saves. Verification checks whether the resulting values match the business rule. Do both: a formula that saves can still calculate the wrong thing.
## Validate before you save
Every `edit_table_blocks` write validates before it saves, and a write is atomic: the whole change saves, or none of it does. A refused write saves nothing and says which field caused the refusal. Validation errors block a write; formula warnings do not.
Validation checks:
* expression syntax,
* condition syntax,
* Variable and Dimension references,
* function names and arguments, and
* semantic compatibility.
To validate without saving, send a `create` with `dry_run: true`. It checks the new table's structure, references, and formula syntax, and returns `valid`, `warnings`, and `model_effects`.
* A dry run returns no values, because calculation does not run, and it does not detect circular references.
* Its `model_effects` carry the totals statements the committed receipt would put under `level_defaults`.
* With a `tables` list, `dry_run` applies to every table in the request.
* `update`, `reshape`, and `delete` take no `dry_run`. A call that sends one is refused, because edits to saved tables and deletions have no preview. Correct any reported error, then send the intended change.
## Read the receipt
Inspect the write's result before you move on. A table write's receipt reports:
* **What it touched.** Each row and column touched, with the formula it now carries per range.
* **`formula_warning` issues** for formulas that need attention. Most follow a formula that saved, but the last case below follows a write that saved nothing. A `create` reports the same cases as `creation_warning`. The cases:
* an imported Variable read in Forecast months;
* a constant that matches no Dimension item;
* a tie with an existing formula;
* a shared formula the write replaced;
* a read that lands on the generated default, 0, or null for a date or text Variable, because the input lacks one of the read's coordinates;
* a default saved at a grain coarser than the Model's base grain;
* a write in a Scenario other than Main that sets the value the Scenario already inherits. The write stores nothing, and the cell keeps following the Scenario it inherits from: "`` already reads this value from ``, so the write stored no value of its own: the cell keeps following `` and changes when `` changes."
* **Dates used as lookup keys.** A date used as a lookup key against a Dimension of timestamps is not reported when an item sits at that day's midnight and every item of the Dimension comes from its source rows, because the engine then selects that item. A Dimension with its own item list or formula keeps exact matching, and the same date in a condition is still reported.
* **`read_note` entries.** One per imported source that the written formulas read, stating where that source's rows sit on the calendar and which formulas read it. And one for the first Forecast month when the formula reads a prior period, a source, or a Variable with no value there, stating what the formula computed and what each read returned. A note describes the read the formulas asked for; it is not a problem to fix.
* **`scenario_warning`**, naming the Scenario the write used when the call left out `scenario`. [Write in a Scenario](/agents/write-formulas#write-in-a-scenario) quotes it.
* **`default_rollup`**, naming the time rollup each new dated row received, and the statement of the level defaults a breakdown's save wrote or removed.
* **Each default formula's scope**: its condition, its range, its Date grain when it is dated, and its reach. [How results describe a formula's reach](/agents/formula-scope#how-results-describe-a-formulas-reach) explains the wording.
* **A refusal**, when nothing saved. A receipt whose `status` is `rejected` or `blocked` saved nothing, and carries the same `error` object a refusal carries, beside `items[].reason`. A refusal that fits no branch of a union names the one field or shape that was wrong, and a refusal from the calculation service names the formula it was checking. [Refusals for a wrong shape](/agents/mcp-tools#refusals-for-a-wrong-shape) gives the wording of shape refusals.
To see the calculated values without a second read, ask for a readback in the same call with `readback.rows` and `readback.columns`. A selector the readback cannot resolve appears in the issues as `readback_selector_unresolved`, with the nearest coordinates, and the other selections still read.
## Deadlines and readbacks
An MCP request runs under the server's request deadline, a little under two minutes. A table write keeps part of that time, about 10 seconds, to deliver its own account of what happened, so the receipt arrives before the request ends.
* Every step after the commit runs under that answer deadline: the readback, the saved-layout echo, and the calendar checks. A write that committed answers with a receipt that says it saved.
* A write that has not committed by the answer deadline is rolled back and saves nothing. It answers: "This write saved nothing. Its transaction had not committed `` seconds after it began, when the tool's answer was due, so it was stopped and rolled back."
* The optional readback stops after at most 30 seconds, or 2 seconds before the answer deadline, whichever comes first.
* A write whose readback did not finish reports the outcome `committed_with_readback_unavailable`, with its saved revision and a `readback.reason`, such as "The readback's deadline passed while the table was still calculating. The calculation had not failed, and the committed change does not depend on it." The change is saved: do not send it again.
* When the calendar checks did not all finish, the receipt carries one more `creation_warning` on a create, or `formula_warning` on an edit: "The calendar checks of this write's formulas had finished N of M checks when this result was due. Those checks state where the source rows a formula reads sit on the calendar and which forecast periods a read of a source column leaves empty; no other part of this result depends on them."
If your client stops waiting before the answer arrives, wait until two minutes have passed since you sent the call. By then the write has either committed in full or saved nothing. Read the table, or `inspect_history` `changelog`, to see which, and only then decide whether to send it again.
## Verify the calculated values
After a material change, use `inspect_table_blocks` on the saved table. Check representative dates, segments, formula origins, and source information.
Every `read` result carries `calculation.source`, which says how its values were obtained: `cached`, `promoted`, `derived`, `seeded`, `full`, or `recalculated`.
When a value disagrees with what the user sees, with another read, or with arithmetic you can check, and the source is anything but `full` or `recalculated`, read once more with `recalculate: true`. That read:
* calculates the table from scratch, ignoring every cached or reused result;
* makes the fresh result the cached one, so the page shows it too;
* reports in `recalculated` whether the cached result differed, and in how many cells.
It needs `comparison: "none"` and costs a full calculation, so it is not for routine reads.
Examples of checks:
* Compare Gross Profit with Revenue minus Cost of Goods Sold.
* Check a Department formula for two different Departments.
* Check both sides of Last close after a formula-range change.
* Compare the intended Scenario with the Scenario that was written.
## Report clearly
State what was written, and whether verification matched the request. Disclose any inferred table, Scenario, or business assumption that materially affects the result.
# Create and update formulas
Source: https://docs.cfo.ai/agents/write-formulas
For agents: save formulas with edit_table_blocks through visible saved tables or on a Variable, build new tables and rows, declare mappings, set windows and values, write in a Scenario, delete objects, and read what each receipt reports.
A saved formula belongs to its Variable. When the user selects a visible row, column, or cell, the saved Table block gives the target that resolves the right Variable and scope. Every model formula is written through `edit_table_blocks`.
## Shape a call as one action block
`edit_table_blocks` has seven actions: `create`, `update`, `reshape`, `copy`, `delete`, `set_time_rollup`, and `update_calendar`. Fill exactly one block named for the action, and put that action's fields inside it.
* `scenario` sits beside the block and applies to it. It is also accepted inside the block; a call that names two different Scenarios in the two places is refused.
* A field the chosen action does not take is refused, with `accepted_fields` listing that action's own fields plus `scenario`.
[MCP tools and call shapes](/agents/mcp-tools#refusals-for-a-wrong-shape) lists every shape refusal.
## Write through the saved table
First read the table with `inspect_table_blocks` to get its visible row and column references:
```json theme={null}
{ "read": { "target": "Operating Plan" }, "scenario": "Main" }
```
Then send the related formula edits together in one `update`, with the same `scenario` as the read, so both resolve the same saved table:
```json theme={null}
{
"scenario": "Main",
"update": {
"target": "Operating Plan",
"row_updates": [
{
"rows": ["Gross Profit"],
"default_formulas": {
"actuals": { "formula": "Revenue - `Cost of Goods Sold`" },
"forecast": { "formula": "Revenue - `Cost of Goods Sold`" }
}
}
]
}
}
```
* A row with no Date scope takes `formula`. A row that runs across time takes `default_formulas`, with an `actuals` entry, a `forecast` entry, or both. An entry you leave out stays unchanged.
* `clear_formula: true` in place of `formula` removes the formula the row owns for that range.
* The server derives the Variable, the cell address, the formula range, and the write permissions from the visible target. Copy references from the inspection result, such as `Gross Profit#3f2`, instead of inventing IDs, positions, or range identifiers.
### Tables that save a comparison
A table that saves a Scenario or period comparison takes writes too. Name the selected Scenario's own column, with or without the comparison qualifier the read printed, and the write lands in that Scenario. A compared Scenario's column, a compared period's column, and a difference column take no write: the refusal says the table's saved comparison is left out, because those columns show values saved in another Scenario or period, or calculated.
## Formulas the items beneath a row inherit
`child_defaults` on a `row_updates` or `column_updates` entry holds the formulas the cells beneath the row inherit, one entry per breakdown Dimension at any depth:
* Each entry names its `dimension` and carries `actuals`, `forecast`, or, on a timeless table, `formula`. One `row_updates` entry can set the row's own formula and its children's together.
* A deeper level's entry is saved on the level above it. In Total by Owner by Deal, the Deal entry is the formula on `Total[Owner]`.
* An axis row, such as `Total[Stage]`, sets the formula for every item's children; an item row, such as `Total[Discovery]`, sets it for that item only.
* `clear: true` on an entry removes an owned timeless child formula; dated ranges clear through `actuals` or `forecast`. The item's own `clear_formula` removes only the parent row's formula.
## Create a visible derived row
A new Variable can be defined and placed on an existing saved table in the same change. `structure.insert` names the side, the new Variable, and its formula:
```json theme={null}
{
"scenario": "Main",
"update": {
"target": "Operating Plan",
"structure": {
"insert": [
{
"side": "rows",
"node": {
"variable": "Gross Margin",
"default_formulas": {
"forecast": { "formula": "iferror(`Gross Profit` / Revenue, 0)" }
}
}
}
]
}
}
}
```
`structure` inserts, then moves, then removes rows and columns. A removal drops a row or column and its subtree from this table, never the objects themselves, and every Variable and Dimension must keep a row or column on at least one table. The same update can write the cells of inserted rows with `cell_updates` or `values`.
To start a new table instead, use `create` with its complete `rows` and `columns`:
```json theme={null}
{
"create": {
"name": "Margin analysis",
"rows": [
{
"variable": "Gross Margin",
"default_formulas": {
"forecast": { "formula": "iferror(`Gross Profit` / Revenue, 0)" }
}
}
],
"columns": [{ "time": "MONTH" }]
}
}
```
The saved row makes the calculation visible, inspectable, and available for follow-up. A Variable row placed beneath a Dimension row saves its `default_formulas` open over that Dimension, so every item calculates the formula, under the same condition a later `row_updates` on that row writes.
### Create several dependent tables in one call
When a build needs more than one table, and a later table's formulas name Variables an earlier table declares, put the tables in a `tables` list inside one `create` instead of making one call per table:
```json theme={null}
{
"create": {
"tables": [
{
"name": "Workforce assumptions",
"page": "Workforce",
"rows": [{ "variable": "Hours per Month", "formula": "160" }],
"columns": [],
"window": { "granularity": "NO_GRANULARITY" }
},
{
"name": "Roster",
"page": "Workforce",
"rows": [{ "dimension": "Employee", "items": ["E-1", "E-2"] }],
"columns": [{ "variable": "Monthly hours", "formula": "`Hours per Month`$" }]
}
]
}
}
```
* `tables` holds up to 20 tables. They save in list order in one change, so a formula in the second table can name a Variable the first table declares.
* Two entries that name the same new `page` share it.
* Each entry carries its own `name`, `page`, `rows`, `columns`, and settings. `scenario` and `dry_run` sit on the request and apply to every entry.
* The response holds one receipt per table, in list order, and every table shares one `change_log_id`. A refusal of any table refuses them all and names the table.
### A new dated row gets a time rollup
A formula on a month row governs month cells only. Quarter, half-year, and year cells read the Variable's time rollup.
* A `create` or `insert` row may name `rollup`: `SUM`, `LAST`, or `AVERAGE`.
* When it names none and the table's periods are at the Model's base grain, the save writes the rollup the request implies, at every grain coarser than the base, for Actuals and Forecast: `SUM` for a number with no aggregation or with `SUM` or `COUNT`, and `LAST` for `aggregation_function: LAST`. It writes none for a timeless `formula`, `format: PERCENTAGE`, a value that is not a number, or any other aggregation.
* A row created on a table whose periods are quarters or years, or on a table without Date, gets no implied rollup.
* The receipt states the implied rollups in one `default_rollup` issue, and does not list them as `rollups` or count them as `default_formulas`.
* A ratio row created without `format: PERCENTAGE` gets `SUM`, so its year cell adds the monthly ratios. `set_time_rollup`, or a named `rollup`, corrects it.
A `default_formulas` formula written through a table whose periods are coarser than the base grain, such as a yearly table in a monthly Model, is saved at that coarser grain, and the receipt warns: the year cells compute the formula whatever their month cells hold, and the month cells do not read it. Write the formula on a row of a base-grain table, or in a `variables` entry, when it should govern the base periods.
### A breakdown's totals are written for you
When a table breaks a Variable down, `child_defaults` on the deepest axis row is the formula for the lines, saved on each line's full path. The save then gives every coarser level the tables show a default that sums the level beneath it, one ordinary formula per level, and the receipt lists them: "Revenue's level defaults, which add up its \[Customer, Line, Product] lines at each level the tables show: saved Revenue\[Customer, Product], Revenue\[Product] and Revenue."
* A value set on any row reaches every total above it.
* A formula an author writes at a level replaces the default there, and a default edited into other text stays with its author.
* The sums exist only while a line formula claims the lines. Clearing the line formula removes the sums left in its range.
* A Variable with `format: PERCENTAGE` gets no sums and no `0` on its lines. Each level above them gets the line formula or `NULL`, and the receipt states which: "saved the line formula on \[rows], so each of those rows computes that formula from its own inputs", or "saved NULL on \[rows], which reads blank, where the lines hold typed-in numbers or several formulas, or where their formula takes its own row's item of a dimension that row lacks or reads a variable with no value on that row".
* A row's `numberFormat` is display only and changes no formula.
* A `create` dry run runs the same totals step and rolls it back, so its `model_effects` carry the statements the committed receipt puts under `level_defaults`.
[Totals, aggregation, and time rollups](/concepts/totals-and-rollups) explains why totals work this way.
### Add items and write their values in one call
* An `update` adds the items that `dimensions[].add_items` or `replace_items` names before it resolves `values` and `cell_updates`, so one call can add `E9` to Entity and write `["E9", 42]` in `values`.
* A `create`, or an update that adds rows, can also fill a mapping column on the rows it adds. A Department cell on a new employee row saves the one quoted item, `"Sales"`; a new item joins the Dimension's list; and a value that is not one item is refused before anything saves, naming the cell.
* Cells of one lookup save in one mapping write, so filling 400 Departments costs about the same as filling one.
When a write changes a shared mapping row, the receipt's `shared_mapping_changed` notice names the first changed row and its item before and after, and counts the rest: "The shared mapping changed: EMP-001's Department moved from Engineering to Operations, and 3 more rows changed. Other tables and periods that use it can also change." A first assignment reads "is now Sales", and a cleared cell "was cleared; it was Sales". Every table that reads the mapping recalculates in the background after the call returns; `inspect_dimension_mappings` lists those tables.
### Where a timeless formula is written
`formula` and `clear_formula` write a Variable's timeless formula, which applies in every period, Actuals and Forecast alike. `default_formulas.actuals` and `default_formulas.forecast` write formulas limited to Actuals or to Forecast, and `create` and `update` both accept them.
* A `create` writes a timeless `formula` for a Variable it makes, including on a row of a table with Date columns. It refuses one on a row that places an existing Variable.
* An `update` writes a timeless `formula` on an undated row or column, and refuses one on a dated row or column.
* Both refusals use the same sentence, which says where each kind of formula is written.
A `child_defaults` clear on `create` goes through the same planner an update uses, so a child that owns no formula reports `unchanged`.
## An axis that only lists items has no formula to edit
`formula` and `clear_formula` edit the formula of a calculated value: a Variable, a Dimension shown as a value through its mapping or formula, or the Variable that encloses a Dimension breakdown. An axis that only lists a Dimension's items has no calculated value, so a formula edit aimed at it is refused with a reason instead of a resolution error. The reason:
* opens "this table axis shows its dimension with role DIMENSION: it lists the dimension's members, and no calculated value is on either axis";
* says that `dimensions` `add_items`, `remove_items`, `replace_items`, and `rename_items` edit saved items, and that a retained breakdown edits its enclosing Variable's formula;
* adds that "a dimension axis whose role is VARIABLE holds one value of that dimension for each member on the other axis, and a formula on it saves the rule that calculates that value".
The `role` field's description and the item-condition refusal carry that same sentence. A formula that generates a source-linked Dimension's items has no write in the tool: the person writes it in a table, from the Dimension's label-row menu, with **Edit dimension formula**.
## Save a Variable's formula without a table
When the requested formula has no visible table target, name the Variable in a `variables` entry. No table `target` is needed:
```json theme={null}
{
"scenario": "Main",
"update": {
"variables": [
{
"target": "Bookings",
"default_formulas": { "forecast": { "formula": "last(`Bookings source`)" } }
}
]
}
}
```
* The formula is saved on the Variable with one Date term at the Model's base grain, the same address a plain row on a base-grain table writes. A later `row_updates` write on a table that shows the Variable edits this formula instead of adding a rival.
* The same entry takes the Variable's settings, wherever it is read: `name`, `data_type`, `aggregation_function`, `preferred_time_property`, `format`, `decimal_places`, `currency`, and `variance_colors`.
* Item edits on a Dimension still name a table that shows it.
## Filter the items an axis shows
`members` on a `row_updates` or `column_updates` entry is the saved item filter of the selected axis. It takes one of two forms.
**A list of item names** replaces the filter, including a saved condition. `[]` clears it.
* The list may name `empty:None`, the rows with no value.
* Each name is checked against the Dimension's hand-added items and the source's spellings, then, for a name those do not hold, against the calculation's own item list. So an item a formula or mapping computes is saved, and so is a number spelled as the table's rows spell it, such as `13840.0`. Date and number items match by their saved text, such as `"2026-01-01 00:00:00+00"`.
* A name the calculation does not hold either is reported under `unmatched`. A list that names no item at all is refused with the Dimension's first items.
* Only an item filter may name `empty:None`. `add_items`, `replace_items`, `rename_items`, and a mapped item named `empty:None` are refused with "empty:None is the spelling of a dimension's empty member, which holds the rows with no value and exists without being added".
**A string** is a live condition on the Dimension, evaluated over its items at every calculation to narrow the saved list, so an item the saved list already leaves out stays out.
* Literals are quoted strings. A bare number is a reference offset, not an item.
* `Region not in {"West"}` keeps every named item but West. The empty item has no value, so a comparison on it is unknown; a null check inside `where` keeps it: `Region where (Region = null or not (Region in {"West"}))`.
* A condition may read a Dimension mapped from the axis, such as `Employee where Status = "Active"`. A condition on a Dimension the axis is not mapped from is refused, and the refusal names the step that makes it readable: `edit_dimension_mappings` maps that Dimension from the axis, which gives each axis item one item.
* A condition never reads a Variable. `Vendor where FutureSpend > 0` is refused with "a member condition on Vendor reads only Vendor", and the refusal names the step that works: place a Dimension as a value column on a table whose rows are the axis, give it a formula that reads the Variable, such as `if(FutureSpend > 0, "Yes", "No")`, and save that formula with `edit_table_blocks`. The Dimension then has one value per item, and `Vendor where HasFutureSpend = "Yes"` stays current after the next import.
An item row such as `Total[West]` names its axis. `hidden` on the same entry hides or shows the saved axis the selection belongs to, so `hidden: true` on an item row hides every item of that level.
When a receipt that lists every item would exceed the result allowance, each `member_filters` entry reports `members_omitted`, the count, in place of its `members` list, and the edit still answers `outcome: applied`.
## Edit an address that holds several saved formulas
A table edit names an address: the Variable, its condition, and its formula range. Imports and older writes can leave more than one saved formula at one address.
* An exact `$[...]` formula outranks an inheriting `[...]` formula at its grain, and among formulas of the same form the newest save wins. So cells read one formula: the newest exact one when one exists, otherwise the newest inheriting one.
* A `row_updates`, `child_defaults`, or `cell_updates` write at such an address edits the formula the cells read, and deletes the older copies of the same form in the same change. The receipt names each deleted copy: "This write also deleted an older copy of a Forecast rule that Advertising carried at this address: `1` at `[Vendor in any, Date.Month in any]`. No cell read a deleted copy, so no value changed."
* An exact formula and an inheriting `[...]` formula at the same grain are not copies. The write edits the exact one, keeps the inheriting one, and names it in the receipt.
A table read marks a default that has older copies with the `duplicate_default_formula` issue, which counts the copies and says that a write to the default deletes them. The default itself shows as `defined here` and can be written.
## Set where a source's rows sit on the calendar
Where an imported source's rows sit on the calendar is one fact about the source, and a `sources` entry on `update` or `create` states it once:
```json theme={null}
{
"update": {
"sources": [{ "target": "CRM Deals Monthly Export", "preferred_time_property": "synced_month" }]
}
}
```
* `target` is the query reference a read prints under `sources`, and `preferred_time_property` is one of the source's date columns.
* The write saves that column on every Variable and Dimension of the source in the same change. A `variables` or `dimensions` entry in the same request that sets its own `preferred_time_property` wins for that object.
* A column that is not a date column of the source is refused, naming the columns that are.
* An `update` that carries only `sources` needs no table target.
## Declare a mapping in the create that reads it
A `create` whose formulas read a mapped Dimension can declare the mapping in the same call, instead of calling `edit_dimension_mappings` first. The create's `dimensions` entry takes `target`, `keys`, `items`, and `catch_all`, as `edit_dimension_mappings` `map` takes them:
```json theme={null}
{
"create": {
"name": "Historical P&L",
"page": "P&L",
"dimensions": [
{
"target": "P&L Line",
"keys": ["GL Account Name"],
"items": { "4000 - Subscription Revenue": "Revenue", "5000 - Hosting Costs": "Cost of Revenue" },
"catch_all": "Unclassified"
}
],
"rows": [
{
"variable": "Total Revenue",
"default_formulas": { "actuals": { "formula": "sum(`Income Statement Amount`[`P&L Line` = \"Revenue\"])" } }
}
],
"columns": [{ "time": "MONTH" }]
}
}
```
* The mapping is written in the same change, before the rows, so a formula such as ``sum(`Income Statement Amount`[`P&L Line` = "Revenue"])`` resolves in this call. The table, the Dimension, its items, and its rows commit together.
* `items` maps single source values to fixed items, so a nonempty `items` needs exactly one key.
* A mapping formula's `condition` is saved as a relative `[...]` formula; a leading `$` before a nonempty condition is dropped. So when one lookup is keyed by Account and another by Account together with Account ID, a row the two-key lookup has no formula for falls back to the one-key formula instead of the catch-all.
* The catch-all `[]` is unchanged, and a formula saved as `$[...]` earlier keeps its exact condition until a mapping write names its row again.
* The receipt's `items` entry for the Dimension carries the mapping's facts: `keys`, `rows_created`, `rows_updated`, `target_items`, and `catch_all`.
* A refusal on either side saves nothing.
[Dimension mappings and shared Dimensions](/concepts/dimension-mappings) explains how mappings work.
## Set a table's window and grain
`window` is the table's own date range and grain.
* On a `create`, it holds `start`, `end`, and `granularity`. A dated table names its grain, such as `MONTH`; a timeless table names `NO_GRANULARITY`, as the `tables` example above does.
* On an `update`, the range is `{"type": "ABSOLUTE", "start": …, "end": …}`, and `{"inherit": true}` clears the table's own window.
* `start` and `end` accept a plain calendar date, such as `2026-02-01`, as well as a full timestamp, and `create` and `update` save the same window for either spelling.
* `update` with `grain` moves the table's Date columns to that grain, so a monthly table asked for `QUARTER` shows quarter columns, and the receipt reports the moved Date column. A table whose Date header shows two grains at once, such as Year above Month, is refused a new grain.
### Write values into dated columns
* A `values` write names each Date column by the start of its period at the table's grain. On a table with yearly columns, `2024-01`, `2024`, `FY2024`, and `2024-01-01` all write the 2024 cell; `2024-02` is refused as a date inside a YEAR period, and the refusal names the spelling that works.
* A period outside the table's window is refused with the window and the start or end that would hold it, such as "2025-12 is before this table's window 2026-01…2026-12; a window that starts 2025-12-01 holds it". A call may set `window` and write the new periods together.
* A whole number such as `2026` also selects a number item printed `2026.0`, and a row renamed in the same request resolves under either name.
* A JSON number in exponent form saves as the same value in plain decimal digits, so `1.5e-3` saves as `0.0015`; the formula grammar has no exponent literal. A number outside the 64-bit floating-point range is refused with "`` is outside the 64-bit floating-point range".
* Dated inputs in `values` or `cell_updates`, and the periods a `readback` selects, leave the table's saved display window unchanged. A report that must show a requested start and end needs `window` set on the same call, or the table keeps showing its old range with the new months outside it. A readback of selected months proves those cells, not the displayed range.
## Write in a Scenario
`scenario` defaults to the Scenario in view, which can change between turns. Through MCP, that is the Scenario `_context.layerId` names, or Main. When a call that saved something omits `scenario`, each table receipt carries a `scenario_warning` issue that says which Scenario the write used:
* With a Scenario in view: "No scenario was named, so this call used `Breakeven Plan#4c1`, the scenario the person is viewing. The scenario in view can change between turns."
* With none in view: "No scenario was named and none is in view, so this call used `Main`."
A refusal, a dry run, or a call that saved nothing carries no such sentence.
A Scenario stores only what differs from the Scenario it branched from. A cell write in a child Scenario that repeats the value the cell already reads from its parent stores nothing: the edit's outcome is `unchanged`, and a `formula_warning` says the Scenario already reads this value from its parent, so the cell keeps following the parent and changes when the parent changes. A different value is saved as the Scenario's own and holds through the parent's later edits. [Scenarios, Snapshots, and version history](/concepts/scenarios) explains why.
## Delete a Variable or Dimension
* `delete` with a `target` alone removes the whole table, and deletes only the Variables and Dimensions that no other saved table still shows. An object another table shows stays there, with its formulas, values, and items.
* No table owns an object. A `row_updates` or `reshape` write may take an object off the table it was created on while another table shows it; only removing its last occurrence needs it placed elsewhere first, or deleted outright.
* `delete` with a `variables` or `dimensions` list removes only the listed objects, wherever they are placed. An object on another table, or on no table, is reached by name; the named table only scopes the call. A name that matches no readable object is refused with "delete target … matches no readable variable or dimension".
* When other formulas, blocks, or settings still read one of the objects, the delete is refused and lists what reads it under where each object sits, such as "is shown on table Revenue" or "has no recorded table location", ending "With force, the object is deleted and these references break." `force: true` accepts that those references break.
* `force` on the delete covers every listed entry; `force` on one entry covers that entry alone, so a call can force some entries and not others. `update` entries do not take `force`.
* A refused table delete opens with "Nothing was deleted" and the count of dependents. A completed one carries a `links_end` issue, because links to the table in earlier chat messages no longer open it, and a table rebuilt in its place has a different identity.
* Removing a Dimension item is refused, opening with "Nothing was removed", while a formula, a drill-in, a chart or Canvas condition, a row on another table, or a filter that lists only that item names it. Manual sort orders, and filters that still list other items, drop the item instead.
## A formula belongs to its Variable
A `create` row that names an existing Variable by its plain name places that Variable. Rows match names without regard to case, so `Deferred revenue` places `Deferred Revenue`.
A plain name adds to the Variable only where it holds nothing:
* a formula for a range or level that has none,
* a value at a cell that has none and that no formula of the Variable calculates, or
* a type or format that was not set.
A request that would replace what the Variable holds is refused before anything saves, for every table that shows it. When judging that:
* A formula calculates the cell unless it is pinned to one cell. An exact condition with an open term, such as `$[Date.Month in any]`, is a formula over every month.
* A dated formula counts at the Model's base grain and at the grain of the value given, so a formula saved from a quarterly table refuses a quarterly value.
* The generated formulas a breakdown's save recorded as its own are judged by what they compute. The `0` on the lines supplies what a blank cell reads, so a value on a line is an addition; the sum on a level calculates that level alone, so a value on the Variable's own row is a replacement.
* A value on a timeless Variable that holds nothing is an addition.
* Every aggregation that is set, `DO_NOT_AGGREGATE` included, is a setting a plain name does not replace.
* The check does not read which items a condition names, so a formula for one item refuses a value on another item's cell.
The refusal names the two ways forward:
* The Variable's reference, such as `Budget#3f9`, writes that Variable and replaces what it holds.
* `new: true` on the row creates a separate Variable with the same name and leaves the existing one as it is. Throughout a request with `new: true`, in rows, columns, formulas, values, `variables` entries, and later tables of the same create, the plain name means the new Variable, and each existing namesake answers only to its reference.
The receipt's `items` entry for a reused object carries:
* `home`, the table it was first created on, when that table exists and is readable;
* `added`, with the `rules`, `values`, and `settings` the save gave it;
* `beside_rules`, the count of formulas the Variable already held that calculate its cells, which is 0 whenever values were added;
* `rule: "replaced"` when a write by reference replaced a formula the object already had, in Main or in the Scenario being edited;
* and, on an `update` or `reshape` entry for a reused Variable, `level_defaults`, the sums and `0` the save's breakdown added to it or removed from it.
Giving an existing Variable new `default_formulas` by its reference, such as `Revenue#3f2`, while placing it on another table rewrites the formula every table reads. When the formula it replaced was a formula, the receipt names it and the address the Variable carried it at. Writing a value over a plain number, quoted text, a date, or `null` states no replaced formula; the committed edit's `previousText` carries the old value instead. Placing a Variable without new defaults leaves its existing formula alone.
## Names and references
* A stale table reference whose name no longer exists resolves by its suffix when the suffix has at least three hex digits and exactly one table carries it. `inspect_table_blocks` `list` reports the move in a `renamed` note, such as "Vendors#083: no table has that name; its suffix matches the table now named "Anomalo Vendors"".
* Formula names match case exactly. A formula that names `` `Deferred revenue` `` when only `Deferred Revenue` exists is refused with the object it matches: "`` `Deferred revenue` `` matches the existing `` `Deferred Revenue` `` (names match without case); formula names match case exactly, …".
* A reference field and formula text accept the same spelling of a namesake. The short suffix a readback prints, such as `Currency#3` beside `Currency#8`, names the object it prints for, and a suffix of three or more hex digits is checked against the object's retained identity, so a reference issued for a deleted object never moves to a new namesake.
* A backticked name that no object carries, such as `` `Amount#3c2` ``, reads as the reference it quotes. In a table row or column, backticks still mark a literal new name.
## Check before you save
A table write is atomic: the whole change saves, or none of it does. Only `create` takes `dry_run: true`, which validates a new table without writing; edits to saved tables and deletions take no dry run. [Validate and verify formula writes](/agents/validate-and-verify#validate-before-you-save) has the details.
## Read back what you saved
* Ask for a readback in the same write with `readback.rows` and `readback.columns`. `rows` alone reads every column, `columns` alone every top-level row, and a renamed Variable or Dimension resolves under either name. `readback.depth` adds child levels, and `readback.also` reads other saved tables at the same change.
* An `update`, `reshape`, or `delete` may carry `based_on`: the `as_of` of the table read, or the `change_log_id` of the table write, it builds on. The edit saves either way. When someone else saved to the table after that point, the receipt adds a `changed_since_read` issue naming each save and who made it: a person by name, "a person in the app", "Ari in another chat", or "an automated process", up to five saves and then a count.
* Check the intended Scenario, representative dates and segments, formula origins, and the actual numbers. When the receipt carries warnings, read them before moving on. [Validate and verify formula writes](/agents/validate-and-verify) lists them.
# Actuals, Forecast, and Last close
Source: https://docs.cfo.ai/concepts/actuals-and-forecast
How Last close, your month-end boundary, splits the timeline into Actuals and Forecast, why a Variable can use a different formula on each side, what moving Last close changes, why it never moves on its own, and how fiscal years work.
Why does January show your books while February shows a forecast? Every Model splits time at one date, **Last close**: the end of the last period you have closed, your month-end. Periods up to and including Last close are **Actuals**, which usually come from your accounting system. Later periods are the **Forecast**, calculated from your assumptions. A forecast is not a separate model: it is the later part of the same Model's timeline.
## Last close is your month-end boundary
Last close is the end of the last period whose books you have closed, such as March 31 when you have finished closing March. A new workspace starts with Last close at the end of the previous calendar month.
Each Scenario has its own Last close. A new Scenario follows Main's Last close until someone changes it in that Scenario. To set it, see [Set Last close and date defaults](/guides/set-dates-and-last-close).
## Last close does not move on its own
Last close stays where it is until you or Ari move it. Connected systems keep sending new data every night, but a month joins Actuals only when Last close moves past it. That is deliberate: closing a month is your decision, and a month whose books are still open would otherwise show partial numbers as Actuals.
So to have the Model pick up last month's actuals, move Last close forward when you finish each month's close. You can change it yourself, ask Ari to change it, or schedule a [Trigger](/guides/schedule-a-trigger) that asks Ari to move it on the same day every month, after your books usually close.
## A Variable can use one formula for Actuals and another for Forecast
Actuals and Forecast are the two **formula ranges** of the timeline. A Variable can have one formula for each. Payroll, for example, can read your accounting system in Actuals and calculate this formula in Forecast:
```text theme={null}
Headcount * `Average Salary`
```
The Variable keeps one meaning across both ranges: it is Payroll on both sides of Last close.
A formula range is a span of time, so it applies only to a formula whose scope includes time. A formula written for Sales alone, with no time in its scope, cannot be limited to Actuals or Forecast, and Ari refuses to save it with a range. Once a formula's scope includes time, a range combines with the rest of it, so Sales can have its own Forecast formula while every other department uses a broader one. See [How formulas work](/concepts/formulas) and [Use different formulas for Actuals and Forecast](/guides/actuals-and-forecast-formulas).
## What moving Last close changes
Moving Last close changes which months use the Actuals formulas and which use the Forecast formulas. No formula is rewritten; each formula range follows Last close wherever it moves.
Say Last close moves from January 31 to February 28. February now reads your books through the Actuals formulas, and the Forecast starts in March. A forecast that grows from the last actual month now grows from February's actual instead of January's. Every table, chart, and Page shows the new split, because they all read the same Model.
## Connected data before and after Last close
What a Variable shows after Last close depends on whether it has a Forecast formula:
* A Variable with a Forecast formula uses it in every month after Last close. Rows from your accounting system dated after Last close, such as this month's postings so far, are not read in those months.
* A Variable that only reads its connected column shows the source's rows in every month that has them, including months after Last close. Columns that connections bring in today start this way.
Some older imported Variables read their source only through Last close, and show 0 in Forecast months until they get a Forecast formula. For how each imported column gets its starting formula, see [How imported data loads](/reference/imported-data).
## Fiscal years: quarters and years follow the calendar
cfo.ai has no fiscal-year setting. Quarters, half-years, and years always follow the calendar, January to December, in every table and formula. The **Descriptive** date format labels periods as FY2026 or Q3 FY2026, but those labels name calendar years and calendar quarters.
If your fiscal year starts in another month, such as February, keep the Model monthly and ask Ari for fiscal-year totals. Ari can add them as formulas that add up your fiscal months, such as February 2026 through January 2027, or as a Canvas block that groups months into your fiscal years. You can also set a table's date range to start in the first month of your fiscal year. Monthly numbers are the same either way; only the yearly grouping differs.
## Related
* [Set Last close and date defaults](/guides/set-dates-and-last-close)
* [Use different formulas for Actuals and Forecast](/guides/actuals-and-forecast-formulas)
* [How connected data reaches your Model](/concepts/connected-data)
* [Totals, aggregation, and time rollups](/concepts/totals-and-rollups)
* [Scenarios, Snapshots, and version history](/concepts/scenarios)
# What Ari remembers
Source: https://docs.cfo.ai/concepts/ari-memory
What Ari carries from one chat to the next: saved facts in your private memory and the workspace's shared memory, past chats, files from your other chats, and the settings that hold things like your brand colors.
"Do you have the context from my other chats?" Partly. Ari does not reread every past conversation before it answers. It carries a short list of facts you asked it to keep, it can look back at your earlier chats when you point it there, and it can read files you attached in them. This page explains each of these, and where other things you tell Ari, such as your brand colors, are kept.
## Saved facts: Ari's memory
Ari's memory is a short list of facts it applies without being asked, such as "show ending cash in thousands", "our fiscal year starts in February", or "Bookings means signed contract value". Ari saves a fact when you ask it to remember something or when you state a lasting preference. It does not save a transcript of what you said.
When you send a message, Ari looks up the saved facts that match it and uses them. That is how a preference you stated last week reaches a chat you start today.
## Two kinds of memory: yours and the workspace's
Every saved fact lives in one of two places:
| | Your memory | Workspace memory |
| - | - | - |
| Who it applies to | Only your chats | Every chat, for everyone in the workspace |
| Good for | How you like to work: "I run the sales team", "show me quarters, not months" | Facts about the company: "our fiscal year starts in February", "Enterprise means contracts above \$50,000 a year" |
Ari decides where a fact belongs from how you put it. When that is unclear, the fact is saved in your memory only, so a personal preference never spreads to your team by mistake. Say "for the whole workspace" when a fact is about the company.
A fact never moves between the two on its own. When a newer fact replaces an older one, it stays where the older one was. Forgetting a workspace fact removes it for everyone.
To save, list, or remove facts, see [Teach Ari your company's conventions](/guides/teach-ari).
## Memory is separate from chat history
Ari can also look back at your earlier chats, which is different from memory. Memory is the short list Ari applies every time. Chat history is a record Ari reads only when it needs to, for example when you say "use the churn definition we agreed on last week".
Ari sees each of your chats under the name shown in your chat list, including a name you gave it, so naming a chat after what it settled helps Ari find it later. Ari reads only your own chats, never a teammate's. Archiving a chat keeps its messages and files, so Ari can still read it.
## Files from your other chats
A chat can read the files you attached in your other chats in the same workspace, and the files Ari made for you there. Ask for a file by name, and Ari finds the chat that holds it. For each file, Ari sees its name, the chat it is in, when it arrived, and whether a CSV was sent as a **Data source**. That lets it tell two files with the same name apart. Ari cannot read files in a teammate's chats. See [Attach files to a chat](/guides/attach-files).
## Where other things are kept
Some things you tell Ari are saved as settings or as work in the Model, not as memory:
* Brand colors, a logo, and a heading typeface are saved as the workspace brand, which everyone who opens the workspace sees. See [Brand your workspace](/guides/brand-your-workspace).
* Your own color theme is saved to your account and applies only to you. See [Change your theme, sounds, and notifications](/guides/personalize-cfoai).
* A process you repeat, such as your month-end review, is saved as a Skill, with instructions Ari follows each time. See [Save a workflow as a Skill](/guides/save-a-skill).
* Numbers and definitions that should calculate, such as a churn rate, belong in the Model as Variables and formulas, where every Page can use them.
A scheduled Trigger also keeps a short record of each run, so the next run can build on the last one. See [Skills, Triggers, and Guides](/concepts/automation).
## Related
* [How Ari works with you](/concepts/working-with-ari)
* [Teach Ari your company's conventions](/guides/teach-ari)
* [Attach files to a chat](/guides/attach-files)
# Skills, Triggers, and Guides
Source: https://docs.cfo.ai/concepts/automation
How Skills, Triggers, and Guides let Ari repeat work: what each one is for, how Ari picks a Skill, how a Trigger runs with nobody watching, what a run cannot do on its own, and the record each run keeps.
Skills, Triggers, and Guides let Ari repeat work, so you explain a job once instead of every month, and the work happens on time even when you are not there. A **Skill** tells Ari how to do a job. A **Trigger** tells Ari when to do it. A **Guide** shows a person around the work, one step at a time.
Take a month-end review. A Skill holds your steps: compare Actuals with Forecast for the closed month, flag any line more than 10% off, and write the summary in your usual format. A Trigger runs the review on the fifth of each month and emails you the summary. A Guide walks your board through the variance Page before the meeting.
## How Ari picks a Skill
A Skill has a name, a description, and instructions. On every request, Ari sees the name and description of each Skill it has: the built-in Skills, and your own Skills that apply in this workspace. When a request matches a description, Ari reads that Skill's instructions and follows them. Nobody has to name the Skill. That is why a description works best when it is written as "Use when…": it is what Ari matches against.
Skills come in two kinds:
* Built-in Skills come with cfo.ai, which keeps them up to date. Nobody can edit them. Most are always on; three optional ones, **Model audit**, **Flash report**, and **Model migration tie-out**, stay off until someone in the workspace turns them on.
* Your own Skills belong to you, the person who saved them. Ari follows them whenever it works for you, in every workspace where you are a member, so a fractional CFO who saves a close review once has it in every client's workspace. Other people in a workspace have their own Skills, and yours do not change their chats. A Skill of yours cannot reuse a built-in Skill's name.
A Skill can also be limited to one workspace, for a procedure built on one company's own accounts, systems, people, or figures. Ari follows a limited Skill only in that workspace. When you ask Ari to save a Skill that names one company's own setup, it limits the Skill to that company's workspace and tells you so.
A new or changed Skill reaches a new chat at once, and a chat already open within a message or two. A Skill holds a process. A fact that everyone at a company should share, such as how the company defines bookings, belongs in workspace memory instead, which applies to every chat for everyone in the workspace; see [What Ari remembers](/concepts/ari-memory).
## How a Trigger runs with nobody watching
A Trigger is a saved request with a schedule. The schedule is stored in UTC. At each scheduled time, cfo.ai starts a new chat for the run and Ari works through the saved instructions, with the access of the person who set up the Trigger. The saved instructions are the authorization for the run.
A run starts with no Page open and works in Main, unless the instructions name another Scenario. It works on exactly what the instructions name. If a Page or a connection the instructions name was renamed or removed, the run reports that instead of picking something that looks similar.
Nobody reads the chat while a run works, so nobody can answer it. When Ari would ask you a question, it makes a reasonable assumption, keeps going, and states the assumption in its result. Its questions, plans, and buttons stay in the chat for you to read later. Answering one later starts a new turn in that chat; it does not change what the run already did.
## What a run cannot do on its own
A run never waits for a person, so anything that needs a person's approval stops short:
* A Scenario merge or deletion that the run prepares waits in its chat until someone presses the confirmation button.
* An email to anyone but you waits as a preview in the chat until someone presses **Send**.
* A run can message only you, by email or in Slack, and only when its instructions name that channel. It cannot post in a Slack channel.
A Trigger does one run at a time. When a run is still going at the next scheduled time, that time is skipped. Runs use AI usage like any chat with Ari; see [Plans, AI usage, and connection slots](/concepts/plans-and-usage).
## The record each run keeps
Each run's chat is its record, listed in the Trigger's history. A run ends by reporting its outcome: the saved task was done, it was not done, with the run's own sentence for why, or the run ended without reporting either. Only a run that reports the task done counts as a success. A Trigger that refreshes a connection succeeds only when the import that run started finishes.
Each finished run also saves a summary to Ari's memory, tagged with the Trigger and the date. You can ask Ari about earlier runs in any chat, such as "What did my cash Trigger find last month?" The next run reads the last run's record: what it found, how long that has held, and what it already told you. A report the instructions ask for every time goes out every time. A warning goes out when something changes, so the same warning does not repeat on every run.
## Guides show people around
A Guide is a saved walkthrough that spotlights parts of the workspace, such as a Page, a block, or one cell, with a short note at each stop. A Guide can be a step-by-step tour, a walkthrough of an analysis, or a single explanation. Ari builds Guides from your description, and they are saved in the workspace for everyone. People start a saved Guide by asking Ari for it, or with the **Start tour** or **Start walkthrough** button Ari posts in the chat. A button in a Canvas block, or **Take the tour** on an invitation, can also start one. Saved Guides are not listed in the Help center. See [Walk someone through a Page with a Guide](/guides/create-a-guide).
## Choosing between them
| You want | Use |
| - | - |
| Ari to follow your steps whenever a certain job comes up | A Skill |
| A job done on a schedule, without asking | A Trigger |
| Your steps followed on a schedule | A Skill for the steps, and a Trigger that asks for the job |
| A teammate or your board shown around a Page | A Guide |
| Ari to remember a fact or a preference | Memory; see [What Ari remembers](/concepts/ari-memory) |
## Related
* [Save a workflow as a Skill](/guides/save-a-skill)
* [Turn on a built-in Skill](/guides/turn-on-built-in-skills)
* [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger)
* [Walk someone through a Page with a Guide](/guides/create-a-guide)
* [Sample Skills](/reference/sample-skills)
# How connected data reaches your Model
Source: https://docs.cfo.ai/concepts/connected-data
How a record in QuickBooks, a CSV, or another system becomes a number on your Pages: integrations versus uploads versus MCP servers and APIs, the warehouse and saved queries, when data is ready, the nightly refresh, background repairs, and plan slots.
cfo.ai brings data from your other systems into your Model in three ways, and each one updates on its own schedule. Which way a source uses answers the common questions: why did my numbers change overnight, is my bank balance live, and does an MCP server's data land in my Model?
## Three ways data reaches cfo.ai
| | Integration | Upload | MCP server or API |
| - | - | - | - |
| Examples | QuickBooks Online, Xero, Gusto, HubSpot, Stripe (Direct), a database, a Google Sheet | A CSV you upload or attach as a data source | Mercury, Linear, Notion, exchange rates |
| What it is | A sync that copies the source's records into your Model | A one-time copy of a file's rows | A tool Ari calls live when you ask a question |
| When it updates | Every night, and when you ask Ari | Never; upload a new version | Each time Ari calls it. Nothing is stored unless Ari saves a sync setup. |
| Where it lands | Variables and Dimensions in your Model | Variables and Dimensions in your Model | Ari's answer in the chat, or a value Ari writes into a table |
| Plan slot | Uses one | None | None, unless Ari saves a sync setup |
Integrations and uploads only read from the source, so a number you edit in cfo.ai changes your Model and leaves your books alone. An MCP server or API can also act in its service, such as creating an issue in Linear, but only through the tools you allow Ari to use.
## From a source record to a number on your Page
Take one line of your QuickBooks profit and loss: \$4,200 of software subscriptions in September 2026. It travels in three steps.
1. The sync copies it into the warehouse. cfo.ai keeps a private copy of each source's tables in its data warehouse, and QuickBooks' monthly profit and loss lands there as a table, row for row.
2. A saved query reads it. A saved query is a SQL statement that selects, cleans, and labels the rows your Model needs. cfo.ai adds standard ones for supported sources, such as **QuickBooks Profit & Loss**, and Ari writes the rest. This one puts the \$4,200 in an **Expense** column, beside the account name, the **Entity**, and the date.
3. Each column becomes a Variable or a Dimension. **Expense** becomes a Variable that adds up, segmented by the account Dimension and the Entity Dimension. Its starting formula sums the source column, so September's software subscriptions read \$4,200 on any Table block that shows Expense by account.
When the next sync brings a corrected amount, the warehouse row changes, the saved query runs again, and every Page that reads Expense updates. Nothing about your formulas or tables has to change. [How imported data loads](/reference/imported-data) lists the rules for each step.
A CSV follows the same path: the upload replaces the warehouse copy, and a saved query brings its rows in. A Google Sheet is different: Ari imports the spreadsheet itself and rebuilds its tabs, formulas, and values in your Model, the same way it [imports a spreadsheet model](/guides/import-a-spreadsheet-model).
## Connected is not the same as ready
A source becomes useful in three stages, and cfo.ai reports each one separately. First it is linked: you signed in and the connection exists, but no data has moved. Then its tables reach the warehouse, which your Model cannot use until a saved query reads them; that is why a connection can read **No queries yet**. Finally saved queries put rows into your Model, and only then does the sidebar show the connection as **Synced**.
So "I connected QuickBooks" and "my Model has my QuickBooks numbers" can be minutes or hours apart. Some sources load your most recent months first, so Ari can start, and fill in the rest of the history in the background; [How much history loads](/reference/imported-data#how-much-history-loads) lists each source. Ari tells you in the chat when the data is in. Until then, ask Ari where the load stands rather than treating a smaller Model as missing data.
Ready also says nothing about whether the numbers match your books. A query can filter rows on purpose, and your Model's **Last close** decides which months read as Actuals. [Check that your numbers match your books](/guides/check-imported-data) covers both.
## When data updates
Every integration refreshes once a day, in a shared nightly run that starts at 02:00 UTC. A source that publishes its daily data after that run would always land a day late, so you can give a connection its own daily time, such as 6:30 AM in your time zone. You can also ask Ari to refresh a connection now; there is no button for it.
The nightly refresh does not always re-read your whole history. Some sources, such as QuickBooks Online, re-read only recent months, so a correction to an older period arrives only when you ask Ari for a full-history import. How far back each source re-reads is listed in [How much history loads](/reference/imported-data#how-much-history-loads) and in each source's entry in the [integrations directory](/reference/integrations).
Uploads never refresh, because a file has nothing to sync from; upload the new version. A connected Google Sheet refreshes every night. Every source's schedule is listed in [Refresh schedule](/reference/imported-data#refresh-schedule).
So is your bank balance nightly? Through your accounting system, yes: it is the balance on your synced balance sheet, as of the last sync and as current as your books. Through an MCP server such as Mercury's, it is live each time Ari looks it up.
## Integrations and MCP servers do different jobs
An integration answers questions about history: how much revenue came in each month, how payroll grew, where cash went. It brings in past periods, keeps recent ones current, and turns them into Variables your formulas and Scenarios can use. How far back each source loads is listed in [How much history loads](/reference/imported-data#how-much-history-loads).
An MCP server or an API answers questions about now, or acts in another tool: what is in the bank today, which invoices are open, which issues are blocked. Ari calls it live, so the answer is current, but nothing stays in your Model unless Ari writes a value into a table, and that value is a plain number that does not update.
So adding every relevant MCP server does not replace your integrations. If you connect Rippling's MCP server, Ari can look up an employee or count today's headcount. Your headcount plan, though, needs a roster in your Model that updates every night, and the Rippling integration brings that. When you do want a server's data on a schedule, ask Ari for a sync setup. A sync setup copies one read-only tool's results into your data each night, and a saved query brings them into your Model, like an integration. At that point the server uses a plan slot.
## Connected data is shared across Scenarios
Every ordinary Scenario reads Main's imported rows, so a sync updates Main and those Scenarios together, while a Snapshot keeps the numbers it was pinned with. See [Scenarios, Snapshots, and version history](/concepts/scenarios#a-scenario-holds-only-what-is-different).
## When a source stops updating
Most failures fix themselves. A provider's sync that failed for a moment is retried on the next nightly run, and a table that failed to refresh is retried while the rest of the source stays usable. The sidebar marks these with a steady orange dot.
Sometimes the nightly sync of a source you connected from the connection manager fails right after a successful one. No chat is there to hear about it, so cfo.ai starts one background task for Ari, which acts for you with your permissions. Ari checks the connection and repairs what it can, such as a saved query that a change in the source's layout broke. A source counts as restored only when its queries return values again for the records they returned before. If the repair restored the source, it says nothing. If it needs your decision, could not finish, or changed something you should know about, it opens a chat titled **\[Connection name] stopped updating** in your chat list. A source that keeps failing starts no further task.
Problems only you can fix, such as an expired sign-in, wear a red triangle until you act. [Fix a connection that needs attention](/guides/fix-a-connection) walks through each one.
## Connection slots
Your plan limits how many sources you can sync, so each integration uses one of its connection slots until you remove it; uploads and live MCP servers and APIs use none. Removing a connection stops its syncs, and the data it already loaded stays in your Model. The exact counting rules are in the [integrations directory](/reference/integrations#standard-and-premium-integrations), and how plans set the number of slots is in [Plans, AI usage, and connection slots](/concepts/plans-and-usage#connection-slots-limit-what-you-can-sync).
## Related
* [Connect your accounting system](/guides/connect-accounting)
* [Give Ari access to another service](/guides/give-ari-another-service)
* [Refresh data or change when it refreshes](/guides/refresh-data)
* [How imported data loads](/reference/imported-data)
* [Integrations directory](/reference/integrations)
# Dimension mappings and shared Dimensions
Source: https://docs.cfo.ai/concepts/dimension-mappings
How a mapping groups the items of one Dimension, such as GL accounts, into the items of another, such as P&L lines; how catch-alls and conditions work; and how accounting connections share Entity, Currency, and other Dimensions.
How do 300 accounts in your books become 12 lines on your P\&L? A **Dimension mapping** assigns each item of one Dimension to an item of another. Map account 5001 to COGS and account 6001 to Payroll, and every table that reports by the mapped Dimension groups those accounts' amounts under COGS and Payroll. People also call this grouping accounts into categories, or mapping a chart of accounts.
## A mapping groups items into categories
A mapping is a lookup from a source Dimension, such as Account, to a target Dimension, such as Reporting Line:
| Account (source) | Reporting Line (target) |
| - | - |
| 4000 Subscription revenue | Revenue |
| 5001 Hosting | COGS |
| 6001 Salaries | Payroll |
| 6002 Payroll taxes | Payroll |
The classification lives in one place. Change account 6002 to another line in the mapping, and every table, formula, and chart that reads Reporting Line follows. Statement lines read their accounts through a mapping this way, so a new account in your books needs one new mapping row, not a new formula.
A mapping does the same job for any set of labels that should report together: two systems' department names into one list, or each employee into a department.
## Mappings go one way
A mapping always runs from source to target. Pick the shared reporting Dimension as the target, and map each source into it. If two companies' accounts must land in one reporting structure, map each company's Account Dimension into the same target. A mapping that would lead back to where it started is refused.
## What a mapping can say
* An assignment applies in every period. Mappings have no effective dates.
* An assignment names a fixed target item. It cannot calculate which item to use.
* One assignment can match several items at once with a condition, such as "8001 or 8002 maps to Other Income."
* An assignment can match a pair of items, such as an account together with a company, so two companies' account 6000 can land on different lines.
* A condition tests the source items only. It cannot test a Variable's value, so a list of exceptions that depends on an amount stays a list of items.
* One target can be fed by a one-key lookup and a two-key lookup, such as Account alone and Account with Account ID. A row that the two-key lookup has no assignment for falls back to the one-key assignment.
A Dimension whose items come from a formula cannot also be a mapping target.
## The catch-all covers everything else
Each target can have one **catch-all**: the item that every unmatched source item maps to, such as Other. Without a catch-all, an unmatched item stays unmapped, and its cell in a mapping column is blank, so you can see what is left to classify.
## How mapped items show in tables
A table can show a mapping in two ways:
* As a mapping column beside each source item, such as accounts on rows and Reporting Line as a column. Each cell shows that row's assignment, and picking another item there changes the mapping for every table.
* As a breakdown that nests the target over the source, such as Department, then Role. Each Role shows once, under the Department its mapping gives it, so subtotals count each item once. A Role that no assignment matches and no catch-all covers forms no row, and neither does a Department that no Role maps to.
Renaming an item, on a table or through Ari, also renames it in the mapping and in formulas that compare against it, as one change that one undo reverses.
## Shared Dimensions across accounting connections
When a connection's accounting statements load for the first time, each category column, such as **Entity**, **Currency**, or **Vendor**, joins an existing Dimension that matches it. A match has the same accounting system, column name, type, and aggregation. If several match, it joins the one that the most sources already feed, and the older one when two are tied. So two QuickBooks Online companies share one Entity Dimension and one Currency Dimension, and a Model can report on each company and on the group.
A few limits apply:
* Joining happens once, when a statement first loads. Later syncs never regroup, so a Dimension that stayed separate stays separate.
* Dates and amounts are never combined.
* Two companies with the same name keep separate Entity Dimensions.
* Different accounting systems, such as one QuickBooks Online company and one Xero organisation, get separate Dimensions.
Ari can combine compatible Dimensions into one, or split a combined one apart:
* Combining joins Dimensions with matching types and aggregation, keeps each item's exact label, and rewrites the formulas and tables that read the others. Amounts are never combined, and nothing is saved if the combine is refused. Canvas blocks are not rewritten; Ari lists the ones that may read a retired Dimension so it can check them.
* Splitting turns a combined Dimension back into one Dimension per source column. Formulas that read amounts stay as they are, so a breakdown may no longer add up to its total, and Ari says so.
## Related
* [Map one Dimension's items to another](/guides/map-dimensions)
* [Model several entities and consolidate them](/guides/consolidate-entities)
* [Variables and Dimensions](/concepts/variables-and-dimensions)
* [How connected data reaches your Model](/concepts/connected-data)
# How formulas work
Source: https://docs.cfo.ai/concepts/formulas
How a formula calculates a Variable, where it applies, how cfo.ai picks one when several could fill a cell, and why a cell can have its own value, a different formula from its parent, or a circular reference.
A formula tells cfo.ai how to calculate a Variable. It works like a spreadsheet formula, but it names what the numbers mean instead of where they sit. Gross Profit's formula is:
```text theme={null}
Revenue - `Cost of Goods Sold`
```
That one formula calculates Gross Profit in every month and for every department, so there is nothing to copy across a row. Names that contain spaces are wrapped in backticks. For the full syntax and every function, see [Formula syntax and operators](/reference/formula-syntax) and [Formula functions](/reference/functions).
## A formula has an expression and a place where it applies
Every formula has two parts. The **expression** says what to calculate, such as `Revenue - Expenses`. The formula's **scope** says which of the Variable's cells it calculates: every cell, one department, every department when the Variable is broken down by Department, only the Forecast months, or a single cell.
Where you write a formula decides its scope. A formula on a Variable's row applies to the whole row. A formula on one item's row, such as Sales, applies to that item. A formula typed into one cell applies to that cell alone. See [Write a formula](/guides/write-a-formula).
## Names in a formula follow the cell being calculated
A plain name in a formula reads the other Variable at the same address as the cell being calculated. When cfo.ai calculates Revenue for Sales in March, `Headcount` in Revenue's formula reads Headcount for Sales in March.
Brackets change part of that address and keep the rest. `Revenue[Department = "Sales"]` reads Sales whatever row the formula runs on, and `Revenue[-1]` reads the previous period.
A plain name can only find a value that exists at that address. A value typed into a cell exists only at that cell's address. If Price was typed in month by month with no department, there is no Sales price, and `Price` in a Sales cell reads 0. A formula on Price's row covers every department, so it does not leave that gap. A **fixed reference**, written with `$` after the name, does not follow the cell being calculated and keeps only the parts of the address you name. For example, `` `Reported June MRR`$ `` reads the same value in every row and every period.
## A Variable can have several formulas
A Variable can use different formulas in different parts of the Model:
* One default for the whole Variable.
* A different formula for one item, such as Sales.
* One formula for Actuals and another for Forecast; see [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast).
* A value typed into one cell.
Keep one clear default, and add other formulas only where the business really works differently. Many overlapping exceptions make a Model hard to follow.
## How cfo.ai picks the formula for each cell
When several formulas could fill a cell, cfo.ai uses the most specific one. It compares them in this order:
1. A value or formula typed into one cell wins in that cell.
2. A formula that names more Dimensions beats one that names fewer. A formula for every department in every region beats a formula for every department, which beats the Variable's default.
3. Among formulas that name the same Dimensions, one that applies to its own rows only beats one that also applies to the rows beneath it.
4. Then a formula for a named item beats one that covers every item, so a formula for Sales beats a formula for every department. One item beats a list of items.
5. When two formulas are still tied, the one saved most recently wins.
Formulas that cfo.ai generates, such as the read of a connected column or the 0 for an empty cell, rank below every formula someone saved. A formula range, Actuals or Forecast, decides when a formula applies, not how it ranks.
For example, Payroll has a default formula, a formula for every department, and a formula for Sales. The Sales cells use the Sales formula, Engineering uses the formula for every department, and the company total, which has no department, uses the default.
## Why a cell has a different formula from its parent
Each row and cell takes the most specific formula that covers it, so a row often has a different formula from the row above it. That is usually by design. Three reasons are common:
* The parent is a breakdown total. When a table breaks a number down, the parent row adds up the rows beneath it, while the bottom rows use the Variable's own formula. A percentage is never added up, and text and dates get no total; see [Percentages and ratios are not added up](/concepts/totals-and-rollups#percentages-and-ratios-are-not-added-up).
* Someone gave the row or cell its own formula, such as a different price for one product, or typed a value into it. That is an override.
* The cells fall in different formula ranges. A closed month uses the Actuals formula, and a later month uses the Forecast formula.
To see which formula a cell uses and where that formula is saved, open the cell's formula, or ask Ari about the cell; see [Ask Ari about a number](/guides/ask-about-a-number).
## A typed value is the cell's own formula: overrides
When you or Ari type a number into a cell, cfo.ai saves it as that cell's own formula. The number is the expression. The scope is the cell's full address: its Variable, its items, and its period. A typed value is a formula that happens to be constant, so cfo.ai ranks it like every other formula. Nothing is more specific than one cell, so the typed value wins in that cell and nowhere else. This is an **override**: it overrides the formula the cell would otherwise take from its row.
A dashed underline marks a cell whose formula is saved for that cell alone, such as a typed number. A cell that follows its row's formula has no underline, and neither does a cell where someone picked an item or a date from a list.
An override appears when someone types or pastes into a cell, or when Ari sets a value for one period, such as an opening cash balance. To remove one, clear the cell's own value; the cell then follows its row's formula again. See [Enter, paste, and clear values](/guides/enter-values). If you are not sure which cells hold their own values, ask Ari to list the overrides on a Variable.
## Breakdown totals and summary columns calculate differently
A breakdown total, the parent row above a breakdown such as Revenue above its products, is a formula that cfo.ai saves with the table. You can replace it with your own, such as a ratio. A summary column at the end of a group of columns, headed **SUM** or **LAST**, for example, saves no formula and summarizes only the cells shown on that table. For how each kind of total calculates, and why a percentage is never added up, see [Totals, aggregation, and time rollups](/concepts/totals-and-rollups).
## A cell that no formula covers
A cell that no formula covers and that holds no value reads a default that depends on the Variable's type: 0 for a number, and an empty value for text or a date. So a Salary nobody entered adds 0 to a total, and an End Date entered only for people who left is empty for everyone still employed.
An imported Variable reads its connected column instead; see [How imported data loads](/reference/imported-data). A quarter or year cell of a monthly Variable that has no time rollup also reads 0; see [Why a quarterly view shows zeros](/concepts/totals-and-rollups#why-a-quarterly-view-shows-zeros).
## A formula belongs to its Variable, so changing it changes every table
A formula is saved on the Variable, not on the table where you wrote it. Every table, chart, and Page that shows the Variable reads the same formulas, so changing a formula from one table changes the number everywhere in the same Scenario. That is how one fix reaches every report. A change in Main also reaches the Scenarios that read that formula from Main; see [Scenarios, Snapshots, and version history](/concepts/scenarios#a-scenario-holds-only-what-is-different).
When one report needs a different calculation, use a different Variable, or a formula for a narrower part of the Model. Placing an existing Variable on another table leaves its formulas alone. Ari tells you when a change it makes replaces a formula that other tables read.
## Why a formula shows a circular reference
A circular reference means that calculating a cell needs that same cell's result, so cfo.ai cannot finish. The cell shows `#CIRC!`. Three shapes cause most of them:
* Two Variables read each other in the same period: Interest reads this month's Cash, and Cash reads this month's Interest.
* A Variable reads itself in the same period, such as a new formula for Cash that reads `Cash`.
* A parent's formula reaches the rows beneath it. A formula written on a parent row also applies to the rows beneath it that have no formula of their own. If it adds those rows up, each of them ends up adding itself.
A formula that reads the previous period is not circular, because last month's value is already known. A running balance such as ``Cash[-1] + `Net Cash Flow` `` works for that reason. To break a loop, read the previous period instead of the current one, or give the rows beneath a parent their own formula. To have Ari find and fix it, see [Fix a formula that shows an error](/guides/fix-formula-errors). Every error code is listed in [Formula errors and warnings](/reference/formula-errors).
## Related
* [Variables and Dimensions](/concepts/variables-and-dimensions)
* [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast)
* [Totals, aggregation, and time rollups](/concepts/totals-and-rollups)
* [Write a formula](/guides/write-a-formula)
* [Have Ari write, change, or explain formulas](/guides/change-formulas-with-ari)
* [Formula syntax and operators](/reference/formula-syntax)
# How cfo.ai works
Source: https://docs.cfo.ai/concepts/how-cfoai-works
How your Model fits together: your workspace, Variables, Dimensions, formulas, Pages, connected data, Scenarios, and Ari, and why an answer from Ari leaves behind work you can open and change.
cfo.ai is built around one shared financial Model of your business. Ari, your AI finance coworker, answers your questions by reading and building that Model, and you can open, check, and change everything it builds. An answer from Ari leaves behind work you can use again.
## Your workspace
A workspace is your company's home in cfo.ai. It holds the Model, the Pages that present it, the Scenarios you are testing, the systems you connected, your chats with Ari, saved Skills and Triggers, and your team. Everything you or Ari build belongs to the workspace, not to a single chat. Files are the exception: a file you attach, or one Ari makes for you, stays in its chat, which only you can open. See [What Ari remembers](/concepts/ari-memory#files-from-your-other-chats).
## How your Model fits together
The Model is a live representation of how your business works across time. Three ideas carry it:
* Variables hold the values you care about, such as Revenue, Headcount, or Cash, and the assumptions behind them, such as a price. Each Variable has a value for every period.
* Dimensions organize those values into business categories, such as Department, Product, or Region, so Revenue can be read by Product as well as in total.
* Formulas connect Variables to each other, so a change in one place flows to everything that depends on it.
Take a hiring decision. Two engineers start in March. Headcount rises in March, payroll rises with it, expenses follow, and the cash balance in every later month goes down. You change one assumption, and the Model recalculates the rest.
The timeline has two parts. Months up to your **Last close** are Actuals, usually read from your accounting system. Later months are the Forecast, calculated from your assumptions. See [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast).
## Pages present the Model
A Page is a report built from blocks: Table blocks that show Variables across time, Canvas blocks that draw charts and other visuals, and Text blocks for notes. Pages do not hold copies of numbers. A board report, a hiring plan, and a cash forecast can all show the same Cash Variable, and when Cash changes, every Page shows the new value. See [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks).
## Ari works on the same Model you do
When you ask Ari a question, it reads the Model, your connected data, and the Page you have open. When the answer needs something new, such as a forecast or a hiring Scenario, Ari builds it into the Model as Variables, formulas, and Pages. The result stays after the chat ends, and you can open it, trace any number to its source, and change it yourself. Every change, yours or Ari's, is recorded in version history. See [How Ari works with you](/concepts/working-with-ari).
Ari does not do the arithmetic. It writes formulas, and cfo.ai's calculation engine computes every number from them. That is why you can check a result: open the cell's formula, follow its inputs, and see the data they read.
Other AI tools can work on the same Model too. An AI client such as Claude Code connects through the cfo.ai MCP server and uses the same tools Ari uses, under your account. See [cfo.ai for AI agents](/agents).
## Data flows in from your systems
Connected systems, such as QuickBooks, Xero, Rippling, or a Google Sheet, bring Actuals and operating data into the Model, and refresh once a day. Uploaded files bring in a one-time copy. See [How connected data reaches your Model](/concepts/connected-data).
## Scenarios hold alternatives
Main is your plan. A Scenario is a branch of the Model where you can try a different plan, such as a hiring freeze or a price change, without changing Main, and then compare the two or adopt the change. A Snapshot keeps a plan as it was at one moment, such as the budget your board approved. See [Scenarios, Snapshots, and version history](/concepts/scenarios).
## What this means in practice
* Each Scenario has one version of each number. Fix it once, and every Page and chat in that Scenario sees the fix, as does every Scenario that reads the number from it.
* You can always see why a number is what it is: its formula, its inputs, and where its data came from.
* You choose how to work. Ask Ari, edit a table yourself, or do both on the same Page.
## Related
* [Build your first Model](/tutorials/first-model)
* [Variables and Dimensions](/concepts/variables-and-dimensions)
* [The cfo.ai interface](/reference/interface)
* [Glossary](/reference/glossary)
# Concepts
Source: https://docs.cfo.ai/concepts/index
Explanations of how cfo.ai works and why: Ari, the Model, formulas, time, Scenarios, connected data, Pages, sharing, automation, and plans.
These pages explain how cfo.ai works, so you can predict what it will do and make sense of what you see. Read them when you want to understand something, away from a task. For steps, see the [How-to guides](/guides); for exact labels and limits, see the [Reference](/reference).
## Start here
[How cfo.ai works](/concepts/how-cfoai-works) shows how your Model fits together: your workspace, Variables, Dimensions, formulas, Pages, Scenarios, and Ari. Most other pages build on it.
## Ari
How your AI finance coworker decides what to read and what to change, what it needs you for, and what it keeps between conversations.
* [How Ari works with you](/concepts/working-with-ari): what Ari can and cannot do, when it changes your Model, and what still needs you.
* [What Ari remembers](/concepts/ari-memory): saved facts, your earlier chats, and files from your other chats.
## The Model
How the Model represents your business: the values it holds, how it organizes them, how formulas calculate, how time splits into Actuals and Forecast, and how alternatives live side by side.
* [Variables and Dimensions](/concepts/variables-and-dimensions): line items and assumptions, and the breakdowns that organize them.
* [How formulas work](/concepts/formulas): what a formula calculates, and which formula fills each cell.
* [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast): where your books end and your forecast begins.
* [Totals, aggregation, and time rollups](/concepts/totals-and-rollups): why a total adds up, averages, or shows zero.
* [Dimension mappings and shared Dimensions](/concepts/dimension-mappings): how accounts in your books group into the lines you report.
* [Scenarios, Snapshots, and version history](/concepts/scenarios): which one to use to try a plan, keep a plan, or undo a change.
## Data, Pages, and sharing
How data gets from your systems into the Model, how Pages present it, and who can see what.
* [How connected data reaches your Model](/concepts/connected-data): when synced data updates, and when a new connection is ready to use.
* [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks): why one change shows on every Page, and which block to use.
* [Sharing and access](/concepts/sharing-and-access): who can see and change what, and what a share link's reader sees.
## Automation and plans
How Skills, Triggers, and Guides let Ari repeat work, and how plans, AI usage, and connection slots work.
* [Skills, Triggers, and Guides](/concepts/automation): how Ari repeats a job on request or on a schedule.
* [Plans, AI usage, and connection slots](/concepts/plans-and-usage): when you are charged, what uses AI usage, and which connections take a slot.
# Pages, Table blocks, and Canvas blocks
Source: https://docs.cfo.ai/concepts/pages-and-blocks
How Pages present your Model: what Table blocks calculate and what they only present, how Canvas blocks read live data and Scenarios, why controls never change the Model, how Ari checks a visual, and when to use which block.
Why does the same Revenue number appear on three Pages, and what happens when you change it on one? Because a Page does not hold numbers. A Page is a report that reads the Model, so every Page that shows Revenue shows the same value, and a change made on any of them reaches all of them.
## A Page is a view of the Model
A Page, which some people call a sheet or a tab, is a stack of blocks that read from the Model. Your P\&L, your hiring plan, and a board report can all show the same Cash Variable. When a new hire moves cash, every Page that shows Cash changes with it, because each one asks the Model for the value.
Pages nest. A Page can hold subpages, so a Page with subpages works like a folder. For example, a Board Page can hold one subpage per meeting, and a Plans Page can hold the forecast and the headcount plan. cfo.ai has no other kind of folder.
Ari creates Pages as it builds, such as a Page for each statement in a forecast. You can tell it where its work goes, such as "add this to the Board Page" or "put the new Pages under Plans", and ask it to rename, move, or delete Pages. See [Create and organize Pages and blocks](/guides/create-pages-and-blocks).
Pages follow Scenarios too. The Pages in Main appear in every Scenario that branches from it, but a Page you or Ari add while viewing a Scenario exists only in that Scenario until you merge it into Main. See [Scenarios, Snapshots, and version history](/concepts/scenarios).
## Three kinds of block
| Block | What it is for |
| - | - |
| **Table block** | A grid of Variables across time and categories: a P\&L, a headcount roster, a variance report. You can type into it. |
| **Canvas block** | A chart, a scorecard, a what-if control, or another custom visual that Ari builds from live Model data. |
| **Text block** | Words: a summary, notes, or headings between other blocks. |
Some Pages also have chart blocks from earlier versions of cfo.ai, which still show their charts and can still be restyled. New charts are Canvas blocks: the **Add block** menu offers a blank Canvas block, not a chart, and Ari builds the chart in it.
## What a Table block calculates and what it only presents
A Table block holds no numbers of its own. It names the Variables to show, the Dimensions that break them down, such as Department, and the dates across the top. The Model calculates each value from the Variable's formula, and the table presents the result.
That split decides where a change lands:
* Typing a value or writing a formula changes the Variable in the Model, so every table and chart that shows that cell changes too.
* The Variable's own number format, such as currency with no decimals, is where every table and chart starts. Ari can change it for you.
* Presentation belongs to the table alone. That covers which rows and columns show, their order, filters, hidden rows, column widths, the date range, and Scenario comparison columns. It also covers what you pick in the table's **Format and display** menu, such as a fill color, bold text, an indent, or a different number format or precision for this table.
* A summary column, such as a total at the end of a group of months, only summarizes the cells the table shows and saves no formula. A breakdown total, the parent row above a breakdown, is a formula. See [How formulas work](/concepts/formulas#breakdown-totals-and-summary-columns-calculate-differently).
So you can build a second table of the same Variables with a different layout or format without copying any data. See [Format and arrange a table](/guides/format-a-table).
## Canvas blocks read live data
A Canvas block reads the Model the way a table does. It names datasets, each a set of Variables broken down by Dimensions and dates, and the Model calculates them every time the block loads. When Actuals sync overnight or someone changes an assumption, the chart redraws with the new values. A blank or error cell, such as `#DIV/0!` from a formula, reaches the visual as no value; the fix is in the Model, not in the chart.
A Canvas block reads the Scenario of the Page you are viewing, unless a dataset names another one. One block can read up to three Scenarios, so a single chart can show Main, an Upside, and a Downside side by side. Everyone who can read the Page sees the data of every Scenario the block was saved to read, including someone who opened it through a share link. See [Sharing and access](/concepts/sharing-and-access).
A Canvas block takes its colors, heading typeface, and corner shape from the theme in effect: your workspace brand, or each person's own theme. A color that Ari fixed on purpose, such as a brand color, stays the same in light and dark mode. A Canvas block cannot show an image from the web or from a file, so a logo or icon inside one is drawn with shapes and text.
## Controls change the view, never the Model
A Canvas block can carry sliders, toggles, menus, and buttons. Moving a slider shows what would happen, such as how cash runway responds to a later hiring date, without saving anything. A control never writes a value into the Model. To change an assumption, edit it in a Table block or ask Ari.
A block can save a control's setting, such as monthly or quarterly, when it was built to. The saved setting belongs to that block in that Scenario, and everyone who opens the Page sees it. A button in a block can open another Page, start a Guide, or start a chat with Ari, but it never changes the Model either. See [Change a chart or Canvas block](/guides/change-a-visual).
## How Ari checks a visual
To check a Canvas block it built or changed, Ari renders the saved block in a browser with live data, once in light mode and once in dark mode, at a width of 1,200 pixels. It reads the errors and warnings from those renders, such as a chart series with blank or error cells, and can fix the block before it answers.
The check has limits. It does not click buttons, type into fields, follow links, or start Guides, so what those do is first tried when you use them. It does not check the block at other widths. A clean render also does not prove the numbers are right; that comes from the Model's formulas, which you can trace from any table. See [How Ari works with you](/concepts/working-with-ari).
## The Page's date range
A Page that holds a table, a chart, or a Canvas block with data has a date-range control in its title row. Changing it sets the same range on every table, chart, and Canvas dataset on the Page at once.
## Choosing a block
| You want readers to | Use |
| - | - |
| See exact numbers in rows and columns, such as a P\&L or a variance report | A Table block |
| Type in assumptions or record values | A Table block |
| Compare a plan with a Scenario number by number | A Table block with comparison columns |
| See a trend, a mix, or how a total changed | A Canvas block with a chart |
| Check a few headline numbers at a glance | A Canvas block with a scorecard |
| Try a what-if without changing the plan | A Canvas block with a control |
| Read the story behind the numbers | A Text block |
To choose the right chart for a question, see [Prompt library](/reference/prompt-library).
## Related
* [Build a dashboard or board report](/guides/build-a-dashboard)
* [Change a chart or Canvas block](/guides/change-a-visual)
* [Create and organize Pages and blocks](/guides/create-pages-and-blocks)
* [Variables and Dimensions](/concepts/variables-and-dimensions)
* [The cfo.ai interface](/reference/interface)
# Plans, AI usage, and connection slots
Source: https://docs.cfo.ai/concepts/plans-and-usage
What a cfo.ai plan pays for and when you are charged: the free trial, why a plan is priced per workspace, what uses AI usage and what does not, what happens when usage runs out, which connections take a slot, and what stops when a workspace has no plan.
A cfo.ai plan buys a workspace two things: a monthly amount of AI usage, which pays for Ari's work, and a number of connection slots, which limit how many data sources you can sync. Plans do not limit the number of Pages, Scenarios, or people. That is why adding a teammate does not raise the bill, and why Ari can stop partway through an answer when usage runs out.
## A plan belongs to the workspace
A plan is priced for the workspace, not for each person in it. Inviting another administrator does not change what you pay, and everyone in the workspace shares the same AI usage and connection slots.
Each workspace has its own plan and its own bill. A second workspace, such as one for another company, needs its own plan, and each workspace can have one free trial. See [Create a second workspace or switch workspaces](/guides/manage-workspaces).
## When you are charged
A workspace's first plan starts with a 14-day free trial. You add a card to start it, and nothing is charged until the trial ends. Then the paid plan starts on its own and charges that card.
If you cancel during the trial, nothing is charged, and you keep the workspace until the trial's end date. A canceled paid plan also stays active until the end of the term you paid for. Every price, date, and billing message is in [Plans and billing](/reference/plans-and-billing#free-trial). To cancel, see [Cancel your subscription or delete your data](/guides/cancel-or-delete).
## AI usage pays for Ari's work
Every time Ari reads, thinks, and writes, it uses AI usage. That includes:
* Your chats with Ari, in cfo.ai or in Slack.
* Background work Ari starts from a chat, such as building several Pages at once.
* Every run of a scheduled Trigger.
* Images Ari creates when you ask for one. A higher-quality image costs more.
Some AI work is not charged to you. That includes work cfo.ai starts on its own, such as Ari's first look at a newly connected source and the saved queries cfo.ai writes after a sync. It also includes work cfo.ai repeats after a failure on its side. A sync itself uses no AI usage. Work you ask Ari to do with the synced data, such as building Pages from it, uses AI usage like any other request.
Your plan includes an amount of AI usage each billing cycle, and the **Usage** section of Settings shows the share used so far. It resets when the cycle renews. Unused included usage does not carry over.
## When included usage runs out
After the included usage is gone, Ari draws on usage you bought. You can buy it once, have cfo.ai buy it automatically when your balance runs low (auto-reload), and cap how much extra usage can cost in a month (a spend limit). Usage you bought does not reset each month.
When no usage is left, or the spend limit is reached, Ari stops at its next step and says why in the chat. Nothing is lost: your Pages, your data, and the changes Ari already saved stay, and connected data keeps syncing. Once you add usage or raise the limit, send a new message and Ari picks up where it left off. With auto-reload on, Ari pauses for a moment while the purchase goes through and then continues. See [Buy AI usage, set a spend limit, or turn on auto-reload](/guides/manage-ai-usage).
During a free trial, the trial includes its own amount of AI usage. When it is used up, you can buy extra usage or start your paid plan early, which resets your included usage; see [Start a trial or choose a plan](/guides/choose-a-plan).
## Connection slots limit what you can sync
A plan allows a number of standard connections and a number of premium connections. Plans differ in how many connections you can hold, not in what a connection does. Each source that syncs data into your Model takes a slot, while CSV uploads and the MCP servers and APIs that Ari calls live take none. Which systems are premium, and exactly how slots count, such as for Google Sheets or a second company, are listed in the [integrations directory](/reference/integrations#standard-and-premium-integrations).
When every slot is used, a new connection is refused until you remove one or move to a plan with more. You also cannot move to a plan with fewer slots than you use until you disconnect enough. For the messages you see, see [Connection statuses and actions](/reference/connection-statuses#messages-when-a-new-connection-is-refused).
## What stops when a workspace has no plan
A workspace with no plan, such as after a canceled plan ends, locks: its administrators see the plan chooser, and nobody can work in it until someone chooses a plan. Its connected sources stop refreshing. The data already in the workspace stays. A payment that fails at renewal does not stop syncing; cfo.ai retries the charge, and access stops only if the payment still fails. See [Plans and billing](/reference/plans-and-billing).
## Related
* [Plans and billing](/reference/plans-and-billing)
* [Start a trial or choose a plan](/guides/choose-a-plan)
* [Buy AI usage, set a spend limit, or turn on auto-reload](/guides/manage-ai-usage)
* [How connected data reaches your Model](/concepts/connected-data)
# Scenarios, Snapshots, and version history
Source: https://docs.cfo.ai/concepts/scenarios
Scenario, Snapshot, or version history: which one to use to test a decision, keep a plan as it was, or undo a change. How Main, Scenarios, and Snapshots relate, and what flows between them.
A Scenario answers a "what if" question, such as "What if we hire two engineers in Q1?", without changing Main, the plan your team relies on. A Snapshot pins a plan at one moment, such as the budget your board approved, so later syncs and later edits to that plan do not reach it. Version history records every change, yours or Ari's, so you can undo any of it, and nothing you try is permanent by accident.
## Main is the plan
Every Model has one **Main** Scenario. Main holds the numbers your team treats as the current plan, and it is what Pages show unless you pick another Scenario.
## A Scenario holds only what is different
A Scenario branches from Main, or from another Scenario. It stores only what differs from the Scenario it branched from, such as values, formulas, Variables, Pages, and blocks, and reads everything else from there. A Page you add in a Scenario stays in that Scenario until you merge it.
Take a hiring Scenario in which two engineers start in January. When QuickBooks syncs new Actuals overnight, Main updates, and so does the hiring Scenario, because it reads those Actuals from Main. The two extra salaries stay in the Scenario alone. You never have to copy new data into your Scenarios by hand.
Two consequences follow:
* A value that the Scenario inherits from Main changes when Main changes. If you set a cell in a Scenario to the value it already inherits, nothing is saved, and the cell keeps following Main. To make a number hold still while Main moves, give the Scenario a different value.
* Connected data is shared. A Scenario changes Model formulas, values, and presentation; it never makes a private copy of an integration. Saved queries that read your connections also belong to Main, so every ordinary Scenario sees the same imported rows.
## Merging brings a Scenario into Main
When you decide to adopt a Scenario, you merge it. Merging applies every change the Scenario holds to the Scenario it branched from, which is Main for most Scenarios, then deletes the merged Scenario. The merge shows in the version history of the Scenario it merged into, and undoing it takes the changes back out and brings the merged Scenario back.
A Scenario that has its own editable child Scenario cannot be merged until that child is merged or deleted. A Snapshot taken from the Scenario does not block the merge.
## A Snapshot pins a moment
A **Snapshot** pins Main or a Scenario at one point in its history. After that point, nothing from the source reaches it:
* Integration syncs. When QuickBooks syncs, Main updates and the Snapshot keeps the numbers it had.
* Edits. A formula or value someone changes in the source later stays out of the Snapshot.
* Table setup. Later changes to a table's settings or column widths in the source do not carry over.
Use a Snapshot to keep the budget you submitted, the plan your board approved, or a reporting baseline you will compare against later.
A Snapshot is not read-only. You and Ari can still type values over its numbers, and those edits save, so treat a Snapshot as a copy your team agrees not to touch. A Snapshot cannot be merged into Main, cannot be restored to an earlier point, and cannot itself be snapshotted. To pin an earlier moment of a Scenario, take the Snapshot from that point in its version history.
## Version history records every change
Every write to a Scenario is recorded, whether you made it or Ari did. Version history gives you two ways to go back:
* **Undo** reverses one change, or one related batch of changes, and leaves everything after it in place. If later changes depended on it, cfo.ai asks before undoing.
* **Restore** returns the whole Scenario to an earlier point and rolls back every change made after it. The restore is itself a recorded change, so you can undo it too. Ari calls this a revert.
A change a Scenario inherited from Main cannot be undone from inside the Scenario. Undo it in Main.
## Scenario, Snapshot, or version history: which one to use
| You want to | Use |
| - | - |
| Try a change and maybe adopt it | A Scenario, then merge it |
| Compare a plan with the current one | A Scenario, shown side by side with Main on a table |
| Keep a plan exactly as it was on a date | A Snapshot |
| Take back a change you or Ari just made | Undo in version history |
| Go back to how Main or a Scenario looked last week | Restore it in version history |
## Where Scenarios show up
* A Page shows the Scenario you are viewing in the sidebar's Scenario picker.
* A Table block can show other Scenarios beside its own, and a Canvas block can read up to three Scenarios at once.
* A share link shows the one Scenario you choose for it. See [Sharing and access](/concepts/sharing-and-access).
* Ari makes changes in the Scenario your chat is in, unless you name another one.
So if a Page or a change seems to be missing, check the Scenario picker: it may be in another Scenario.
## Related
* [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario)
* [Compare Scenarios side by side](/guides/compare-scenarios)
* [Merge a Scenario into Main](/guides/merge-a-scenario)
* [Save a plan as a Snapshot](/guides/save-a-snapshot)
* [Undo a change or restore a version](/guides/undo-changes)
* [Scenario and version history controls](/reference/scenarios-and-history)
# Sharing and access
Source: https://docs.cfo.ai/concepts/sharing-and-access
Who can see and change what in cfo.ai: why everyone in a workspace is an administrator, why read-only access means a share link, what a link's reader sees, who can see your chats, and how Ari follows the same access.
People ask two questions about access: "Can someone have read-only access?" and "Can I show this to someone who has no cfo.ai account?" In cfo.ai the answer to both is a share link. Everyone inside a workspace has full access, and everyone outside it reads through a link.
## Everyone in a workspace is an administrator
Everyone you invite to a workspace joins as an administrator, whether you invite them in Settings or through Ari. Administrators can see and edit every Page, invite other people, and manage billing and settings.
There is no view-only role, no editor role, no per-person access to single Pages, and no user groups. You cannot give a teammate a lower role, by hand or through Ari, because no lower role exists. To add or remove people, or to give the administrator role to someone who joined without it, see [Invite or remove teammates](/guides/invite-teammates).
## Read-only access means a share link
A share link lets someone read one or more Pages without joining the workspace and without a cfo.ai account. An investor, a board member, a judge, or an accountant can open it in any browser. The reader does not appear in your list of administrators.
Take a board update. You create a link to the Board summary Page that also opens the Cash forecast Page, choose the Board plan Scenario, and add a password. The board members open the link, enter the password, and read both Pages. They cannot type into a cell or change a chart, and nothing else in the workspace opens for them.
A link:
* Opens the Pages it names, in the one Scenario saved with it.
* Never allows editing, whoever holds it.
* Needs no sign-in. No setting can require one.
* Works until someone turns it off. Links do not expire.
* Can require a password, which the reader types before the Page opens.
A link adds view access and takes nothing away. A teammate who opens it keeps their own access, including the right to edit, so a link is not a way to make a teammate read-only.
Only Pages can be shared. A Scenario, a chat, a Variable, or a formula has no link of its own; share the Page that shows it. To create, change, or turn off a link, see [Share a Page outside your workspace](/guides/share-a-page).
## What a link's reader sees
A reader sees the Pages as they are when they open them, with live numbers. When your Model changes, the shared Page changes too. To share numbers that stay fixed, such as the plan your board approved, choose a Snapshot as the link's Scenario; see [Scenarios, Snapshots, and version history](/concepts/scenarios).
A block shows the reader what it was saved to show:
* A Table block shows the comparison columns it was saved with, so a Scenario comparison renders for the reader.
* A Canvas block shows the data of every Scenario it was saved to read, so a chart of Main beside a Downside Scenario renders too. Nothing else in those Scenarios opens through the link.
* Sliders and other controls in a Canvas block respond while the reader uses them, but nothing they do is saved, and no control ever changes the Model.
The other Pages in your workspace stay closed to the reader. A reader who is not signed in sees a strip above the Page. When the workspace has a brand, the strip shows the brand's logo and name with **Powered by cfo.ai**; otherwise, it invites the reader to try cfo.ai. See [Brand your workspace](/guides/brand-your-workspace).
## Keeping access to a shared Page
A reader who never signs in has access in that browser for seven days from their first visit. After that, opening the link again lets them back in while the link is on. cfo.ai offers such readers a **Keep your access** card: signing in with an email address keeps the shared Pages with their account, in any browser.
## Turning a link off
Turning a link off stops anyone from opening it again. By default it also removes access from everyone who already opened the Page through the link. Changing a link's Scenario or password does the same to the old link: cfo.ai replaces it with a new link at a new address.
## Your chats are yours
A chat belongs to the person who started it. Your teammates do not see your chats in their chat list, and you do not see theirs. A chat cannot be shared. To show a teammate what Ari found, share the Page Ari built or ask Ari to summarize the answer in a Text block on a Page. Files you attach stay in your own chats; see [What Ari remembers](/concepts/ari-memory).
## Ari follows the same access
Ari works with the access of the person it is talking to. For an administrator, that is the whole workspace. If someone who can only view a Page asks Ari to change it, Ari refuses and saves nothing. Ari can create a share link for you, with the limits listed in [Share a Page outside your workspace](/guides/share-a-page).
## Related
* [Share a Page outside your workspace](/guides/share-a-page)
* [Invite or remove teammates](/guides/invite-teammates)
* [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks)
* [The cfo.ai interface](/reference/interface#share-panel)
# Totals, aggregation, and time rollups
Source: https://docs.cfo.ai/concepts/totals-and-rollups
How cfo.ai adds numbers up: breakdown totals on parent rows, summary columns on a table, time rollups for quarters and years, and the aggregation of imported rows. Why percentages are not summed, and why a quarterly view can show zeros.
cfo.ai adds numbers up in four places, and each one follows its own arithmetic. Knowing which kind of total you are looking at explains why a year column can add up monthly percentages, or why a quarterly view can show zeros:
| Kind of total | Where you see it | How it calculates |
| - | - | - |
| Breakdown total | A parent row above its breakdown, such as Revenue above its products | A formula saved with the table that adds up the rows directly beneath it |
| Summary column | A total column at the end of a group of columns, headed **SUM**, **LAST**, or **TOTAL**, for example | A summary of the cells shown in the group, on that table only; no formula is saved |
| Time rollup | Quarter, half-year, and year cells | A formula of the Variable's own at each coarser grain: the sum, last value, or average of its months |
| Source aggregation | A Variable that reads a connected system | How the source's rows in one period combine into one value |
## Breakdown totals: parent rows add up the rows beneath them
When a table breaks a number down, such as Revenue by Product and then Customer, each row above the bottom rows adds up the rows directly beneath it. Each customer row adds its lines, each product row adds its customers, and the Revenue row adds its products. These sums are ordinary formulas that cfo.ai saves for you, one for each level, and you can open them from the row.
When the bottom rows hold numbers you typed, cfo.ai also saves a formula of 0 on them, which every typed number overrides. A blank bottom row then counts as 0, and every total still adds up. Because each level adds the level beneath it, a change on any row reaches every total above it: set one line to 0, and its customer, its product, and Revenue all go down. A line that appears under two customers is counted under each of them.
When adding up is the wrong arithmetic, give the parent its own formula. Revenue per customer, for example, is total revenue divided by customer count, not a sum; a formula you write on a parent replaces the sum at its level, and cfo.ai keeps your version. Removing a level from the table removes that level's sum, and the level above then adds up the next level that remains.
Not every Variable gets these formulas:
* Text, date, true-or-false, and ID Variables get neither the sums nor the 0.
* An imported Variable gets no 0, because its bottom rows read the source.
* A formula of your own on the Variable's row gives every row its value, so the sums and the 0 are removed.
* A formula you write on one bottom row wins in that row only; the other rows keep the 0, and every total keeps its sum.
* A default for every child of one parent, such as every customer under one product, covers that parent's children alone and saves no totals.
A table of weeks in a monthly Model gets the same sums at week grain. A table that is saved without changing which levels it shows, for example after sorting or formatting, changes no formulas.
## Percentages and ratios are not added up
Three rates of 5% do not make a rate of 15%, so a Variable whose own format is a percentage is never summed above its breakdown. Each row above the bottom rows gets one of two formulas instead:
* The bottom rows' formula, calculated from the row's own inputs, when one formula calculates the bottom rows and everything it reads has a value on that row. A margin of `Profit / Revenue` above the segments then reads total profit over total revenue: the ratio of the totals.
* A blank everywhere else. That covers bottom rows that hold typed-in rates, bottom rows that several formulas calculate, and a formula that needs the row's own item of a Dimension the row does not have. cfo.ai does not average rates, because no rate for the whole follows from the parts' rates without weights.
A rate you type on one bottom row blanks the rows above it; remove it, and the formula returns.
The Variable's own format decides this, not a table's. Formatting a row as a percentage on one table changes only how it looks there, and the Variable keeps adding up. To make a ratio stop adding up, ask Ari to make the Variable a percentage. A ratio written with no time in its formula, such as ``iferror(Revenue / `Paying customers`, NULL)``, is right at every level and in every period, quarters and years included.
## Summary columns: total columns at the end of a group
A summary column summarizes the cells shown in its column group, such as the months under each year, on that one table. It saves no formula, so it never changes a number anywhere else. Its header names the summary it uses, such as **SUM**, **AVG**, or **LAST**. The **None** summary is headed **TOTAL** and shows the Variable's own value for the whole group, such as its year value beside its months. Every summary, and the one each table starts with, is listed in [Table block controls and messages](/reference/table-blocks#total-columns). To change a column's summary, see [Format and arrange a table](/guides/format-a-table).
For a full-year total, the two most useful summaries differ. **Sum** adds the months the table shows. **None** shows the Variable's own year value, which comes from its time rollup or from a formula that applies to years. They agree for a flow such as revenue when the table shows all twelve months. They differ for a balance, for a ratio, and for a table that shows part of a year. A year column on Sum adds up monthly percentages, which is wrong; on None, a margin written with no time in its formula shows the year's profit over the year's revenue.
## Time rollups: how months become quarters and years
A formula written for months fills month cells only. Quarter, half-year, and year cells read the Variable's **time rollup**: a formula of its own at each coarser grain that combines the months.
| Rollup | What a quarter or year shows | Fits |
| - | - | - |
| **SUM** | The months added up | Flows, such as revenue and expenses |
| **LAST** | The last month's value | Balances and counts, such as cash and headcount |
| **AVERAGE** | The average of the months | Means, such as average deal size |
**LAST** keeps a missing closing month empty instead of borrowing an earlier month's value.
When Ari adds a new number to a monthly table, it gives the Variable a rollup in the same save. It uses **SUM** in most cases, and **LAST** for a Variable that takes the last value of its source rows, such as a balance. Some Variables get no rollup: a percentage, text, a date, a true-or-false value, and a number aggregated another way. A Variable whose formula has no time in it gets none either, because that formula calculates quarters and years directly. A Variable you create by hand from a table gets no rollup. Ask Ari to add or change a Variable's rollup.
## Why a quarterly view shows zeros
A table that shows quarters or years reads each Variable's quarter or year value. A Variable with no time rollup, and no formula that applies at that grain, has no such value. Its quarter and year cells show 0, or a blank for text and dates. cfo.ai does not add up the months on its own. When Ari reads such a cell, it reports that the Variable has no time rollup instead of treating the 0 as a total.
To fix it, ask Ari to give the Variable a rollup, such as **SUM** for revenue or **LAST** for cash. Or show the months on the table with summary columns, which add up the months shown without a rollup.
## Source aggregation: how imported rows become one value
A Variable that reads a connected system combines the source rows that fall in each period by its **aggregation**, a setting on the Variable. Sum adds the rows, which is right for transaction amounts. Last takes the latest row, which is right for a balance or for an employee's current department. The right setting depends on the source: changing every Variable to Last would break the sums of a transaction ledger. See [How imported data loads](/reference/imported-data).
Formulas can also combine values on purpose. `sum(Revenue[Department in any])` adds up every department's revenue, and `average`, `min`, `max`, `count`, `median`, `first`, and `last` work the same way. If Sales has 50, Marketing 30, and Engineering 20, the sum is 100, the average is about 33.33, and the median is 30. Every aggregation function is listed in [Formula functions](/reference/functions).
## Related
* [How formulas work](/concepts/formulas)
* [Variables and Dimensions](/concepts/variables-and-dimensions)
* [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast)
* [Break a number down](/guides/break-down-a-number)
* [Format and arrange a table](/guides/format-a-table)
# Assumptions and breakdowns: Variables and Dimensions
Source: https://docs.cfo.ai/concepts/variables-and-dimensions
What a Variable (a line item or assumption) and a Dimension (a breakdown or category) are, how to choose between them, how segments and addresses name one number, and how spreadsheet, FP&A, and BI terms translate.
Every number in your Model answers two questions: what is being measured, and for which part of the business. A **Variable** answers the first. It is a line of the Model, such as Revenue, Headcount, or Price per seat, and it is what most people call a line item or an assumption. A **Dimension** answers the second. It is a list of categories, such as Department or Region, that breaks a Variable down. Most people call these breakdowns or categories.
## A Variable is a line of your Model, such as Revenue or an assumption
A Variable is a named set of values, with one value for each period and each part of the business it is broken down by. Revenue is one Variable, even though its value for the West in March differs from its value for the East in April.
A Variable's values can come from three places:
* A connected system, such as your accounting software.
* Values you or Ari type in, such as a price or a starting cash balance.
* A formula that calculates them from other Variables.
The source can differ by period without changing what the Variable means. Revenue can read your books in closed months and come from a formula in later months, and it is still Revenue.
Assumptions are Variables too. Price, churn rate, average salary, and conversion rate are inputs rather than results, but they are values you can change, compare, and use in formulas. When Ari estimates a value you did not give it, it labels that value as an assumption.
Most Variables hold numbers. A Variable can also hold text, a date, or true or false, such as an employee's start date.
A Variable is shared. The same Revenue Variable can appear on a P\&L, a chart, and a board report, and when it changes, every place that shows it changes.
## Where to find every Variable
There is no screen that lists every Variable in your Model. Workspace search (**⌘ K**) finds a Variable or Dimension by name and opens the table that shows it. Ari can list every Variable and Dimension in your Model and tell you where each one is used.
You create a Variable when you add a row with a new name to a table, when Ari builds part of your Model, or when a connection brings in a column. See [Build a Table block by hand](/guides/build-a-table) and [Find a Page, number, or file](/guides/find-things).
## A Dimension is a breakdown, such as Department or Region
A Dimension is a list of categories that breaks Variables down. Each entry in the list is a **Dimension item**, or item for short: Sales is an item of Department, and West is an item of Region. Common Dimensions are Department, Region, Product, Customer, Vendor, Account, Entity, and Date.
One Dimension serves every Variable that uses it. Department splits Payroll, Headcount, and Expenses the same way, so when you add a new department, every Variable broken down by Department has a place for it.
A Dimension can play two roles on a table. It can break a Variable down into one row or column per item. It can also show which item each row belongs to, such as each employee's department; that second role is a mapping, explained in [Dimension mappings and shared Dimensions](/concepts/dimension-mappings).
## Is the timeline always in months?
No. Months are the default, and most Models calculate monthly. Each Scenario's **Default date granularity** sets its base time grain: **Raw**, day, week, month, quarter, half-year, or year. New tables in that Scenario show that grain, and a formula Ari saves without a table uses it. A formula written through a table takes that table's grain, so one written on a table of years fills years, not months. Changing the setting leaves the tables and formulas you already have at their own grain. See [Set Last close and date defaults](/guides/set-dates-and-last-close).
Date is a Dimension too. Its items are periods, and a period is one bucket of time at the base grain, such as March 2026 in a monthly Model. A table can show coarser periods than the base grain: quarters and years in a monthly Model come from each Variable's time rollup, explained in [Totals, aggregation, and time rollups](/concepts/totals-and-rollups). A table can also show finer periods, such as weeks in a monthly Model, but a formula written for months does not fill week columns; weeks need formulas written for weeks.
## Variable or Dimension: how to choose
Ask one question: is this the value I want to measure, or the way I want to organize that value? A value is a Variable. A way of organizing values is a Dimension.
| Variables: values you measure | Dimensions: ways to organize them |
| - | - |
| Revenue, Cash balance, Headcount | Department, Legal entity, Region |
| Units sold, Average salary, Gross margin | Product, Customer, Vendor |
| Price, Churn rate, Conversion rate | Account, Date |
Do not choose by the kind of data:
* Employee ID holds numbers, but it identifies people, so it is a Dimension.
* Gross margin is a percentage, but it is a calculated value, so it is a Variable.
* Headcount is a count, not a list of people, so it is a Variable.
* Date holds dates, but it organizes values across time, so it is a Dimension.
Accounts are the common hard case. An account from your books, such as account 6100, classifies transactions, so Account is a Dimension. A line you report, such as Revenue or Rent expense, is a Variable. When several accounts roll up into one line, a mapping says which accounts belong to it; see [Dimension mappings and shared Dimensions](/concepts/dimension-mappings).
Alternative futures are neither. A base case, an upside, and a downside never happen together, so adding them up answers nothing. Each one is a Scenario; see [Scenarios, Snapshots, and version history](/concepts/scenarios).
## How Ari applies the choice
Ari asks one question of everything it builds: when the business adds one more department, product, or customer, what has to change? The right answer is one new item in a Dimension, plus one mapping row when the new item needs a category. Every total, table, and chart then follows on its own.
So Ari builds Payroll broken down by Department, not separate Engineering Payroll and Sales Payroll Variables. It builds Revenue by Product even when you sell one product today, so a second product is one new item that the formulas and totals already reach. It keeps base, upside, and downside cases in Scenarios, not as three Variables.
## Segments and addresses name one exact number
A **segment** is a slice of a Variable named by one or more items: Revenue for Sales, or Revenue for Sales in the West. On a table, **Segment by** shows a Variable's segments as rows or columns. Moving a Dimension between rows and columns changes the layout, not the numbers.
An **address** names one value completely: the Variable, an item from each of its Dimensions, and a period. "Revenue for Sales in the West in March 2026" is one address. In a spreadsheet you would point at a cell such as B7; in cfo.ai you name what the number means, which is why a formula can work in every month and every department at once. See [How formulas work](/concepts/formulas).
## Worked example: revenue by department
The question: how much revenue did each department bring in each month?
* Revenue is the Variable, the value being measured.
* Department is a Dimension that organizes Revenue by team. Sales, Marketing, and Engineering are its items.
* Date is a Dimension that organizes Revenue by month.
One result is "Revenue is 250,000 for Sales in January 2026." Revenue, Sales, and January 2026 together are its address. The Scenario you view, Main or another, supplies the rest of the context.
Suppose Revenue's formula is:
```text theme={null}
`Units Sold` * Price
```
When the table breaks Revenue down by Department, and Units Sold and Price are broken down by Department too, the Sales row multiplies Sales' units sold by Sales' price. A name in a formula reads the same department and month as the cell it calculates. If Sales prices differently, Revenue can have a second formula that applies to Sales alone, while the first keeps applying to every other department.
## For spreadsheet and FP\&A users
| What you call it | What cfo.ai calls it |
| - | - |
| Line item | Variable |
| Assumption or operating input | Variable |
| Department or cost center | Dimension |
| Entity, product, region, or customer | Dimension |
| A category entry, such as Sales or West | Dimension item |
| Month, quarter, or year columns | Date, shown at a time grain |
| A P\&L by department | A Variable broken down by a Dimension |
| Actuals columns and forecast columns | Formula ranges, split by Last close |
| Budget version or plan case | Scenario |
| Worksheet | Table block |
| Tab or sheet of reports | Page |
| Cell reference, such as B7 | Address |
A Page is closer to a report than to a worksheet: it holds Table blocks, charts, and notes, and the numbers live in the Model, not on the Page. See [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks).
## For BI and data-modeling users
| Dimensional-modeling term | cfo.ai term |
| - | - |
| Measure or fact | Variable |
| Dimension or attribute | Dimension |
| Dimension entry | Dimension item |
| Group by, slice, or dice | Segment by and filters |
| Grain | The Dimensions and Date grain of a value |
| Cube cell | A Variable's value at one address, in one Scenario |
Grain matters because every address calculates its own formula with its own inputs. A margin shown by department is calculated from each department's revenue and cost; it is not derived from the company-wide margin. You do not need to design a star schema or a cube to work in cfo.ai: Variables hold values, Dimensions organize them, and an address names one result.
## Related
* [How formulas work](/concepts/formulas)
* [Totals, aggregation, and time rollups](/concepts/totals-and-rollups)
* [Dimension mappings and shared Dimensions](/concepts/dimension-mappings)
* [Break a number down](/guides/break-down-a-number)
* [How cfo.ai works](/concepts/how-cfoai-works)
* [Glossary](/reference/glossary)
# How Ari works with you
Source: https://docs.cfo.ai/concepts/working-with-ari
What Ari, your AI finance coworker, can and cannot do, what it can see, when it reads your Model and when it changes it, what still needs you, and what happens during a long build.
"What can you do?" is one of the first things people ask Ari. The short answer: Ari answers business questions about hiring, cash, growth, and costs by reading and building your Model, the live financial model of your company that you can open and change yourself. This page explains what Ari can see, when it changes your numbers, where it needs you, and where its job ends.
## What Ari can do for you
Ari works on the same Model you do, so most of what it does leaves something behind that you can open and check:
* Explain a number, a formula, a table, or an unusual change, and trace a number to its source.
* Build a forecast, a hiring plan, financial statements, or a cash outlook from your data, your files, or a description of your business, or rebuild a spreadsheet model you already have.
* Connect accounting, payroll, CRM, and other systems, and tell you when their data has loaded.
* Create Scenarios to test a decision, and compare them with Main.
* Build Pages with Table blocks and Canvas blocks, such as a board summary with charts.
* Save a workflow you repeat as a Skill, schedule a Trigger that runs on its own, and build a Guide that walks someone through a Page.
* Read files you attach, search the web for public information, and email you or message you in Slack.
For prompts that get each of these, see the [Prompt library](/reference/prompt-library).
## What Ari and cfo.ai do not do
cfo.ai works from your books and your other systems, and the jobs below stay in those systems.
### Bookkeeping
cfo.ai reads your accounting system. It does not record transactions, categorize them, or post journal entries there.
### Posting changes back to QuickBooks or other systems
Accounting, payroll, and other data connections bring data into cfo.ai and send nothing back. To change a number at its source, change it in that system; the next refresh brings it in. Ari can act in another service only through an MCP server or API you connect for it, and it asks before calls that need your approval. See [Give Ari access to another service](/guides/give-ari-another-service).
### Month-end close
You close the books in your accounting system. After the close, cfo.ai reads the new Actuals, and Ari can compare them with your forecast and explain the differences. See [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast).
### Taxes and VAT
cfo.ai does not prepare or file tax or VAT returns. You can plan tax payments in your forecast like any other cost.
## What Ari can see
When you ask Ari something, it can use:
* The Page you have open, and any cell, row, block, or Page you mention in the chat.
* Your whole Model: its Pages, Table blocks, Variables, Dimensions, formulas, Scenarios, and the history of every change.
* The data your connections brought in, down to the rows each source synced.
* Files attached in this chat and in your other chats in the workspace. See [What Ari remembers](/concepts/ari-memory).
* Facts you or your team asked it to remember, the built-in Skills, and your own Skills that apply in this workspace.
* Public web pages, through web search, and these docs, when you ask how cfo.ai works.
Ari acts as you. It can open and change what you can, and nothing more. It cannot read a teammate's private memory or the files in a teammate's chats.
If web search fails because of a problem on cfo.ai's side, such as its search provider account running out of credit, Ari tells you so. Nothing in your request caused it, and web search stays unavailable until cfo.ai fixes it.
## Reading the Model versus changing it
A question such as "Why did revenue fall in March?" can be answered by reading what the Model already holds, and nothing changes. A request such as "Build a revenue forecast" asks Ari to add to the Model, and the change is visible on your Pages.
Some questions sit in between. The answer may need a calculation the Model does not have yet, such as gross margin by product when no table shows it. Then Ari has to add that calculation to the Model to work it out. Every calculated number in the Model comes from cfo.ai's calculation engine and a saved formula, so a new calculation needs a saved formula. If you want Main left alone, ask Ari to do the work in a Scenario. See [Ask Ari about a number](/guides/ask-about-a-number).
Ari does not ask before each change to the Model, because every change is recorded and can be undone. It asks first before things it cannot take back, such as an email to someone other than you or a change to who can see your data.
## How your numbers stay right
Ari does not type finished results into your tables. It writes formulas and inputs, and the calculation engine computes every number from them, the same way for you, Ari, and anyone else. That is why you can trace any value: to the formula behind it, to the inputs that formula reads, and from an input to the source rows it came from. When an input is an estimate, Ari labels it as an assumption so you know which numbers to replace with real ones.
To check a whole Model before your board sees it, ask Ari to review it, or turn on the built-in **Model audit** Skill. See [Turn on a built-in Skill](/guides/turn-on-built-in-skills).
## Your plan and Ari's proposals
A number you typed, imported, or agreed to is part of your plan. Ari changes it only when you ask for the change or agree to it. A number Ari produces is a proposal until you have seen it. Your plan is what the Model showed the last time you looked, so a value Ari saved without your asking does not become your plan until you have looked at it.
When two things you entered disagree, such as a growth rate beside monthly amounts you typed in full, Ari shows you both with its recommendation and keeps your plan until you choose.
## What still needs you
Ari does most of the work, but some steps are yours:
* You connect each outside system yourself, by signing in to it or by entering its API key or access token. Ari cannot sign in for you.
* You answer Ari's questions when a detail changes the answer, and approve its plan before a large build. See [Answer Ari's questions and approve its actions](/guides/approve-ari-actions).
* You approve calls to connected services that need approval, and emails Ari drafts to anyone other than you.
* You add a password to a share link or turn a link off. Ari can create a read-only link to a Page, but it cannot do either. See [Share a Page outside your workspace](/guides/share-a-page).
Every change Ari makes is recorded in version history, so you can take back any of it. See [Undo a change or restore an earlier version](/guides/undo-changes).
## During a long build
A large build, such as a full Model from your accounting data, can take a while. A turn has no fixed length: it usually ends when the work is done or when Ari needs you. It also ends when you stop it, or when an error or a billing limit stops it early; see [When a reply stops early or shows an error](/reference/chat#when-a-reply-stops-early-or-shows-an-error). While Ari works, the chat shows its steps as they happen. The Pages it builds appear in the sidebar one at a time, so you can read the first while the next one forms.
Ari works on cfo.ai's servers, so you can close the tab, switch chats, or leave, and Ari keeps going. When you come back, the chat shows everything Ari did, and the Pages it built are in the sidebar.
The chat list marks each chat with what Ari is doing:
* **Ari is working…** while it works.
* **Needs your response** when it is waiting for an answer, an approval, or a sign-in.
* **Unread response** when it has finished.
If a chat seems paused, look for **Needs your response**: the question or card Ari is waiting on is at the bottom of the chat. To be told when Ari finishes, turn on browser notifications. See [Chat controls and messages](/reference/chat#while-ari-works).
You can redirect Ari at any point without waiting. See [Steer, stop, or add to Ari's work](/guides/steer-ari).
## Related
* [How cfo.ai works](/concepts/how-cfoai-works)
* [What Ari remembers](/concepts/ari-memory)
* [Scenarios, Snapshots, and version history](/concepts/scenarios)
* [Get better answers from Ari](/guides/get-better-answers)
* [Make a precise change with Ari](/guides/change-your-model-with-ari)
* [Chat controls and messages](/reference/chat)
# Use different formulas for Actuals and Forecast
Source: https://docs.cfo.ai/guides/actuals-and-forecast-formulas
Give a Variable one formula for closed months (Actuals) and another for later months (Forecast), such as imported payroll before Last close and headcount times salary after it.
Use separate formulas when closed months should show what your books recorded and later months should show a plan. For example, Payroll can read QuickBooks through **Last close** and calculate Headcount times Average Salary after it. Every workspace has two formula ranges: Actuals, the months through Last close, and Forecast, the months after it. A Variable can have a formula for each. To understand the boundary, read [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast). To move it, see [Set Last close and date defaults](/guides/set-dates-and-last-close).
A formula range needs the Model's base periods: months, unless your Model uses another grain. A formula you save on a row of a table with those periods already covers them, and a row on a table with no periods has one formula for every period. Write range formulas on a table at the base grain. In a monthly Model, a formula written on a table by year is saved at year grain and fills only the year cells; the month cells do not read it.
Name the Variable and both calculations:
> Use imported Payroll for Actuals and Headcount times Average Salary for Forecast.
To change only one range, name it:
> Forecast Software Spend as 4% of Revenue. Leave Actuals as they are.
Ari saves each formula for its range and tells you if a Variable cannot take a range-specific formula, for example because the table it sits on has no months. A range formula Ari saves on a Variable outside any table covers the Model's base periods, usually months.
On a table with months, point at the row name and select the **fx** button, **Edit variable formula**. The editor shows one section for each range, **Actuals** and **Forecast**.
You can also double-click the row's cell in the **Actuals Formula** or **Forecast Formula** column. The editor opens with that section expanded.
Select a section's name to expand or collapse it.
For the example, leave **Actuals** reading the imported values and type this in **Forecast**:
```text theme={null}
Headcount * `Average Salary`
```
Each section reads what you type the way a cell does: a leading `=` marks a formula, `1,000` saves as the number 1000, and plain words in a text Variable save as text.
Press **Enter**.
Check the first Forecast month after you save. A formula that reads the previous month, such as `` `Cash Balance`[-1] + `Net Cash Flow` ``, starts from the last Actuals month, so that month needs a value.
## When a section shows a dimmed formula
A section with no formula of its own shows the formula it inherits, dimmed. Point at it to see the source: **Inherited from Actuals formula** (or **Forecast**), and the address that owns it.
* Select **Edit source formula** to open the formula it inherits and change it there. Saving it updates that formula instead of creating a second copy.
* Click the dimmed formula, press **→**, or press **Tab** to copy it into this section and change it for this range only.
* Start typing to replace it.
A formula the section owns opens at full strength and edits in place.
## Show the Actuals and Forecast formulas side by side
On a table with months, the formula column splits into **Actuals Formula** and **Forecast Formula**. To show the formula columns, see [Show or hide the formula columns](/guides/write-a-formula#show-or-hide-the-formula-columns).
* To hide one, right-click its header and choose **Hide column**.
* To bring it back, right-click the other one's header and choose **Show Actuals column** or **Show Forecast column**.
* **Hide all formula columns** hides both.
A row that cannot split by range, such as a row with no months, shows its one formula in the first formula column shown.
## Related
* [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast)
* [Write a formula](/guides/write-a-formula)
* [Fix a formula that shows an error or a wrong number](/guides/fix-formula-errors)
* [Formula syntax](/reference/formula-syntax)
# Answer Ari's questions and approve its actions
Source: https://docs.cfo.ai/guides/approve-ari-actions
Respond when Ari waits for you and the chat shows Needs your response. Answer a question form, approve or change a plan before a build, allow or decline a call to a connected service, finish a form from an MCP server, and review an email before it is sent.
Use this when a chat shows **Needs your response**, or Ari stops and shows a card. Ari waits for you before details that change the answer, before a large build, before calls to connected services that need approval, and before emailing anyone but you. Answer in the chat, and Ari continues on its own.
## Answer a question form
Choose an option, or press its letter. To answer in your own words, choose **Other…** and type. If a question takes files, select **Attach files to your answer**.
Select **Dismiss** beside a question to skip it. Ari continues without it. **Undo** brings it back before you send.
Select **Next** to move through the questions, then **Submit answers** (**Submit** on a one-question form).
If you scrolled up when the form arrived, select **Scroll to latest messages** to reach it. To skip every open question at once, select **Dismiss** at the bottom of the form; the answers you already picked are still sent. See [Chat controls and messages](/reference/chat#question-form) for every control.
## Approve a plan
For a larger build, Ari shows a plan card before it starts: what it will create and the assumptions it will use.
Each assumption shows Ari's suggested choices. Pick another, or choose **Other** and type your own value. If the card says **Answer above to continue**, an assumption Ari needs has no answer yet.
Select **Build it** to start. To change something the choices cannot express, such as which Pages to build, select **Make changes** and describe it in the message box.
Your approval appears in the chat as **Build it — with these selected assumptions:** followed by the assumptions you changed, so the record of what you approved stays in the conversation.
## Allow or decline a call to a connected service
When Ari wants to use an MCP server or API you connected, and the call needs approval, Ari pauses and shows **Ari is asking to run "\[tool]" on \[service].**
* Select **Allow** to run this one call.
* Select **Always allow** to let Ari use this tool on this service without asking again, for the whole workspace. Only people who can manage workspace settings see it.
* Select **Decline** to skip the call. Ari continues without it.
Anyone in the chat can allow or decline one call. After you choose, Ari usually continues without another message from you. If it stays paused, for example because a notice says the approval saved but Ari was not prompted to continue, send any message and Ari picks up from there. If the card can no longer be acted on, for example because a teammate already answered it, a notice says why. See [Chat controls and messages](/reference/chat#tool-call-approval-card). To change which tools ask first, see [Give Ari access to another service](/guides/give-ari-another-service).
## Finish a form from an MCP server
Some MCP servers send their own form, such as a form to create an issue, filled with what Ari proposed. It appears in the chat under **View from \[server]: \[tool]**. Check the fields and finish it there, for example by selecting its **Create issue** button. If the form asks to run another call, approve it on the card that appears under the form. If it asks to open a page, check the address and select **Open link** or **Cancel**.
These forms appear only in chats in the cfo.ai web app.
## Review an email before it is sent
Ari emails you at your sign-in address as soon as you ask. An email to anyone else waits for you on a **Preview** card.
Check the recipients, the subject, and the message.
Select **Edit**. Type addresses separated by commas or **Enter**; each becomes a removable chip. Select **Preview email** to see your changes.
Select **Send**. Nothing leaves cfo.ai until you do.
Scheduled Triggers work the same way, so a run that would email someone else stops at the preview in its chat.
## Answer questions from a scheduled run
A scheduled Trigger runs without you, so it cannot wait for an answer. Questions it asks stay in its chat, which shows **Needs your response**. When you open the chat, questions the run asked at different points appear together on one form, and one reply answers them all.
## Confirm a Scenario merge, delete, or revert
If you asked Ari to let you confirm before it merges, deletes, or reverts a Scenario, it shows a card with a button you press yourself; see [Scenario confirmation card](/reference/chat#scenario-confirmation-card). See also [Merge a Scenario into Main](/guides/merge-a-scenario) and [Undo a change or restore an earlier version](/guides/undo-changes).
## Related
* [How Ari works with you](/concepts/working-with-ari#what-still-needs-you)
* [Steer, stop, or add to Ari's work](/guides/steer-ari)
* [Chat controls and messages](/reference/chat#cards-in-the-chat)
# When Ari is slow, stuck, or wrong
Source: https://docs.cfo.ai/guides/ari-is-slow-stuck-or-wrong
What to do when Ari takes a long time, seems stuck, stops with an error, gives a number that looks wrong, or changes something it should not have. Also how to reach a person at cfo.ai.
Use this page when a chat with Ari is not going the way you expect. Find what you see, then follow the steps under it. Changes Ari saved before a problem stay in your Model, and you can take back any of them in version history.
## Ari is taking a long time
Ari keeps working on cfo.ai's servers until it finishes, even if you close the tab or switch chats. A large job, such as building a whole Model or importing a big workbook, takes many steps.
* Check the status line above the message box. While its phrase changes and its meter counts up, Ari is working.
* To see what Ari has done so far, open **Activity** under your message. Each finished step has a check mark.
* To change direction without waiting, type your message and press **Enter**. Ari reads it at its next step. See [Steer, stop, or add to Ari's work](/guides/steer-ari).
* To get a notice when Ari finishes, turn on **Notify me when Ari finishes in this browser** in **Settings → Ari**. See [Settings](/reference/settings).
## Ari seems stuck
1. Read the status line. **Ari hit a temporary connection issue. Retrying now.** and **Ari hit a temporary model-provider issue. Retrying now.** mean Ari is retrying on its own. Wait a moment.
2. If nothing changes for a long time, type "What are you working on right now?" and press **Enter**. Ari answers at its next step.
3. If you still get no answer, press **Esc** in the message box, or select **Stop**. Then ask for a smaller piece of the work, such as one Page or one quarter.
Stopping keeps what Ari already saved. To check what it finished, ask "What did you change before I stopped you?"
## Ari stopped or showed an error
* If the chat says **Ari stopped before finishing.**, select **Retry**. Ari picks the work back up.
* If your message shows **Not sent.**, select **Send again** to send the same text as a new message.
* If the chat says **This request is too large for Ari to continue.**, the chat has grown too long. Start a new chat with **⌘ /**, restate what you need, and attach only the files the new request needs.
* If the chat says **No internet connection** or **Connection lost**, reconnect, then select **Retry** if it is offered. Ari keeps working on the server meanwhile.
* If a reply starts **I had to stop here**, billing stopped the turn. The message names the cause and where to fix it. See [Billing stops](/reference/chat#billing-stops-out-of-ai-usage-or-over-a-limit).
For every message a reply can end with, and what to do about each, see [Chat controls and messages](/reference/chat#when-a-reply-stops-early-or-shows-an-error).
## Ari says web search is unavailable
When web search fails because of a problem on cfo.ai's side, Ari tells you so. Nothing in your request caused it. Ask for the parts of the work that do not need the web, and ask for web research again later. See [How Ari works with you](/concepts/working-with-ari).
## Ari's answer or number looks wrong
1. Ask Ari to show its work, for example "Show me the formula and the inputs behind that number." See [Ask Ari to show its work](/guides/get-better-answers#ask-ari-to-show-its-work).
2. Trace the number from the table: right-click the cell, choose **Mention cell in chat**, and ask where it comes from. See [Ask Ari about a number](/guides/ask-about-a-number).
3. If a number does not match your books, ask Ari to compare one closed month with the source. The usual causes are a connection that is still loading and a **Last close** earlier than you expect. See [Check that your numbers match your books](/guides/check-imported-data).
4. Tell Ari what is wrong and what is right in one message, for example "Churn should be 3% a month, not 3% a year." See [Correct Ari when it guesses](/guides/get-better-answers#correct-ari-when-it-guesses).
## Ari changed something it should not have
* To take the change back, open **Version history**, find Ari's change, and select **…** → **Undo this change**. See [Undo a change or restore an earlier version](/guides/undo-changes).
* To check a formula change before you keep it, see [Review Ari's formula changes](/guides/review-ari-formula-changes).
* To get an answer with no change to your Model, say so in your message: "Don't change anything." See [Say whether you want an answer or a change](/guides/get-better-answers#say-whether-you-want-an-answer-or-a-change).
* To try changes away from your main plan, ask Ari to work in a Scenario. See [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario).
## A table will not calculate
A table that cannot calculate shows a notice instead of values, usually with **Retry**. Select **Retry**, or wait a moment and try again. When the notice offers **Fix**, select it to ask Ari about the table. Each notice and what to do about it is in [Formula errors and warnings](/reference/formula-errors#table-calculation-notices).
## Ask a person
If none of this helps, press **⌘ ?** to open the Help center, choose **Ask a human** under **Getting started**, and describe what happened. You can attach up to 3 screenshots. You can also email [success@cfo.ai](mailto:success@cfo.ai). See [Ask a human](/reference/interface#ask-a-human).
## Related
* [How Ari works with you](/concepts/working-with-ari)
* [Steer, stop, or add to Ari's work](/guides/steer-ari)
* [Get better answers from Ari](/guides/get-better-answers)
* [Chat controls and messages](/reference/chat)
# Ask Ari about a number
Source: https://docs.cfo.ai/guides/ask-about-a-number
Point Ari at a cell, row, table, or Page and ask where a number comes from, which assumptions drive it, why it changed, or what depends on it, with or without changing your Model.
Use this when a number surprises you, or someone asks "where did this come from?". Point Ari at the exact cell instead of describing it, and ask. Ari reads the cell's Variable, its Dimension items, its period, and its formula, so you do not have to name them.
Right-click the cell and choose **Mention cell in chat**, or select the cell and press **⌘ L** (**Ctrl L** on Windows and Linux). The cell appears as a chip in the message box of the chat you have open.
To ask about more than one cell, mention a larger part:
* A row or column: right-click its label and choose **Mention in chat**.
* A whole table: open its block menu and choose **Mention table in chat**.
* A Page: select **Mention page in chat** on the Page's row in the sidebar.
Type your question after the chip and send it. For example:
> Where does this number come from? Show me the formula and the source.
> Why did this move so much from March to April?
> Which source rows make up this number?
Ari answers in the chat and can open the rows behind a total on your screen, the same as clicking a row's arrow. Opening rows this way does not change the saved table.
## Investigate an unexpected change
When a cost or a revenue line jumps, ask Ari to break the change down and look for the cause:
> Why did software spend increase last month? Break the change down by vendor and tell me whether any new contract explains it.
Ari compares the periods, splits the change by the category you named, and points to the source records behind the largest pieces.
## Ask where an assumption came from
An assumption is an input Ari or someone else chose, such as a 35% benefits load or a 5% monthly churn rate. Mention the cell that holds it, or name it, and ask:
> Where did the 35% fringe rate come from?
> List every assumption behind this forecast, where each one came from, and which ones you estimated.
Ari says whether each one came from your connected data, a file, something you told it, a public source, or its own estimate. It labels its estimates as assumptions, so you know which ones to replace with your own numbers. To replace one, tell Ari the right value and why.
## See what a number feeds into
cfo.ai has no "trace dependents" button. To see what uses a number, ask Ari:
> What depends on Headcount? List the formulas, tables, and Pages that read it.
Ari lists the formulas, tables, and blocks that read the Variable. Do this before you change or delete an input you are not sure about.
## Get an answer without changing the Model
Say so in your message:
> Explain why gross margin fell in March. Don't change anything.
Ari reads what the Model already holds and answers. If the answer needs a calculation the Model does not have yet, Ari tells you what it would have to add. To let it add the calculation without touching your plan, ask it to work in a Scenario:
> Create a Scenario called Analysis, add gross margin by product there, and show me the result without changing Main.
To have Ari check a formula before it saves anything, ask it to draft the formula and validate it. A check confirms the formula is valid but does not calculate values, because only saved formulas calculate. See [Have Ari write, change, or explain formulas](/guides/change-formulas-with-ari).
## Check a number yourself
* To see a cell's formula, right-click the cell and choose **Edit formula**. Closing the editor without saving changes nothing.
* To see the rows that make up a total, see [Break a number down](/guides/break-down-a-number).
* To see a quick total, select several cells: a bar shows their **Count**, **Sum**, and **Avg**.
* To see who changed a number and when, open version history. See [Undo a change or restore an earlier version](/guides/undo-changes).
## If the answer looks wrong
If a number does not match your books, ask Ari to compare one closed month with the source and list every account that differs. Common causes include a connection that is still loading and a **Last close** earlier than you expect. See [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast) and [Check that your numbers match your books](/guides/check-imported-data).
## Related
* [Find a Page, number, or file](/guides/find-things)
* [How Ari works with you](/concepts/working-with-ari)
* [How formulas work](/concepts/formulas)
* [Get better answers from Ari](/guides/get-better-answers)
* [Chat controls and messages](/reference/chat#start-a-chat)
# Attach files to a chat
Source: https://docs.cfo.ai/guides/attach-files
Send Ari a screenshot, Excel workbook, PDF, CSV, or other file in a chat, choose whether a CSV becomes a data source, use a file from another chat, download files again, and fix a failed upload.
Use this when you want Ari to read something that is not in your Model yet, such as a screenshot of a report, a bank statement PDF, an Excel model, or a CSV export. You can attach any type of file. For the number of files per message and the size limits, see [Limits and file types](/reference/limits#chat-with-ari).
Select **Attach files** in the message box and choose the files, or paste a file such as a screenshot, or drop files on the message box. Each file shows as a tile while it uploads.
A CSV tile shows **Data source** and **Chat only**. **Data source**, the default, also imports the file into cfo.ai when you send, so its rows can feed your Model. **Chat only** shares the file with Ari in this chat and imports nothing.
Add a message that says what you want, then send. A file can also go on its own, with no text.
> Here is our 2026 budget workbook. Rebuild the Revenue and Headcount tabs as Pages in cfo.ai.
> What does this screenshot show, and does it match the cash number on my Board summary Page?
You can attach files while Ari is working on an earlier message, for example a screenshot of what you see.
## How Ari reads each kind of file
* Images and PDFs reach Ari directly, and it reads them like a page.
* Text files and CSVs up to the [size limit for text in the chat](/reference/limits#chat-with-ari) are included in the conversation as text.
* Excel workbooks, larger text files and CSVs, and other files open in Ari's private workspace, where Ari reads sheets, formulas, cached values, and the rest of the contents with code. Nothing Ari runs there can be reached from outside cfo.ai, and what Ari makes there reaches you only as a file it delivers to the chat.
For a large workbook, tell Ari which sheet, date range, or question matters most. To rebuild a whole spreadsheet model in cfo.ai, see [Import an existing spreadsheet model](/guides/import-a-spreadsheet-model). To bring in a CSV as a data source that you refresh by uploading again, see [Upload a CSV as a data source](/guides/upload-a-csv).
## Use a file from another chat
You do not need to attach a file again in a new chat. Ari can read the files you attached in your other chats in this workspace, and the files it made for you there. Ask for the file by name:
> Use the March bank statement I sent last week.
If two files share a name, Ari tells them apart by the chat they are in and when they arrived. Ari cannot read files in a teammate's chats.
## Download a file again
Each file you sent shows as a tile in the chat. Select the tile's download button to get the file back, for example to hand a colleague the workbook a decision was based on. Files Ari makes for you appear as a download card under its answer. See [Chat controls and messages](/reference/chat#download-a-file-ari-made).
Archiving a chat keeps its files.
## If an upload fails
A file that fails to upload shows a red tile, and a notice titled **Attachment upload failed** gives the file's name and the reason. **Send** stays off until you remove the file or attach it again. Attaching a file with the same name replaces the failed tile.
If one file fails every time while others upload normally, a security filter may be blocking its contents; send it to [support@cfo.ai](mailto:support@cfo.ai). If Ari says it cannot open a file, such as a damaged or password-protected workbook, attach an unprotected copy or a different export. For every attachment message, see [Chat controls and messages](/reference/chat#attachment-messages).
Ari cannot read files sent in Slack. Attach them in a chat in the cfo.ai web app. See [Talk to Ari from Slack](/guides/use-ari-in-slack).
## Related
* [What Ari remembers](/concepts/ari-memory#files-from-your-other-chats)
* [Limits and file types](/reference/limits#chat-with-ari)
* [Chat controls and messages](/reference/chat)
# Brand your workspace
Source: https://docs.cfo.ai/guides/brand-your-workspace
Give your cfo.ai workspace your company's name, logo, colors, heading typeface, and corner shape, built by Ari from your website or set by hand. See who sees the brand, including share-link readers, how to change, copy, or remove it, and how it differs from your personal theme.
Brand your workspace when you want cfo.ai, and the Pages you share, to show your company's look: its name, logo, colors, heading typeface, and corner shape. Everyone who opens the workspace sees the brand, and so does anyone who opens a Page you share. The fastest way to get one is to point Ari at your company's website.
Name your website:
> Make this workspace look like yourfirm.com.
Ari reads the site's home page and stylesheets. It picks the accent color, the nearest heading typeface it offers, the corner shape, and the logo, then saves the brand. It chooses colors for the mode the site does not show, so the brand works in both light and dark mode. Your open app switches to the brand at once.
Ari then lists what it read from the site, what it chose, and any color cfo.ai adjusted to keep text readable. If Ari cannot reach the site, it asks you for the colors and a logo file.
You can also give Ari the brand directly: attach your logo and say which colors and typeface to use. A logo must be a PNG, JPEG, or WebP image of 1 MB or less and at most 2,000 pixels wide or tall. If your site's logo is an SVG file, Ari converts it to PNG or asks you for an image file.
In **Settings → General → Brand**, typing your site in **Website address** and selecting **Build from a website** starts the same chat.
Open the account menu at the bottom of the sidebar, select **Settings**, and on the **General** tab find **Brand**.
Select **Set up by hand**. The form starts from your own theme's colors and the workspace's name.
Set the **Name**, upload a **Logo** and, if the first one is hard to see on a dark background, a **Logo for dark mode**. For **Light mode** and **Dark mode**, enter the **Page surface**, **Panel surface**, **Text**, and **Accent** colors as hex or OKLCH values. Pick a **Heading typeface** and a **Corner shape**. The preview beside the form shows the result.
Select **Save**. cfo.ai moves any color that would make text hard to read to the nearest readable color.
## What a brand contains
| Part | What it is |
| - | - |
| Name | 1 to 40 characters, usually your company's name. Readers of shared Pages see it. |
| Logo | Optional. One image for light mode and an optional second one for dark mode. |
| Colors | For each of light and dark mode: the page surface, the panel surface, the text, and the accent, which marks selections, focus, and the highlights in Canvas blocks |
| Heading typeface | Optional. **cfo.ai default**, **Source Serif**, **Newsreader**, **Fraunces**, **Zilla Slab**, or **DM Sans**. It sets Page titles, headings, and block titles; body text and numbers keep cfo.ai's own fonts. |
| Corner shape | **Square**, **Soft** (cfo.ai's own), or **Round** |
| Chart colors | Optional, through Ari: up to three colors that replace the first chart series colors |
## Who sees the brand
Everyone who opens the workspace sees the brand. Pages, tables, charts, and Canvas blocks all take its look with no change to any of them. The sidebar shows your logo, or your name's first letter when there is no logo, and a **Powered by cfo.ai** line. Each person still chooses light, dark, or automatic mode for themselves.
People who open a shared Page see it too. A reader who follows a share link sees the Page in your brand, under a strip with your logo, your name, and **Powered by cfo.ai**. The link's preview card in chat and social apps does not name your brand. See [Sharing and access](/concepts/sharing-and-access).
Documents Ari makes for you, such as a PDF report or a deck, use your brand's colors, heading typeface, and logo. See [Get a PDF report or slide deck](/guides/get-a-pdf-or-slides).
Any administrator can set or change the brand, and everyone in a workspace is an administrator.
## Change, copy, or remove the brand
To change the brand, ask Ari, such as "Use a darker accent color", or select **Edit** in **Settings → General → Brand**. A change replaces the brand as a whole, and everyone sees it at once. To go back to the earlier brand, ask Ari in the same chat to put it back; Ari keeps the brand it replaced.
To use the brand in your other workspaces, select **Copy to other workspaces** in the Brand section, tick the workspaces, and select the copy button. Or ask Ari, in the other workspace, to copy the brand from this one. Each copy is separate: a later change to one workspace's brand leaves the others as they are.
To remove the brand, select **Remove brand** and confirm, or ask Ari to remove it. Everyone sees their own theme again, and shared Pages lose your name and logo. Copies in other workspaces stay.
## A brand and your personal theme
A brand belongs to the workspace and everyone sees it. A theme belongs to you and changes only what you see. In a workspace with a brand, the brand replaces your theme, and **Settings → General → Appearance** says which brand the workspace uses. Your own theme still applies in your workspaces that have no brand. To set your theme, see [Change your theme, sounds, and notifications](/guides/personalize-cfoai).
Every label and message in the Brand section is listed in [Settings](/reference/settings).
## Related
* [What Ari remembers](/concepts/ari-memory)
* [Share a Page outside your workspace](/guides/share-a-page)
* [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks)
# Break a number down
Source: https://docs.cfo.ai/guides/break-down-a-number
Split a row by a Dimension with Segment by, such as Revenue by Region, collapse or expand the breakdown, and see the inputs behind a calculated number with Drill in. Also add, rename, delete, or hide a Dimension's items, and choose which combinations of items a table shows.
Use this guide to split a number into the parts that make it up, such as Revenue by Region. It also shows how to see what a calculated number is made of, such as the headcount and salaries behind Payroll. To learn what Dimensions and segments are, read [Assumptions and breakdowns: Variables and Dimensions](/concepts/variables-and-dimensions).
You need a desktop browser and edit access to the Page.
## Split a row by a Dimension (Segment by)
> Break Revenue down by Region on the P\&L table.
Point at the row name, such as Revenue, and select the branch button, **Segment by**. Or right-click the row name and choose **Segment by**.
Search for the Dimension, such as Region, and select it. To create a new one, type its name and select **Create dimension: \[name]**.
The table updates at once, with a row for each item under Revenue. An item whose values are all empty can be left out; see [Why a table looks empty](/reference/table-blocks#why-a-table-looks-empty). Revenue's own row now adds up the rows beneath it, so a value you change on any item reaches the total; see [Totals, aggregation, and time rollups](/concepts/totals-and-rollups). Columns work the same way: point at a column header and select **Segment by**.
To change a breakdown, select **Segment by** on the row again. The editor that opens, **Segment Revenue by**, lists the levels. Select **Add** to nest another Dimension under the first, such as Product under Region, drag the levels to reorder them, or select **Remove level**.
A Dimension can break down the rows or the columns of one table, not both.
On a column header for one item, such as a single region, **Segment by** breaks down that item only.
## Collapse or expand a breakdown
Select the arrow beside a row name to collapse the rows under it, and select it again to expand them. **Shift**-click the arrow, or **⌘**-click it on a Mac, to do the same for every row at its level under the same parent, such as each department under Revenue. You can also right-click the row and choose **Collapse child rows** or **Expand child rows**.
Collapsing changes only your own view, and your browser remembers it. You cannot set a breakdown to start collapsed for everyone who opens the table. Column groups collapse the same way, with the arrow in the group's header; see [Show, hide, or pin total columns](/guides/format-a-table#show-hide-or-pin-total-columns).
## Remove a breakdown
Right-click the Dimension's label row, such as **REGION**, and choose **Remove dimension**. The rows leave this table; the Variable's values stay in your Model. On the table where the Dimension was created, the action reads **Delete dimension** instead and deletes the Dimension everywhere; see [Build a Table block by hand](/guides/build-a-table#remove-a-row-or-delete-a-variable).
## See the inputs behind a calculated number (Drill in)
Drill in shows what a calculated number is made of without changing the table.
1. Point at the row name of a calculated row, such as Payroll, and select the magnifier button, **Drill in**.
2. The rows its formula reads, such as Headcount and Salary, open beneath it on a hatched background. Drill in on one of them to keep tracing. For a Variable that reads a connected system, drilling reaches the source records, such as the individual transactions.
3. Select **Clear drill-in** on the row to close them, or right-click and choose **Remove all drill-in rows**.
Drill-in rows are not saved: they close when you reload the Page, and nobody else sees them. To label each input row with its Variable's name, right-click it and choose **Show variable name**.
Use **Segment by** to ask how a number splits across categories, and **Drill in** to ask which inputs produced it. To have Ari explain a number, see [Ask Ari about a number](/guides/ask-about-a-number).
## Add an item to a Dimension
An item is one entry of a Dimension, such as West in Region.
* At the end of a breakdown's items, select the **+** button, **Create Region item**. Type the name and press **Enter**.
* Or right-click the Dimension's label row, such as **REGION**, and choose **Create Region item**. From an item, the same entry is under **Region dimension settings**.
* Or point at the left edge of an item row, select **Insert row below**, type the name, and press **Enter**.
The new item appears on every table that breaks down by that Dimension.
You can add items only to a Dimension of text items, not of numbers, dates, or true-or-false values. While the Dimension is filtered, the action is unavailable until you clear the filter; see [Filter and sort a table](/guides/filter-and-sort-a-table#clear-a-filter-so-every-item-shows-again).
## Rename an item
Double-click the item's name, or right-click it and choose **Rename**. Type the new name and press **Enter**.
You can rename only items you created; items that come from a connected system keep their source name. A rename also updates the formulas, filters, drill-ins, and charts that use the item, and a notice counts what it changed.
## Delete or hide an item
To delete an item you created, right-click it on the table where the Dimension was created and choose **Delete Region item**.
* An item nothing uses is deleted at once. The notice that confirms it offers **Undo**.
* An item that formulas, filters, or other tables still use opens **Delete item?**, which lists what would break. Choose **Cancel**, or **Delete anyway**.
On any other table, hide the item instead: right-click it and choose **Filter out this item**. Items from a connected system cannot be deleted. Every notice is listed in [Table block controls and messages](/reference/table-blocks#other-notices).
## Choose which combinations of items appear
A breakdown can show only the items and combinations that have data, such as the products each region sells, or every combination. Right-click the Dimension's label row and choose **Segment generation**:
* **Show only matching segments** shows only the combinations that have data. This is the standard choice.
* **Expand to create all segments** shows every combination, so you can type values into ones that have none yet, except in cells that come from a connected system. For a Dimension of numbers, also set **Start Value** and **End Value**.
## Use a Dimension as a breakdown or a mapping
A Dimension on a table either breaks Variables down, one row per item, or shows which item each row belongs to, such as each employee's department. To switch, right-click the Dimension's label row and choose **Use as**, then **Segmentation** or **Mapping**. See [Map one Dimension's items to another](/guides/map-dimensions).
## Related
* [Assumptions and breakdowns: Variables and Dimensions](/concepts/variables-and-dimensions)
* [Totals, aggregation, and time rollups](/concepts/totals-and-rollups)
* [Filter and sort a table](/guides/filter-and-sort-a-table)
* [Table block controls and messages](/reference/table-blocks)
# Build a budget and compare it with actuals
Source: https://docs.cfo.ai/guides/budget-vs-actuals
Build an annual or department budget, compare each closed month's actuals with it, show the variance in dollars and percent, and have Ari explain the largest differences.
Ask Ari to build a budget, for the year or by department, and then compare each closed month's actual results with it. You see the variance in dollars and percent, and the reasons behind the biggest differences. Actuals come from your connected accounting system; see [Connect QuickBooks, Xero, NetSuite, or Rillet](/guides/connect-accounting).
## Build the budget
Describe the budget and where its numbers come from:
> Build a 2027 operating expense budget by department as its own Variables, starting from this year's actuals plus 10%.
Or attach the budget you already have:
> Load the attached board-approved budget as Budget Variables by department and month.
Ari labels every number it had to assume. See [Attach files to a chat](/guides/attach-files).
On a table with months, add a row and create a Variable such as `Budget Operating Expense`; see [Build a Table block by hand](/guides/build-a-table). To budget by department, break it down by Department; see [Break a number down](/guides/break-down-a-number).
Type or paste the monthly amounts into the budget rows; see [Enter, paste, and clear values](/guides/enter-values).
For a budget you compare every month, keep it in its own Variables, such as Budget Operating Expense, beside the Variables that hold your actuals. The budget then stays as you entered it while actuals keep arriving, and the variance is a formula on the same table. To keep an approved budget exactly as it was on the day it was approved, also save a Snapshot; see [Save a plan as a Snapshot](/guides/save-a-snapshot).
## Compare actuals with the budget
> Add a budget vs. actuals table for operating expenses by department, with Actual, Budget, Variance, and Variance %.
Ari adds the variance as formulas on the table, so it updates when new actuals arrive.
Add the actual Variable and the budget Variable as rows on the same table.
Add a row named `Variance` with this formula; see [Write a formula](/guides/write-a-formula):
```text theme={null}
`Operating Expense` - `Budget Operating Expense`
```
Add a row named `Variance %` with this formula, and format it as a percentage; see [Format and arrange a table](/guides/format-a-table):
```text theme={null}
if(`Budget Operating Expense` <> 0, (`Operating Expense` - `Budget Operating Expense`) / abs(`Budget Operating Expense`), null)
```
Dividing by the budget's absolute value keeps the sign right for a negative budget. A month with a budget of 0 has a variance but no percentage, so the cell stays blank.
Variance is actual minus budget. For a cost, a positive variance means you spent more than you budgeted; for revenue, it means you earned more.
## Compare after each month-end close
A month shows its actuals once your books for it are closed and **Last close** has moved past it; see [Set Last close and date defaults](/guides/set-dates-and-last-close). Compare complete months. When you ask Ari, it uses the latest month that both the actuals and the budget cover, and it labels a comparison of a month that is not complete.
To read the current month before it closes, use the built-in **Flash report** Skill, which compares the month so far with your plan; see [Turn on a built-in Skill](/guides/turn-on-built-in-skills).
## Explain the largest differences
Ask Ari about the variance after each close:
> Compare last month's operating expenses with our budget by department. Explain the five largest differences and tell me which ones appear likely to repeat.
> Explain the budget-to-actual gap for operating expense as a waterfall.
Ari reports the largest differences with the accounts, teams, or transactions behind them, and labels which explanations are supported by your data and which are guesses. Without an approved budget, Ari can compare with your forecast or a draft plan, and says that the comparison is a what-if.
## Keep another version of the budget in a Scenario
Use a Scenario to try other versions of the plan, such as a budget with slower hiring. A Scenario reads everything it does not change from Main, including new actuals. Values you want to keep fixed must be entered in the Scenario itself; see [Scenarios, Snapshots, and version history](/concepts/scenarios).
To compare a budget that lives in a Scenario, show the Scenario beside Main on the table with **Compare**, and turn on **Variance** and **Variance %**; see [Compare Scenarios side by side](/guides/compare-scenarios).
## Related
* [Get your P\&L, balance sheet, and cash flow statement](/guides/build-financial-statements)
* [Compare Scenarios side by side](/guides/compare-scenarios)
* [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast)
* [Ask Ari about a number](/guides/ask-about-a-number)
# Build a dashboard or board report
Source: https://docs.cfo.ai/guides/build-a-dashboard
Have Ari build a Page of the numbers you review every month, such as a KPI dashboard with burn and cash runway or a board report, from scratch or from Canvas block templates, and choose between tables and charts.
Ask Ari for a Page that shows the numbers you review every month, such as a KPI dashboard with burn, cash, and cash runway, or a board report with a summary, a P\&L, and charts. Ari builds the Page from Canvas blocks and Table blocks, which read your Model so the numbers stay current as your data changes, and from Text blocks for a summary or notes.
A dashboard shows numbers from your Model. If a number is not in your Model yet, such as a cash forecast, Ari builds the missing Variables as it goes and labels each assumption it makes. You can also start with [Forecast cash and cash runway](/guides/forecast-cash).
Describe what the Page should show and who will read it:
> Build a KPI dashboard Page with monthly burn, cash balance, and months of cash runway, and a chart of cash over the next 12 months.
> Prepare a board-reporting Page with revenue, gross margin, cash, headcount, and the largest changes since last quarter.
Ari adds the Page to the sidebar's **Model** section and links it in its answer. The Page is built in the Scenario your chat is in.
To put the work somewhere specific, say so: "Add these charts to the Board Page" or "Make it a subpage of Board."
In the chat's message box, select the palette icon, **Browse Canvas block library**. The **Browse templates and components** window opens.
On the **Templates** tab, select one or more templates, such as **KPI scorecard**, **Cash balance**, **Budget variance table**, or **Executive summary**. The **Components** tab holds single parts, such as a KPI card, a chart, or a slider. Use **Search controls, charts, layouts…** to find one.
Select **Add**, which shows how many you picked, such as **Add (2)**. Each template joins your message as a chip.
Type what you want built from them and where, and send:
> Build these on a new Page called Monthly dashboard, using our cash and burn.
Ari follows each template's recipe with your Model's data. Your words decide whether it builds one block per template or one combined Page.
A blank Canvas block on a Page offers the same library through **Browse library**, and the Help center's **Start from a Canvas template** walks you through it.
## Choose tables or charts
Ask for a Table block when readers need exact numbers, such as a P\&L, or will type in assumptions. Ask for a chart or scorecard when a trend, a mix, or one headline number matters more. A board report usually has both; see [Choosing a block](/concepts/pages-and-blocks#choosing-a-block).
For prompts that get the right chart, such as a waterfall for what changed between two quarters, see [Prompt library](/reference/prompt-library).
## Next steps
* [Change a chart or Canvas block](/guides/change-a-visual)
* [Share a Page outside your workspace](/guides/share-a-page)
* [Get a PDF report or slide deck](/guides/get-a-pdf-or-slides)
* [Walk someone through a Page with a Guide](/guides/create-a-guide)
* [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger)
# Build a revenue and expense forecast
Source: https://docs.cfo.ai/guides/build-a-forecast
Have Ari forecast revenue and expenses from how your business works, such as customers, pipeline, ARR, churn, hiring, and vendors, starting from your connected actuals, with every assumption labeled. Includes changing a run rate and planning a new business with no data yet.
Ask Ari for a monthly forecast of revenue and expenses, such as a 12-month operating plan. Ari builds it from what moves your numbers: customers and price, deals in your pipeline, or people and vendors. The forecast lands in your Model as Pages you can open, trace, and edit.
Before you start, connect your accounting system so the forecast starts from your actuals; see [Connect QuickBooks, Xero, NetSuite, or Rillet](/guides/connect-accounting). Then check that Last close is the last month you have closed; see [Set Last close and date defaults](/guides/set-dates-and-last-close). If your business has no data yet, skip to [Plan a new business that has no data yet](#plan-a-new-business-that-has-no-data-yet).
Tell Ari what creates revenue, what creates cost, and what decision the forecast is for. Name the time span you want; otherwise Ari covers the past 12 months and the next 24.
> We sell annual software subscriptions in three plans. Forecast revenue for the next 12 months from customers by plan: new customers each month, churn, and renewals. Forecast expenses from headcount and our five largest vendors. Label every assumption you make.
For a larger build, Ari first shows a plan card that lists the assumptions it intends to use. Change any that are wrong, and select **Build it**. Pages appear in the sidebar as Ari builds them.
Say how each part of the business really works, so the forecast follows it:
| Your revenue comes from | Ask Ari to forecast it from |
| - | - |
| Subscriptions or ARR | Customers by plan: opening customers, new customers, churn, renewals, and price |
| A sales pipeline | Bookings from your deals: won deals in the month they closed, and open deals weighted by their probability in the month each is expected to close. Revenue then follows each contract's start date and terms. |
| Usage or transactions | Volume times price |
| Projects or services | Projects or billable people times their rate |
| A steady history | A run rate or growth rate from your recent actuals |
Costs work the same way. Payroll follows headcount, hosting follows customers or usage, and a percentage of revenue fits only a cost that really moves with revenue. When several methods would work, Ari recommends one, uses it, and tells you what the others would change.
Ari reads your connected actuals through Last close, and the forecast starts the month after. A forecast that grows from the last actual month repeats anything unusual in it, such as a one-time bill or a large refund. Ask Ari to start from an average instead when that month is not typical.
> Which numbers in this forecast are assumptions, and where did each one come from?
Ari lists each assumption and its source: your books, something you told it, research, or its own estimate. Change any of them by typing the new value in its cell, or by telling Ari.
> Check that revenue by plan adds up to total revenue, that department payroll matches the employee detail, and that quarterly and yearly totals add up correctly.
Ari reads the tables, compares the totals, and tells you what ties and what does not. For why a total might not add up, see [Totals, aggregation, and time rollups](/concepts/totals-and-rollups).
## Change the forecast method, such as a 3-month to a 6-month run rate
Name the Variable and the new method:
> Change the forecast for Software spend from the average of the last 3 months of actuals to the average of the last 6 months.
Ari changes the Variable's Forecast formula, and every forecast month recalculates.
On a table with months, point at the Variable's row name and select the **fx** button, **Edit variable formula**. Expand the **Forecast** section. See [Use different formulas for Actuals and Forecast](/guides/actuals-and-forecast-formulas).
In the formula, select the date filter that reads **Last 3 months of actuals**, and choose **Last 6 months of actuals**. The other choices are **Last 12 months of actuals**, **Year to date**, **Quarter to date**, and **Custom range**.
Press **Enter**. Every forecast month recalculates.
## Plan a new business that has no data yet
With no books to start from, Ari builds the forecast from your description of the product, its price, and its costs. It labels every number it estimates as an assumption you can change.
In a new workspace, the starting card asks **Where would you like to start?**
* Choose **My business**, then **I want to type or upload context**, and describe your product, price, costs, team, and cash. Ari builds a complete first plan from your words, with every number it estimated labeled as an assumption.
* Choose **Something hypothetical** to see a small, simple sample first. Choose **A business like mine**, or **Another business, I'll choose** and type a name. Ari builds a few Pages, labeled as a sample, and a short walkthrough of them. Ask for more when you want it.
In a workspace you already use, describe the business in a chat:
> I'm launching a meal-kit subscription next spring at \$60 a month. I expect 200 subscribers in the first month and 15% growth each month after. Food and shipping cost about \$35 per box. Two founders, no salaries for the first six months, and \$250,000 in the bank. Build a 24-month plan with cash runway, and label every assumption.
Then test prices and hiring plans in Scenarios, so your base plan stays as it is:
> Create a Scenario where the price is \$70 a month and growth is 10%, and compare cash runway with Main.
> Create a Scenario where we hire two engineers in month 6 at \$140,000 a year each, and show the month cash runs out.
See [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario).
## Related
* [Forecast cash and cash runway](/guides/forecast-cash)
* [Build a headcount plan](/guides/build-a-headcount-plan)
* [Build a budget and compare it with actuals](/guides/budget-vs-actuals)
* [Get your P\&L, balance sheet, and cash flow statement](/guides/build-financial-statements)
* [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast)
* [Assumptions and breakdowns: Variables and Dimensions](/concepts/variables-and-dimensions)
# Build a headcount plan
Source: https://docs.cfo.ai/guides/build-a-headcount-plan
Turn your HR system's employee records, or an uploaded roster, into a roster and a monthly headcount and payroll table, then add planned hires and see what they cost.
Ask Ari for a headcount plan to see who is employed each month, what they cost, and what planned hires will add. Ari builds a headcount Page from your employee records, with the formulas behind every number.
Before you start, connect your HR or payroll system, such as Rippling, or upload an employee roster as a CSV; see [Connect payroll, HR, CRM, payments, or a database](/guides/connect-other-sources) or [Upload a CSV as a data source](/guides/upload-a-csv). Then set Last close to your last closed month, so the months in your books read the records and later months read your plan; see [Set Last close and date defaults](/guides/set-dates-and-last-close).
> Build a headcount plan by department from our Rippling data, with loaded payroll cost.
Ari reads the connection's fields and finds what the plan needs:
* Employee ID, which tells people apart. Two people with the same name stay two people.
* Start date and termination date, which decide who is employed in each month. An empty termination date means the person is still employed.
* Pay and how it is paid: annual, monthly, or hourly. Hourly pay needs an hours assumption, which Ari keeps as an input you can edit.
* Department, which groups people on the monthly table.
Job title, employment type, location, pay currency, and other fields come along when the source has them. Name the Page you want, or accept Ari's name for it.
The Page has two tables built from the same records:
* A roster, with one row per Employee ID and each field in its own column. It shows each person's latest values.
* A monthly table of headcount and payroll cost, grouped by department. Each month reads the records dated in that month.
A connected HR system adds each sync's values as that period's record, so a raise or a transfer shows in the month it happened. A one-time roster upload has no history, so each person's latest pay and department carry into every month they are employed. Their start and termination dates still decide which months count them.
Ari follows these conventions unless you tell it otherwise:
* A month's headcount counts everyone employed at any point in the month, so the start month and the termination month both count. If you report month-end headcount, say so.
* Annual pay divided by twelve is a full month's pay, and a monthly rate is a month's pay as it stands. Partial months follow the employment dates.
* Loaded cost, such as taxes and benefits, comes from the records when they have it. Otherwise it comes from an assumption Ari states in the Model, where you can change it.
> Add three sales hires starting in March at \$120,000 a year each.
Ari adds each planned hire as its own labeled row with a department, start date, and pay you can edit, kept separate from the people in your records. To try a hiring plan without changing your main plan, ask for it in a Scenario:
> Create a Scenario with three more sales hires and show how they affect payroll and cash over the next two quarters.
If you have no roster at all, tell Ari how many people you employ, in which departments, and roughly what they cost. Ari creates placeholder employees named as placeholders, such as Placeholder hire 1, that add up to the numbers you gave.
## Related
* [Build a revenue and expense forecast](/guides/build-a-forecast)
* [Forecast cash and cash runway](/guides/forecast-cash)
* [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario)
* [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast)
# Build a Table block by hand
Source: https://docs.cfo.ai/guides/build-a-table
The exact clicks to add a Table block, create a Variable or Dimension without Ari, add, insert, rename, and remove rows and columns, set the dates and the time grain, show quarters and years, and delete a Variable safely.
Use this guide to build or extend a table yourself, such as a hiring plan with Salary, Headcount, and Payroll by month. You can create Variables and Dimensions, add rows and columns, and set the dates without Ari. To learn what Variables and Dimensions are, read [Assumptions and breakdowns: Variables and Dimensions](/concepts/variables-and-dimensions).
To have Ari build the table, describe it in a chat: "Add a table to this Page with Revenue, Cost of Goods Sold, and Gross Profit by month."
You need a desktop browser and edit access to the Page.
## Add a Table block
On the Page, select **Add block** and choose **Table block**. For other ways to add a block, see [Create and organize Pages and blocks](/guides/create-pages-and-blocks#add-a-block-to-a-page).
The new table is blank and reads **Build your table** and **Add a row below or a column above**. It starts with the default date range and time grain of the Scenario you are viewing, months unless that Scenario uses another; see [Set Last close and date defaults](/guides/set-dates-and-last-close).
## Create a new Variable without Ari
A Variable is one line of your Model, such as Revenue or Headcount. You create one by adding a row with a new name.
Select **Add row** at the bottom of the table. To add the Variable as a column instead, select the **+** button at the right end of the column headers, **Add column**.
In the search field, **Create or search**, type the new name, such as `Headcount`.
Select **Create variable: Headcount**.
The Variable appears on the table, ready for values. When it is the first row of a blank table, cfo.ai also adds date columns, one for each period in the table's date range, such as one per month.
A new Variable holds numbers. To show it as currency, a percentage, or a date, see [Format and arrange a table](/guides/format-a-table). A Variable that holds text, such as notes or job titles, can be created by Ari, not by hand.
Variables belong to the whole Model, not to one table. To use an existing Variable on another table, add it the same way and select its name from the results. Changing its values or formula on any table changes it everywhere.
## Add a row or column in a particular place
* Point at the left edge of a row and select the **+** button, **Insert row below**. A blank row opens with **Type a name or / to search**. Type a new name and press **Enter** to create a Variable there, or type `/` to search for an existing one. Below an item row, such as the Sales row of a Department breakdown, the name creates a new item of that Dimension instead.
* Right-click a row name and choose **Insert above** or **Insert below**. On a column header, choose **Insert left** or **Insert right**. The same search opens.
* Open the table setup panel with the **Show table setup** button at the left of the table header, and select **Add** under **Rows** or **Columns**.
A table can hold Variables in its rows or in its columns, not both. When Variables are in the rows, the column search offers only Dimensions, and the reverse.
## Create a Dimension for a breakdown or a mapping
A Dimension is a list of categories, such as Department or Region.
Select **Add row** (or **Add column**), and type the name, such as `Department`.
Select **Create dimension: Department**.
If cfo.ai asks, choose **List dimension items** to give each item its own row or column, or **Add as mapping** to show which item each row belongs to, such as each employee's department.
A new Dimension shows **No items** until you add some. To add items, or to split an existing row such as Revenue by Department, see [Break a number down](/guides/break-down-a-number). To change the choice later, right-click the Dimension's label row (or choose **\[Dimension] dimension settings** from one of its items), then **Use as**, then **Segmentation** or **Mapping**. For mappings, see [Map one Dimension's items to another](/guides/map-dimensions).
## Rename a row, column, or table
To rename a Variable, double-click its row name or column header, type, and press **Enter**. The new name shows on every table, and formulas that use it keep working. To rename a Dimension item you created, double-click its name, or right-click it and choose **Rename**.
To rename the table, click the title in the table header, type, and press **Enter**. An unnamed table reads **New Table Block**. To have cfo.ai suggest a title from what the table shows, point at the title and select the sparkle button, **Regenerate title**.
## Add a blank row between sections
Right-click a row name and choose **Insert space**. The empty row reads **Space** when you point at it. Click it to replace it with a row, or right-click it and choose **Remove space**.
## Set the dates and the time grain
The table header has two date controls:
* Select the date button to set the range: pick the first and last period on the calendar. A complete range applies at once, and **Today** jumps the calendar to the current period.
* Select the grain button to choose **Raw**, **Daily**, **Weekly**, **Monthly**, **Quarterly**, **Half-yearly**, or **Yearly**. **Raw** shows each date without grouping it into periods. The button reads **Mixed** when the table's Date axes use different grains.
A Page that holds a table also has a date range beside its title. Changing it sets the same range on every table, chart, and Canvas block with data on the Page.
To change one Date axis without changing the others, right-click a date header, choose **Date dimension settings**, then **Date granularity**. **Inherit** follows the table's grain.
Quarter and year cells read each Variable's time rollup, which adds up or carries forward its months. A Variable you create by hand has no time rollup, so its quarters and years show 0 until you ask Ari to give it one. See [Why a quarterly view shows zeros](/concepts/totals-and-rollups#why-a-quarterly-view-shows-zeros).
### Show quarters and years at the same time
Each Date axis shows one grain at a time. To see quarters and years side by side, copy the table. Open the block menu (the six-dot handle at the table's left edge), choose **Duplicate block**, and set one table to **Quarterly** and the other to **Yearly**. Both tables show the same Variables, so they always agree.
## Remove a row or delete a Variable
Right-click the row name and choose **Remove item and drill-ins**. The row, and any breakdown rows under it, leave this table only; the Variable stays in your Model and on every other table. In the table setup panel, point at the row and select **Remove from table**.
The table where a Variable was created is its original table. There, removing its last row reads **Delete variable** instead. It deletes the Variable from your Model and from every table. If formulas or other blocks still use it, cfo.ai first asks **Delete variable or dimension?** and counts what would break; choose **Cancel** to keep it, or **Delete anyway**. A Dimension works the same way with **Remove dimension** and **Delete dimension**.
To find a Variable's original table, right-click its row on another table and choose **Go to original table**. When you point at a row whose Variable also appears on other tables, a link mark appears beside its name. Point at the mark to see how many tables show it and which one is the original. Deleting a whole table or Page keeps every Variable that another table still shows; see [Delete a table that other content uses](/guides/create-pages-and-blocks#delete-a-table-that-other-content-uses).
## Keep a large table fast
Every Variable on a table is calculated for each item of each Dimension on the table. A Dimension with more than about 1,000 items, such as every transaction or every customer, can make the table slow. Filter the Dimension to the items you need; see [Filter and sort a table](/guides/filter-and-sort-a-table).
## If something goes wrong
The notice **Couldn't apply that change** says why. The layout is not allowed, for example Variables in both the rows and the columns, so cfo.ai restores the previous table.
The table already has Variables on the other axis. Add the new Variable on the same side as the Variables already there.
Every notice and its meaning is listed in [Table block controls and messages](/reference/table-blocks#when-a-change-is-not-saved).
## Related
* [Assumptions and breakdowns: Variables and Dimensions](/concepts/variables-and-dimensions)
* [Enter, paste, and clear values](/guides/enter-values)
* [Write a formula](/guides/write-a-formula)
* [Format and arrange a table](/guides/format-a-table)
* [Table block controls and messages](/reference/table-blocks)
# Get your P&L, balance sheet, and cash flow statement
Source: https://docs.cfo.ai/guides/build-financial-statements
Have Ari build an income statement (P&L), balance sheet, and cash flow statement that tie together from your connected accounting data, check the reporting lines and the balance check, and update the statements after each month-end close.
Ask Ari for your three financial statements when you want a P\&L, a balance sheet, and a cash flow statement that update each time your books close. Ari builds them as Table blocks on a Page, reading your accounting data, so each Actuals month traces to the accounts behind it and each Forecast month to its formulas and assumptions.
You need a connected accounting system, such as QuickBooks Online, Xero, NetSuite, or Rillet; see [Connect QuickBooks, Xero, NetSuite, or Rillet](/guides/connect-accounting). Without one, Ari can build a draft from numbers you give it, with every figure it had to assume labeled as an assumption.
> Build a P\&L, balance sheet, and cash flow statement that tie together.
Add anything that matters for your company, such as the months to show, the company to report on if you connected more than one, or the reporting lines you use:
> Build our three statements by month for this year and next. Group operating expenses into Sales and Marketing, Research and Development, and General and Administrative.
To review the plan first, add: "Show me the reporting lines and which accounts go in each before you build."
Ari puts each account from your ledger into a reporting line, such as Revenue, Cost of Revenue, or an operating expense group. Ari keeps this account mapping in its own table, with accounts on rows, so you can change where an account belongs in one place; see [Map one Dimension's items to another](/guides/map-dimensions).
Open a line on the P\&L to see the accounts beneath it. Accounts Ari could not place stay visible as their own group, so the statement still adds up to your ledger.
Ask:
> Check that assets equal liabilities plus equity in every month, and name any difference.
Ari adds a balance check to the balance sheet. When the check is not zero, Ari names the amount, the month, and the accounts behind the difference.
The cash flow statement shows operating, investing, and financing cash flow and the net change in cash. That change must match the change in cash on the balance sheet. Ask:
> Check that the cash flow statement ties to the balance sheet cash in every month.
The P\&L's subtotals, such as Gross Profit and Operating Profit, are formulas on the reporting lines, so they recalculate when an account moves or new data arrives. To read or change one, see [Write a formula](/guides/write-a-formula).
## Update the statements after month-end close
1. When your books close, wait for the next refresh, or ask Ari to sync now; see [Refresh data or change when it refreshes](/guides/refresh-data).
2. Move **Last close** to the end of the closed month, so that month shows as Actuals instead of Forecast. Ask Ari, "September is closed. Move Last close to September 30," or see [Set Last close and date defaults](/guides/set-dates-and-last-close).
3. Ask Ari to run the balance and cash checks again.
Each month on the statements is either all Actuals or all Forecast, on the two sides of Last close. To read the current month before it closes, use the built-in **Flash report** Skill; see [Turn on a built-in Skill](/guides/turn-on-built-in-skills). For the whole month-end routine, see [Update your plan after you close the month](/guides/close-the-month).
## Build the same statements every time
To have Ari build the statements the same way every time, save a Skill with your layout and checks. The ready-to-save sample Skill **three-statement-buildout** builds an Income Statement, a Cash Flow Statement, and a Balance Sheet on one Page from your existing Variables and mappings. Copy it from [Sample Skills](/reference/sample-skills), and see [Save a workflow as a Skill](/guides/save-a-skill).
Before your board or investors see the statements, run the built-in **Model audit** Skill. It checks that the statements hold together and tie to your ledger and bank; see [Turn on a built-in Skill](/guides/turn-on-built-in-skills).
## If something looks wrong
* If an account is in the wrong line, change its reporting line in the mapping table, or tell Ari: "Move account 6540 Software to Research and Development."
* If the balance check is not zero, ask Ari to explain the difference. Ari names the amount, the month, and the accounts behind it.
* If a number does not match your accounting system, ask Ari to trace it: "Show me the accounts and transactions behind October revenue." See [Ask Ari about a number](/guides/ask-about-a-number).
To share the statements, see [Share a Page outside your workspace](/guides/share-a-page) or [Export a Page to Excel](/guides/download-a-spreadsheet).
## Related
* [Build a budget and compare it with actuals](/guides/budget-vs-actuals)
* [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast)
* [Dimension mappings and shared Dimensions](/concepts/dimension-mappings)
* [How connected data reaches your Model](/concepts/connected-data)
# Cancel your subscription or delete your data
Source: https://docs.cfo.ai/guides/cancel-or-delete
Cancel your cfo.ai free trial or subscription so you are not charged, and keep access until the end date. Also remove or replace your card, download invoices and receipts, undo a cancellation, and ask cfo.ai to delete a workspace or your account.
Cancel your free trial or subscription in **Settings → Billing** to stop being charged. You keep access until the end date. A canceled trial runs to its end date, and you are not charged. A canceled paid plan runs to the end of the term you paid for and is not charged again. If you do not cancel a trial, your paid plan starts on the day the trial ends, and cfo.ai charges your card that day.
Canceling does not delete your Model or data; they stay in the workspace. To have a workspace or your account deleted, you ask cfo.ai; see [Delete a workspace or your account](#delete-a-workspace-or-your-account).
## Cancel your subscription or trial
Open the account menu, your name or picture at the bottom of the sidebar, choose **Settings**, and select **Billing**.
Under **Cancel subscription**, select **Cancel**.
Choose one from **Select a reason**. If you choose **Other — please tell us why**, type a short explanation.
Select **Cancel subscription**. To change your mind before confirming, select **Keep subscription**.
A notice says the plan was canceled and names the last day you keep access. Your plan or trial stays active until that day. Fees you already paid are not refunded.
You cannot pause a subscription to resume it later. Canceling is the only way to stop it; when you want cfo.ai again, choose a plan. Ari cannot cancel or change a plan for you.
## Remove or replace your card
1. Open **Settings → Billing**.
2. Under **Payment methods**, open the card's menu and select **Remove**. To swap in a new card instead, select **Replace…**.
A workspace with a plan or a trial must keep one card on file, and so must a workspace with auto-reload on. To remove your only card, add another card first. If you want to stop being charged, cancel your subscription instead; a canceled plan is not charged again. See [Plans and billing](/reference/plans-and-billing#payment-methods).
## Download an invoice or receipt
Open **Settings → Billing**. Under **Billing history**, select **View** on a row to open its invoice or receipt in a new tab. **View all** shows older rows.
Settings does not open while a workspace is locked, so download the invoices you need before your plan ends.
## Undo a cancellation
Any time before the last day, open **Settings → Billing** and select **Keep** followed by your plan's name. A notice reads **Cancellation reversed**, and your plan renews as usual. Choosing a plan in the plan chooser also calls the cancellation off.
## What happens when the plan ends
On the last day, the workspace locks. Administrators see the plan chooser, which reads **Choose a plan to continue**, and Ari and your Pages are unavailable until someone chooses a plan. Because the workspace has used its free trial, a new plan is charged the same day. Your connected sources stop refreshing while the workspace has no plan. See [When the workspace locks](/reference/plans-and-billing#when-the-workspace-locks).
Your Model and data stay in the workspace when you cancel. The Standard Terms and Conditions say that, once your agreement ends, cfo.ai keeps your data for 15 days if you paid for your first term, and may delete it after that. See [Privacy, security, and legal documents](/reference/privacy-and-legal).
## Delete a workspace or your account
cfo.ai has no delete button for a workspace or an account, and Ari cannot delete either. To have one deleted, with its data:
* Email [legal@cfo.ai](mailto:legal@cfo.ai) from the address you sign in with. Name the workspace, or say that you want your account deleted. The Terms of Use name this address for closing and deleting an account.
* For a request about your personal information only, you can also use the data request form on the [Privacy choices](https://cfo.ai/privacy-choices) page.
When cfo.ai deletes a workspace, its subscription is canceled at once and no new charges start. Past invoices and charges stay on record.
Removing a teammate from a workspace takes away their access but does not delete their account; see [Invite or remove teammates](/guides/invite-teammates#remove-a-teammate).
## Related
* [Plans and billing](/reference/plans-and-billing)
* [Privacy, security, and legal documents](/reference/privacy-and-legal)
* [Start a trial or choose a plan](/guides/choose-a-plan)
# Change a chart or Canvas block
Source: https://docs.cfo.ai/guides/change-a-visual
Change a chart, scorecard, or other Canvas block by asking Ari. Point Ari at one part with Select element, restyle an existing chart block yourself, and keep a control's setting after you reload the Page.
To change a chart, a scorecard, or any other Canvas block, tell Ari what you want, such as stacked bars instead of a line, or one segment instead of all of them. Canvas blocks are the custom visuals Ari builds from your Model's data, and they have no code editor. If a Page already has a chart block, you can also restyle it yourself in its sidebar.
Hover the block and select **Mention in chat** at its top corner, or open the block's menu from the six-dot handle at its left edge and choose **Mention canvas in chat**. The block appears in the message box as a chip.
To change one bar, number, label, or control, select **Select element** at the block's corner and then click that part. It joins the message box as its own chip, so Ari changes that part and leaves the rest alone.
Say what you want to see, and send:
> Turn this into stacked bars by customer segment.
> Make this bar orange and start the axis at zero.
> Show this quarterly instead of monthly.
Ari changes the block in place, so it keeps its position on the Page. A blank Canvas block offers **Mention block in chat** instead, so Ari can build it from your description. To learn how Ari checks a visual it changed, see [How Ari checks a visual](/concepts/pages-and-blocks#how-ari-checks-a-visual).
You can restyle a chart block that a Page already has. You cannot add a new chart block yourself; ask Ari for a new chart.
Select **Show chart setup** at the left of the chart's header.
On the **Setup** tab, pick the chart type, such as **Line**, **Bar**, **Bar Stacked**, **Area**, **Pie**, **Combo**, or **Waterfall**. Under **X-Axis**, change the date grain. Under **Y-Axis**, add or remove Variables, select a Variable's swatch (**Pick color**) to change its color, or select **Segment by** to split it by a Dimension. In a pie or donut chart, that list is headed **Values**, and there is no **X-Axis**. In a combo chart, each series can be a line or bars.
On the **Display** tab, turn on or off **Show chart title**, **Show legend**, **Show X axis**, **Show Y axis**, **Show horizontal grid lines**, and the axis titles, and type the title in **Chart title text**.
Select **Compare** in the chart's header and choose **Scenarios** to show other Scenarios beside the one you are viewing.
A chart's numbers use each Variable's own number format. To show values differently everywhere, ask Ari to change the Variable's format. To start over, open the sidebar's **…** menu (**Open Chart Options**) and choose **Reset chart**.
## Keep a control's setting
Sliders, toggles, and menus in a Canvas block change only what you see, unless the block was built to save that setting. If a setting goes back when you reload the Page, ask Ari to keep it:
> Make this chart remember whether I pick monthly or quarterly.
A saved setting belongs to that block in that Scenario, so everyone who opens the Page in that Scenario sees it. A control never changes a value in your Model. To change an assumption, such as a hiring date, edit it in a Table block or ask Ari. See [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks#controls-change-the-view-never-the-model).
## If a block shows an error
* If the block says **This block failed to render.**, select **Fix** to hand the block to Ari.
* If it says the block **didn't start** or was **still loading its libraries**, the block's software did not finish loading. Select **Retry**.
* If it says the block's data **failed to load**, select **Mention in chat** and ask Ari to check the data the block reads.
Every Canvas block message is listed in [The cfo.ai interface](/reference/interface#canvas-blocks).
## Related
* [Build a dashboard or board report](/guides/build-a-dashboard)
* [Prompt library](/reference/prompt-library)
* [Format and arrange a table](/guides/format-a-table)
# Have Ari write, change, or explain formulas
Source: https://docs.cfo.ai/guides/change-formulas-with-ari
Ask Ari to write or change a formula in plain language, say where it applies and in which Scenario, point Ari at a cell or formula, have Ari explain a formula, and hand Ari a cell that shows an error.
Describe the calculation in plain language, and Ari writes the formula, saves it on the Variable, and checks the result in the table. You do not need to know the formula syntax or where a Variable's formula lives. To write a formula yourself, see [Write a formula](/guides/write-a-formula).
## Ask Ari to write or change a formula
Name the Variable and the calculation:
> Set Gross Profit to Revenue minus Cost of Goods Sold.
> Add Gross Profit to this table, calculate it as Revenue minus Cost of Goods Sold, and check the result for the last three months.
Ari saves the formula on the Variable, so every table that shows the Variable uses it. A new Variable needs a place on a table, so Ari adds it to one, such as the table you name.
## Say where the formula applies
Add where the formula applies only when it matters. Ari reads the table you are looking at to work out the rows and items you mean.
| You want the formula for | Ask |
| - | - |
| The Variable everywhere | "Use this calculation for Gross Profit everywhere." |
| Each item of a Dimension | "When Payroll is broken down by Department, calculate each department as Headcount times Average Salary." |
| One item | "For the Sales department, calculate Commission as Revenue times Commission Rate." |
| Actuals or Forecast | "Use imported Payroll for Actuals and Headcount times Average Salary for Forecast." |
| One cell | "Set March 2027 Marketing Spend to 50,000 and leave the other months alone." |
A formula for Actuals or Forecast needs months. If the Variable cannot take one, Ari says why. See [Use different formulas for Actuals and Forecast](/guides/actuals-and-forecast-formulas).
## Say which Scenario
Ari changes the Scenario your chat is in, unless you name another one:
> Make this change in the Hiring freeze Scenario.
Name the Scenario and the formula range, Actuals or Forecast, whenever that choice matters, for example when a change could otherwise land in the wrong place. When you name no Scenario, Ari's save records which one it used, so you can ask Ari to confirm it.
## Point Ari at a cell or a formula
Ari works best when it knows exactly which number you mean.
* Right-click a cell and choose **Mention cell in chat**, or select it and press **⌘ L** (**Ctrl L** on Windows and Linux). The cell joins your message.
* In the formula editor, select the **@** button, **Mention formula in chat**, to add the formula you are editing.
Then ask:
> Explain the formula for this row.
> Why is this value different in the Sales column?
## Ask Ari to explain a formula
Reading formulas does not change the Model. Ask about a Variable, a cell, or a difference you see:
> How is Gross Margin calculated?
> Which formula applies to Payroll in Forecast?
> Show me the formulas for Payroll, including the ones that apply only to some departments.
> Explain this formula in plain language and tell me where it applies.
A useful explanation covers what the formula calculates, which Variables it reads, where it applies, whether a more specific formula replaces it somewhere, and whether it is for Actuals, Forecast, or both. To answer a question with numbers already in the Model without saving anything, see [Ask Ari about a number](/guides/ask-about-a-number).
## Hand Ari a cell or table that shows an error
* Right-click a cell that shows an error code and choose **Fix**. A new chat opens with the cell and the message "Help me fix this cell."
* When a table cannot calculate because of the Model's setup, its notice has a **Fix** button that opens a chat with "Help me fix this table."
To have Ari find the cause before it changes anything, say so:
> Explain why this formula works for Sales but not Marketing. Do not change anything yet.
> Check whether a more specific formula is replacing this one.
## Check a formula before Ari saves it
Ask Ari to show you the formula and wait:
> Show me the formula you would use, and wait for my OK before saving.
When the formulas belong to a new table, Ari can also run a check that saves nothing:
> Draft the new table and check its formulas, but don't save anything yet.
The check confirms the table's layout, the Variables and Dimensions its formulas name, and their syntax. It reports whether the table is valid, any warnings, and what saving it would change. It calculates nothing, so it returns no values and cannot find circular references. Changes to an existing table and deletions have no such check: read the formula Ari proposes, then check the numbers after it saves.
After Ari saves, see [Review Ari's formula changes](/guides/review-ari-formula-changes).
## Related
* [Review Ari's formula changes](/guides/review-ari-formula-changes)
* [Write a formula](/guides/write-a-formula)
* [How formulas work](/concepts/formulas)
* [Formula errors and warnings](/reference/formula-errors)
# Make a precise change with Ari
Source: https://docs.cfo.ai/guides/change-your-model-with-ari
Have Ari change one assumption, formula, or table and leave everything else alone: name the target, fence off what must not move, try risky changes in a Scenario, review what changed, and undo it.
Use this when you want one targeted change, such as a new price, a later start date for two hires, or a fix to one formula, and nothing else touched. Ari does not ask before it changes the Model, because every change can be undone, so a precise request is the best way to get a precise result.
Mention the exact cell, row, or table. Right-click a cell and choose **Mention cell in chat**, right-click a row or column label and choose **Mention in chat**, or open a table's block menu and choose **Mention table in chat**. Or select a cell or a table and press **⌘ L** (**Ctrl L** on Windows and Linux). Ari then edits that place instead of guessing which one you meant. You can also name it: "the Price assumption on the Revenue Page" is clearer than "the price".
Customers often call the inputs of a forecast "assumptions". In cfo.ai each one is a Variable or a value in a Table block, and you can name it either way.
State the change and fence off everything else in the same message:
> Change the Starter plan price to \$49 from January. Only change that price. Leave the other plans, the dashboard, and the table layout as they are.
> Move the start dates of the two Q1 engineering hires to April. Don't change salaries or any other hire.
> Fix the formula for Gross margin in the Downside Scenario only. Don't rename anything and don't add Pages.
If you are not sure you want the change, have Ari make it in a Scenario, so Main, your plan, stays as it is until you decide:
> Create a Scenario called Price test, change the Starter price to \$49 there, and compare cash with Main.
See [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario).
Before a change that touches many tables, ask Ari to show its plan before it changes anything:
> Before you change anything, show me your plan for restructuring the expense tables.
Ari shows a plan card. Read what it will create and the assumptions it will use, change any that are wrong, and select **Build it**, or select **Make changes** to describe what to do differently. See [Answer Ari's questions and approve its actions](/guides/approve-ari-actions#approve-a-plan).
While Ari works, each edit plays on the Page with a caption showing the old and new value, such as `5% → 4%`. When it finishes, ask:
> What exactly did you change? List every table, formula, and value.
Every change is also listed in version history, newest first, with who made it. See [Chat controls and messages](/reference/chat#edits-playing-on-the-page) for what the captions and cards show.
## Take a change back
If Ari changed more than you wanted, ask:
> Undo that last change.
Ari undoes the most recent change as a whole. To undo an older change, describe it, such as "Undo the change to the hiring plan from this morning," or undo it in version history. See [Undo a change or restore an earlier version](/guides/undo-changes).
## If Ari keeps changing things you did not ask for
* Stop it as soon as you see it: select **Stop**, then say what to leave alone. See [Steer, stop, or add to Ari's work](/guides/steer-ari).
* Make the fence part of the request every time, such as "Only change X" or "Work in the Price test Scenario only".
* Ask Ari to remember a lasting preference, such as "Never rename Variables without asking", so it carries the preference into later chats. See [Teach Ari your company's conventions](/guides/teach-ari).
## Make the change yourself
To change a value or formula by hand, see [Enter, paste, and clear values](/guides/enter-values) and [Write a formula](/guides/write-a-formula).
## Related
* [How Ari works with you](/concepts/working-with-ari)
* [Ask Ari about a number](/guides/ask-about-a-number)
* [Have Ari write, change, or explain formulas](/guides/change-formulas-with-ari)
* [Review Ari's formula changes](/guides/review-ari-formula-changes)
# Check that your numbers match your books
Source: https://docs.cfo.ai/guides/check-imported-data
Why a number in cfo.ai does not match QuickBooks, Xero, or your other books, and how to fix it. Compare a closed month with the source, check the common causes, trace a number to its source rows, and fix data that loaded the wrong way.
When a number in cfo.ai does not match your books, compare one closed month with the source and have Ari list every account that differs. Most differences come from a handful of causes, and each one has a quick check.
## Compare one closed month with your books
Pick a month you have closed in your accounting system, and ask:
> Compare September 2026 in the Model with QuickBooks and list every account that differs, with both amounts.
Ari reads your Model and the synced source side by side and names each difference. Fix the cause, then ask again until the month ties.
## Common causes
### The connection is still loading
QuickBooks Online and Xero load your three most recent complete months first and the rest of your history after, so older months can be missing for a while. Ask Ari which periods have loaded:
> Which months has our QuickBooks connection loaded?
### Last close is not the month you think
Your Model's **Last close**, your month-end, decides which months read as Actuals. In months after it, a Variable with a Forecast formula calculates that formula instead of reading your books. A Variable that only reads its connected column keeps showing the source's rows after Last close, so moving Last close does not change what it shows. If last month still shows forecast numbers, move Last close; see [Set Last close and date defaults](/guides/set-dates-and-last-close) and [Connected data before and after Last close](/concepts/actuals-and-forecast#connected-data-before-and-after-last-close).
### A saved query filters rows on purpose
A saved query brings in only the rows your Model needs, so your Model can hold fewer rows than the source. Ask Ari what the query keeps and what it leaves out before you treat a smaller count as missing data.
### The same cost is counted twice
When two sources describe the same thing, a total can add both. For example, salaries can arrive in your profit and loss from QuickBooks and again as payroll from Gusto. Ask:
> Is payroll counted twice in total expenses? Show me which Variables the total adds.
### The source changed after the last sync
A correction you made in your books reaches cfo.ai at the next sync, but QuickBooks Online re-reads only the last three months each night. For an older month, ask Ari to re-import the full history; see [Refresh data](/guides/refresh-data#re-import-past-months).
### The opening cash balance is an estimate
A Model that Ari started from estimates holds an estimated opening balance. Once your books are connected, ask Ari to take the opening cash from your synced balance sheet. See [Replace estimates with your real numbers](/guides/switch-to-real-numbers).
### The balance sheet does not balance
Ask Ari to reconcile it month by month:
> Check that assets equal liabilities plus equity for every month, and name the accounts behind any gap.
An amount the source did not classify loads into the statement's unclassified amount, and it stays part of the total, so Ari can name it.
## Trace a number to its source rows
Right-click a cell and choose **Mention cell in chat**, then ask "Where does this number come from? Show me the source rows." Ari shows the formula and the source rows behind the value. See [Ask Ari about a number](/guides/ask-about-a-number). To split a total into the items that make it up, see [Break a number down](/guides/break-down-a-number).
## Check which periods loaded
Ask Ari, or open the connection's page: when two or more tables have synced, **Source tables** shows each table's **Last synced** time and the date its **Data through** reaches.
## Check how many rows loaded
Ask Ari:
> How many rows did the bank transactions upload load, and how many does its saved query keep?
When the file or table holds more rows than your Model, Ari explains what the query filters.
## Read the saved queries
Open the connection's page and find **Queries**. Each saved query shows when it last ran, such as **Ran 2 hours ago · Completed**, with **Show SQL** and **Copy SQL**. The queries are read-only there. To change one, ask Ari. Ari shows you what a change would remove from your Model before it saves. [How imported data loads](/reference/imported-data#saved-queries) lists the rules.
## Fix a source that has no date column
A table that shows an imported source by month stops calculating when nothing tells cfo.ai which column dates the source's rows. Its error says the source **has no date column to read it by**, and names the source. A column named `Date` does not count by itself. Ask Ari to set the column that dates the rows:
> The Posted Date column dates each row of the bank transactions upload. Use it.
If the source has no date at all, add a date column at the source and sync or upload it again. A table that does not split the source by date, such as one showing the latest value, still calculates. [Formula errors and warnings](/reference/formula-errors) has the full message.
## Fix a column that loaded as the wrong kind
A code or ID can load as a Variable that adds up, such as a store number summed across stores. Ask Ari to switch it:
> Load Store Number as a Dimension instead.
Ari switches the column in place, so formulas, tables, and filters that use it keep working, and later syncs keep the new role. A number column that becomes a Dimension keeps number items, so leading zeros do not come back. To keep them, ask Ari to load the column as text under a new name. A column that feeds a Dimension other queries also feed cannot switch roles for one query; Ari names the shared Dimension instead. [How imported data loads](/reference/imported-data#how-columns-become-variables-and-dimensions) explains how roles are first chosen.
## Run a Model audit
The optional **Model audit** Skill checks the whole Model at once. It checks whether formulas, mappings, and statements hold together, whether the Model ties to your ledger and bank, and what is still typed in or estimated. It writes a short audit page and fixes problems when you ask. Turn it on first; see [Turn on a built-in Skill](/guides/turn-on-built-in-skills). Then ask:
> Audit the model before the board meeting.
## Related
* [How connected data reaches your Model](/concepts/connected-data)
* [How imported data loads](/reference/imported-data)
* [Fix a connection that needs attention](/guides/fix-a-connection)
# Start a trial or choose a plan
Source: https://docs.cfo.ai/guides/choose-a-plan
Start your 14-day free trial: your card is not charged until the trial ends, then your paid plan starts and the card is charged. Cancel before then to pay nothing. Also start the paid plan early, change plans or billing period, and talk to sales about Enterprise.
A workspace's first plan starts with a 14-day free trial of the plan you choose. You add a card to start it, and nothing is charged until the trial ends. On the day it ends, your paid plan starts by itself and cfo.ai charges your card. If you cancel before then, you are not charged, and you keep the trial until its end date.
After the trial, your plan renews each month or each year, and each renewal charges your card. A paid plan you cancel stays active until the end of the term you paid for. A workspace gets one free trial. After that, a plan charges your card the day it starts.
For what each plan includes, see [Plans and billing](/reference/plans-and-billing); for how plans and usage work, see [Plans, AI usage, and connection slots](/concepts/plans-and-usage).
## Start your free trial
When you sign up, cfo.ai shows the plans before your workspace opens. The page reads **Try cfo.ai free for 14 days**.
Use the **Monthly** and **Annual** switch above the plans. The plans open on **Annual**, and the switch names the saving annual billing gives you.
On the plan you want, select **Start free trial**. Starting a plan means you agree to the Standard Terms and Conditions and acknowledge the Privacy Policy; see [Privacy, security, and legal documents](/reference/privacy-and-legal).
In **Add a payment method**, enter your card and select **Save payment method**. A card is required to start the trial, and nothing is charged today. The dialog names the date to cancel by if you do not want to be charged.
The page reads **Finishing setup** while cfo.ai opens your workspace, and Ari starts on your first chat.
If the workspace has had a trial before, the page reads **Choose a plan to continue** and **Your free trial has already been used. Paid plans charge immediately.** Choosing a plan charges your card the same day.
## What happens when the trial ends
Your paid plan starts by itself on the day the trial ends, and cfo.ai charges the card on file. You do not need to do anything to keep your plan. To see the date and the amount, open **Settings → Billing**: under **Current plan** it reads **You will be charged** \[amount] **when your trial ends on** \[date].
Three days before the end, a notice above your workspace says when the paid subscription starts. To end the trial without being charged, cancel before that day; see [Cancel your subscription or delete your data](/guides/cancel-or-delete).
## Start your paid plan before the trial ends
You can start the paid plan early once the workspace has used all the AI usage included with the trial. Until then there is nothing to start: the plan begins on its own when the trial ends.
When the trial's usage is used up, **Start paid plan** appears in a notice above your workspace, next to your plan in **Settings → Billing**, and in **Settings → Usage**. In a chat, the usage panel above the message box offers **Start plan & save**.
Select **Start paid plan**.
The dialog **Start paid plan now?** explains the change. Select **Start** followed by your plan's name and **now**.
cfo.ai charges your card now, ends the trial, resets your included AI usage, and starts a new billing cycle today. If you would rather keep the trial until its end date, buy extra usage instead; see [Buy AI usage, set a spend limit, or turn on auto-reload](/guides/manage-ai-usage).
## Change your plan or switch between monthly and annual
Open **Settings → Billing** and select **Change** next to your plan. Pick monthly or annual billing with the switch, select **Switch to** followed by the plan's name, and confirm in **Confirm plan change**. The dialog shows what you will be charged and when, before anything happens:
| Change | What happens |
| - | - |
| Any change during a trial | The trial switches to the new plan today, at no charge. The trial's end date stays the same. |
| A higher plan | You switch now. If it costs more, you pay the difference for the rest of your current term. |
| A lower plan, or the same plan billed monthly or annually | The change is scheduled for your next renewal date; nothing is charged now. |
While a change is scheduled, **Keep** followed by your plan's name calls it off. You cannot move to a higher plan and change between monthly and annual billing in one step; the plan card says which to do first. A plan with fewer integration slots than you use is unavailable until you disconnect some, and the card says how many. For every message, see [Plans and billing](/reference/plans-and-billing).
## Talk to sales about Enterprise
Enterprise has custom pricing and is not chosen from the plan cards. Select **Talk to sales** on the Enterprise card to book a call with the sales team.
## If your card is declined
The card form stays open with what you typed and says what happened, ending with **Nothing was charged.** Try another card, or ask your bank why it declined the charge. See [Plans and billing](/reference/plans-and-billing#payment-messages).
## Related
* [Plans and billing](/reference/plans-and-billing)
* [Plans, AI usage, and connection slots](/concepts/plans-and-usage)
* [Buy AI usage, set a spend limit, or turn on auto-reload](/guides/manage-ai-usage)
* [Cancel your subscription or delete your data](/guides/cancel-or-delete)
# Choose which tables a source syncs
Source: https://docs.cfo.ai/guides/choose-synced-tables
Pick the tables a database or app syncs when you connect it, then ask Ari to add or remove tables later, such as syncing one more table from Postgres or dropping one you do not need.
Use this guide when you connect a database or an app through Fivetran, which can offer hundreds of tables, or when you want to add or drop a table later. Each connection syncs only the tables selected for it. You choose them in the table picker when you connect, and ask Ari to add or remove tables later. Fewer tables make the first sync faster, and you can always add one later.
## Which sources let you choose
| Source | Table choice |
| - | - |
| Databases and data warehouses, and most apps through Fivetran, such as Shopify or Sage Intacct | A table picker at setup. Every table starts selected. |
| NetSuite SuiteAnalytics | A table picker at setup. The tables the statements need are locked; tables for NetSuite features you do not use start unchecked. Tables that already sync cannot be removed later. |
| HubSpot, Salesforce, Rippling | No picker. cfo.ai selects the tables its reports need and keeps them locked. Ask Ari to add other tables later. |
| QuickBooks Online, Xero, Rillet, Stripe (Direct), HR and payroll systems through Merge, Google Sheets, Google Drive, CSV uploads | No table choice |
The [integrations directory](/reference/integrations) lists what each source syncs.
## Choose tables when you connect
After you sign in, the table picker lists every table the source offers, grouped by schema. Search with **Search tables**. The count reads, for example, **84 of 312 tables selected (max 100)**.
Keep the tables that hold the records your plan reads, such as transactions and their lines, invoices and payments, customers and subscriptions, orders and products, and employees and pay. Also keep the lookup tables they refer to, such as accounts or departments. Leave out logs, audit trails, sessions, webhooks, staging tables, and personal data you have no use for. A connection syncs at most 100 tables, so a source that offers more makes you uncheck some before you can finish.
Select **Finish setup & sync \[N] tables**. The button names how many tables it will sync, and a larger selection makes the first sync longer.
The picker cannot be opened again after setup; change the selection by asking Ari. **Cancel setup** ends the setup: a new connection that never finished is removed, and a connection that was already linked is kept.
## Add or remove tables later
Ask Ari, naming the connection and the tables:
> Also sync the subscriptions table from our Postgres database.
> Stop syncing the audit\_log table from Postgres.
Tables you do not name keep their state. When you add a table:
* It loads its full history, as far back as the connection's other tables reach. Ari starts a sync for it.
* It reaches the warehouse first and your Model only once a saved query reads it. Ask Ari for a query over it, such as "Load the subscriptions table into the Model."
## What Ari refuses, and why
* Removing a table a saved query reads. The query would keep showing the rows that last landed while the source moves on. Ask Ari to delete the query first.
* Removing the last selected table. To stop syncing a source, remove the connection instead.
* Changing a connection whose setup is not finished. Finish setup in the picker first.
* Adding a table past the 100-table limit.
* Removing a table HubSpot, Salesforce, Rippling, or NetSuite needs for cfo.ai's reports.
To see what a connection syncs, ask Ari "Which tables does our HubSpot connection sync?" Ari lists them in the source's own names and says whether cfo.ai or you chose them.
## Related
* [Connect payroll, HR, CRM, payments, or a database](/guides/connect-other-sources)
* [Integrations directory](/reference/integrations)
* [How connected data reaches your Model](/concepts/connected-data)
# Update your plan after you close the month
Source: https://docs.cfo.ai/guides/close-the-month
A routine for each month-end close: bring the closed month in from your books, move Last close, check the month against your books, compare actuals with your plan, update assumptions, and get your board Pages ready. Then put it on a schedule.
Use this routine each month after your books close. It brings the closed month into cfo.ai as Actuals, checks it against your books, updates your Forecast with what you learned, and gets your board Pages ready to share. Each step is a request you can type to Ari, with a link to the guide that has the details.
Integrations such as QuickBooks refresh once a day on their own. To bring the month in now, ask Ari:
> Refresh QuickBooks and tell me when the import has finished.
QuickBooks Online re-reads only the last three months each night. If you corrected an older month, ask Ari to re-import the full history. See [Refresh data or change when it refreshes](/guides/refresh-data).
Last close does not move on its own. Until you move it, the closed month stays in the Forecast.
> We finished closing September. Set Last close to September 30, 2026.
See [Set Last close and date defaults](/guides/set-dates-and-last-close).
> Compare September's Actuals with QuickBooks and list every account that differs.
See [Check that your numbers match your books](/guides/check-imported-data).
> Compare last month's Actuals with the Forecast and explain the three biggest differences.
See [Build a budget and compare it with actuals](/guides/budget-vs-actuals).
> Update the assumptions behind those differences so the Forecast reflects what we now know. Show me each change.
Every change Ari makes is in version history, so you can take any of them back. See [Undo a change or restore an earlier version](/guides/undo-changes).
> Review the Board summary Page. Flag any number that looks stale or inconsistent.
Then share the Page or export it; see [Share a Page outside your workspace](/guides/share-a-page) and [Export a Page to Excel](/guides/download-a-spreadsheet). To keep this month's plan exactly as it is before next month's changes, [save a Snapshot](/guides/save-a-snapshot).
## Read the month before it closes
To see how the current month is going before your books close, use the built-in **Flash report** Skill. It compares the month so far with your plan. See [Turn on a built-in Skill](/guides/turn-on-built-in-skills).
## Put the routine on a schedule
To have Ari do part of the routine each month, schedule a Trigger. For example:
> On the 5th of every month at 9 AM, move Last close to the end of the previous month, compare that month's Actuals with the Forecast, and email me the three biggest differences.
See [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger). After your first month, list what you still do by hand and ask Ari which of those steps it can take over as a Skill or a Trigger.
## Related
* [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast)
* [Get your P\&L, balance sheet, and cash flow statement](/guides/build-financial-statements)
* [Get better answers from Ari](/guides/get-better-answers)
# Compare Scenarios side by side
Source: https://docs.cfo.ai/guides/compare-scenarios
Show another version of your plan, or an earlier period, beside a table's values: compare Scenarios or time periods, choose the variance measures, and see how Variance and Variance % are calculated.
Use a comparison when you want to see two versions of your plan, such as Main and a Scenario, or this period against an earlier one, on the same table. A comparison is saved with the table, so everyone who opens the Page sees it, including readers of a share link.
> Add a comparison to the cash table showing the Hiring freeze Scenario beside Main, with the variance in dollars and percent.
Or create the Scenario and compare in one request:
> Create a Hiring freeze Scenario, pause new hires through December, and compare payroll and ending cash with Main.
To read values that already exist without changing any table:
> How does Operating Expense in the Hiring freeze Scenario compare with Main for next quarter? Don't change anything.
Select **Compare** in the table's header.
Choose **Scenarios** and tick the Scenarios to show beside the one you are viewing. Or choose **Time periods** to compare each period with an earlier one, and pick the earlier period.
Turn on the measures you want: **Value**, **Variance**, and **Variance %**. A time comparison also offers **Comparison value**.
Show the comparisons in **Columns** or **Rows**, grouped **By scenario** or **By variable**.
To remove the comparison, open **Compare** and select **Reset**.
A table compares either Scenarios or time periods, not both at once. Comparing time periods needs exactly one Date row or column on the table.
## How Variance and Variance % are calculated
In a Scenario comparison, **Variance** is the compared Scenario's value minus the value in the Scenario you are viewing. **Variance %** is that difference divided by the viewed value. In a time comparison, **Variance** is the current period minus the earlier one. Whether a positive variance shows as good or bad depends on the Variable's **Variance colors** setting, **Higher is better** or **Lower is better**. Change it on the Variable, and every table that shows it follows; see [Format and arrange a table](/guides/format-a-table).
## Related
* [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario)
* [Scenarios, Snapshots, and version history](/concepts/scenarios)
* [Table block controls and messages](/reference/table-blocks)
# Connect a Google Sheet or Drive file
Source: https://docs.cfo.ai/guides/connect-a-google-sheet
Connect a Google Sheet, or an Excel or CSV file in Google Drive, so Ari imports it into your Model and updates the Model when the file changes.
Connect a Google Sheet when numbers you still keep in a spreadsheet should feed your Model, such as a hiring tracker or a pricing table. Ari imports the whole file, rebuilds it in your Model, and re-reads it every night, so a change you make in the sheet reaches cfo.ai without a new upload. Use Google Drive to connect an Excel workbook or a CSV file the same way.
## Before you start
* A Google account that can open the file.
* A started trial or an active plan with a free connection slot. Every Google Sheets connection in a workspace shares one slot; each Google Drive connection uses its own. See [Standard and premium integrations](/reference/integrations#standard-and-premium-integrations).
cfo.ai can open only the files you pick in Google's file picker, nothing else in your Drive. You can remove its access from your Google account at any time.
## Connect the file
In a chat, ask Ari to connect the sheet and say what it holds:
> Connect our hiring tracker from Google Sheets and use it for the headcount plan.
Ari opens the connection manager with Google Sheets. Select **Connect**, sign in with Google, and pick the file. Ari imports it in this chat, so it can ask you about the sheet while it works.
In the sidebar, point at **Connections** and select **+** (**Add integration**).
On the **Integrations** tab, select **Connect** on **Google Sheets** for a spreadsheet, or on **Google Drive** for a Google Sheet, an Excel workbook (`.xlsx`), or a CSV file. Both are listed under **Data upload** when you filter the directory by type.
Sign in with Google. Google's file picker opens; choose one file. The card reads **Opening Google authentication…**, **Choosing in Google…**, **Completing connection…**, and then **Connected**.
Ari imports the file in the background. The connection's row reads **Waiting for the first sync to read this file…**, then **Ari is importing this file…**. When the import is done, it reads **Imported to** and the Page Ari built, with **Ari updates the model when the file changes.**
If you close Google's picker without choosing a file, the connection stays unfinished and reads **Setup not finished**. Select **Finish setup** on it to pick the file.
## What Ari builds from the file
Ari reads every tab of the spreadsheet with its formulas, values, and number formats. It rebuilds the file in your Model the way it [imports a spreadsheet model](/guides/import-a-spreadsheet-model): tabs become Pages, repeated sections become Dimensions, and formulas become cfo.ai formulas. A file of plain data, such as a list of transactions, is imported as data.
## Keep the file in sync
cfo.ai re-reads the file every night, and when you ask Ari to refresh it. When a value, a formula, a tab, or a defined name changed, Ari updates the parts of your Model that came from the changed sections. Edits you made in cfo.ai to sections that did not change stay. A sync that finds the same content starts no update and uses no AI usage, and the row adds **The last sync found no change.** A change to formatting alone, such as colors, column widths, or comments, starts no update either.
Where the update happens follows where you connected the file. A file connected from a chat updates in that chat. A file connected from the connection manager updates in the background, and Ari opens a chat with you only when it needs you.
## Limits
* One file per connection. The same file cannot be connected twice in a workspace.
* A Google Sheet can be up to 50 MB when exported as an Excel workbook. An Excel or CSV file in Drive can be up to 10 MB.
* A larger file shows **This file is too large to import. Make it smaller and the next sync imports it.**
## If something goes wrong
* **Ari stopped before finishing this import. The next sync tries again.** The next nightly sync resumes it, or ask Ari to refresh the connection now.
* **Ari stopped before finishing this import. Continue in the chat that connected it.** Open that chat and ask Ari to continue.
For other problems, see [Fix a connection that needs attention](/guides/fix-a-connection).
## Related
* [Import an existing spreadsheet model](/guides/import-a-spreadsheet-model)
* [Upload a CSV as a data source](/guides/upload-a-csv)
* [Refresh data or change when it refreshes](/guides/refresh-data)
* [Integrations directory](/reference/integrations#google-sheets)
# Connect QuickBooks, Xero, NetSuite, or Rillet
Source: https://docs.cfo.ai/guides/connect-accounting
Connect QuickBooks Online, Xero, NetSuite, or Rillet so Ari plans from your real books. What you need first, how long the first load takes, what to do while you wait, and how to connect more than one company.
Connect your accounting system when you want Ari to plan from your real books instead of estimates. cfo.ai pulls your profit and loss and balance sheet, refreshes them every night, and never writes anything back to your accounting system.
## Before you start
You need:
* Admin access to your accounting system, or a teammate who has it. The table below shows what each system asks for.
* A started trial or an active plan with a free connection slot. QuickBooks Online, Xero, and Rillet are standard integrations. NetSuite is a premium integration and needs the Enterprise plan. See [Plans and billing](/reference/plans-and-billing).
* Time for the first load, from a few minutes to a day or more, depending on the system and the size of your books. See [How long the first load takes](#how-long-the-first-load-takes).
| System | What you sign in with |
| - | - |
| QuickBooks Online | A login with admin access to the company. You approve the connection yourself, and cfo.ai never sees your password. |
| Xero | A login with admin access to the organisation |
| NetSuite | An access token for the **Data Warehouse Integrator** role, which a NetSuite admin creates |
| Rillet | An API key. Find or create one in Rillet under **Organization Settings → API access**. |
If someone else holds admin access to your books, [invite them to your workspace](/guides/invite-teammates) and ask them to connect, or use one of the [fallbacks below](#if-you-cannot-connect).
## Connect
Tell Ari what to connect and what you want from it:
> Connect QuickBooks Online so we can build monthly financial statements.
Ari opens the connection manager and points you to QuickBooks Online. Select **Connect** on its card and sign in. If you close the window, select **Connect integrations** on the card Ari left in the chat to open it again.
In the sidebar, point at **Connections** and select **+** (**Add integration**).
On the **Integrations** tab, QuickBooks Online, Xero, and Rillet are under **Featured**. Search for NetSuite. Select **Connect** on the card.
For QuickBooks Online or Xero, a window opens. Sign in, choose the company or organisation, and approve access.
For NetSuite, Fivetran's setup window opens. Enter your NetSuite account and the access token. A table picker follows. The tables the statements need are locked. Tables for NetSuite features you do not use, **Multiple Currencies**, **Multiple Calendars**, or **Multi-Book Accounting**, start unchecked. Select **Finish setup & sync \[N] tables**.
For Rillet, paste the API key into the card and select **Connect**. The card reads **Verifying API key…**, then **Connected**.
The window closes, and the connection appears under **Connections** with a blinking orange dot while it syncs.
## How long the first load takes
| System | Usual time until the data reaches your Model |
| - | - |
| QuickBooks Online | A few minutes |
| Rillet | A few minutes |
| Xero | About an hour, and a day or more for a large ledger. Xero limits how many requests an app can make for an organisation. While cfo.ai waits for that allowance to reset, the connection's card says how long it has waited. |
| NetSuite | Many hours, because Fivetran copies every table's full history first |
For QuickBooks Online and Xero, your three most recent complete months load first, so Ari can start working. The rest of your history, from January 1 of last year through today, follows in the background. To bring in older years, ask Ari for a full-history import later; see [Re-import past months](/guides/refresh-data#re-import-past-months).
During the load, cfo.ai copies your books into its data warehouse. Then it adds saved queries that turn them into your Model's income statement and balance sheet, such as **QuickBooks Profit & Loss** and **QuickBooks Balance Sheet**. See [How connected data reaches your Model](/concepts/connected-data).
## What to do while it loads
You can keep working, and you do not need to stay on the page.
* Start planning now. Ari can start a forecast from what you tell it and from labeled estimates. When your records land, have Ari rebuild the forecast on them so they take the place of those estimates; see [Replace estimates with your real numbers](/guides/switch-to-real-numbers).
* Do not reconnect or connect the source again to speed things up. Signing in again does not change how fast the data loads, and cfo.ai does not offer **Reconnect** while a sync is running.
## Check that the data is ready
Ari posts in the chat when the first sync finishes or fails. A connection you started from a chat reports to that chat. While your workspace is new, a connection you start from the sidebar reports to your most recent chat.
In the sidebar, the orange dot disappears when the data is in. Point at the row: the card reads **Synced** and shows **Last synced**.
You can also ask:
> Check whether QuickBooks finished syncing and tell me which months are available.
Ari names the periods that loaded, such as January 2025 to September 2026. Then compare one closed month with your books before you rely on the numbers; see [Check that your numbers match your books](/guides/check-imported-data).
## Connect more than one company
If you run several companies or entities (multi-entity), connect each one as its own connection. A workspace can hold more than one QuickBooks Online company, Xero organisation, or NetSuite account, and each connection uses its own plan slot. Rillet allows one connection per workspace, and that connection brings in every subsidiary.
To add another company, open the connection manager, select **Add another** on the source's card, and sign in to the next company.
QuickBooks Online, Xero, and Rillet statement rows carry an **Entity** column, which names the company or subsidiary, and a **Currency** column. When a second company of the same system loads, such as a second QuickBooks Online company, its accounts and entity join the first company's Dimensions. You can then report on each company and on the group. Companies in different systems, such as one in QuickBooks Online and one in Xero, do not share Dimensions on their own. To combine them, or to line up two different charts of accounts, see [Consolidate entities](/guides/consolidate-entities).
You cannot connect the same QuickBooks company or Xero organisation twice. If you sign in to one the workspace already holds, the card reads **\[Company] is already connected**.
## If you cannot connect
* If your system is not in the directory, such as QuickBooks Desktop or QuickBooks Enterprise, export a general ledger, or a monthly profit and loss and balance sheet, and [upload it as a CSV](/guides/upload-a-csv). A general ledger with a date, an account, and an amount on each row gives Ari the most to work with. Check the [integrations directory](/reference/integrations#check-whether-your-system-is-supported) first, because Sage Intacct, Acumatica, Zoho Books, and many other systems are there.
* If you are not an admin in the accounting system, invite the person who is, or export the reports yourself and upload them.
* If your numbers live in a spreadsheet model, attach it to a chat and [import it](/guides/import-a-spreadsheet-model).
* If the card shows **Upgrade plan** or **Choose plan** instead of **Connect**, your plan has no room, or the workspace has no trial or plan yet. A **Premium** badge means the source needs the Enterprise plan; a trial does not unlock it. The tooltip says what is missing, and [Connection statuses and actions](/reference/connection-statuses#messages-when-a-new-connection-is-refused) lists each message.
An upload does not refresh, so upload a new export each month until you can connect directly.
## If something goes wrong
If the sign-in window closes early, the card reads **\[Source] was not connected**, such as **QuickBooks Online was not connected**. Select **Try again**; it reopens the same unfinished connection.
If the connection shows a red triangle or an orange dot, point at it and use the action the card offers. [Fix a connection that needs attention](/guides/fix-a-connection) covers each one, and [Connection statuses and actions](/reference/connection-statuses) lists every message.
## Related
* [Integrations directory](/reference/integrations)
* [How connected data reaches your Model](/concepts/connected-data)
* [Replace estimates with your real numbers](/guides/switch-to-real-numbers)
* [Check that your numbers match your books](/guides/check-imported-data)
# Connect Claude, ChatGPT, Codex, or another AI tool to cfo.ai
Source: https://docs.cfo.ai/guides/connect-an-ai-client
Use your cfo.ai workspace from Claude Code, Claude's desktop or web app, ChatGPT, Codex, Cursor, or another AI tool: add the cfo.ai MCP server, approve it in your browser, choose the workspace, and fix sign-in problems. cfo.ai has no API keys.
Connect another AI tool when you want to work on your cfo.ai workspace from the tool you already use. For example, ask Claude to update this month's forecast, or have Codex build a table. The tool connects to the cfo.ai MCP server at `https://api.cfo.ai/mcp`, and you approve it once in your browser. After that it can read your Model and make the changes you ask for, as you.
## Is there a cfo.ai API or API key?
No. cfo.ai does not issue API keys or access tokens to paste into a project, a script, or an environment variable. Outside software, including an agent you build yourself, works with a workspace through the cfo.ai MCP server. It connects as a remote MCP client, and a person signs in and approves it in a browser. The connection then acts as that person, in the workspaces their account can open.
If you are building your own agent, [MCP tools and call shapes](/agents/mcp-tools) lists the server's sign-in details and every tool it offers.
## What you need
* A cfo.ai account that can open the workspace. If you don't have one, sign up at [app.cfo.ai](https://app.cfo.ai).
* A workspace that has started its free trial or a plan. Until then, the AI tool's requests fail with **Start the free trial in cfo.ai before using its MCP tools**. See [Start a trial or choose a plan](/guides/choose-a-plan).
* An AI tool that can add a remote MCP server. Some tools call this a custom connector.
* The server address, `https://api.cfo.ai/mcp`. You can also copy it in cfo.ai: open the account menu at the bottom of the sidebar, choose **Settings**, then **Connections**. The address is under **Connect via MCP**, beside its copy button, **Copy cfo.ai MCP URL**.
## Connect Claude Code
In a terminal, run:
```bash theme={null}
claude mcp add --transport http cfoai https://api.cfo.ai/mcp
```
```bash theme={null}
claude
```
Type `/mcp`, select `cfoai`, and choose **Authenticate**. A browser window opens. [Approve the connection](#approve-the-connection-in-your-browser).
## Connect Codex
```bash theme={null}
codex mcp add cfoai --url https://api.cfo.ai/mcp
```
```bash theme={null}
codex mcp login cfoai
```
A browser window opens. [Approve the connection](#approve-the-connection-in-your-browser).
Start Codex with `codex`, then type `/mcp` and check that `cfoai` is listed.
## Connect Claude's desktop or web app
Claude's desktop app and claude.ai add outside servers as custom connectors. On a Claude Free, Pro, or Max plan, you add the connector yourself:
In Claude, go to **Customize** > **Connectors**, select **+ Add**, then **Add custom connector**.
Name the connector `cfo.ai`, enter `https://api.cfo.ai/mcp` as the server URL, and select **Continue**. Review the sign-in settings Claude detected, and select **Continue** again.
Under **Authentication**, choose **Sign in now**, then select **Add**. [Approve the connection](#approve-the-connection-in-your-browser) in the browser.
In a chat, select the **+** button, then **Connectors**, and turn on cfo.ai.
On a Claude Team or Enterprise plan, an owner adds the connector for the organization under **Organization settings** > **Connectors**. Each member then finds it under **Customize** > **Connectors**, selects **Connect**, and approves the connection with their own cfo.ai account. Claude's menus change from time to time; Anthropic's [custom connector instructions](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) have the current steps.
## Connect ChatGPT
ChatGPT can add a custom remote MCP server on the plans and workspaces that allow it, and a workspace admin may need to turn on developer mode first. ChatGPT calls these servers apps, connectors, or plugins, depending on your version.
Add a custom MCP server named `cfo.ai`, with the server URL `https://api.cfo.ai/mcp` and **OAuth** as the authentication. ChatGPT then sends you to cfo.ai to [approve the connection](#approve-the-connection-in-your-browser). Where this setting lives depends on your ChatGPT plan, so follow OpenAI's [steps for adding a custom MCP server](https://help.openai.com/en/articles/12584461-developer-mode-and-mcp-apps-in-chatgpt).
## Connect Cursor
Add cfo.ai to `~/.cursor/mcp.json` to use it in every project, or to `.cursor/mcp.json` in one project:
```json theme={null}
{
"mcpServers": {
"cfoai": {
"url": "https://api.cfo.ai/mcp"
}
}
}
```
Restart Cursor if it does not pick up the change. When Cursor asks you to sign in to cfo.ai, a browser window opens. [Approve the connection](#approve-the-connection-in-your-browser).
[Cursor's MCP documentation](https://cursor.com/docs/mcp) shows where its MCP settings are.
## Connect another AI tool
Any AI tool or agent framework that supports remote MCP servers over HTTP with browser sign-in can connect. Follow your tool's steps for adding a remote MCP server: add `https://api.cfo.ai/mcp`, then sign in when the browser opens. A tool that only accepts an API key or a fixed token cannot connect, because cfo.ai has no API keys.
## Approve the connection in your browser
Every AI tool signs in the same way:
1. Your browser opens cfo.ai. If you are not signed in, sign in first. cfo.ai then shows the approval page.
2. The page is headed **Connect CFO.ai** and says an external MCP client is asking to use your account. Continue only if you started this connection from your AI tool, and check that you are signed in to the cfo.ai account you want the tool to use.
3. Select **Connect CFO.ai**. cfo.ai finishes the sign-in and returns you to your AI tool. Select **Cancel** instead to stop.
The approval page stays valid for 60 minutes. It still works if it loads twice or you have several open.
## Choose the workspace
If your account belongs to one workspace, the AI tool uses it automatically. If you belong to several, tell the tool which workspace to use. It can list your workspaces by name and then works in the one you chose.
Changes apply right away. Unless you name a Scenario, the tool works in Main, the Scenario that holds your current plan. To try something without changing your plan, ask the tool to create a new Scenario and work there. See [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario).
## Check that it works
Ask the AI tool to list the Pages in your workspace. If it shows your Pages, the connection works.
If the tool does not list cfo.ai at all, restart it or start a new session, and check its list of MCP servers again.
## Let your AI tool set itself up
To have your AI tool do the setup, paste this prompt into it. It walks you through every step, from creating an account to a working connection, and tells you when it needs you:
```text theme={null}
Set up cfo.ai for me. Fetch https://docs.cfo.ai/agent-instructions.md and follow the instructions there. If I don't have a cfo.ai account yet, walk me through creating one first, then connect the cfo.ai MCP server to this harness and verify it's working.
```
If you are an AI agent reading this page, fetch `https://docs.cfo.ai/agent-instructions.md` and follow it.
## What a connected AI tool can do
* It uses the same workspace tools Ari uses, under your account. It can read your Model and, when you ask, change Pages, tables, formulas, Scenarios, and connections.
* You, Ari, and the AI tool work on one Model. What the tool builds appears in your workspace right away, where you can check it, edit it, or keep going with Ari.
* Every change it makes to the Model is recorded in version history, so you can take it back. See [Undo a change or restore an earlier version](/guides/undo-changes).
* Many AI tools ask you before they run something that changes your workspace. cfo.ai marks its read-only tools, so the tool can tell a lookup from a change.
* A few things only work inside cfo.ai: Ari asking you questions in the chat, playing a Guide, showing you a Scenario or table rows on screen, and downloading a Page as a spreadsheet.
## If sign-in fails
If the approval page shows one of these messages, nothing was approved. Start the sign-in again from your AI tool.
| The page says | What happened |
| - | - |
| **No cfo.ai account is signed in to this browser, so the approval was not recorded.** | The browser signed out of cfo.ai. Sign in at [app.cfo.ai](https://app.cfo.ai) in the same browser, then start again. |
| **This approval page was opened under a different cfo.ai account than the one signed in to this browser now, so the approval was not recorded.** | You switched cfo.ai accounts after the page opened. Sign in to the account the tool should use, then start again. |
| **This approval page was opened** … **minutes ago. An approval page is valid for 60 minutes, so the approval was not recorded.** | The page was more than an hour old. |
| **This approval does not match the sign-in request it was sent for, so it was not recorded.** | The approval did not belong to this sign-in request. |
| **This approval did not come from a cfo.ai approval page that is still current, so it was not recorded.** | The page was out of date. |
| **Unable to complete MCP login** | cfo.ai accepted your approval but could not hand the sign-in back to your AI tool. If it happens again, ask Ari what happened. |
Other problems:
* If the AI tool says to start the free trial, start the workspace's free trial or a plan, then try again. See [Start a trial or choose a plan](/guides/choose-a-plan).
* If the tool says you do not have access to the workspace, it is signed in with a cfo.ai account that is not a member of that workspace. Sign in again from the tool with the right account, or ask someone in the workspace to [invite you](/guides/invite-teammates).
* If it worked before and now fails to authorize, repeat your AI tool's sign-in step, such as **Authenticate** in Claude Code or `codex mcp login cfoai` in Codex.
### Ask Ari what happened
In cfo.ai, ask Ari why your AI tool cannot connect. Ari can list your own sign-in attempts to the cfo.ai MCP server from the last 30 days, ten newest first. Each one shows as approved, refused with its reason, approved but not completed, or an AI tool that connected under its own name. Nobody else sees your attempts, not even a workspace administrator. A sign-in that never reached the approval page leaves no record.
## Related
* [Give Ari access to another service with MCP or an API](/guides/give-ari-another-service), for the other direction: Ari calling an outside MCP server or API
* [cfo.ai for AI agents](/agents)
* [How cfo.ai works](/concepts/how-cfoai-works)
# Connect payroll, HR, CRM, payments, or a database
Source: https://docs.cfo.ai/guides/connect-other-sources
Connect Gusto, Rippling, or another payroll or HR system, HubSpot or Salesforce, Stripe, or a database so Ari plans from your records. What each one needs, how long the first sync takes, what reaches your Model, and what to do when you cannot connect.
Connect your other business systems when your plan needs more than your books: payroll and HR for headcount and pay, a CRM for pipeline and bookings, a payments system for revenue detail, or a database for your own records. Each one syncs every night, and cfo.ai only reads from it. To connect QuickBooks, Xero, NetSuite, or Rillet, see [Connect your accounting system](/guides/connect-accounting).
## Before you start
You need:
* Admin access or credentials for the source, as the table below shows.
* A started trial or an active plan with a free connection slot. Most of these sources are standard integrations. Databases, data warehouses, and the other sources the [integrations directory](/reference/integrations) marks premium need the Enterprise plan. See [Plans and billing](/reference/plans-and-billing).
* Time for the first sync, from minutes to a day or more, depending on the source and how much history it holds. See [How long the first sync takes](#how-long-the-first-sync-takes).
| Source | What you need |
| - | - |
| Gusto, Justworks, ADP Workforce Now, Paychex, Hibob, UKG, BambooHR, Deel, and the other HR and payroll systems that connect through Merge | Admin access to the company in the HR or payroll system |
| Rippling | A Rippling API key. API access may need to be turned on as a paid feature in your Rippling account. |
| HubSpot, Salesforce | A HubSpot or Salesforce login, which you use in Fivetran's setup window |
| Stripe (Direct) | A Stripe login that can install apps on the account |
| A database or data warehouse, such as PostgreSQL, MySQL, Snowflake, or BigQuery | The database's connection details and a user that can read the tables you want |
| Shopify, Square, PayPal, Brex, Ramp, and the other sources that connect through Fivetran | The sign-in or credentials that the source's setup window asks for |
The [integrations directory](/reference/integrations) lists every source and how it connects.
## Connect
Name the source and what you want to do with it:
> Connect Gusto so we can build a headcount plan from our payroll.
Ari opens the connection manager and points you to the source. Select **Connect** on its card and sign in. If you close the window, select **Connect integrations** on the card Ari left in the chat.
In the sidebar, point at **Connections** and select **+** (**Add integration**).
On the **Integrations** tab, search by name, or filter by type, such as **HR & Payroll** or **CRM**. Select **Connect** on the card.
An HR or payroll system opens its own sign-in window: sign in and choose the company. Rippling first shows **Before you connect Rippling**; select **Proceed** when you have the API key. Stripe (Direct) opens Stripe, where you install cfo.ai's Stripe app with read-only access. Most other sources open Fivetran's setup window, where you sign in or enter the source's credentials.
Databases and most sources through Fivetran show a table picker. Every table starts selected; uncheck the ones you do not need, keep 100 or fewer, and select **Finish setup & sync \[N] tables**. See [Choose which tables a source syncs](/guides/choose-synced-tables).
The connection appears under **Connections** with a blinking orange dot while it syncs.
## How long the first sync takes
| Source | Usual time |
| - | - |
| HR and payroll through Merge | Minutes, or hours when Merge is still building its first sync of your account. cfo.ai waits up to 20 hours, and the card says how long it has waited. |
| Rippling | Minutes |
| HubSpot, Salesforce | Minutes to an hour |
| Stripe (Direct) | A day or more for an account with a long history, because the first sync lists every object |
| Databases and other sources through Fivetran | Depends on size. Fivetran copies each table's full history first, which can take many hours. |
## What to do while it syncs
You can keep working while it syncs.
* When you connect from a chat, Ari posts there when the first sync finishes or fails. In the sidebar, the orange dot disappears when the data is in.
* Ask Ari whether the load is still running before you treat a long wait as a failure.
* Do not connect the source again to speed things up. cfo.ai does not offer **Reconnect** while a sync is running.
## What reaches your Model
| Source | What lands |
| - | - |
| HR and payroll systems | An employee roster with jobs, pay, and groups such as departments, through the **HRIS Employees** saved query. Payroll systems that connect through Merge, such as Gusto, also sync each payroll run with each employee's gross and net pay; ask Ari for a saved query to bring them into your Model. Rippling syncs the roster tables, without payroll runs. |
| HubSpot | Deals with their pipelines, stages, owners, contacts, companies, line items, and invoices, through **HubSpot Deals** |
| Salesforce | Opportunities, accounts, and users, through **Salesforce Opportunities** |
| Stripe, Shopify, databases, and other apps | The tables they sync. Ari writes the saved queries that bring them into your Model after the first sync. |
If a connection reads **No queries yet**, its data synced but nothing brings it into your Model yet. Select **Set up queries with Ari** on its card.
## If you cannot connect
* If your system is not in the directory, such as Toast or bill.com, export the data and [upload it as a CSV](/guides/upload-a-csv). For payroll, a payroll register or an employee list with one row per employee or pay run works well. For payments, use a transactions export with a date and an amount on each row. You can also attach the export to a chat and ask Ari to load it.
* If you are not an admin in the system, [invite the person who is](/guides/invite-teammates) to your workspace and ask them to connect it, or export the data yourself and upload it.
* If you cannot get a Rippling API key, upload an employee export as a CSV. To let Ari look up people in Rippling without syncing, connect Rippling's MCP server, which needs a Rippling AI trial or subscription. See [Give Ari access to another service](/guides/give-ari-another-service).
* If your data is in a database cfo.ai does not list, copy it into a PostgreSQL-compatible database if you can, and connect that database.
* If you only need a live lookup, such as today's balance or an open invoice, connect the service's MCP server or API instead. See [How connected data reaches your Model](/concepts/connected-data#integrations-and-mcp-servers-do-different-jobs).
* If the card shows **Upgrade plan** or **Choose plan**, or the setup says **start the free trial to add integrations**, your workspace needs a trial or a plan with room first. If the card shows a **Premium** badge, the source needs the Enterprise plan; a trial does not unlock it. [Connection statuses and actions](/reference/connection-statuses#messages-when-a-new-connection-is-refused) lists each message.
An upload does not refresh, so upload a new export when the numbers change.
## If something goes wrong
Point at the connection in the sidebar and use the action its card offers. [Fix a connection that needs attention](/guides/fix-a-connection) covers each one, including a first sync that is not ready and a source that is missing access.
## Related
* [Integrations directory](/reference/integrations)
* [Choose which tables a source syncs](/guides/choose-synced-tables)
* [How connected data reaches your Model](/concepts/connected-data)
* [Connection statuses and actions](/reference/connection-statuses)
# Model several entities and consolidate them
Source: https://docs.cfo.ai/guides/consolidate-entities
Report each legal entity and the group total in one Model, also called multi-entity consolidation. Connect more than one QuickBooks Online company or Xero organisation, ask Ari for a P&L by entity, and handle shared Dimensions, account mapping, currencies, and intercompany transactions.
Ask Ari for a P\&L by entity when your business has more than one legal entity, such as a US company and a UK subsidiary. You get each entity's results and the group total in one Model. People call this multi-entity reporting or consolidation.
Before you start, connect each entity's books. Connect each QuickBooks Online company or Xero organisation as its own connection; a workspace can hold several of each. Rillet is different: a workspace holds one Rillet connection, and it brings in every subsidiary. See [Connect QuickBooks, Xero, NetSuite, or Rillet](/guides/connect-accounting). Each connection uses an integration slot on your plan, so check that your plan has a slot for every company you connect; see [Plans, AI usage, and connection slots](/concepts/plans-and-usage).
> Build a P\&L Page by entity, with a group total for each month.
The group total adds up the entities. For a balance sheet and cash flow statement too, see [Get your P\&L, balance sheet, and cash flow statement](/guides/build-financial-statements).
Every row of a QuickBooks Online or Xero company's statements carries **Entity**, the company's name, and **Currency**. When another company from the same accounting system loads for the first time, its Entity and Currency columns join the Dimensions the first company created. One Entity Dimension then lists every company. Rillet's statements carry one set of rows for each subsidiary, with Entity naming the subsidiary. NetSuite's statements have no Entity or Currency column: the **NetSuite Income Statement** names each row's subsidiary in **Subsidiary** and its currency in **Currency Name**.
Companies in different accounting systems, such as one in QuickBooks Online and one in Xero, get separate Dimensions. So do two companies with the same name. To combine two compatible Dimensions, ask Ari:
> Combine the Entity Dimensions from QuickBooks Online and Xero into one.
For how Dimensions are shared and combined, see [Dimension mappings and shared Dimensions](/concepts/dimension-mappings).
Each company has its own chart of accounts, so each company's accounts must map into the same reporting lines. If they do not, ask Ari:
> Map both companies' accounts into one set of P\&L lines: Revenue, COGS, Payroll, Software, Rent, and Other. Where the same account number means different things in the two companies, map by company and account together.
See [Map one Dimension's items to another](/guides/map-dimensions).
cfo.ai does not convert currencies on its own. Amounts load in the currency the source reports them in, so a total across companies that report in different currencies adds unlike numbers. Ask Ari to convert to your reporting currency at a rate you choose:
> Convert the UK company's results from GBP to USD at 1.27, and show the group total in USD.
Ari keeps the rate as an assumption you can change.
Adding entities up does not remove the transactions between them. When the US company charges the UK company a management fee, the fee is revenue in one and expense in the other, and both stay in a simple sum. cfo.ai does not eliminate them on its own. Rillet removes them only in its own consolidated reports, so adding Rillet's subsidiaries up does not match Rillet's consolidated numbers.
Ask Ari to add the eliminations as their own lines:
> Add an eliminations line that removes the management fee between the US and UK companies from the group total.
## Related
* [Dimension mappings and shared Dimensions](/concepts/dimension-mappings)
* [Map one Dimension's items to another](/guides/map-dimensions)
* [Connect QuickBooks, Xero, NetSuite, or Rillet](/guides/connect-accounting)
* [How connected data reaches your Model](/concepts/connected-data)
# Walk someone through a Page with a Guide
Source: https://docs.cfo.ai/guides/create-a-guide
Have Ari build, run, update, or remove a Guide: a saved, step-by-step walkthrough that points at blocks, table cells, rows, columns, Canvas elements, or parts of the app, with a short card at each step.
Use a Guide to walk someone through a Page one step at a time, such as a new teammate learning your Model or your board reviewing this quarter's numbers. A Guide is a saved walkthrough, or tour. It points at one thing at a time, such as a table row or a chart, with a short card of text beside it. Ari builds, runs, and changes Guides for you; there is no Guide editor in the app. For how Guides relate to Skills and Triggers, see [Skills, Triggers, and Guides](/concepts/automation).
## Create a Guide
Tell Ari who the Guide is for, which Page it covers, and the stops in order, with what each card should say:
> Build a walkthrough of the Board summary Page for a new analyst. Start with a short welcome, then point at the Revenue row of the P\&L table and explain where revenue comes from, then the cash chart, then the Hiring plan Page's headcount column. End with a note on where to ask questions.
Ari saves the Guide under a name and tells you what it saved. To try it, ask Ari to start it, as in [Run a Guide](#run-a-guide).
### Choose the kind of Guide
| Kind | Card buttons | Use it for |
| - | - | - |
| Tour (the default) | **Back**, **Next**, **Skip tour**, and **Done** on the last step, with progress | Onboarding and feature tours |
| Walkthrough | **Back**, **Continue**, **Exit walkthrough**, and **Done**, with progress | Narrating an analysis, such as board prep |
| Explanation | **Got it** | One quick spotlight, opened on request |
A Guide can switch to a set Scenario when it starts, so its numbers match what the cards say. Ari uses the Scenario you are viewing unless you name another.
### What a step can point at
| Target | Example |
| - | - |
| A whole block on a Page | The cash chart |
| One cell, row, or column of a Table block, named by its Variables and Dimension items | The Revenue row, the March column, or a Scenario comparison column |
| One element inside a Canvas block | A KPI tile or a slider. Ask Ari to give the element a stable name when it builds the block, so a Guide can find it. |
| A part of the app | The **Share** button or the chat list in the sidebar |
| Nothing | A centered card, for a welcome or a closing note |
A step whose target is on another Page opens that Page first. A step whose target is not on screen when the Guide reaches it, such as a row the table no longer shows, is skipped.
### What a step contains
| Part | What it is |
| - | - |
| Title | The card's heading |
| Text | The card's body. Line breaks you include show in the card. |
| Richer content | Instead of a title and text, a sequence of headings, small labels, and paragraphs |
| Card position | Above, below, left, or right of the target, or chosen automatically. The card moves if it would not fit. |
| Card width | Standard, narrow, or narrowest |
| Step key | A short readable name, such as `roster`, so you can start the Guide at that step later |
| Action | Optional. The step does something in the app, described below. |
A step can carry one of these actions instead of only pointing:
* Open the chat with suggested prompts to try.
* Open the Canvas block library with templates already picked.
* Show the formula behind one table cell.
* Open a table's **Compare** settings to show where Scenarios are compared. This step is skipped on a phone and for anyone who cannot change the table.
* Light up a Canvas control, such as a slider, for the reader to try, and move on when it reaches a value you name.
* Open version history on a recorded change.
* Walk through the Model: its Scenarios, then its Pages.
## Run a Guide
Ask Ari to start it by name, or at a step:
> Start the Board summary walkthrough.
> Start the onboarding tour at the roster step.
A tour or walkthrough waits for you: select **Start tour** or **Start walkthrough** under Ari's answer. An explanation opens at once. While it runs, use the card's buttons, or ask Ari to go to the next step, go back, or stop. A floating chat moves out of the way of each step and returns when the Guide ends.
If a Guide cannot open, a notice reads **Couldn't open that guide** because the Guide or one of its targets was deleted or is no longer accessible. Ask Ari to update the Guide.
A Guide never starts by itself when someone opens a Page. To offer one:
* Ask Ari to add a button to a Canvas block on the Page that starts the Guide. The button opens a new chat that asks Ari for the Guide, and a tour still waits for its **Start tour** button.
* Name the Guide when you ask Ari to invite a teammate. Their first visit shows a **Take the tour** button. See [Invite or remove teammates](/guides/invite-teammates).
Saved Guides are not listed in the Help center. A walkthrough or explanation you ran since the app last loaded appears under **Recently viewed** in the Help center, with a button that asks Ari to refresh it with your latest numbers.
## Update or remove a Guide
Ask Ari for the change, such as "Add a step about gross margin after the Revenue step" or "Update the board walkthrough with this month's numbers." Ari saves the Guide again with your changes. To remove one, ask Ari to delete it. To see what you have, ask "Which Guides do we have?"
## Related
* [Skills, Triggers, and Guides](/concepts/automation)
* [Build a dashboard or board report](/guides/build-a-dashboard)
* [The cfo.ai interface](/reference/interface#help-center)
# Create and organize Pages and blocks
Source: https://docs.cfo.ai/guides/create-pages-and-blocks
Create, rename, nest, reorder, and delete Pages, use a Page with subpages as a folder, keep the Pages Ari creates in order, and add, move, copy, or delete the Table, Text, and Canvas blocks on a Page.
Use this guide to set up the Pages your team reads, such as a Board Page with one subpage per meeting, and to arrange the blocks on each Page. A Page is a report made of blocks. To learn what Pages and blocks are, read [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks).
You need a desktop browser. A phone browser shows Pages read-only.
## Create a Page
Name the Page and, if it belongs inside another Page, name that one too:
> Create a Page called Board pack inside the Plans Page.
Ari gives each Page it creates an icon and a color. Ari can also rename a Page, change its icon, move it under another Page or out to the top level, or delete it.
In the sidebar's **Model** section, select the **+** button, **New page**. cfo.ai creates a Page named **New Page** and opens it.
To create a Page inside another one, point at that Page in the sidebar and select its **+** button, **New subpage**.
Click the title at the top of the Page, type a name, and press **Enter**.
A workspace with no Pages yet has no **Model** section. Its empty screen reads **Ready to model, \[your name]?** Choose **Create with Ari**, or choose **Add block** and a block type to create the first Page with that block on it.
## Rename a Page
Click the title at the top of the Page, type, and press **Enter**. Press **Esc** to cancel. You can also double-click the Page in the sidebar, or select it and press **F2**.
Clicking the Page name in the Page header does not rename it. It opens the list of Pages so you can switch to another one.
## Change a Page's icon
Select the icon beside the Page name in the Page header. In the picker, search with **Search icons**, pick a color and a category, select **Shuffle icon** for a random one, or select **Remove page icon**.
## Put Pages in folders by nesting them
cfo.ai has no folders. A Page that holds other Pages works as one: create a Page such as **Board** and put a subpage inside it for each meeting. You can ask Ari to do the moving:
> Move the March, April, and May board Pages inside a new Page called Board.
By hand:
* To nest a Page, drag it onto another Page in the sidebar. Its subpages move with it.
* To reorder Pages, drag a Page between two others. While you drag, move the pointer left or right to choose how deeply the Page nests.
* To use the keyboard, select a Page in the sidebar and press **⌥ ↑** or **⌥ ↓** to move it among its neighbors, **⌥ →** to nest it under the Page above it, or **⌥ ←** to move it out one level. On Windows and Linux, use **Alt**.
* To show or hide a Page's subpages, point at the Page. Its icon turns into an arrow; select it (**Expand subpages** or **Collapse subpages**).
## Keep the Pages Ari creates in order
Ari creates Pages as it builds, so tell it where new work goes before it starts:
> Put any new Pages you create today inside the Q3 forecast Page.
To make that a standing instruction, see [Teach Ari your company's conventions](/guides/teach-ari). Ari can also move the Pages it already created, or delete the ones you no longer need; name them in your request. Ari usually gives a subpage the same icon color as its parent, and Pages on one subject a shared color, so related Pages stand out together in the sidebar.
While Ari is still writing a Page, its sidebar row shows a mark labeled **Ari is building this page**, and the Page header shows **Building…**. **Add block** stays hidden on that Page until Ari finishes.
## Delete a Page
Point at the Page in the sidebar and select its trash button, **Delete page**.
* The Page and its blocks are deleted. Its subpages move up one level and stay.
* When you are viewing a Scenario other than Main, the Page is deleted in that Scenario only. Main keeps it.
* Most deletes happen without a question. cfo.ai asks first only when the Page holds the table where a Variable or Dimension was created and something else still uses it. See [Delete a table that other content uses](#delete-a-table-that-other-content-uses).
To bring a Page back, press **⌘ Z** (**Ctrl Z**) in the same browser tab, or undo the change in version history. See [Undo a change or restore an earlier version](/guides/undo-changes).
## Add a block to a Page
* Select **Add block** at the bottom of the Page and choose **Table block**, **Text block**, or **Canvas block**.
* To add a block in a particular place, point at the left edge of the block above that place and select the **+** button, **New block below**. Choose **Table**, **Text**, or **Canvas**.
* On an empty Page, select one of the **Table block**, **Text block**, or **Canvas block** cards.
The block menu has no chart block. Ask Ari for a chart, and it builds one in a Canvas block; see [Change a chart or Canvas block](/guides/change-a-visual). To build a table, see [Build a Table block by hand](/guides/build-a-table).
## Move, copy, or delete a block
Point at the left edge of a block to show its handle, a grip of six dots. Drag the handle up or down to reorder the block. Click the handle to open the block menu; with a block selected, **Shift F10** opens it too.
* To move the block to another Page, choose **Move to page**, then a Page, or **New page** to move it to a new one.
* To copy the block, choose **Duplicate block**. A copy of a table shows the same Variables, not new ones, so a value you type in either table appears in both. To try different numbers without changing the plan, use a Scenario; see [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario).
* To delete the block, choose **Delete block**.
Moving a block, on its Page or to another Page, keeps all its settings. Deleting a block and building it again does not.
There is no command to copy a whole Page. Duplicate its blocks and move them to a new Page.
### Delete a table that other content uses
Each Variable and Dimension you create on a table has that table as its original table. When you delete a table or a Page:
* A Variable or Dimension that another table still shows is kept. One of those tables becomes its original table.
* One that no other table shows is deleted with the table. If formulas, other blocks, or Canvas blocks still use it, cfo.ai first asks **Delete variable or dimension?** and counts what would break. Choose **Cancel** to keep everything, or **Delete anyway**. See [Removing and deleting](/reference/table-blocks#removing-and-deleting).
To keep a Variable while you remove its table, first add it to another table, or move the table to another Page instead of deleting it.
## Write and format text in a Text block
Click into a Text block and type. The text saves as you type.
Select some text to show the formatting toolbar: **Heading** (**Heading 1**, **Heading 2**, **Heading 3**, or **Normal**), **Bold**, **Italic**, **Underline**, **Strikethrough**, **Code**, **Link**, and **Bullet list**. **Link** asks for a web address; select it on linked text to remove the link. **Add selection to chat** hands the selected text to Ari, for example to rewrite it.
A link to a Page in this workspace opens that Page in the app. **⌘**-click, **Ctrl**-click, or **Shift**-click opens a link in a new tab.
## Related
* [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks)
* [Build a Table block by hand](/guides/build-a-table)
* [Build a dashboard or board report](/guides/build-a-dashboard)
* [Keyboard shortcuts](/reference/keyboard-shortcuts#pages-and-blocks)
* [The cfo.ai interface](/reference/interface)
# Export a Page to Excel
Source: https://docs.cfo.ai/guides/download-a-spreadsheet
Export a Page, or several Pages, to an Excel workbook with live formulas: select Download spreadsheet in the Page header or ask Ari. What the workbook contains, whether formulas stay live, and how to get CSV or Google Sheets.
To export a Page to Excel, select **Download spreadsheet** in the Page header, or ask Ari to export it. You get an Excel workbook (`.xlsx`) with one sheet for each table on the Page, and its numbers are live Excel formulas, not pasted values. To put several Pages in one workbook, such as your three financial statements, ask Ari.
Name the Page, or several Pages for one workbook:
> Export the Board summary Page to Excel.
> Export the P\&L, Balance sheet, and Cash flow Pages to one Excel workbook, in the Downside Scenario.
Ari exports the Scenario your chat is in unless you name another. The finished workbook appears in the chat as a download card, and Ari can open it again later. One workbook can hold up to 64 Pages; their formulas link across the sheets, and they share one **Model inputs** sheet.
If the export fails, Ari tells you which step and which table stopped it, and whether trying again can help.
Open the Page, and check that the sidebar's Scenario picker shows the Scenario you want in the workbook.
Select **Download spreadsheet**, the download icon in the Page header. The button appears only on a Page that has a Table block.
The export runs in the background, and pointing at the button shows its progress. You can open other Pages while you wait, but closing or reloading the browser tab loses the download. When the file is ready, it downloads and a **Spreadsheet download started** notice names it.
A workspace prepares one spreadsheet at a time. If someone else's export is running, yours waits and starts by itself when theirs ends.
## Are the formulas live?
Yes. Each number a formula calculates in cfo.ai is an Excel formula in the workbook. The numbers those formulas read from outside the Page, such as typed assumptions and imported Actuals on other tables, sit on a **Model inputs** sheet. Change an input in Excel and every cell that depends on it recalculates.
The workbook is a copy. Changes you make in Excel do not flow back into cfo.ai.
Formulas and the **Model inputs** sheet need access to every Variable and Dimension in the Scenario. Someone without it, such as a person who opens the Page through a share link, gets the numbers the Page shows as plain values, with no **Model inputs** sheet.
A few formulas cannot be written as Excel formulas, such as one longer than Excel allows. Those cells keep their calculated value instead of a formula, and an **Export notes** sheet lists each one. The download notice counts them and offers **Ask Ari to fix**, which asks Ari to export the Page again and write a working formula into each of those cells.
## What you get
* One worksheet for each table on the Page, in the Scenario you exported. A table's Scenario comparison columns come along, and rows and columns hidden on the table are left out.
* Text blocks, charts, and Canvas blocks are left out, because a workbook holds tables.
* Each number keeps its format: currency, percentage, or a multiple such as `3.5x`, with up to five decimal places. Negative currency amounts and plain numbers show in parentheses.
* A blank input stays blank rather than becoming 0, and an input that holds text, such as a customer name, stays text.
## Other formats
* For Google Sheets, upload the workbook to Google Drive and open it with Google Sheets.
* For a CSV file, ask Ari for a CSV of a table, and it gives you the file in the chat. There is no CSV button.
* For a PDF, see [Get a PDF report or slide deck](/guides/get-a-pdf-or-slides).
## If something goes wrong
A failed export shows **Could not download spreadsheet** with the Page's name and the reason. Every progress label and message is listed in [The cfo.ai interface](/reference/interface#download-spreadsheet). For export time and size limits, see [Limits and file types](/reference/limits#tables-pages-sharing-and-exports).
## Related
* [Share a Page outside your workspace](/guides/share-a-page)
* [Find a Page, number, or file](/guides/find-things)
* [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks)
# Enter, paste, and clear values
Source: https://docs.cfo.ai/guides/enter-values
Change a number yourself in a Table block, such as a team member's salary: type numbers, text, and dates, paste from Excel or Google Sheets, fill cells, and total a selection. Also see whether a number is typed or calculated, and clear a cell's own value or formula so it follows its row again.
Use this guide to type or change the numbers in a table yourself, such as a new hire's salary or next quarter's price. It also shows how to remove a value you typed, so the cell goes back to its row's formula. To learn how a typed value and a row's formula work together, read [How formulas work](/concepts/formulas).
You need a desktop browser and edit access to the Page.
## Change a value, such as a team member's salary
Name the person or line, the new value, and when it starts:
> Set Maria Lopez's salary to \$140,000 a year starting in March.
Ari changes the value in your Model, and the change appears in version history.
Open the table that shows the value, such as the headcount table, and find the cell: Salary in Maria Lopez's row, for example. Workspace search (**⌘ K**) finds a Page, a table, or a Variable by name.
Select the cell and start typing; the first key replaces what the cell showed. To edit the current entry instead, double-click the cell or press **Enter**.
Press **Enter** to save and move down, or **Tab** to save and move right. **⌘ Enter** (**Ctrl Enter**) saves and stays on the cell. **Esc** cancels.
The new value shows at once while cfo.ai recalculates everything that depends on it.
If cfo.ai refuses the change and says the cells come from an integration, the value comes from a connected system, such as your payroll provider. Change it there; it arrives with the next sync. See [Refresh data or change when it refreshes](/guides/refresh-data).
## Type numbers the way you would in a spreadsheet
cfo.ai reads a number written the way people write one: `1,000`, `$1,250.75`, `50%`, and `(500)` each save the number they mean. A currency sign or a percent sign also shows that cell as **Currency** or **Percentage**. Every form cfo.ai reads is listed in [What you can type into a cell](/reference/table-blocks#what-you-can-type-into-a-cell).
Anything else you type in a number cell is read as a formula, such as `Revenue * 1.1`; a leading `=` is optional. See [Write a formula](/guides/write-a-formula).
## Type text, dates, and items
In a Variable that holds text, such as Job Title, what you type is saved as words: `Finance assistant`, `N/A`, and `2026-01-01` all stay as typed. Start with `=` to enter a formula instead.
A cell of a date Variable, such as Start Date, opens a calendar. Pick a day, or select **Edit formula** to write a formula.
A column that shows which item each row belongs to, such as each employee's Department, opens a list of the Dimension's items. Search, then pick one. To use a new item, type its name and choose **Add** with that name. The **Edit formula** button opens the formula editor.
When an item column reads its values from a connected system, the list shows **Source** with the name of the import. Picking another item saves your choice over it, and the list then reads **Overrides source**. **View source** opens that connection.
## See whether a number is typed or calculated
A dashed underline marks a cell that holds its own value or formula, such as a number someone typed. That value replaces the row's formula in this cell only. A cell without the underline takes its number from the row's formula or from a connected system. A picked item or date is the exception: it belongs to the cell but never shows the underline.
To see the formula behind a cell, open it. A formula the cell inherits shows dimmed; point at it to see **Inherited from** and where the formula comes from. See [Override one cell](/guides/write-a-formula#override-one-cell).
## Clear a value or a cell's own formula
Select the cell, or several cells, and press **Delete** or **Backspace**. Each cell loses the value or formula it held on its own, the dashed underline disappears, and the cell follows its row's formula again.
You can also open the cell, delete everything in it, and press **Enter**.
A cell with nothing of its own has nothing to clear. Its number comes from the row's formula or from a connected system, or is 0 when neither covers it; see [A cell that no formula covers](/concepts/formulas#a-cell-that-no-formula-covers). To change a calculated number, change the row's formula; see [Write a formula](/guides/write-a-formula). To make one cell different from its row, type a value into it. Cells that come from a connected system can be changed only in that system.
## Copy, cut, and paste
* **⌘ C** copies the selected cells, **⌘ X** cuts them, and **⌘ V** pastes. On Windows and Linux, use **Ctrl**.
* **⌘ Shift V** pastes values only, without formulas.
* Right-click a cell for **Copy value**, **Copy formula**, and **Paste**.
A cut works the way it does in Excel: the cut cells stay in place, outlined, until you paste, and the paste clears them. Press **Esc**, or copy something else, to cancel the cut. You can paste into another table.
You can paste a range from Excel or Google Sheets. Each pasted cell is read the way a typed entry is, so `$1,000` becomes 1000. Text is saved exactly as copied, and a spreadsheet cell with line breaks stays one cell. A formula copied from a cfo.ai table pastes as that formula.
## Fill neighboring cells
Select one cell and drag the small square at its corner across the cells next to it. Each of those cells gets the same value or formula.
## Add up selected cells
Select a range of cells. A bar at the lower right of the Page shows **Count:**, **Sum:**, and **Avg:** for the numbers in the selection. **⌘ A** selects every cell in the table.
## If a value does not save
When cfo.ai refuses a change, the cell returns to its earlier value and a notice says why. Common reasons are a value that comes from an integration, a selection whose cells take different formulas, and a table you can only read. For a value you typed, the formula editor reopens with your entry so you can fix it.
Every notice and what to do about it is listed in [Table block controls and messages](/reference/table-blocks#when-a-change-is-not-saved). To take back a change, press **⌘ Z**, or see [Undo a change or restore an earlier version](/guides/undo-changes).
## Related
* [How formulas work](/concepts/formulas)
* [Write a formula](/guides/write-a-formula)
* [Build a Table block by hand](/guides/build-a-table)
* [Keyboard shortcuts](/reference/keyboard-shortcuts#table-cells)
* [Table block controls and messages](/reference/table-blocks)
# Filter and sort a table
Source: https://docs.cfo.ai/guides/filter-and-sort-a-table
Show only some of a Dimension's items on a Table block, hide one item, find and clear a filter (unfilter a row or column), filter a list by a column's value, and sort items A to Z, Z to A, or in your own order.
Use a filter to show only some of a Dimension's items on a table, such as two departments out of ten, and a sort to put the items in order. Filters and sorts change only this table, and everyone who opens it sees the same view. They never change a number in your Model.
You need a desktop browser and edit access to the Page.
## Filter a table to show only some items
> On the Payroll table, show only the Sales and Marketing departments.
Right-click the Dimension's label row, such as **DEPARTMENT** above its items. Or right-click one of its items and choose **\[Department] dimension settings**.
Choose **Filter items**. A list of the Dimension's items opens.
Check the items to show. Type in **Search items** to find one in a long list.
Select **Save**.
**Filter items** is in the menu for Dimensions in the rows. For a Dimension in the columns, use the table setup panel: select **Show table setup** at the left of the table header, point at the Dimension under **Columns**, and select its funnel button. The same list opens.
## Hide one item
Right-click the item, such as **Contractors**, and choose **Filter out this item**. The table must keep at least one item, so you cannot filter out the last one.
## Clear a filter so every item shows again
A filter leaves no mark on the table itself. To check whether a Dimension is filtered, open **Show table setup**: a funnel with a number beside a Dimension shows how many items the filter keeps. In the right-click menu, **Filter items** shows the same number.
To clear the filter:
Right-click the Dimension's label row and choose **Filter items**, or select its funnel in the table setup panel.
Select **All Items** at the top of the list.
Select **Save**. Every item shows again, including any you filtered out with **Filter out this item**.
## Filter a list by a column's value
You can filter a table that lists records, such as one row per employee, by a column's value. This works for a column that shows which item each row belongs to, such as Status or Department.
1. Right-click the column header, such as **Status**, and choose **Filter**.
2. Check the values to keep under **Show rows where Status is**, and select **Save**.
A funnel then appears in the column header. Point at it to see which values the filter keeps. To change or clear it, select the funnel; choose **Any value** and **Save** to show every row again.
## Sort items A to Z, Z to A, or in your own order
> On the Payroll table, list the departments in this order: Sales, Marketing, Engineering, then the rest.
Right-click the Dimension's label row, or choose **\[Department] dimension settings** from one of its items.
Choose **Sort**, then:
* **Ascending** for A to Z, or smallest to largest for number items.
* **Descending** for Z to A.
* **Manual** for your own order. Drag the items in the list that opens; each move saves.
* **None** to return to the standard order, A to Z.
You can also drag item rows up or down by the grip at their left edge. The Dimension then switches to **Manual**. The table setup panel has the same choices behind each Dimension's sort button.
A sort applies to the whole level, so every group on the table that shows that Dimension lists its items in the same order.
## Sort rows by their numbers
A table cannot sort by the values in a column, such as the largest Revenue first. **Sort** orders a Dimension's items by their names. To show the largest first, set a **Manual** order, or ask Ari to put the items in order of their values.
Variable rows have no sort; drag them into the order you want. Date columns always run in date order; to show fewer periods, change the table's date range.
## If Filter items or Sort is missing
The menu leaves out actions that cannot apply. Check which case you are in:
Ask a page owner for edit access.
Dates cannot be filtered or sorted. Change the date range instead; see [Build a Table block by hand](/guides/build-a-table#set-the-dates-and-the-time-grain).
Variables cannot be filtered or sorted. Hide or drag them instead; see [Format and arrange a table](/guides/format-a-table).
That is a mapping. Use **Filter** on its column header, as described above, or change it with **Use as**.
A breakdown added with **Segment by** on a single item is not a level of the whole table, so it has no filter or sort.
The table hides its label rows. Right-click an item and use **\[Department] dimension settings**, or turn on **Show dimension headers** in the table's **…** menu, **Table block settings**.
## Related
* [Break a number down](/guides/break-down-a-number)
* [Format and arrange a table](/guides/format-a-table)
* [Table block controls and messages](/reference/table-blocks#dimension-menu)
# Find a Page, number, or file
Source: https://docs.cfo.ai/guides/find-things
Find a Page (sheet or tab), a table, a Variable, a chat, or a file Ari made: workspace search with ⌘ K, the sidebar, and asking Ari. Also what to check when a Page, a chat, or your Model seems to be gone.
Use workspace search, the sidebar, or Ari to find a Page, a number, a chat, or a file Ari made for you, such as the P\&L Ari built last week or the workbook it exported. If something you expect is missing, see [If a Page or your Model seems to be gone](#if-a-page-or-your-model-seems-to-be-gone).
## Search your workspace
Press **⌘ K** (**Ctrl K** on Windows and Linux), or select **Search** at the top of the sidebar. Before you type, the window lists the Pages you opened recently in this browser.
Type part of what you remember:
* A Page title, or a word from a Page's text
* A table or chart title
* A Variable, such as Revenue
* A Dimension, or one of its items, such as a customer's name
* A chat's title
* The name of another workspace
`rev` finds Revenue, and a small typo is forgiven in longer words.
Use **↑** and **↓** to move and **Enter** to open, or **⌘ Enter** to open it in a new tab. A Variable opens the table that shows it, with its row marked. A block or a passage of text scrolls into view.
Search looks in the Scenario the sidebar shows and lists only what you can open. It finds a Variable or Dimension only when a table on a Page shows it; to list every one, [ask Ari](#ask-ari-where-something-is). Search is not available while you view a past version. For every result group and matching rule, see [Workspace search](/reference/interface#workspace-search).
## Browse the sidebar
The sidebar's **Model** section lists the Pages of the Scenario chosen in its Scenario picker. To see the subpages inside a Page, point at the Page and select the arrow that replaces its icon. The **Chats** section lists your chats, newest activity first.
On a Page, select the Page's name in the Page header to open the same list of Pages with a **Search pages** field.
## Ask Ari where something is
Ari can search your Model and reply with links you can open:
> Where is our P\&L?
> Which Page shows headcount by department?
> Where is the Gross margin Variable used?
> List every Variable and Dimension in the Model.
Ari answers with links to the Pages it finds, and can tell you which tables and Pages use a Variable. Its list of Variables and Dimensions includes the ones no table shows, which workspace search cannot find. Ari can also find a file from your other chats when you name it or describe it, such as "the board report PDF you made last Tuesday."
## Find a file Ari made
A file Ari makes for you, such as an Excel workbook, a PDF, or an image, is in the chat where Ari made it: a download card under the answer that ends that turn.
1. Open the chat. Search for its title with **⌘ K**, or look in the **Chats** section.
2. Scroll to Ari's answer and select the file's card to download it.
If you do not remember the chat, ask Ari in any chat to find the file by name or by what it holds.
If Ari mentions a file but no card appears under its answer, the file was not handed over. Ask Ari to send you the file in the chat. A download link Ari sent you, such as one in a scheduled run's email, stops working after a while, but the card in the chat keeps working. See [Download a file Ari made](/reference/chat#download-a-file-ari-made) for how long each kind of link lasts.
## If a Page or your Model seems to be gone
Check these in order:
1. Check the Scenario. The sidebar lists the Pages of the Scenario in its Scenario picker, and a Page built in a Scenario does not appear in Main until that Scenario is merged. Open the picker in the **Model** section and choose the Scenario where the Page was built, or **Main Scenario**.
2. Check the workspace. If you belong to more than one workspace, you may be in another one. Open the account menu at the bottom of the sidebar and choose the workspace under **Workspaces**.
3. Check the sidebar itself. If the **Model** section is closed, select **Model** to open it. If the sidebar is folded to a narrow strip, press **⌘ \\** (**Ctrl \\**) or select **Expand navigation**. On a phone, select **Open navigation**.
4. Check whether you are viewing a past version. If a banner says so, select **Back to current**.
5. Check for a deletion. Deleting a Page is recorded in version history. Open any Page, select **Version history** in its header, filter to **Pages and blocks**, and undo the deletion to bring the Page back. See [Undo a change or restore an earlier version](/guides/undo-changes).
In a new workspace, the **Model** section appears once the workspace has its first Page.
If a chat is missing, it may be archived: the chat list shows active chats by default, so open the list's filter and choose **Archived** or **All**. Your chat list never shows a teammate's chats, because each chat belongs to the person who started it. Ask them to share the Page the chat built; see [Sharing and access](/concepts/sharing-and-access).
## Related
* [The cfo.ai interface](/reference/interface)
* [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks)
* [Ask Ari about a number](/guides/ask-about-a-number)
* [Keyboard shortcuts](/reference/keyboard-shortcuts)
# Fix a connection that needs attention
Source: https://docs.cfo.ai/guides/fix-a-connection
What to do when a connection failed, is stuck syncing, or shows a red triangle or an orange dot in the sidebar. Finish setup, reconnect after cfo.ai lost access, retry a failed sync, grant missing access, wait out a slow first sync, and handle partial or empty syncs.
Use this guide when a connection in the sidebar shows a red triangle, an orange dot, or a gray dot, or when its data stopped updating. Point at the connection: the card that opens names the problem and offers the one action that fixes it. [Connection statuses and actions](/reference/connection-statuses) lists every status and message.
## Find out what is wrong
* Look at the mark at the end of the connection's row. A red triangle means you must act. A steady orange dot needs nothing from you: the sync resumes by itself, only cfo.ai can fix it, or someone else must act. A gray dot means setup was not finished, someone stopped the latest sync, or the source was added but never connected. See [Sidebar marks](/reference/connection-statuses#sidebar-marks).
* Point at the connection's row in the sidebar to read its status and the line under it.
* Select the row to open the connection's page, which shows every action it offers.
* Ask Ari: "What's wrong with my Xero connection?" Ari reads the same status, including the provider's own report when it gave one.
## Setup not finished: finish or cancel it
The connection reads **Setup not finished** when someone started connecting and did not finish, for example because the sign-in window was closed. Select **Finish setup** to continue the same connection, or **Cancel setup** to remove it. Canceling asks nothing, because the connection holds no data, and it frees the plan slot the connection was holding.
Connecting the same source again, from its card, from a link, or by asking Ari, also continues the unfinished connection instead of adding a second one.
Stripe (Direct) cannot resume a setup. Its card reads **Setup wasn’t finished. Cancel it and connect Stripe (Direct) again.** Select **Cancel setup**, then connect again.
## cfo.ai lost access: reconnect
The connection reads **cfo.ai lost access** with **Reconnect to start syncing again.** when the source revoked access, for example after a password change or an expired authorization.
Select **Reconnect** on the card or the connection's page.
Sign in to the company or organisation the connection already holds. A sign-in to a different company is refused, and so is one to a company another connection in the workspace already holds.
If the provider's screen says you are already connected and offers **Finish**, choose **Relink integration** instead. **Finish** keeps the old authorization, and the card then reads **\[Source] was not reconnected**. Select **Try again**.
For Xero, if the consent screen lists your organisation as already connected, disconnect cfo.ai's app under **Connected apps** in Xero, then try again. The card names the app as Xero lists it.
Asking Ari to connect a source never reconnects an existing connection. Ask Ari to reconnect the connection by name, or use **Reconnect**.
## A sync failed: retry it
The connection reads **Sync failed**. Read the line under it:
* **\[Source]’s sync hit an error. cfo.ai will try again on the next scheduled sync.** The provider's own run failed, often for a moment. Do nothing, or select **Retry** to run it now. When the line includes the provider's report, such as a user that cannot access a table, fix that at the source first.
* Any other reason, or **The last sync couldn’t finish.** Select **Retry**. If it fails again, select **Investigate with Ari**, which opens a chat where Ari looks for the cause.
**Retry** runs the import again with the same sign-in and does not use another plan slot.
When setup itself failed, the connection reads **Connection failed** with **Retry to set it up again.** Select **Retry**.
## NetSuite is missing access
The connection reads **Missing access in NetSuite SuiteAnalytics** when NetSuite hides tables the financial statements need. The connection's page lists the missing tables.
Have your NetSuite admin create an access token for the **Data Warehouse Integrator** role. Entering a different role ID with the old token does not work, because a NetSuite token belongs to one role.
Select **Reconnect** and enter the new token. The next sync checks the tables again and clears the warning.
If a missing table belongs to a NetSuite feature you do not use, **Multiple Currencies**, **Multiple Calendars**, or **Multi-Book Accounting**, no role change can share it. Uncheck that feature's tables in the table picker when you first connect. For a connection that linked before the picker existed, email [success@cfo.ai](mailto:success@cfo.ai).
## The first sync is taking hours
HR and payroll systems, and sources that sync through Fivetran, run their own first sync of your account before cfo.ai can load anything. While cfo.ai waits, the connection reads **Syncing**, and the line says how long it has waited, what the provider last reported, and when the wait ends. Xero can also wait for its request allowance to reset. You do not need to do anything.
If the provider still has not finished 20 hours after cfo.ai began waiting, the connection reads **First sync not ready**. The next nightly sync starts a new wait, or select **Retry** to start one now.
A payments system with a long history, such as Stripe, can spend a day or more in its first sync. Ask Ari whether the load is still running before you treat it as a failure.
## Partly synced
The connection reads **Partly synced** when the last sync finished without some tables, such as **Stripe’s last sync couldn’t refresh balance transactions. Everything else is up to date, and cfo.ai will try again on the next sync.** The data that arrived is usable, and the next sync retries the missing tables. You do not need to do anything.
## Paused until next sync
A connection reads **Paused until next sync** when the source's daily request limit ran out. Progress is saved, and the next scheduled sync picks it up. You do not need to do anything; signing in again would not change the limit.
## Sync stopped
A connection reads **Sync stopped**, with **Contact cfo.ai support if it isn’t fixed soon.**, when cfo.ai's data warehouse refused the data. Only cfo.ai can fix it. If it lasts, email [success@cfo.ai](mailto:success@cfo.ai).
## No queries yet
The connection reads **No queries yet** when its data synced but no saved query brings it into your Model. Select **Set up queries with Ari**. A new chat opens with a request for Ari to review what synced, propose the queries worth saving, and set them up; read it, then send it. If you cannot edit the connection, the line reads **Data synced. Someone with access can set up queries with Ari.**
## No data found
The connection reads **No data found** when the sync finished but found no rows. Select **Investigate with Ari**, and Ari checks what synced and whether a query filters everything out.
For QuickBooks Online, a company with no posted transactions since January 1 of last year loads empty reports. Ask Ari for a full-history import from an earlier year; see [Refresh data](/guides/refresh-data#re-import-past-months).
## An uploaded file did not load
The file's row reads **The file couldn’t load**, followed by what was wrong. Fix the file, open its page, and select **Update file**. See [Upload a CSV as a data source](/guides/upload-a-csv#if-the-file-does-not-load).
## When cfo.ai repairs a source for you
When the nightly sync of a source you connected from the connection manager fails right after a successful one, Ari starts a background repair, acting with your permissions. It checks the connection and fixes what it can, such as a saved query that a change in the source's layout broke. If the repair worked, you hear nothing. If it needs your decision, could not finish, or changed something you should know, a chat titled **\[Connection name] stopped updating** appears in your chat list. Answer Ari there.
## If no action appears
No action appears while a sync is running, when only cfo.ai support can fix the failure, when cfo.ai has turned the source off, or when you cannot manage connections. A broken connection keeps its plan slot until you remove it. To start over, open the connection's page and select **Remove connection**; the data it already loaded stays in your Model.
## Related
* [Connection statuses and actions](/reference/connection-statuses)
* [Refresh data or change when it refreshes](/guides/refresh-data)
* [How connected data reaches your Model](/concepts/connected-data#when-a-source-stops-updating)
# Fix a formula that shows an error or a wrong number
Source: https://docs.cfo.ai/guides/fix-formula-errors
Find and fix the cause when a formula cell shows an error code such as #CIRC!, #REF!, or #DIV/0!, reads 0 where you expect a value, totals too high, or works in one place but not another.
Use this guide when a formula cell shows an error code or a number you do not expect. Find the problem you see, then check its causes in order. Every code and message is listed in [Formula errors and warnings](/reference/formula-errors).
The fastest first step is often to ask Ari. Right-click the cell and choose **Fix**: a new chat opens about that cell, and Ari traces the formula and its inputs. To find the cause yourself, read on.
## A cell shows an error code
Point at the cell. The message says what went wrong. When the error started in another cell, the lines that begin with **originating in** and **via** name the Variable where it started and each step in between. Fix the first one: the cells that read it recover on their own.
To keep a calculation going while you fix its input, wrap the risky part in `iferror`: ``iferror(`Gross Profit` / Revenue, 0)``.
## The cell shows #CIRC!, a circular reference
`#CIRC!` means the formula reads its own cell, directly or through other formulas, in the same period. cfo.ai cannot calculate a value that depends on itself. Check these causes:
* A running balance reads itself. `` `Cash Balance` + `Net Cash Flow` `` asks for this month's balance to calculate this month's balance. Read the previous month instead: `` `Cash Balance`[-1] + `Net Cash Flow` ``.
* Two Variables read each other in the same month. For example, Interest reads Cash Balance, and Cash Balance includes Interest. Break the loop by reading one of them from the previous month: `` `Cash Balance`[-1] * `Interest Rate` ``.
* A total formula also runs on the rows beneath it. If you give Revenue's whole row the formula `sum(Revenue[Department in any])`, the formula also runs on each department's row, where it reads that department's own cell. Delete the row's formula: the rows above a breakdown add up the rows beneath them unless they have a formula of their own; see [Totals, aggregation, and time rollups](/concepts/totals-and-rollups). Or give the department rows their own formula: right-click the Revenue row and choose **Edit formula for Department**.
When Ari saves a formula that reads its own Variable, its receipt warns which cells can show `#CIRC!`. To see how cfo.ai decides which formula runs on which row, read [How formulas work](/concepts/formulas).
## The cell shows #REF!
The formula names a Variable or Dimension that does not exist. Someone may have deleted it, or the name has a typo.
1. Open the formula. A name that matches nothing is underlined in red with **Unknown variable or dimension name**.
2. Check the spelling and capital letters. Names match exactly, and a name with spaces needs backticks.
3. To swap in the right Variable, select the reference and select its name to search; see [Point a reference at something else](/guides/write-a-formula#point-a-reference-at-something-else).
A renamed Variable does not cause `#REF!`: formulas follow the Variable, not its old name.
## The cell shows #NAME?
The formula calls a function cfo.ai does not have, often a spreadsheet function such as `VLOOKUP` or `SUMIF`. Use the cfo.ai form listed in [Excel functions that are not available](/reference/functions#excel-functions-that-are-not-available).
## The cell shows #VALUE!
Point at the cell to read which of these it is:
* A function has the wrong number of arguments, such as **IF requires exactly 3 arguments**. `if` always needs a third argument: `if(Revenue > 0, Revenue, 0)`.
* A value has the wrong type, such as a date function given a number. Check that each argument is the kind of value the function expects; see [Formula functions](/reference/functions).
* The formula has a syntax mistake. Open it and read the red underline; see [Formula editor messages](/reference/formula-errors#formula-editor-messages).
## The cell shows #DIV/0!
The formula divides by zero or by an empty value, often a month before any revenue. Decide what the result should be when the divisor is zero, and say so in the formula:
```text theme={null}
if(Revenue <> 0, `Gross Profit` / Revenue, 0)
```
## A number reads 0 where you expect a value
cfo.ai shows 0 for a number cell that no formula or value covers. A formula reads that 0 when it looks for a value where none is saved. Check these causes:
* The input has no months. A salary typed once per employee has no months, so a monthly formula that names it plainly reads 0. Read it through a fixed reference: `` `Annual Salary`$[Employee = this.Employee] / 12 ``. See [Read a per-item input that has no months](/guides/write-a-formula#read-a-per-item-input-that-has-no-months).
* The input is imported and the month is in Forecast. An imported Variable has values only where its source has rows, usually through **Last close**, so a formula that reads it in a Forecast month reads 0. Give the imported Variable a Forecast formula; see [Use different formulas for Actuals and Forecast](/guides/actuals-and-forecast-formulas).
* A filter names an item that is spelled differently. `Step = "Committed"` reaches nothing when the item is `Committed out`. Copy the item's exact spelling, or pick it from the filter's list.
* The first Forecast month reads an empty last Actuals month. A formula with `[-1]` starts from the last closed month. Give that month a value, such as an opening balance.
* A quarter or year shows 0 but its months have values. The Variable has no time rollup, so its months are not added into the longer period. Ask Ari to give the Variable a time rollup; see [Why a quarterly view shows zeros](/concepts/totals-and-rollups#why-a-quarterly-view-shows-zeros).
## A cell does not follow its row's formula
A cell, or a row under a breakdown, has a formula or value of its own, called an override; see [Override one cell](/guides/write-a-formula#override-one-cell). A more specific formula, such as one for a single department, can also replace the row's formula there.
* To make the cell follow its row again, select it and press **Delete** or **Backspace**; see [Clear a value or a cell's own formula](/guides/enter-values#clear-a-value-or-a-cells-own-formula).
* To see which formula fills the cell and where it comes from, open the cell: an inherited formula shows dimmed, with **Inherited from** when you point at it. Or ask Ari, "Which formula applies to this cell, and why?"
* To see who added the override and when, open version history; see [Undo a change or restore an earlier version](/guides/undo-changes).
## A formula you saved does not change the numbers
The formula saved, but the cells you are looking at still show another value. Work through these checks in order:
1. Check the formula range. A formula saved for Actuals does not fill Forecast months, and the reverse. Open the row's formula and check both sections.
2. Check the grain. A formula for one range must cover the Model's base periods, usually months. In a monthly Model, a formula written on a table by year is saved at year grain and fills only the year cells; the month cells do not read it.
3. Check for a more specific formula. A formula for one item or one cell replaces the row's formula there; see [A cell does not follow its row's formula](#a-cell-does-not-follow-its-rows-formula).
4. Check the Scenario. A formula saves in the Scenario shown in the sidebar's Scenario picker. Check that you are viewing the Scenario you changed.
## A total is higher than expected
A lookup that reads several items, such as `Revenue[Department in any]`, a set, or a `where` test, may be adding up more values than you meant.
* Wrap the lookup in the function that matches the question: `average`, `count`, `max`, `last`, or another; see [Aggregation functions](/reference/functions#aggregation-functions).
* A filter on one Dimension keeps the cell's other Dimensions. A filter that names the Dimension a table breaks down by, such as `sum(Amount[Account in {"A", "B"}])` on rows by Account, repeats the same total on every account row.
## The formula works for one item but not another
* A more specific formula wins for that item. Open a cell of the item that looks wrong and check where its formula comes from.
* The input has no value for that item. A formula that reads Commission Rate for each department reads 0 for a department with no rate.
* The reference follows the cell when it should not, or the reverse. A fixed reference (`$`) reads the same value for every item. A plain reference reads each item's own value. See [Fix a reference so it reads one value everywhere](/guides/write-a-formula#fix-a-reference-so-it-reads-one-value-everywhere).
## The formula works in Actuals but not in Forecast
Actuals and Forecast can have different formulas, so the Forecast months may run a formula you did not write, or none. Open the row's formula and check the **Forecast** section, and check where **Last close** sits. See [Use different formulas for Actuals and Forecast](/guides/actuals-and-forecast-formulas).
## A date filter matches nothing
* The grain and the date text do not match. A monthly filter needs `Date.Month` and a month, such as `Revenue[Date.Month = "2026-01"]`. See [Dates](/reference/formula-syntax#dates) for the date text cfo.ai reads.
* The Dimension holds timestamps. An imported Dimension can hold items such as `2026-07-01 00:00:00+00`. In a formula's condition, `date(2026, 7, 1)` never equals such an item. Use date text such as `"2026-07-01"`; see [Dates against imported timestamps](/reference/formula-syntax#dates-against-imported-timestamps).
## A date or text cell reads empty
A date or text Variable that nobody filled in reads empty, not 0. A comparison with an empty value is empty too, so `if` takes its third argument: `` `End Date` < date(2030, 1, 1) `` is not true for someone with no end date. Test for the empty value directly:
```text theme={null}
if(`End Date` = null, 1, 0)
```
## A percentage adds up, or its total row is blank
cfo.ai does not add up a Variable formatted as a percentage above a breakdown. The row above shows the lines' formula calculated on its own inputs, such as total profit over total revenue. It reads blank when no one formula fits the whole, for example when a line holds a typed-in rate.
* If the row above is blank, give that row its own formula, or remove the typed-in rate.
* If a ratio adds up its lines, write the ratio as the row's own formula, such as ``iferror(`Gross Profit` / Revenue, null)``. It then calculates on its own inputs on every row. A quarter or year column beside the months combines them by its own setting, which the concept page linked below explains.
See [Percentages and ratios are not added up](/concepts/totals-and-rollups#percentages-and-ratios-are-not-added-up).
## The table shows a notice instead of numbers
A notice such as **Calculation capacity was full when this calculation ran.** means the calculation service had a problem, not your formulas. Select **Retry**, or wait. A notice that names a setup problem needs a change to the table. Every notice is listed in [Table calculation notices](/reference/formula-errors#table-calculation-notices).
## Ari refused or warned about a formula
Ari's formula saves are checked before they save, and some save with a warning. Read what each one means in [Ari's save refusals](/reference/formula-errors#aris-save-refusals) and [Ari's save warnings](/reference/formula-errors#aris-save-warnings), and see [Review Ari's formula changes](/guides/review-ari-formula-changes).
## Related
* [Formula errors and warnings](/reference/formula-errors)
* [Write a formula](/guides/write-a-formula)
* [How formulas work](/concepts/formulas)
# Forecast cash and cash runway
Source: https://docs.cfo.ai/guides/forecast-cash
Have Ari forecast your monthly cash balance and cash runway, from the cash on your balance sheet plus forecast inflows and outflows, add a 13-week weekly view, and test a raise or a spending cut in a Scenario.
Ask Ari for a monthly cash forecast to see how long your cash lasts and what would change that. The forecast names the month cash falls below your minimum or runs out, and you can test the decisions that move that month. People also call this a burn forecast or a cash runway model.
Before you start, connect your accounting system so the forecast starts from the cash on your balance sheet; see [Connect QuickBooks, Xero, NetSuite, or Rillet](/guides/connect-accounting). A revenue and expense forecast gives Ari the money coming in and going out; see [Build a revenue and expense forecast](/guides/build-a-forecast). Without either, tell Ari your cash balance and your main costs, and it labels them as assumptions.
Say how far ahead to look and what cash level matters to you:
> Forecast monthly cash for the next 18 months and show when cash falls below six months of operating expenses.
Ari starts from your cash at Last close, then adds the money coming in and subtracts the money going out, month by month. It names the first month the balance falls below your minimum, or below zero if you ask when cash runs out. If cash never falls that low within the forecast, Ari says it lasts through the last forecast month.
> Where does the starting cash balance come from, and which inflows and outflows move the forecast the most?
The starting balance comes from the cash accounts on your balance sheet at Last close. When Ari has no balance to read, it uses a starting balance you can edit, and it tells you the cash runway depends on that number.
Cash moves when money is paid, which can be a different month from when it is earned. Ask Ari to use your real payment terms, such as customers paying 30 days after the invoice or annual contracts paid up front. A balance that goes negative stays visible in the forecast, and money you have actually raised stays separate from a raise you are only considering.
> Build a 13-week cash forecast by week, starting next week.
Ari builds a weekly table and writes formulas for weeks, because formulas written for months do not fill week columns. Your accounting statements arrive one month at a time. Weekly actuals need a source with a date on every transaction, such as a bank export; see [Upload a CSV as a data source](/guides/upload-a-csv).
Try each decision in its own Scenario, so Main stays as it is:
> Create a Scenario called Bridge round: raise \$2 million in March, and compare cash runway with Main.
> Create a Scenario where we cut marketing spend by 30% starting next month, and show the month cash runs out.
See [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario) and [Compare Scenarios side by side](/guides/compare-scenarios).
Your cash actuals move forward when Last close moves past a month; see [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast). To get a cash summary on a schedule, see [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger).
## Related
* [Build a revenue and expense forecast](/guides/build-a-forecast)
* [Build a headcount plan](/guides/build-a-headcount-plan)
* [Get your P\&L, balance sheet, and cash flow statement](/guides/build-financial-statements)
* [Scenarios, Snapshots, and version history](/concepts/scenarios)
# Format and arrange a table
Source: https://docs.cfo.ai/guides/format-a-table
Show numbers as currency or percentages, set decimals, color and bold rows, and see why numbers turn green or red. Also color a Dimension's items, change date headers, show or pin total columns, hide or reorder rows and columns, resize or pin (freeze) columns, and show values in thousands with Ari.
Use this guide to make a table easy to read: money as currency, margins as percentages, section rows in bold, and the columns your readers need first. Most formatting changes only how the table looks; it never changes a number. To learn which settings belong to a table and which to a Variable, read [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks).
You need a desktop browser and edit access to the Page. Formatting lives in the right-click menu: right-click a row name, a column header, or a cell, and choose **Format and display**. On a Mac, **Control**-click works too.
## Show numbers as currency, percentages, or decimals
> On the P\&L table, show Revenue and Gross Profit as currency with no decimals, and Gross Margin as a percentage with one decimal.
Right-click the row name, such as Revenue, and choose **Format and display**.
Choose **Format**, then **Auto**, **Number**, **Currency**, **Percentage**, **Integer**, or **Multiple**. After **Currency**, choose the currency under **Currency**, such as **USD (\$)** or **EUR (€)**.
Choose **Precision**, then **Default**, **1**, **1.0**, **1.00**, or more decimal places, up to five.
**Format** and **Precision** apply to this table only, at the place you right-clicked: the row, the column, or the cell. A cell's own setting wins over its column's, and a column's wins over its row's. To remove the setting, choose **Format**, then **Reset**.
Two choices reach further. The currency you pick applies to the Variable on every table. **Date** in the **Format** list makes the Variable a date Variable everywhere, with a calendar in its cells.
A format changes only how numbers look. A row shown as a percentage on one table still adds up in totals; see [Percentages and ratios are not added up](/concepts/totals-and-rollups#percentages-and-ratios-are-not-added-up).
## Show numbers in thousands or millions
cfo.ai has no format that shows numbers in thousands or millions. Ask Ari for a Variable that divides by 1,000, such as Revenue (thousands).
## Format several rows at once
Select several row names: drag down the row-name column, or press **⌘ A** to select the whole table. Right-click one of the selected row names. The menu entry reads **Format \[N] variables** and applies to every selected row.
## Color, bold, and indent rows
In **Format and display**, choose **Background** to fill the row, column, or cell with a color, or **No fill** to remove it. Choose **Text style** for **Bold**, **Italic**, **Underline**, or **Strikethrough**. On a row, **Indent** and **Outdent** move the row name in or out, as in an income statement. **Reset formatting** removes everything set at that place on this table.
These settings belong to this table, so the same Variable can look different on another table.
### Color the cells you type into
cfo.ai does not color typed-in cells automatically. It marks a cell that holds its own value with a dashed underline; see [See whether a number is typed or calculated](/guides/enter-values#see-whether-a-number-is-typed-or-calculated). To color your inputs, select those cells or rows and set a **Background**.
## Why a number is green or red
Numbers turn green or red only in comparison columns: **Variance** and **Variance %**. Green means the change is good and red means it is bad, and an increase shows a **+**. Plain values are never colored by their sign.
Which direction counts as good is a setting on the Variable, so it applies on every table:
1. Right-click the Variable's row name and choose **Format and display**.
2. Choose **Variance colors**, then **Higher is better** or **Lower is better**.
New Variables use **Higher is better**. Set **Lower is better** for costs, so spending less than the plan shows green. To add comparison columns, see [Compare Scenarios side by side](/guides/compare-scenarios). For the colors in the formula editor and on items, see [What colors and marks mean](/reference/table-blocks#what-colors-and-marks-mean).
## Color a Dimension's items, or show them as plain text
Right-click the Dimension's label row, such as **DEPARTMENT** above its items, or right-click one of its items and choose **\[Department] dimension settings**. Then choose **Format and display**.
Choose **Dimension color** to set the color of the Dimension's item pills on every table. Choose **Display as** to show the items as a **Pill** or as **Plain text**; that choice applies to this table, and only in your browser.
## Change how dates read in the headers
Right-click a date header, choose **Format and display**, then **Date format**, and pick a style such as **Abbreviated** (`Sep-26`), **Full month** (`September 2026`), or **Compressed year** (`Sep ’26`, or `W35 ’26` for a week). The style applies to this table. Every style, at every grain, is listed in [Date formats](/reference/table-blocks#date-formats).
## Show, hide, or pin total columns
A total column, also called a summary column, sits at the end of each group of columns and summarizes the cells shown in that group, such as each region's months. It is different from a parent row that adds up the rows beneath it; see [Totals, aggregation, and time rollups](/concepts/totals-and-rollups).
* To show or hide them, open the table's **…** menu, **Table block settings**, and choose **Show total columns**.
* To keep them beside the row names while you scroll, choose **Pin total columns** in the same menu.
* To choose what a total column shows, right-click its header and choose **None**, **Sum**, **Average**, **Minimum**, **Maximum**, **Count**, or **Last**. Only summaries that fit the Variables are offered. The header names the choice, such as **SUM**, **LAST**, or **TOTAL** for **None**.
* To show only a group's total, select the arrow in the group's header.
The summary choice is saved with the table. **Show total columns** and **Pin total columns** are not saved: a table always opens with total columns shown and unpinned.
## Hide rows and columns
Select **Show table setup** at the left of the table header. In the panel, point at the row or column and select its eye button (**Hide \[name]**). Select it again (**Show \[name]**) to bring it back. Hidden rows and columns keep their settings, and the table hides them for everyone.
Hiding a level inside a group of columns keeps the group's total column, so you can hide the months and keep each region's total.
To show or hide the label rows above each Dimension's items, such as **DEPARTMENT**, open **Table block settings** and choose **Show dimension headers**.
## Resize and reorder rows and columns
* To resize a column, drag the right edge of its header. Double-click the edge to fit the column to its content, or choose **Auto-fit all columns** in **Table block settings** to fit every column. Widths are saved for everyone.
* To reorder rows, point at the left edge of a row and drag its grip. Dragging the items of a Dimension puts the Dimension in a manual order; see [Filter and sort a table](/guides/filter-and-sort-a-table).
* To reorder columns, drag a column header. Date columns stay in date order.
## Pin (freeze) a column while you scroll
The row names always stay in view while you scroll. To keep another column in view too, such as Department on an employee table, right-click its header and choose **Pin column**. You can also drag the header into the pinned area beside the row names. **Unpin column** releases it. Date columns cannot be pinned. A pin is saved with the table for everyone.
## Flip rows and columns
A table has no transpose button. To flip a table, such as putting the months down the side and the Variables across the top, ask Ari:
> Flip the Cash table so the months run down the rows and the Variables across the columns.
Ari moves the Variables and the dates between the rows and the columns. Moving them changes only the layout; every cell keeps its value. By hand, you can drag an item between **Rows** and **Columns** in the table setup panel. A table must keep all of its Variables on one side, so a full flip is easiest with Ari.
## Change the table's height
Drag the table's bottom edge. Double-click the edge, or select it and press **Enter**, to fit the table to its content again. The height is saved in your browser only.
## Related
* [Table block controls and messages](/reference/table-blocks#format-and-display-options)
* [Totals, aggregation, and time rollups](/concepts/totals-and-rollups)
* [Compare Scenarios side by side](/guides/compare-scenarios)
* [Filter and sort a table](/guides/filter-and-sort-a-table)
* [Export a Page to Excel](/guides/download-a-spreadsheet)
# Get a PDF report or slide deck
Source: https://docs.cfo.ai/guides/get-a-pdf-or-slides
cfo.ai has no PDF export button: ask Ari for a PDF report, a board deck, or a Word document built from your Model and styled with your workspace brand. Also print one Canvas block, and what to do when printing a Page from the browser looks wrong.
To get a PDF report or a slide deck, such as a board deck, ask Ari. Ari makes the file from your Model's numbers and gives it to you as a download in the chat. cfo.ai has no button that saves a Page as a PDF, and printing a Page from your browser does not give a clean report. Asking Ari is the way to get a finished document.
## Ask Ari for a PDF report
Say what the report is for and what it should hold:
> Make a PDF board report for September: a one-paragraph summary, the P\&L against plan, cash and cash runway, and headcount by department.
> Turn the Cash forecast Page into a two-page PDF I can send to our lender.
Ari reads the numbers from your Model and lays out the document in a private work area of its own. It gives you the finished file as a download card under its answer. When your workspace has a brand, the document uses the brand's colors, heading typeface, and logo; see [Brand your workspace](/guides/brand-your-workspace).
To change the report, say what to change, such as "Add the variance column" or "Make the summary shorter." Ari makes a new version of the file in the same chat.
Ari can also make a Word document (`.docx`) when you want to edit the text yourself.
## Ask Ari for a slide deck
Ask for the deck as a PDF, with one slide per page:
> Make a 6-slide board deck as a PDF: revenue, gross margin, burn, cash runway, hiring, and the largest changes since last quarter.
Ari has file tools for PDF, Word, Excel, CSV, and image files, and none for PowerPoint, so a PDF is the deck format to ask for.
## Print one Canvas block
A Canvas block can carry its own print button that prints that block alone, not the whole Page. Ask Ari to add one:
> Add a Print button to this chart.
The button opens your browser's print dialog, where you can print the block or save it as a PDF.
## When printing a Page from your browser looks wrong
cfo.ai has no print layout for a Page. Your browser prints the app as it is drawn on screen. A table draws only the rows and columns in view, so a long or wide table is cut off. For a clean copy, use one of these instead:
* Ask Ari for a PDF report, as above.
* Export the Page to Excel and print from Excel. See [Export a Page to Excel](/guides/download-a-spreadsheet).
* Send a read-only link, so readers see live numbers. See [Share a Page outside your workspace](/guides/share-a-page).
## Find the file later
The file stays in the chat where Ari made it. If you cannot find it, or a link Ari sent you no longer works, see [Find a file Ari made](/guides/find-things#find-a-file-ari-made).
## Related
* [Build a dashboard or board report](/guides/build-a-dashboard)
* [Download a file Ari made](/reference/chat#download-a-file-ari-made)
* [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger)
# Get better answers from Ari
Source: https://docs.cfo.ai/guides/get-better-answers
How to write prompts that get Ari to the right answer the first time. Write a good first message, name the decision, the period, and the detail you need, fence off what Ari may change, and correct Ari when it guesses.
Use these habits when Ari's answers miss what you meant, or you want to get it right the first time. Talk to Ari the way you would brief a finance coworker: say what you are deciding, point at what you mean, and say whether you want an answer or a change. Ari works out the details and asks when it needs something.
## Write a good first message
In a new workspace, you do not need a long brief that spells out every table and formula. A good first message names the decision, gives a few facts, and says where your numbers are:
> We sell \[an annual software subscription] to \[small accounting firms] for about \[\$6,000 a year]. We have \[9] employees and about \[\$1.2 million] in the bank. Our books are in \[QuickBooks]. Build a 12-month plan so I can see how long our cash lasts and whether we can hire two engineers in Q1.
Ari labels every number it estimates as an assumption you can change later, and asks about anything that changes the answer. If you already have a model in a spreadsheet, attach it instead. See [Import an existing spreadsheet model](/guides/import-a-spreadsheet-model).
## Say what decision the answer is for
Ari builds a better Model when it knows what the answer is for.
| Instead of | Try |
| - | - |
| "Build a forecast." | "Build a 12-month forecast so I can see whether we can hire two engineers in Q1." |
| "Show me expenses." | "Which departments drove the increase in operating expenses last month?" |
| "Revenue report." | "Which customer segments explain the gap between our Forecast and Actuals?" |
## Name the period, the comparison, and the detail
Say which period matters and what to compare it with:
> Compare March Actuals with the March Forecast and with February Actuals.
Ask for the business categories that explain the result, such as department, customer, product, or region:
> Break payroll down by department, then show the employees behind the largest change.
If you are working in a Scenario other than the one you are viewing, name it.
## Point at what you mean
Mention the exact cell instead of describing it. Right-click the cell, choose **Mention cell in chat**, and ask "Why is this lower than last month?" A description such as "the revenue number in that table" leaves Ari to guess which one you mean. See [Ask Ari about a number](/guides/ask-about-a-number).
## Say whether you want an answer or a change
Ari reads the Model to answer a question and changes it when you ask for something new. Make the difference explicit, and fence off what must not move:
| You want | Say |
| - | - |
| An answer, with nothing changed | "Read only. Don't change anything. Why did gross margin fall in March?" |
| A change in a safe place | "Work in the Downside Scenario only, and don't change Main." |
| A change to the layout but not the numbers | "Reorder these tables, but don't change any model numbers." |
| One targeted change | "Only change the Starter price. Leave everything else as it is." |
| A check before saving | "Draft the formula and check whether it is valid, but wait before applying it." |
To try a change without risk, use a Scenario. See [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario) and [Make a precise change with Ari](/guides/change-your-model-with-ari).
## Ask Ari to show its work
Follow-ups that make an answer easy to check:
* "Which source data supports that conclusion?"
* "Which assumptions did you make, and which did you estimate?"
* "What changed compared with the prior period?"
* "Which formula applies to this cell?"
* "Can you reconcile this total to the detail below it?"
* "Where can I change this assumption and compare another outcome?"
## Correct Ari when it guesses
Instead of starting over in a new chat, tell Ari what is true and ask it to remember:
> Our customers pay annually up front, not monthly. Fix the revenue timing, and remember this for the whole workspace.
See [Teach Ari your company's conventions](/guides/teach-ari).
## Redirect a long build instead of waiting
If a build is going the wrong way, type the correction while Ari works, or select **Stop** and say what you want instead. See [Steer, stop, or add to Ari's work](/guides/steer-ari).
## Undo instead of starting over
If a change goes wrong, do not delete the Page. Ask "Undo that last change", or undo it in version history. See [Undo a change or restore an earlier version](/guides/undo-changes).
## Related
* [Update your plan after you close the month](/guides/close-the-month)
* [Prompt library](/reference/prompt-library)
* [How Ari works with you](/concepts/working-with-ari)
* [Your first week](/tutorials/first-week)
# Give Ari access to another service with MCP or an API
Source: https://docs.cfo.ai/guides/give-ari-another-service
Connect an MCP server or an API so Ari can look things up or act in another service live, such as a bank balance in Mercury or issues in Linear; control which tools Ari may use; and have Ari save a nightly sync of a service's data into your Model.
Connect an MCP server or an API when you want Ari to look something up or act in another service while you chat. For example, Ari can read today's balance in Mercury, your open issues in Linear, or an exchange rate. Ari calls the service live each time it needs it, and nothing is copied into your Model unless you ask Ari to save a sync setup.
An MCP server or an API does not replace an integration. An integration syncs a source's history into your Model every night; an MCP server answers questions about right now. [How connected data reaches your Model](/concepts/connected-data#integrations-and-mcp-servers-do-different-jobs) explains the difference.
## Before you start
* You need permission to manage connections. Someone without it sees **A workspace admin must connect this service.**
* For an MCP server: the service's MCP address, and a sign-in to the service or a token. Many common servers are in the catalog with their address filled in.
* For an API: an API key, unless the API needs none, and an OpenAPI 3 specification for an API that is not in the catalog.
* MCP servers and APIs use no plan slot until Ari saves a sync setup for one.
## Connect a service
Name the service and what you want from it:
> Connect Mercury's MCP server so you can read our bank balances.
Ari reads the provider's official documentation and posts a card titled **Connect \[service]**. The card shows the service's logo, its address, how it signs in, such as **Bearer token** or **API key as Basic username**, and a **Provider documentation** link. Select **Connect**. A service that uses OAuth opens its sign-in window; one that takes a token or key shows a secure field on the card. What you type there goes to cfo.ai's connection service, not to Ari or the chat. Never paste a key into a chat message.
The card reads **Connecting…**, then **Checking connection…**, then **Connected** for an MCP server or **API saved** for an API. Ari then checks that the tools or endpoints answer before it relies on them. **API saved** means the specification loaded, not that the key works, so Ari tests the key with a read.
In the sidebar, point at **Connections**, select **+** (**Add integration**), and choose the **MCP** or **API** tab. You can also go to **Settings → Connections**.
Search the catalog and select **Connect** on the service's card. An OAuth service opens its sign-in window; a service that uses a token or key asks for it.
Select **Add manually**. For an MCP server, **Add MCP server** asks for a **Name** and the **MCP URL**. Under **Advanced**, choose the **Authentication**: **OAuth**, **Bearer token**, or **API key header**, which also takes a **Header name**, such as `X-API-Key`, and the **API key**.
For an API, **Add API** asks for a **Display name**, the **Authentication** (**Bearer token**, **API key header**, **Basic authentication**, or **No authentication**), the **API key**, and the **OpenAPI specification**, as a **Specification URL** or with **Paste JSON**. A specification that declares several servers also needs the **Base URL** under **Advanced**.
The service appears in the list with its status, such as **Ready for Ari**. Ask Ari a question that needs it.
**Add with Ari** on the same tabs opens a chat that starts the Ask Ari path. While the workspace cannot use AI, the button is off and a line under it reads **Add with Ari is unavailable.** with the reason, such as **This workspace is out of AI usage.** **Add manually** still works.
### Rippling's MCP server
Add Rippling's server with **Add manually**. The dialog shows a **Rippling setup** note with three requirements:
* Copy your Remote MCP server URL from **IT → My IT → MCP connectors → Rippling MCP**.
* Ask a Rippling admin to assign you access in **IT → MCP Gateway → Rippling MCP → Access assignments**.
* Make sure your company has an active Rippling AI trial or subscription, or its tools return no data.
## Choose what Ari may do
Each tool of an MCP server, and each endpoint of an API, has a permission:
| Permission | What Ari does |
| - | - |
| **Allow** | Uses it without asking |
| **Ask first** | Asks you to approve each call. See [Answer Ari's questions and approve its actions](/guides/approve-ari-actions). |
| **Block** | Never uses it |
A new MCP server starts with every tool on **Ask first**. A new API starts by method: reads (`GET` and `HEAD`) on **Allow**, deletes on **Block**, and every other call on **Ask first**. When Ari asks and you choose **Always allow**, that tool stays on **Allow** for the whole workspace.
To change them, select **Manage** on the service in **Settings → Connections**. Tools are grouped as **Read-only tools** and **Write/delete tools**, and endpoints as **Read endpoints** and **Write/delete endpoints**. **Auto-classify** sets them for you:
* For an MCP server: read-only tools to **Allow**, and write or delete tools to **Ask first**.
* For an API: reads to **Allow**, writes to **Ask first**, and deletes to **Block**.
When Ari calls a write tool and the answer is lost on the way back, it does not repeat the call on its own. Check the other service before you ask Ari to try again.
## Copy a service's data into your Model every night
To keep a service's data in your Model, rather than look it up live, ask Ari for a sync setup:
> Save a daily sync of our Mercury account balances into the Model.
Ari picks one read-only tool or endpoint, saves how to call it, and runs it. Each night the sync copies the results into your data, and a saved query brings them into your Model as Variables and Dimensions, like an integration. The tool or endpoint must be set to **Allow**. You can give it its own daily time, as with any connection; see [Refresh data](/guides/refresh-data#change-when-a-connection-refreshes).
The service's page lists its setups under **Syncs**, each **Enabled** or **Paused**, with when it last synced. To stop one, ask Ari to remove it; the data it already copied stays. Once a service has a sync setup, it uses one standard plan slot. [How imported data loads](/reference/imported-data#sync-setups-for-mcp-servers-and-apis) lists the sync types and limits.
## Disconnect a service
In **Settings → Connections**, open the service's **…** menu and select **Disconnect**. cfo.ai warns **Ari will no longer be able to use this server's tools until you reconnect.**, or for an API, **Ari will no longer be able to use this API until you reconnect it.**
## If something goes wrong
* **The provider must approve cfo.ai's sign-in URL to connect.** The provider has to allow cfo.ai's sign-in address first; trying again does not help until it does.
* **This provider signs in only apps registered with it in advance.** The server cannot be connected with OAuth. Use a token if the provider offers one.
* **Reconnect this source to continue.** The saved sign-in no longer works. Sign in to the server again.
[Connection statuses and actions](/reference/connection-statuses#messages-about-mcp-servers-and-apis) lists every message.
## Related
* [Integrations directory: Connections for Ari](/reference/integrations#connections-for-ari)
* [How connected data reaches your Model](/concepts/connected-data)
* [Connect Claude, ChatGPT, Codex, or another AI tool to cfo.ai](/guides/connect-an-ai-client), for the other direction: an outside AI tool working in cfo.ai
# Import an existing spreadsheet model
Source: https://docs.cfo.ai/guides/import-a-spreadsheet-model
Move your financial model from Excel, Google Sheets, or another planning tool into cfo.ai. Give Ari the workbook, review its plan, check that the rebuilt Model ties to your spreadsheet, and keep it in sync.
If your financial model lives in a spreadsheet, Ari can rebuild it as a cfo.ai Model with its tabs, assumptions, formulas, and cases, then check each section against your workbook. Use this when you are moving off a spreadsheet, or off another planning tool that exports to Excel or Google Sheets. Ari rebuilds the logic in cfo.ai's structure, so the layout will not match your spreadsheet cell for cell.
## Before you start
* Have the workbook ready: an Excel file to attach to a chat, or a Google Sheet to [connect](/guides/connect-a-google-sheet). A workbook with its formulas gives Ari the most to work from. From another planning tool, export the model to Excel or Google Sheets first.
* A PDF or a values-only export also works, but Ari has to infer the logic, and it labels every relationship it inferred as an assumption.
* If your accounting or payroll system is already connected, Ari can use its Actuals instead of rebuilding the workbook's actuals tabs.
* Expect a large workbook to take a while. Ari rebuilds it in stages that run well past one reply, and you can keep working meanwhile.
## Import the workbook
Attach the workbook to a chat and say what to build. In a new workspace, you can instead choose **Upload an existing model** on the starting card and attach the file there.
> Import this workbook into cfo.ai. Build everything in Main.
If your books are connected, add:
> Use our connected QuickBooks data for actuals instead of rebuilding the actuals tabs.
Ari reads the whole workbook first and tells you what it found. It names the tabs that hold inputs, data, calculations, and reports, the repeated sections that are really one list, the cases that become Scenarios, and anything it cannot carry over. For a larger build it shows a plan card. Correct anything that is wrong, then select **Build it**.
Each tab you are importing becomes a Page with the same name. Each labeled section on it becomes a table named after that section, so you can compare them side by side. Pages appear in the sidebar as Ari finishes them.
Turn on the optional **Model migration tie-out** Skill; see [Turn on a built-in Skill](/guides/turn-on-built-in-skills). Then ask, with the workbook attached:
> Does this tie to my workbook? Build the tie-out page.
Ari builds a Page with a bridge from your workbook's numbers to cfo.ai's. The differences are grouped so you can drill into each one, and buttons hand each decision to Ari. Building the page changes nothing in your Model. Without the Skill, ask:
> Reconcile revenue, payroll, and cash to the attached workbook for every quarter, and explain each difference.
A rebuilt model can be more correct than the spreadsheet, so a difference is not always a mistake. Ari tells you which differences come from errors it corrected in the workbook, which from stale or broken links, and which it cannot explain. Tell Ari which result to keep.
Set **Last close** to the last month your workbook treats as actual, so closed months read as Actuals and later months as Forecast. See [Set Last close and date defaults](/guides/set-dates-and-last-close).
## What carries over
| In your spreadsheet | In cfo.ai |
| - | - |
| A tab of inputs, data, or calculations | A Page with the same name, with a table for each labeled section |
| Copies of one layout, such as one tab per product or department | One Page and one Variable segmented by a Dimension, such as Product |
| Revenue, payroll, balances, and other line items | Variables |
| Spreadsheet formulas | cfo.ai formulas that keep the same method and inputs, written with names instead of cell addresses |
| Lookup tables | Dimension mappings |
| Case selectors and copies of tabs per case | Scenarios |
| Assumptions you tune | Editable inputs |
| Statement and dashboard tabs | Pages with Table blocks, rebuilt to read from the Model |
Values that came from macros or from links to other workbooks arrive as values; Ari says which ones it could not rebuild as formulas. Ari keeps the source's forecasting method. If it thinks another method fits better, it proposes the change instead of making it silently.
## Update the Model after the spreadsheet changes
A connected Google Sheet is re-read every night. When a section changed, Ari updates that section's tables, formulas, and inputs, and leaves sections that did not change as they are, including edits you made in cfo.ai. For an Excel file, attach the new version and ask:
> Here is the new version of the workbook. Update the import.
## Read a workbook without building anything
To have Ari explain a model before you import it, say so:
> Review this workbook and explain how the forecast works. Do not change the Model.
## Import a large workbook in passes
For a very large workbook, you can scope the first pass to the core model, check that it ties, and add reports and Scenarios after:
> Import the inputs, revenue, payroll, and cash tabs first. Skip the summary and dashboard tabs for now.
## If something goes wrong
* If Ari stopped partway, select **Retry** on the message, or ask Ari to continue. Anything it saved stays in your Model.
* If a rebuilt number does not match, ask Ari to trace it to its source cells. See [Check that your numbers match your books](/guides/check-imported-data).
* If you want to start over, undo the import's changes from Version history. See [Undo a change or restore an earlier version](/guides/undo-changes).
## Related
* [Replace estimates with your real numbers](/guides/switch-to-real-numbers)
* [Connect a Google Sheet or Drive file](/guides/connect-a-google-sheet)
* [Attach files to a chat](/guides/attach-files)
* [How formulas work](/concepts/formulas)
# How-to guides
Source: https://docs.cfo.ai/guides/index
Step-by-step guides for the jobs people do in cfo.ai: connect data, work with Ari, build and edit the Model, plan with Scenarios, report and share, automate, and manage your account.
Each guide walks you through one task, such as connecting QuickBooks, fixing a connection, or sharing a Page. The guides assume you know your way around. If you are new, start with [Build your first Model](/tutorials/first-model). Most tasks can be done by asking Ari or by hand, and the guides show both.
## Bring in your data
Most people start here. Connect the systems that hold your Actuals, or bring in a file or an existing model.
* [Connect QuickBooks, Xero, NetSuite, or Rillet](/guides/connect-accounting)
* [Connect payroll, HR, CRM, payments, or a database](/guides/connect-other-sources)
* [Connect a Google Sheet or Drive file](/guides/connect-a-google-sheet)
* [Upload a CSV as a data source](/guides/upload-a-csv)
* [Import an existing spreadsheet model](/guides/import-a-spreadsheet-model)
* [Replace estimates with your real numbers](/guides/switch-to-real-numbers)
## Keep your data current
Integrations and Google files refresh once a day, and MCP servers and APIs answer live when Ari calls them. These guides cover refreshing sooner, fixing a connection that stopped, choosing which tables a source syncs, and checking that the numbers match your books.
* [Refresh data or change when it refreshes](/guides/refresh-data)
* [Fix a connection that needs attention](/guides/fix-a-connection)
* [Choose which tables a source syncs](/guides/choose-synced-tables)
* [Check that your numbers match your books](/guides/check-imported-data)
## Ask Ari
Ask about your numbers, find what Ari built, and give it the files it needs.
* [Ask Ari about a number](/guides/ask-about-a-number)
* [Find a Page, number, or file](/guides/find-things)
* [Get better answers from Ari](/guides/get-better-answers)
* [Attach files to a chat](/guides/attach-files)
## Guide Ari's work
Ask for exactly the change you want, and redirect Ari while it works. Recover when it is slow or wrong, approve what it proposes, teach it how your company does things, and talk to it from Slack.
* [Make a precise change with Ari](/guides/change-your-model-with-ari)
* [Steer, stop, or add to Ari's work](/guides/steer-ari)
* [When Ari is slow, stuck, or wrong](/guides/ari-is-slow-stuck-or-wrong)
* [Answer Ari's questions and approve its actions](/guides/approve-ari-actions)
* [Teach Ari your company's conventions](/guides/teach-ari)
* [Talk to Ari from Slack](/guides/use-ari-in-slack)
## Build plans with Ari
Have Ari build the plans you run the business on, from your books and your assumptions.
* [Build a revenue and expense forecast](/guides/build-a-forecast)
* [Forecast cash and cash runway](/guides/forecast-cash)
* [Build a headcount plan](/guides/build-a-headcount-plan)
* [Get your P\&L, balance sheet, and cash flow statement](/guides/build-financial-statements)
* [Build a budget and compare it with actuals](/guides/budget-vs-actuals)
* [Update your plan after you close the month](/guides/close-the-month)
* [Model several entities and consolidate them](/guides/consolidate-entities)
## Change the Model with Ari
Have Ari write and explain formulas, check what it changed, and take changes back.
* [Have Ari write, change, or explain formulas](/guides/change-formulas-with-ari)
* [Review Ari's formula changes](/guides/review-ari-formula-changes)
* [Undo a change or restore an earlier version](/guides/undo-changes)
## Edit Pages and tables yourself
Everything Ari builds, you can build and change by hand.
* [Create and organize Pages and blocks](/guides/create-pages-and-blocks)
* [Build a Table block by hand](/guides/build-a-table)
* [Enter, paste, and clear values](/guides/enter-values)
* [Format and arrange a table](/guides/format-a-table)
* [Filter and sort a table](/guides/filter-and-sort-a-table)
* [Break a number down](/guides/break-down-a-number)
* [Map one Dimension's items to another](/guides/map-dimensions)
## Write formulas yourself
Write a Variable's formula by hand, use one formula for Actuals and another for Forecast, and fix a formula that shows an error.
* [Write a formula](/guides/write-a-formula)
* [Use different formulas for Actuals and Forecast](/guides/actuals-and-forecast-formulas)
* [Fix a formula that shows an error or a wrong number](/guides/fix-formula-errors)
## Plan with Scenarios
Try a decision without changing your plan, compare the outcomes, and keep the version you approved.
* [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario)
* [Compare Scenarios side by side](/guides/compare-scenarios)
* [Merge a Scenario into Main](/guides/merge-a-scenario)
* [Save a plan as a Snapshot](/guides/save-a-snapshot)
## Report and share
Turn the Model into reports people read, and get them to the people who need them.
* [Build a dashboard or board report](/guides/build-a-dashboard)
* [Change a chart or Canvas block](/guides/change-a-visual)
* [Share a Page outside your workspace](/guides/share-a-page)
* [Export a Page to Excel](/guides/download-a-spreadsheet)
* [Get a PDF report or slide deck](/guides/get-a-pdf-or-slides)
* [Walk someone through a Page with a Guide](/guides/create-a-guide)
* [Brand your workspace](/guides/brand-your-workspace)
## Automate recurring work
Have Ari run work on a schedule, save a workflow it can repeat, and turn on the Skills that come with cfo.ai.
* [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger)
* [Save a workflow as a Skill](/guides/save-a-skill)
* [Turn on a built-in Skill](/guides/turn-on-built-in-skills)
## Manage your workspace and account
Add teammates, work across workspaces, and set Last close. Choose or cancel a plan, manage AI usage, and change your personal settings.
* [Invite or remove teammates](/guides/invite-teammates)
* [Create a second workspace or switch workspaces](/guides/manage-workspaces)
* [Set Last close and date defaults](/guides/set-dates-and-last-close)
* [Start a trial or choose a plan](/guides/choose-a-plan)
* [Buy AI usage, set a spend limit, or turn on auto-reload](/guides/manage-ai-usage)
* [Cancel your subscription or delete your data](/guides/cancel-or-delete)
* [Change your theme, sounds, and notifications](/guides/personalize-cfoai)
## Connect AI tools
Use cfo.ai from an AI tool such as Claude or ChatGPT, or let Ari use another service through MCP or an API.
* [Connect Claude, ChatGPT, Codex, or another AI tool to cfo.ai](/guides/connect-an-ai-client)
* [Give Ari access to another service with MCP or an API](/guides/give-ari-another-service)
# Invite or remove teammates
Source: https://docs.cfo.ai/guides/invite-teammates
Add a user to your cfo.ai workspace by inviting them as an administrator, from Settings or by asking Ari; resend, cancel, or fix an invitation that did not arrive; remove someone; and give read-only access with a share link instead.
To add a user to your cfo.ai workspace, invite them by email. Everyone you invite joins as an administrator: they can see and edit every Page, invite others, and manage billing and settings. cfo.ai has no view-only or lower role, so an invitation always means full access. To let someone read reports without joining, send a share link instead; see [Give someone read-only access](#give-someone-read-only-access).
Ask Ari to invite one or more people:
> Invite [dana@example.com](mailto:dana@example.com) and [lee@example.com](mailto:lee@example.com) to the workspace.
Ari shows exactly who it will invite and waits for you to confirm. You can add a welcome for the people you invite, with any of these:
* A short note.
* A Page to open first.
* Up to three questions they can send to Ari with one click.
* A saved Guide, offered as a tour.
For example:
> Invite [dana@example.com](mailto:dana@example.com) with a note saying she'll own the hiring plan. Open the Headcount Page first and offer the onboarding tour.
If someone already belongs to the workspace without the administrator role, ask Ari to make them an administrator. That changes their role and sends no invitation.
Open the account menu, your name or picture at the bottom of the sidebar, choose **Settings**, and select **Workspace**.
Under **Invite admin**, type the person's **Email address**.
Select **Add admin**. The form reads **Admin invitation created for** the address, and the person appears in the **Admins** list marked **Invited**.
Adding people does not change your price: a plan is priced for the workspace, not per person. See [Plans, AI usage, and connection slots](/concepts/plans-and-usage).
## What the invited person sees
The person gets an email invitation to the workspace. When they sign in with the invited address, the workspace is in their account. If you added a welcome, it shows in the email and in Ari's empty chat when they first open the workspace. It is labeled **Note from** your name, with **Take the tour** for the Guide and **Open** followed by the Page's name. The person can dismiss it.
## Resend an invitation or fix one that did not arrive
An invitation stays pending, marked **Invited**, until the person joins. If they cannot find the email:
1. Check the address in the **Admins** list in **Settings → Workspace**. If it is wrong, cancel that invitation and invite the right address.
2. Ask them to check their spam or junk folder.
3. Send the email again: enter the same address under **Invite admin** and select **Add admin**, or ask Ari to resend the invitation. Resending keeps any welcome you added.
4. Or skip the email: ask them to go to [app.cfo.ai](https://app.cfo.ai) and sign in with exactly the invited address. A pending invitation is accepted when that address signs in, and the workspace appears.
If the person signs in with a different address, such as the one on their Slack profile, invite that address instead.
## Cancel an invitation
In **Settings → Workspace**, open the menu on the person's **Invited** row, choose **Cancel invitation**, and confirm. They can no longer join through it. If they joined before you confirmed, they keep their access; to take it away, remove them.
## Remove a teammate
In **Settings → Workspace**, open the menu on the person's **Admin** row, choose **Remove member**, and confirm. You can also ask Ari, for example "Remove [lee@example.com](mailto:lee@example.com) from the workspace."
The person loses access to this workspace right away, and any pending invitations for their email are canceled. Their access to other workspaces and their cfo.ai account stay as they are. To delete an account, see [Cancel your subscription or delete your data](/guides/cancel-or-delete).
You cannot remove yourself or the workspace's last administrator, in Settings or through Ari. To leave a workspace, ask another administrator to remove you.
## Give someone read-only access
To show reports to an investor, a board member, or a client without giving them access to everything, share a Page with a link instead of inviting them. A link opens the Pages you choose, read-only, without a cfo.ai account. See [Share a Page outside your workspace](/guides/share-a-page) and [Sharing and access](/concepts/sharing-and-access).
## Related
* [Sharing and access](/concepts/sharing-and-access)
* [Settings](/reference/settings#workspace)
* [Create a second workspace or switch workspaces](/guides/manage-workspaces)
* [Talk to Ari from Slack](/guides/use-ari-in-slack)
# Buy AI usage, set a spend limit, or turn on auto-reload
Source: https://docs.cfo.ai/guides/manage-ai-usage
Keep Ari working when your AI usage (credits) runs low or runs out: check how much is left, buy more, cap what extra usage can cost each month, and have cfo.ai top up automatically with auto-reload.
Use these settings when your plan's included AI usage runs low or runs out and you want Ari to keep working. You can buy extra usage, which Settings calls credits. Buy it once, or have cfo.ai buy it automatically with auto-reload, and cap what it can cost each month with a spend limit. Extra usage is used only after your plan's included usage for the month is gone. For what uses AI usage, see [Plans, AI usage, and connection slots](/concepts/plans-and-usage).
## Check how much usage is left
Open the account menu, your name or picture at the bottom of the sidebar, choose **Settings**, and select **Usage**. **Included plan usage** shows how much of this cycle's usage is used and the date it resets. You can also ask Ari, for example "How much of our AI usage is left this month?"
As usage runs low, a notice above the message box in your chats says how much is left, such as **50% of your workspace's monthly AI usage remains.**
## Buy usage
In **Settings → Usage**, under **Extra usage**, select **Buy credits**.
Pick **\$100**, **\$250**, or **\$1,000**, or select **Other** and type a whole-dollar amount.
Your default payment method is selected. Pick another saved card, or select **Use a new payment method** to add one here.
Select **Pay** followed by the amount and **now**. A notice reads **Added** \[amount] **of usage**.
When Ari has stopped because usage ran out, the panel above the message box in your chat also offers to buy usage, such as **Add \$100**. Purchased usage stays in your balance until Ari uses it; it does not reset each month.
## Set a monthly spend limit
A spend limit caps what extra usage can cost in one billing cycle, whether you buy it or auto-reload does.
In **Settings → Usage**, next to **Monthly spend limit**, select **Adjust limit**.
Type the most extra usage may cost per month and select **Save**. The limit takes effect right away. To remove it, select **Remove limit**.
A purchase that would go over the limit is refused or reduced to fit, and Ari stops when the limit is reached. Raise the limit to keep going.
## Turn on auto-reload
Auto-reload buys more usage for you when your balance falls below an amount you choose, so Ari keeps working without stopping. It charges your default payment method, so add one in **Settings → Billing** first.
In **Settings → Usage**, under **Extra usage**, turn on **Auto-reload**.
The line reads **When the balance falls below** \[amount], **add** \[amount]. Enter the balance that starts a reload, such as \$10, and how much each reload adds, such as \$100.
Select **Save**. The balance line reads **Auto-reload on**.
To stop it, turn **Auto-reload** off. If a reload payment fails, auto-reload pauses and the balance line reads **Auto-reload paused**. It starts again after you update your payment method in **Settings → Billing** or buy credits.
## When usage runs out
When the workspace has no usage left, Ari stops at its next step and explains why in the chat, in a message that starts **I had to stop here**. Your Pages, data, and the work Ari already saved stay as they are, and connected data keeps syncing. Fix the cause, such as buying usage or raising the spend limit, then send a new message; Ari picks up where it left off. For every stop message, see [Chat controls and messages](/reference/chat#billing-stops-out-of-ai-usage-or-over-a-limit).
During a free trial, using up the trial's included usage also lets you start your paid plan early, which resets your included usage; see [Start your paid plan before the trial ends](/guides/choose-a-plan#start-your-paid-plan-before-the-trial-ends).
## Related
* [Plans, AI usage, and connection slots](/concepts/plans-and-usage)
* [Plans and billing](/reference/plans-and-billing)
* [Start a trial or choose a plan](/guides/choose-a-plan)
# Create a second workspace or switch workspaces
Source: https://docs.cfo.ai/guides/manage-workspaces
Create another workspace, such as a second project for another company or one for each client you advise. Switch between workspaces, rename one, and set up one workspace per client as a fractional CFO or advisor.
Create another workspace to keep a separate business apart, such as a second venture, or one company per client if you are a fractional CFO or an advisor. Each workspace is separate: it has its own Model, Pages, connections, chats, Skills, Triggers, team, and plan.
To report several legal entities of one business together, such as a US company and its UK subsidiary, keep them in one workspace instead; see [Model several entities and consolidate them](/guides/consolidate-entities).
## Create a workspace
Select your name or picture at the bottom of the sidebar.
Under **Workspaces**, select **Create workspace**.
Type the **Workspace name** and press **Enter**, or select **Create** followed by the name.
Before the new workspace opens, cfo.ai asks you to choose a plan for it. Each new workspace can start its own free trial; see [Start a trial or choose a plan](/guides/choose-a-plan). The workspace then opens on its own Welcome chat, the same start a new account gets. Ari cannot create a workspace for you.
## Switch between workspaces
Open the account menu. **Workspaces** lists every workspace you belong to, with a check beside the one that is open. Select another to open it.
You can also press **⌘ K** (**Ctrl K** on Windows and Linux), type part of the workspace's name, and choose it under **Workspaces** in the search results.
Workspaces have no folders or groups; the account menu lists all of them by name. Give each one a name you can tell apart at a glance.
## Rename a workspace
Open **Settings**, select **General**, type the new **Name** under **Workspace**, and select **Save**. You can also ask Ari to rename it.
Renaming changes the workspace's web address. Bookmarks and links to its Pages and chats that use the old address stop working; share links keep working.
## How many workspaces a plan covers
Each workspace has its own plan and its own bill, and creating a workspace does not change the plan of the ones you have. In the plan chooser, the Starter plan card lists **1 dedicated workspace**, and the Enterprise card lists **Multiple workspaces**. To ask about Enterprise, select **Talk to sales** on its card. See [Plans and billing](/reference/plans-and-billing).
## Set up one workspace per client
If you advise several companies, give each client a workspace of its own, so their books, Models, and people never mix:
1. Create a workspace and name it after the client.
2. Choose a plan for it. Each workspace is billed on its own, to the payment method added in that workspace.
3. Bring in the client's books: [connect their accounting system](/guides/connect-accounting), or invite the person who holds admin access to their books and ask them to connect it.
4. Save the workflows you repeat for every client, such as your monthly close review, as a Skill once. Your Skills are yours, so Ari follows them in every workspace where you are a member, and the client's team does not get them. Limit a Skill to one client's workspace when it is built on that client's own accounts, systems, people, or figures. Triggers belong to one workspace. See [Save a workflow as a Skill](/guides/save-a-skill).
5. Decide how the client sees your work:
* To give the client full access, [invite them](/guides/invite-teammates). They join as an administrator who can change anything, including billing.
* To deliver reports read-only, [share Pages with a link](/guides/share-a-page) or [download a Page as a spreadsheet](/guides/download-a-spreadsheet).
6. To show your firm's or the client's look on the Pages you share, [brand the workspace](/guides/brand-your-workspace). A brand you set up once can be copied to your other workspaces.
## Related
* [Invite or remove teammates](/guides/invite-teammates)
* [Start a trial or choose a plan](/guides/choose-a-plan)
* [Plans, AI usage, and connection slots](/concepts/plans-and-usage)
* [The cfo.ai interface](/reference/interface)
# Map one Dimension's items to another
Source: https://docs.cfo.ai/guides/map-dimensions
Group GL accounts into P&L lines, or two systems' department names into one list, by mapping each item of one Dimension to an item of another, with Ari or by hand in a table.
Use a mapping when several labels should report as one category, such as grouping your accounts into P\&L lines, or two systems' department names into one list of departments. To understand how mappings work, see [Dimension mappings and shared Dimensions](/concepts/dimension-mappings).
Name the items and where each one goes:
> Map account 5001 to COGS and account 6001 to Payroll in Reporting Line.
Or describe the structure you want and let Ari propose the assignments:
> Group our GL accounts into these P\&L lines: Revenue, COGS, Payroll, Software, Rent, and Other. Put any account that doesn't fit in Other.
If the target Dimension does not exist yet, Ari creates it with the lines you named as its items. Mapping an item that is already mapped replaces its assignment. Each change saves every row it names, or none of them.
Ari can also do what the table cannot:
* Map several items with one condition: "Map accounts 8001 and 8002 to Other Income."
* Map by a pair of items: "Map account 6000 to Payroll for the US company and to Contractors for the UK company."
* Set or clear the catch-all: "Set the catch-all for Reporting Line to Other."
* Remove rows or a whole mapping: "Unmap account 5001," or "Remove the mapping from Account to Reporting Line."
To check a mapping before you change it, ask:
> Show me how Account maps to Reporting Line, and which tables use it.
Ari lists every lookup that feeds the target, with its rows, and every table that shows, groups by, or calculates from the mapping.
Add a new, empty Table block to a Page; see [Create and organize Pages and blocks](/guides/create-pages-and-blocks). Select **Add row**, search for the source Dimension, such as Account, and select it. Choose **List dimension items**. Each account becomes a row, and because the table holds no Variables, it has no months.
Select **Add column** and type the target Dimension's name, such as Reporting Line. Select it, or select **Create dimension:** with the name to create a new one. Choose **Add as mapping**.
Select a cell in the mapping column. In the picker, search the target's items and choose one, or type a new name and select **Add** with that name.
The cell saves that row's assignment in the mapping, and every table that reads the mapping changes with it.
Fill in every row you want to map. A row you leave blank stays unmapped.
To change a column you already added, right-click its header and set **Use as** to **Mapping** or **Segmentation**. For a catch-all, a condition, or removing a mapping, ask Ari.
To map two companies' accounts into one reporting structure, map each company's Account Dimension into the same target; see [Model several entities and consolidate them](/guides/consolidate-entities).
## Related
* [Dimension mappings and shared Dimensions](/concepts/dimension-mappings)
* [Build a Table block by hand](/guides/build-a-table)
* [Get your P\&L, balance sheet, and cash flow statement](/guides/build-financial-statements)
# Merge a Scenario into Main
Source: https://docs.cfo.ai/guides/merge-a-scenario
Adopt a Scenario as your plan: review what it would change, merge it into Main (or the Scenario it branched from), and undo a merge if you need to.
Merge a Scenario when you decide to adopt it as your plan. Merging applies every change the Scenario holds to the Scenario it branched from, and then deletes the merged Scenario. For most Scenarios, that is Main, the Scenario that holds your current plan.
Ask Ari what the merge would change first:
> Is the Hiring freeze Scenario ready to merge into Main? What would change?
Ari summarizes the differences. Then tell it to merge:
> Merge the Hiring freeze Scenario into Main.
Ari merges directly. To confirm the merge yourself, ask Ari to let you confirm. It then adds a **Merge** button to the chat for you to press.
Select the Scenario in the Scenario picker in the sidebar.
Select **Merge** beside the Scenario picker. Its tooltip names where the changes go, such as **Merge all changes into Main Scenario**.
The dialog lists every change the Scenario holds, across the whole Model, not only the Page you are on. It also reminds you that the Scenario is deleted after the merge.
Select the confirm button, which counts the changes, such as **Merge 12 changes**.
## Undo a merge
A merge shows in the version history of the Scenario it merged into, usually Main, as **Merged \[Scenario] into \[target]**. To take it back, point at that entry, select its **…** button, and choose **Undo this change**. Undoing a merge takes its changes back out of the target and brings the merged Scenario back. See [Undo a change or restore an earlier version](/guides/undo-changes).
## If the merge is not available
* **Merge** is dimmed with **No changes to merge** when the Scenario holds no changes.
* A Scenario that has its own editable child Scenario cannot be merged. The refusal names the child, such as **scenario cannot be merged while it has 1 child scenario**. Merge or delete the child first. A Snapshot taken from the Scenario does not block the merge.
* Main and Snapshots cannot be merged.
## Related
* [Scenarios, Snapshots, and version history](/concepts/scenarios)
* [Scenario and version history controls](/reference/scenarios-and-history)
# Change your theme, sounds, and notifications
Source: https://docs.cfo.ai/guides/personalize-cfoai
Turn on dark mode or light mode, pick a built-in theme or have Ari build your own, play a sound when Ari finishes, and get a browser notification when Ari replies, without changing what your teammates see.
Use these settings to make cfo.ai look and sound the way you like. They change only your own view: your teammates keep theirs. Your theme is saved to your account, so it follows you to any browser. Color mode, Ari's sound, and browser notifications are saved in the browser you set them in.
## Turn on dark mode or light mode
Open the account menu, your name or picture at the bottom of the sidebar, point at **Appearance**, and choose **Auto**, **Light**, or **Dark**. **Auto** follows your device's setting. The same choice is under **Color mode** in **Settings → General → Appearance**.
## Choose a theme
Open **Settings → General**. Under **Appearance**, open **Theme** and choose one of the built-in themes, such as **Warm paper**, which is cfo.ai's own, or **Prism**. The app changes as soon as you pick.
In a workspace that has a brand, **Theme** is replaced by a note that the workspace uses its brand. Everyone there sees the brand's colors, and your theme applies in your workspaces that have no brand. See [Brand your workspace](/guides/brand-your-workspace).
## Build your own theme with Ari
A custom theme can set:
* The page and panel colors, the text color, and one accent color, each for both light and dark mode.
* A heading typeface: **Source Serif**, **Newsreader**, **Fraunces**, **Zilla Slab**, or **DM Sans**. Without one, headings use cfo.ai's own typeface.
* A corner shape: square, soft (cfo.ai's own), or round.
* Up to three chart colors, which replace the first three colors charts and Canvas blocks draw with.
The colors that mark good and bad, such as a red variance, stay the same in every theme.
Describe the look you want, and give it a short name:
> Give me a deep teal accent with a near-black dark mode. Call it Teal dark.
Ari saves the whole theme at once. To change one part, such as the accent, say so; Ari keeps the rest of your current theme. To see what you have, ask "What is my theme set to?"
In **Settings → General → Appearance**, open **Theme** and choose **Custom**.
Type the look you want in the box that reads **Create a custom theme**, such as "warm gray with a green accent".
Select **Personalize**. A chat opens where Ari builds the theme from your description.
## Hear a sound when Ari finishes
Ari can play a short sound when it finishes a reply, which helps when you work in another tab. Open **Settings → Ari**, and under **Sound** check **Play sound when Ari is done**. Choose a volume, **Quiet**, **Normal**, or **Louder**, and an **Alert sound**, **Default** or **Beep Me**. Each change plays the sound so you can hear it.
## Get a notification when Ari finishes
Your browser can notify you when Ari replies, or needs your answer, in a chat you are not looking at.
In **Settings → Ari**, under **Notifications**, turn on **Notify me when Ari finishes in this browser**.
When your browser asks whether cfo.ai may show notifications, allow it.
The setting applies to this browser only, so turn it on in each browser where you want it. Ari stays quiet while you are looking at the chat. Notifications cover chats you have with Ari in cfo.ai, not Slack conversations or scheduled Trigger runs. After a long reply, the chat may offer the same switch above the message box. For what the notifications say, see [Chat controls and messages](/reference/chat#notifications-when-ari-finishes).
If the switch is grayed out with **Notifications are blocked for cfo.ai. Allow them in your browser’s site settings to turn this on.**, allow notifications for cfo.ai in your browser's site settings, then come back to the tab.
## Related
* [Settings](/reference/settings#ari)
* [Brand your workspace](/guides/brand-your-workspace)
* [Review Ari's formula changes](/guides/review-ari-formula-changes), which covers turning off **Show Ari's edits as they happen**
# Refresh data or change when it refreshes
Source: https://docs.cfo.ai/guides/refresh-data
Sync QuickBooks or another connected source now (there is no Sync now button), stop a running sync, re-import past months after you fix them in your books, and change the time a connection refreshes each night.
Use this guide when you need new numbers before the nightly sync, when you corrected a past month in your books, or when a source publishes its data after the nightly run and lands a day late. Integrations sync once a day on their own. Uploads, Google files, and MCP servers work differently; see [Sources that refresh differently](#sources-that-refresh-differently).
## Refresh a connection now
There is no **Sync now** button. Ask Ari:
> Refresh QuickBooks and tell me when the import has finished.
A line above the message box reads **Syncing** with the connection's name. Select it to see the sync's two steps, **Sync source data** and **Run saved queries**, with **View connection** and **Stop**. You can keep working. When the sync finishes or fails, Ari tells you in the same chat.
A refresh reruns the connection's saved queries as they are. If the refresh brings in a table nothing reads yet, ask Ari for a saved query over it.
## Stop a running sync
Select the **Syncing** line above the message box, then **Stop**. The connection reads **Stopped**, with **This sync was stopped before it finished.** Nothing broke, and the data from the last finished sync stays. Select **Retry** on the connection when you want to run it again.
## Re-import past months
The nightly refresh does not always re-read your whole history. QuickBooks Online re-reads the last three months, so a correction to an older month reaches cfo.ai only when you re-import the full history. Xero re-reads its whole range every night. Sources that sync through Fivetran keep each table's full history.
To re-import QuickBooks Online or Xero, ask Ari:
> I corrected last year's books in QuickBooks. Re-import the full history.
To reach further back than January 1 of last year, name the first year, 2015 or later:
> Re-import QuickBooks' full history from 2021.
The months a full-history import brings in stay through later refreshes. A full-history import is slower than a routine refresh.
## Change when a connection refreshes
All connections refresh in a shared nightly run that starts at 02:00 UTC, which is 7 PM Pacific the evening before during daylight saving time. If a source publishes the day's data after that, give the connection its own time:
> Refresh QuickBooks at 6:30 AM Pacific every day.
Ari saves the time and time zone on the connection. From the next nightly run, the connection refreshes once a day at that time and reruns its saved queries when the refresh finishes. A time you set this morning takes effect tomorrow. To return the connection to the shared run, ask Ari to clear its refresh time. Ari tells you each connection's refresh time when you ask about your connections.
## Sources that refresh differently
* CSV uploads never refresh. Upload the new version; see [Upload a CSV as a data source](/guides/upload-a-csv#replace-the-file-with-a-newer-version).
* Google Sheets and Google Drive files are re-read every night, and when you ask Ari. An unchanged file starts no update.
* MCP servers and APIs are called live when Ari needs them. A sync setup Ari saved for one refreshes nightly like an integration, and can take its own time.
## If a refresh does not run
If the workspace has no active plan, or its billing is locked, the nightly refresh skips the workspace. A refresh you ask for gets the answer **data syncing is paused for this workspace — choose a plan, or settle the outstanding payment, to resume syncing**. Syncing resumes as soon as the workspace has an active plan.
If the sync fails, the connection shows why and offers the fix. See [Fix a connection that needs attention](/guides/fix-a-connection).
## Related
* [How connected data reaches your Model](/concepts/connected-data#when-data-updates)
* [How imported data loads](/reference/imported-data#refresh-schedule)
* [Connection statuses and actions](/reference/connection-statuses)
# Review Ari's formula changes
Source: https://docs.cfo.ai/guides/review-ari-formula-changes
Check that a formula Ari saved changed the right Variable, in the right place and Scenario, and gives the right numbers; read Ari's warnings; and fix or undo the change.
After Ari changes a formula, check four things: the Variable it changed, where the formula applies, the Scenario it landed in, and the numbers it now produces. A formula Ari saves has passed its checks, but only the numbers show that it does what you meant.
## Watch the change on the Page
When the Page that Ari is changing is open, Ari points at each change on the table and explains it in a short note. To see only the finished edits marked instead, turn off **Show Ari's edits as they happen** under **Thinking animation** in **Settings → Ari**. The setting is saved for this browser.
## Read the Formulas card in the chat
With **Show Ari's edits as they happen** turned off, Ari's formula saves in a turn collect under a **Formulas** card in the chat. Expand it to see one row for each save, and expand a row to see one card per formula it saved. Each card shows:
* The Variable's name.
* Where the formula applies, as pills: an item such as **Sales**, or a Dimension's name, such as **Department**, when the formula applies to every item. A **+ any** pill means it also applies to finer breakdowns under them. **Any Segment** means it applies everywhere.
* The formula itself.
* The formula range, **Actuals** or **Forecast**, when the formula is limited to one. A card with no range applies to every period.
The card does not name the Scenario. Unless you named another, Ari saves in the Scenario your chat is in, which the Scenario picker in the sidebar shows. If you named another Scenario, select it in the picker to check the change.
## Check the formula in the table
Open the formula the way you would to edit it, for example by pointing at the row name and selecting the **fx** button, **Edit variable formula**. Read the editor's title: it names the Variable and where the formula applies. To see every row's formula at once, show the formula columns; see [Show or hide the formula columns](/guides/write-a-formula#show-or-hide-the-formula-columns).
## Check the numbers
Ask Ari to check the result against what you expect:
> Check the resulting values in the P\&L table and compare them with Revenue minus Cost of Goods Sold.
> Verify this formula for Sales and Engineering for the next three months.
Look in particular at:
* The first Forecast month, where a formula that reads the previous month starts from the last Actuals month.
* The totals above a breakdown, which should match the rows beneath them.
* A few items of each Dimension the formula applies to, not only the first.
## Read Ari's warnings
Some saves come with a warning. The formula saved, but it may not do what its wording suggests, such as a filter that matches no item or an imported Variable read in Forecast months. If a number looks wrong right after a change, ask:
> Read back any warnings from that change.
Every refusal and warning is listed in [Formula errors and warnings](/reference/formula-errors#aris-save-warnings).
## Fix or take back a change
* To find the cause before anything changes, ask Ari: "Explain why this formula works for Sales but not Marketing. Do not change anything yet."
* To correct the change, describe the formula you want and where it applies, then check the numbers again.
* When Ari changed several formulas at once, ask which changes were saved and whether any were refused.
* To undo the change, ask Ari, "Undo that last change," or open **Version history** from the clock icon in the Page header, select the change's **…** actions button, and choose **Undo this change**. **⌘ Z** does not undo Ari's changes. See [Undo a change or restore an earlier version](/guides/undo-changes).
## Related
* [Have Ari write, change, or explain formulas](/guides/change-formulas-with-ari)
* [Fix a formula that shows an error or a wrong number](/guides/fix-formula-errors)
* [Formula errors and warnings](/reference/formula-errors)
* [Scenarios, Snapshots, and version history](/concepts/scenarios)
# Save a workflow as a Skill
Source: https://docs.cfo.ai/guides/save-a-skill
Teach Ari a process you repeat, such as a monthly board update, so it follows the same steps every time, in every workspace you work in. Save it as a Skill in chat, with Build with Ari, or by writing it yourself, limit it to one workspace when it fits only one company, then change or remove it later.
Save a Skill when you repeat the same process and want Ari to follow it the same way every time, such as your monthly board update or your month-end review. A Skill is a named set of instructions that belongs to you. Ari uses it on its own whenever a request of yours matches the Skill's description, in every workspace where you are a member unless you limit it to one, so you save it once. Other people in your workspaces have their own Skills. For how Ari picks a Skill, see [Skills, Triggers, and Guides](/concepts/automation).
Describe the workflow, paste instructions you already use, or attach a Markdown file, and ask Ari to save it:
> Every month I send the board a one-page update with cash, burn, and hiring. Save that as a Skill.
> Save the attached workflow as a Skill named weekly-cash-review.
After Ari finishes a piece of work you want to repeat, you can also ask:
> Save what we did here as a Skill.
Ari picks a name and a description if you do not give them, and saves the Skill without a separate approval step. When the steps are built on this company's own accounts, systems, people, or figures, Ari limits the Skill to this workspace; otherwise the Skill applies in all your workspaces. To choose, say so: "Save that as a Skill for this workspace only." Ari confirms the name, when the Skill applies, and where, with a **View skill** link that opens **Settings → Skills**. A new chat has the Skill at once, and a chat already open within a message or two.
Open the account menu, your name or picture at the bottom of the sidebar, choose **Settings**, and select **Skills**.
Select **Write manually**. To have Ari write it with you in a chat instead, select **Build with Ari**.
Enter a **Name**, such as `board-report`: lowercase letters, digits, and single hyphens. Spaces become hyphens as you type. You cannot change the name later.
In **Description**, say when Ari should use the Skill, starting with "Use when", such as "Use when the user asks for a board or investor report."
In **Instructions**, type or paste the full workflow in Markdown: the steps, the structure of the result, and your conventions. The editor takes no file uploads, so paste a file's contents, or attach the file in a chat with Ari instead.
Under **Use in**, keep **All your workspaces**, or choose **Only in** this workspace for a procedure that fits only this company.
Select **Create skill**. The Skill appears under **Your skills**, and Ari uses it in your chats wherever it applies.
## Write a description that fits
Ari reads every Skill's description on each request to decide which Skill applies. Make the description specific enough that Ari does not use the Skill for unrelated requests, and broad enough to catch the different ways people ask for the same thing:
> Use when the user asks for a board update, investor update, or monthly summary for the board, or asks to prepare board materials.
## Update or remove a Skill
Ask Ari, for example "Update the board-report Skill to include a hiring section" or "Delete the weekly-cash-review Skill." Or open **Settings → Skills**, find the Skill with **Search skills**, and:
* Select **Edit**, change the **Description**, the **Instructions**, or **Use in**, and select **Save**.
* Select **Delete** and confirm. Ari stops using the Skill wherever it applied.
A Skill is yours, so a change or a deletion applies to your chats with Ari and nobody else's. **Settings → Skills** lists all your Skills, including ones limited to other workspaces. A Skill limited to a workspace you have left is not in use; you can still edit it, change **Use in**, or delete it. Built-in Skills cannot be edited or deleted; to change how one works for you, save your own Skill under a different name.
## Share a fact with your team
A Skill holds your own process. A fact everyone at a company should share, such as how the company defines bookings or when its fiscal year starts, belongs in workspace memory, which applies to every chat for everyone in that workspace. Ask Ari: "Remember for the whole workspace that bookings count signed contracts." See [What Ari remembers](/concepts/ari-memory).
## Start from a sample
[Sample Skills](/reference/sample-skills) has four complete Skills, such as a three-statement buildout and a headcount plan built from capacity, that you can save as they are or adapt.
## If something goes wrong
Settings explains why a Skill was not saved, for example when the name is already taken or the instructions are too long. For every message and limit, see [Settings](/reference/settings#skills) and [Limits](/reference/limits#skills-triggers-and-guides).
## Related
* [Turn on a built-in Skill](/guides/turn-on-built-in-skills)
* [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger)
* [Teach Ari your company's conventions](/guides/teach-ari)
* [Skills, Triggers, and Guides](/concepts/automation)
# Save a plan as a Snapshot
Source: https://docs.cfo.ai/guides/save-a-snapshot
Keep a version of your plan, such as the budget your board approved: take a Snapshot of Main or a Scenario as it is today, or as it was at an earlier change, so later edits and data syncs do not change it.
Take a Snapshot when you need to keep a plan exactly as it was, such as the budget the board approved or the forecast you sent investors. Later changes to the plan and integration syncs stop flowing into the Snapshot, so you can compare against it months from now. You can still edit a Snapshot by hand, so treat it as a copy your team agrees not to change. To understand what a Snapshot keeps and what it does not, see [Scenarios, Snapshots, and version history](/concepts/scenarios).
Name the Scenario to preserve and, if you like, the Snapshot's name:
> Take a Snapshot of Main called Board-approved budget, October 2026.
In the sidebar's Scenario picker, select Main or the Scenario you want to keep.
Open the Scenario picker again and select **Take snapshot**.
The **Take snapshot** dialog suggests a name such as **Main Scenario snapshot**. Change it under **Snapshot name** if you like, and select **Create snapshot**.
## Snapshot an earlier version
To keep a Scenario as it was before some later changes, such as last month's forecast, take the Snapshot from its history:
1. Open **Version history** from the clock icon in the Page header.
2. Point at the entry you want to pin, select its **…** actions button, and choose **Take snapshot here**.
3. Name the Snapshot and select **Create snapshot**.
Taking a Snapshot needs permission to create Scenarios. It is not offered on a phone, or while you are viewing a Snapshot.
## Related
* [Compare Scenarios side by side](/guides/compare-scenarios), to compare today's plan with a Snapshot.
* [Scenario and version history controls](/reference/scenarios-and-history)
# Schedule a recurring report with a Trigger
Source: https://docs.cfo.ai/guides/schedule-a-trigger
Have Ari do the same job on a schedule, such as a Monday cash summary: create a Trigger, write instructions that work unattended, get the result by email or Slack, change or pause it, and check whether it ran.
Use a Trigger when you want Ari to do the same job on a schedule without being asked. For example, Ari can write a cash summary every Monday, or check last month's spending after each close. A Trigger is a saved instruction with a schedule. Each time it runs, Ari works through it in a new chat of its own, and you read the result when it suits you. For how unattended runs work, see [Skills, Triggers, and Guides](/concepts/automation).
Tell Ari what to do, when, and where to send the result:
> Every Monday at 8am Pacific, summarize the prior week's cash activity and flag expenses that differ from our recent pattern. Email me the summary.
Ari asks about anything it is missing, saves the Trigger, and tells you its name and schedule. A new Trigger is on, and it first runs at the next scheduled time. You can also set one up by messaging Ari in Slack; see [Talk to Ari from Slack](/guides/use-ari-in-slack).
Open the account menu, your name or picture at the bottom of the sidebar, and choose **Manage triggers**.
Select **Write manually**. To work it out with Ari in a chat instead, select **Build with Ari**.
Enter a **Name**, such as Weekday cash brief. In **Instructions**, describe what Ari should check, update, or prepare each time. See [Write instructions that work unattended](#write-instructions-that-work-unattended).
Under **Schedule**, choose how often it runs in **Repeat**, such as **Weekly**, then the days and the time. You pick times in your own time zone, and cfo.ai saves them in UTC.
Select **Create trigger**. The Trigger appears in the list, and its details show when it runs next under **Next scheduled**.
## Write instructions that work unattended
Nobody is in a Trigger's chat while it runs, so Ari cannot ask you anything. When something is unclear, it makes a reasonable assumption, keeps going, and says what it assumed in its result. Write instructions that need no follow-up:
* Name what to read: the Page, table, or Variable, and the Scenario if it is not Main. A run has no Page open.
* Name the period, such as "the last closed month" or "the prior week".
* Say what to return and how to judge it, such as "flag any expense line more than 10% over Forecast".
* Say where to send it: "email me" or "send it to my Slack direct messages". Without that, the result stays in the run's chat.
> Compare Actuals with Forecast for Operating Expense in the last closed month on the Monthly P\&L Page. Flag any line more than 10% over Forecast, and send the summary to my Slack direct messages.
If you want a report on every run, say so. A run sends a warning only when something has changed since the last run, so the same warning does not arrive week after week; see [The record each run keeps](/concepts/automation#the-record-each-run-keeps).
## Choose where the result goes
Every run's result is saved in its own chat, listed in the Trigger's **History**. A copy goes somewhere else only when the instructions name the channel:
| The instructions say | What happens |
| - | - |
| Email me | Ari emails your sign-in address. Headings, lists, tables, and links in the message show in the email. |
| Send it to my Slack direct messages | Ari sends you a Slack direct message, once Slack is connected. See [Talk to Ari from Slack](/guides/use-ari-in-slack). |
| Email someone else | Nothing is sent. The run leaves an email preview in its chat, and the email goes only when you open the chat and select **Send**. See [Email preview card](/reference/chat#email-preview-card). |
A Trigger can message only you, and it cannot post to a Slack channel. To share a result with others, have the Trigger update a Page and [share the Page](/guides/share-a-page). A Trigger can also export a Page as a spreadsheet. The file is saved in the run's chat, and the email or Slack message can carry a download link that works for a limited time. See [Limits](/reference/limits#skills-triggers-and-guides).
## Change, pause, or delete a Trigger
Ask Ari, for example "Pause my weekly cash Trigger", "Move it to Tuesdays at 9am", or "Delete the old variance Trigger". Or open the account menu, choose **Manage triggers**, and select the Trigger:
* To change its name, instructions, or schedule, select **Edit trigger** (the pencil), make the change, and select **Save changes**.
* To pause it, select **Edit trigger**, turn off the switch so it reads **Disabled**, and select **Save changes**. Turn the switch back on to resume. A paused Trigger reads **Paused** in the list.
* To run it once right away, select **Run now**. This works on a paused Trigger too and leaves the schedule as it is.
* To delete it, select **Delete trigger** (the trash can) and confirm. A run that already started still finishes.
You see and manage only your own Triggers. Your teammates' Triggers do not appear on your Triggers page, and you do not appear on theirs.
## Check whether a Trigger ran
Open the account menu and choose **Manage triggers**. Each Trigger in the list shows what happened last, such as **Finished**, **Did not complete**, or **Skipped: Ari was still busy**. Select a Trigger to see **Next scheduled** and its **History**: every run, newest first, with its status. A run that did not complete shows its own one-sentence reason. Select a run to read its chat. For what each status means, see [The cfo.ai interface](/reference/interface#triggers-page).
You can also ask Ari:
> Did my Monday cash Trigger run last week, and did it finish?
Ari tells a run that only started apart from a run that finished its task.
A Trigger does one run at a time. When a run is still going at the next scheduled time, that scheduled run is skipped, and the Trigger's details read **The last scheduled attempt was skipped**.
## Reply to a run that needs you
A run never waits for an answer. When it has a question, such as which bank account to use, it works on an assumption and leaves the question in its chat. The run then reads **Needs response** in **History**. Open the run and answer there; Ari picks up from your reply. From your first reply, the chat is listed with your other chats. Your reply does not change the result the run recorded.
## If something goes wrong
Schedules are saved in UTC, so a run set for a local time moves by an hour when daylight saving time starts or ends. Edit the Trigger's time after the change.
Check that the instructions name the channel. Then open the run from **History**: Ari's activity in the chat shows whether the email service or Slack accepted the message. For Slack, check that Slack is connected and that your Slack email matches your cfo.ai email; see [Talk to Ari from Slack](/guides/use-ari-in-slack).
The message reads **This trigger already has a queued or active run. Open its latest conversation.** Open that run from **History**, or wait for it to finish.
## Related
* [Skills, Triggers, and Guides](/concepts/automation)
* [Save a workflow as a Skill](/guides/save-a-skill)
* [Talk to Ari from Slack](/guides/use-ari-in-slack)
* [Plans, AI usage, and connection slots](/concepts/plans-and-usage)
# Set Last close and date defaults
Source: https://docs.cfo.ai/guides/set-dates-and-last-close
Move Last close, your month-end date, after each close so your books show as Actuals; set the default grain and date range for new tables; and work with a fiscal year that does not start in January.
Move Last close, your month-end date, forward after you finish each month's close, so that month's numbers from your books show as Actuals and your Forecast starts after it. Last close does not move on its own; until you or Ari move it, a newly closed month stays in the Forecast. For how Last close splits your Model, see [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast).
## Move Last close
Ask Ari, naming the month you closed:
> We finished closing September. Set Last close to September 30, 2026.
Ari changes Last close in the Scenario your chat is in. To change another Scenario, name it.
In the sidebar's Scenario picker, choose the Scenario to change, usually Main.
Open the account menu, your name or picture at the bottom of the sidebar, choose **Settings**, and stay on **General**. Find **Date & Time**.
Select **Last close** and pick the month you closed. When the default date granularity is not **Month**, the calendar shows that period instead, such as quarters. cfo.ai saves the last day of the period, such as September 30.
You can also set it from the Scenario picker: select the gear on the Scenario's row, then **Last close**. See [Scenario and version history controls](/reference/scenarios-and-history#scenario-actions-menu).
Each Scenario has its own Last close. A Scenario follows Main's Last close until someone changes it in that Scenario.
To keep Last close current, schedule a Trigger that asks Ari to move it each month, on a day after your books usually close. See [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger).
## Set the default grain and date range for new tables
In **Settings → General → Date & Time**:
* **Default date granularity** sets the period new tables show, such as **Month** or **Quarter**. It is **Month** unless you change it.
* **Default date range** sets the first and last periods new tables show. Pick both on the calendar.
These defaults apply to Table blocks you create from now on. A table you already have keeps its own grain and range until you change them on the table. Like Last close, the defaults belong to the Scenario you are viewing, so set them while viewing Main to change them for your plan.
## If your fiscal year does not start in January
cfo.ai has no fiscal-year setting. Quarters, half-years, and years always follow the calendar, January to December. To work in a fiscal year that starts in another month, such as April:
1. Keep your Model monthly. Monthly numbers are the same in any fiscal year.
2. Set **Default date range** to start in the first month of your fiscal year, such as April, so new tables open on your year.
3. Ask Ari for fiscal-year totals, naming the months, such as "Add a total for our fiscal year, April 2026 through March 2027."
4. Tell Ari when your fiscal year starts and ask it to remember that for the workspace, so its answers use your year; see [Teach Ari your company's conventions](/guides/teach-ari).
For why years follow the calendar and other ways to show fiscal years, see [Fiscal years](/concepts/actuals-and-forecast#fiscal-years-quarters-and-years-follow-the-calendar).
## Related
* [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast)
* [Use different formulas for Actuals and Forecast](/guides/actuals-and-forecast-formulas)
* [Settings](/reference/settings#date-and-time)
* [Scenario and version history controls](/reference/scenarios-and-history)
# Share a Page outside your workspace
Source: https://docs.cfo.ai/guides/share-a-page
Send an investor, board member, judge, or accountant a read-only link to one or more Pages that opens without a sign-in or a cfo.ai account. Choose the Scenario it shows, add a password, give it a memorable address and preview, and turn it off later.
Use a share link when someone outside your workspace needs to read a report, such as a board update or a cash forecast. The link opens the Page read-only, without a sign-in or a cfo.ai account, in the Scenario you choose. Everyone inside your workspace can already open every Page, so you do not need a link for teammates.
Ask Ari for a view-only link and name the Page:
> Create a view-only link to the Board summary Page.
Ari returns the link's address. To have one link open several Pages, name them all. The Page you name first is where the link lands, and every Page opens in the Scenario your chat is in:
> Create a view-only link to the Board summary Page that also opens the Cash forecast and Headcount plan Pages.
Creating a link does not change the Model. Ari cannot add a password, give the link a memorable address, or turn a link off; do those in the **Share** panel.
Open the Page you want to share. A new link shows the Scenario you are viewing, so pick it first in the sidebar's Scenario picker.
Select **Share** in the Page header. The panel's title names the Page, so you can confirm you are sharing the right one.
Under **Pages to share**, select each other Page the link should open. The Page you started from is marked **Opens here**; readers land on it. One link opens up to 50 Pages.
Under **External access**, select **Create link**. The new link appears with its **Link settings** open.
In **Link settings**, choose another Scenario with **Shared scenario**, or turn on **Require password**. Turning on the password fills in a generated password; type your own instead if you prefer. Select **Save changes**. Saving gives the link a new address, so copy it after you save.
Select the copy button on the link (**Copy external link**) and send it to the reader. Send a password separately from the link.
The link works for anyone who has it until you turn it off, and cfo.ai does not list it publicly. It never lets the reader edit anything. To see exactly what a reader can and cannot see, read [Sharing and access](/concepts/sharing-and-access).
## Change a link's Scenario or password
1. Open the Page and select **Share**.
2. On the link, open **Link settings**.
3. Change **Shared scenario**, turn **Require password** on or off, or select **Generate password** for a new one. **Show password** displays the current password.
4. Select **Save changes**.
Saving replaces the link: the old address stops working, people who opened the Page through it lose that access, and the new address is shown in its place. Send the new address to everyone who should keep reading.
## Give the link a memorable address and a preview
A link without a password gets a short public address and a preview card. The card is the picture, headline, and description that chat and social apps show when someone pastes the link. cfo.ai writes the first preview from the Page.
1. On the link, select **Edit share preview**.
2. Edit the card's **Headline** and **Description**. For a different picture, select **Create new artwork** and choose one of the three options it makes.
3. Select **Publish changes**.
4. To give the link a readable address, type it under **Memorable address**, such as `board-update`, and select **Update**. **Copy link** copies the address.
A memorable address can be guessed, so use a password-free link only for Pages you are happy for anyone with the address to read. Changing the address retires the old one. A link that needs a password has no memorable address or preview to edit.
## Turn a link off
1. Open the Page and select **Share**.
2. On the link, select **Turn off external link** (the trash icon).
3. **Also remove access already granted by this link** starts checked, so everyone who opened the Page through the link loses access too. To let them keep their access, clear it.
4. Select **Turn off link**.
Every message in the **Share** panel, and what a reader sees when a link is off or needs a password, is listed in [The cfo.ai interface](/reference/interface#share-panel).
## Related
* [Sharing and access](/concepts/sharing-and-access)
* [Invite or remove teammates](/guides/invite-teammates)
* [Export a Page to Excel](/guides/download-a-spreadsheet)
* [Get a PDF report or slide deck](/guides/get-a-pdf-or-slides)
* [Brand your workspace](/guides/brand-your-workspace)
# Steer, stop, or add to Ari's work
Source: https://docs.cfo.ai/guides/steer-ari
Change what Ari is doing while it works: redirect it, stop it, or queue a follow-up for after it finishes. Also edit or delete a waiting message, reply to one part of an answer, and pick up a reply that stopped.
Use this when Ari is working and you want to change course, add a detail you forgot, or stop it. You do not have to wait for Ari to finish before you say more.
| You want to | Do this |
| - | - |
| Change direction now | Type the correction and press **Enter** |
| Add something for after Ari finishes | Type it and press **⌥ Enter** (**Alt Enter** on Windows and Linux) |
| Stop Ari | Select **Stop**, or press **Esc** in the message box |
| Ask about one part of an answer | Select the words and choose **Reply** |
| Pick up a reply that stopped | Select **Retry** |
## Steer Ari while it works
Type your message and press **Enter**. The message reaches Ari at its next step, and Ari adjusts what it is doing. Until Ari takes it, the message waits above the message box with the caption **Sending to the current turn**.
> Do this in a Downside Scenario and leave Main unchanged.
> Use our QuickBooks numbers for March, not the estimate.
You can attach a file while Ari works too, such as a screenshot you paste into the message box, and send it with or without text.
## Queue a message for after Ari finishes
Type your message and press **⌥ Enter**. It waits with the caption **Queued for after this turn**, and Ari reads it when it finishes what it is doing. To send a queued message to the current turn after all, select **Steer now** on it.
## Edit or delete a waiting message
Point at a waiting message:
* Select the edit button (**Edit pending message**) to take it back into the message box, change it, and send it again.
* Select the delete button (**Delete pending message**) to remove it before Ari reads it.
A message with files, a quoted excerpt, a plan approval, or answers to Ari's questions can only be deleted, not edited. A message that has already started a turn cannot be edited or deleted. See [Chat controls and messages](/reference/chat#messages-you-send-while-ari-works) for the notices.
## Stop Ari
Select **Stop**, which replaces **Send** while Ari works and the message box is empty, or press **Esc** in the message box. The chat shows **Conversation interrupted - tell Ari what to do differently.** Then say what you want instead.
Changes Ari saved before you stopped it stay in the Model. To see what it finished, look at the Page or ask "What did you change before I stopped you?" To take those changes back, see [Undo a change or restore an earlier version](/guides/undo-changes).
## Reply to one part of an answer
Select the words in Ari's answer that you want to ask about. Choose **Reply** above the selection, or press **⌘ L**, and the passage lands in the message box as a quote. Type your question and send it. You can quote up to five passages in one message. See [Chat controls and messages](/reference/chat#reply-to-part-of-an-answer).
## Recover a reply that stopped
If the chat says **Ari stopped before finishing.**, select **Retry** to have Ari pick the work back up; anything it saved before it stopped is still in the Model. For **Not sent.**, a chat that grew too long, and other errors, see [Ari stopped or showed an error](/guides/ari-is-slow-stuck-or-wrong#ari-stopped-or-showed-an-error).
## Related
* [How Ari works with you](/concepts/working-with-ari#during-a-long-build)
* [Answer Ari's questions and approve its actions](/guides/approve-ari-actions)
* [Keyboard shortcuts](/reference/keyboard-shortcuts#chat-with-ari)
# Replace estimates with your real numbers
Source: https://docs.cfo.ai/guides/switch-to-real-numbers
Move a Model that Ari built from estimates onto your real numbers. Bring in your books, a spreadsheet, or exports, have Ari rebuild on Actuals while keeping the assumptions you want, check a closed month, and set Last close. If you started with a sample business, build a Model of your own business instead.
If Ari built your first Model from estimates, you can move it onto your real numbers at any time. Ari replaces the estimated history with your records and keeps the forecast assumptions you choose, so the plan you worked on carries over. A Model of a sample business does not carry over this way; see [If you started with a sample business](#if-you-started-with-a-sample-business).
## Choose where your real numbers come from
| Your numbers are in | Do this |
| - | - |
| QuickBooks Online, Xero, NetSuite, or Rillet | [Connect your accounting system](/guides/connect-accounting) |
| A payroll, HR, CRM, or payments system | [Connect it](/guides/connect-other-sources) |
| A system cfo.ai does not connect to | Export a report and [upload it as a CSV](/guides/upload-a-csv) |
| Your own spreadsheet model | [Import it](/guides/import-a-spreadsheet-model) |
Your books give Ari the most to work with, because they hold every month's actual revenue, costs, and cash.
## Switch the Model
Connect, upload, or attach your source as the guides above describe. Ari tells you in the chat when the data has loaded. For QuickBooks Online and Xero, your three most recent complete months arrive first, and the rest of your history follows.
Say which assumptions to keep:
> Rebuild the Model on our QuickBooks actuals. Keep the hiring plan and the growth rate we set, and list every estimate you replace.
Ari replaces the estimated months with your records, keeps the assumptions you named as inputs you can still edit, and lists what it changed and what is still an estimate. Labeled estimates that a source now covers give way to that source's numbers.
Pick a month you have closed and ask:
> Compare September in the Model with QuickBooks and list every account that differs.
See [Check that your numbers match your books](/guides/check-imported-data) for the common causes of a difference.
Set **Last close** to your latest closed month, your month-end, so closed months read your books and later months read your forecast. See [Set Last close and date defaults](/guides/set-dates-and-last-close).
To find anything that is still typed in or estimated, turn on the optional **Model audit** Skill and ask Ari to audit the Model. See [Turn on a built-in Skill](/guides/turn-on-built-in-skills).
## If you started with a sample business
A Model built from **Something hypothetical** describes a sample business, labeled as a sample, and holds none of your numbers. Rather than convert it, ask Ari to build a Model of your own business once your data is in:
> Build a 12-month model of our business from our QuickBooks data.
## Take a change back
Every change Ari makes is recorded in Version history, so you can undo the rebuild or restore the Model as it was before. See [Undo a change or restore an earlier version](/guides/undo-changes).
## Related
* [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast)
* [How connected data reaches your Model](/concepts/connected-data)
* [Build your first Model](/tutorials/first-model)
# Teach Ari your company's conventions
Source: https://docs.cfo.ai/guides/teach-ari
Have Ari remember your preferences and your company's terms, see and remove what it remembers, correct what it found about your company at signup, and save repeated work as a Skill.
Use this when you find yourself telling Ari the same thing twice, such as how you format tables, when your fiscal year starts, or what a term means at your company. Ari saves the fact in its memory and applies it in later chats. To learn how memory works, see [What Ari remembers](/concepts/ari-memory).
## Tell Ari what to remember
Say it plainly, and say who it is for:
> Remember that I prefer ending cash shown in thousands.
> For the whole workspace, remember that "Enterprise" means contracts above \$50,000 a year.
If you do not say who a fact is for, Ari decides from how you put it, and saves the fact for you alone when that is unclear. Ask Ari to confirm what it saved and whether it is yours or the workspace's.
You can give several facts in one message, or paste a page of company conventions and ask Ari to remember what is in it; Ari saves each fact it finds. For the size of a page Ari can take at once, see [Limits and file types](/reference/limits).
When Ari guesses wrong during a task, correct it and ask it to remember in the same message:
> Our customers pay annually up front, not monthly. Fix the revenue timing, and remember this for the whole workspace.
## See what Ari remembers
Ask:
> What do you remember about me?
> What has the workspace taught you about how we report?
Ari lists the saved facts for you, for the workspace, or both when you do not say. You can also open the account menu, your name or picture at the bottom of the sidebar, and choose **Manage memories**. It starts a new chat in which Ari lists what it remembers about you and the workspace and helps you change it.
## Change or remove a fact
To change a fact, tell Ari the new version. The new fact stays where the old one was, for you or for the workspace.
To remove a fact, ask Ari to forget it:
> Forget that I prefer quarters.
Ari removes that one fact and confirms. Forgetting a workspace fact removes it for everyone, so say which you mean when the same fact could be yours or the workspace's.
## Correct what Ari found about your company
When you sign up, cfo.ai searches public web pages for you and your company and names the workspace after the company it found. Ari posts what it found as a brief in the Welcome chat, with its sources. If it found the wrong company or got something wrong, tell Ari what is true, and save it for the workspace so every later chat uses it:
> That's not us. We're \[a bookkeeping firm for restaurants], not a cybersecurity consultancy, and we have \[12] employees. Use this instead of the research, and remember it for the whole workspace.
If the workspace has the wrong name, ask Ari to rename it:
> Rename this workspace to \[Company name].
You can also change the name in **Settings**. See [Settings](/reference/settings). For what the brief and its sources show, see [Chat controls and messages](/reference/chat#company-research-and-brief).
## Teach a process instead of a fact
Memory holds short facts. To teach Ari a process you repeat, such as how you review the month after close, save it as a Skill: a named set of instructions Ari follows whenever the task comes up. See [Save a workflow as a Skill](/guides/save-a-skill).
A definition that should calculate, such as how you measure churn, belongs in the Model as a Variable with a formula, so every Page and Scenario uses the same number. Ask Ari to build it there:
> Add a Churn rate Variable calculated as lost customers divided by customers at the start of the month, and use it in the revenue forecast.
## Related
* [What Ari remembers](/concepts/ari-memory)
* [Get better answers from Ari](/guides/get-better-answers)
* [Brand your workspace](/guides/brand-your-workspace)
# Test a decision in a Scenario
Source: https://docs.cfo.ai/guides/test-a-decision-in-a-scenario
Create a Scenario, a what-if version of your plan, to see what a decision such as a new hire or a price increase does to cash and results before you change Main.
Use a Scenario when you want to see what a decision does before you make it, such as two new hires, a price increase, or a hiring freeze. A Scenario is a what-if version of your plan. It starts as a copy of Main, the Scenario that holds your current plan, and keeps receiving Main's updates. Your changes stay in the Scenario until you merge them. To understand how that works, see [Scenarios, Snapshots, and version history](/concepts/scenarios).
Name the Scenario and describe the decision and what you want to see:
> Create a Scenario called Hire two engineers in Q1. Add two engineers at \$150,000 a year each starting in January, and compare cash and cash runway with Main.
Ari creates the Scenario, makes the changes in it, and shows the comparison. Main is not changed. Other decisions work the same way:
> Can we hire three engineers in September? Build a hiring Scenario and show the effect on payroll, cash, and cash runway.
> Create a Scenario where enterprise churn falls by two percentage points and show the effect on revenue and cash over the next year.
To keep working in the Scenario later, open it from the Scenario picker, or name it in your request:
> In the Hire two engineers in Q1 Scenario, move the start date to March.
Open the Scenario picker, the **Main Scenario** control in the sidebar's **Model** section, above your Pages. Select **Create scenario**, type a name, and press **Enter**. cfo.ai creates the Scenario and switches to it.
Edit the values and formulas the decision touches, on any Page. Every edit you make while the Scenario is selected is saved in the Scenario, not in Main.
Read the Pages in the Scenario, or switch back to Main in the picker to see the plan without your changes. To see both versions on one table, [compare Scenarios side by side](/guides/compare-scenarios).
Creating a Scenario needs permission to create Scenarios, and it is not offered on a phone.
## If you cannot find your changes
Check the Scenario picker. Pages show the Scenario selected there. Changes you made in a Scenario, including new Pages, appear only while that Scenario is selected, until you merge it. The address bar changes when you switch Scenarios, so you can bookmark a Scenario or send a teammate a link to it.
## Next steps
* [Compare Scenarios side by side](/guides/compare-scenarios)
* [Merge a Scenario into Main](/guides/merge-a-scenario) when you decide to adopt the plan.
* [Save a plan as a Snapshot](/guides/save-a-snapshot) to keep a version exactly as it is today.
# Turn on a built-in Skill
Source: https://docs.cfo.ai/guides/turn-on-built-in-skills
Turn on cfo.ai's optional built-in Skills for everyone in your workspace: Model audit, Flash report, and Model migration tie-out. Then see what to ask Ari once each one is on.
Turn on a built-in Skill when you want Ari to follow cfo.ai's ready-made process for a common job, such as checking your Model before a board meeting. Most of Ari's built-in Skills are always on. Three are optional and stay off until someone in the workspace turns them on: **Model audit**, **Flash report**, and **Model migration tie-out**.
Open the account menu, your name or picture at the bottom of the sidebar, choose **Settings**, and select **Skills**.
Scroll past **Your skills** to **Built-in skills**.
Turn on the switch next to the Skill. It applies right away to everyone in the workspace.
Ari cannot turn a built-in Skill on or off for you; use the switch in Settings. Once a Skill is on, you do not need to name it. Ari uses it when your request matches what it is for:
| Skill | Use it to | Ask, for example |
| - | - | - |
| **Model audit** | Check that the formulas, mappings, and statements hold together, that the Model ties to your ledger and bank, and what is still typed in or estimated. Ari writes a short audit Page with live checks and fixes problems when you ask. | "Check my model before the board sees it." |
| **Flash report** | Read the current month before the books close, with actuals, committed spend, and estimates kept apart. Ari builds it in a Scenario of its own. | "Where are we this month?" |
| **Model migration tie-out** | Compare your cfo.ai Model with the Excel workbook you are moving from, and explain every difference. Ari builds a tie-out Page where you decide how to treat each one. | "Does this tie to my workbook?" with the workbook attached |
For more on moving from a spreadsheet, see [Import an existing spreadsheet model](/guides/import-a-spreadsheet-model).
## Turn a built-in Skill off
Turn off its switch in **Settings → Skills**. Ari stops using it for everyone in the workspace.
cfo.ai keeps built-in Skills up to date, so you cannot edit them. To make one work differently for you, save your own Skill under a different name; see [Save a workflow as a Skill](/guides/save-a-skill).
## Related
* [Save a workflow as a Skill](/guides/save-a-skill)
* [Skills, Triggers, and Guides](/concepts/automation)
* [Settings](/reference/settings#skills)
# Undo a change or restore an earlier version
Source: https://docs.cfo.ai/guides/undo-changes
Take back a change you or Ari made, find a change in version history, or return a Scenario to how it was at an earlier time.
Every change to the Model is recorded in version history, whether you made it or Ari did, so you can take any of them back. Choose the method by whose change it is and how far back you want to go.
| You want to | Do this |
| - | - |
| Take back an edit you just made | Press **⌘ Z** (**Ctrl Z** on Windows and Linux) |
| Take back a change Ari made | Ask Ari, or undo the entry in version history |
| Take back one older change and keep everything after it | Undo that entry in version history |
| Return the whole Scenario to how it was at a point in time | Restore that version |
## Undo your own last edit
Press **⌘ Z** (**Ctrl Z** on Windows and Linux) to take back the last change you made to the Model, and **⌘ Shift Z** to redo it.
**⌘ Z** undoes only the edits you made in this browser tab since it opened. It does not undo Ari's changes or changes from an earlier visit; use version history for those. It does not undo Model changes while you are typing in a text field or the formula editor. The notices undo and redo show are listed in [Keyboard shortcuts](/reference/keyboard-shortcuts#undo-and-redo).
## Ask Ari to undo
> Undo that last change.
Ari undoes the most recent change as a whole. You can also describe an older change, such as "Undo the change to the hiring plan from this morning." Ari reads the history and undoes the change you meant.
## Undo one change in version history
Select **Version history**, the clock icon in the Page header. Entries are listed newest first, each with who made the change, when, and what it touched.
Use **Filter version history** to show only some people or kinds of change, or **Jump to date** to move to a day.
Point at the entry, select its **…** actions button, and choose **Undo this change**. If later changes depend on it, cfo.ai asks you to confirm in an **Undo this change?** dialog.
An undone entry offers **Redo this change**.
## Restore an earlier version
Restoring returns the whole Scenario to an earlier point and rolls back every change made after it.
1. In version history, select the entry for the point you want. The Model shows how it looked then, with a banner that says you are viewing a version.
2. Select **Restore this version**, or open the entry's **…** actions button and choose **Restore this version**.
3. Review the preview and confirm.
The restore is itself a recorded change, so you can undo it. Restoring needs full access to the Scenario. In a Scenario other than Main, changes made in Main stay as they are.
To leave the past version without restoring it, select **Back to current**.
## Related
* [Scenarios, Snapshots, and version history](/concepts/scenarios)
* [Scenario and version history controls](/reference/scenarios-and-history)
# Upload a CSV as a data source
Source: https://docs.cfo.ai/guides/upload-a-csv
Upload a CSV export, such as bank transactions or a report from a system cfo.ai does not connect to, so its rows feed your Model. How to prepare the file, upload it, replace it with a newer version, and handle Excel workbooks and PDFs.
Upload a CSV when the numbers you need are not in a system cfo.ai connects to, such as an export from an unsupported accounting or payroll tool, a one-off report, or a list you keep by hand. Its rows become Variables and Dimensions you can use in tables and formulas. An upload never refreshes on its own, so upload the new version when the numbers change.
## Prepare the file
* Save it as a `.csv` file of up to 50 MB.
* Put the column names on the first line, and keep one record per row after it, with the same number of fields on every row.
* Use commas between fields.
* Delete report title lines above the table and total lines below it. If you would rather not, attach the file to a chat instead; Ari cleans up a copy and loads that.
An upload uses no plan slot. [How imported data loads](/reference/imported-data#csv-files) lists how cfo.ai reads names, numbers, and dates.
## Upload the file
Attach the CSV to a chat message. The file's tile shows **Data source** and **Chat only**. A CSV starts on **Data source**, which imports it into cfo.ai as a data source when you send. Say what the file holds and which column dates the rows:
> This is our bank transactions export for 2026. Load it as a data source. The Posted Date column dates each row.
Ari tells you when the rows are in. Choose **Chat only** instead when you want Ari to read or summarize the file without loading it.
In the sidebar, point at **Connections** and select **+** (**Add integration**). On the **Integrations** tab, select **Upload**.
**Import CSV files** opens: **Each file creates a separate data source in cfo.ai.** Drop one or more files, or select **Click to upload CSVs or drag and drop**.
Select **Import** and the number of files. Each file appears under **Connections**, named after the file.
Ask Ari to use the data, and name the column that dates the rows, such as an invoice date. Ari writes the saved query that brings the rows into your Model.
## Replace the file with a newer version
Select the file's row in the sidebar to open its page, and select **Update file**. **Update CSV file** opens; choose the new file. Its rows replace the old ones. When the columns keep their names, your tables and formulas keep reading the same Variables.
You can also attach a file with the same name to a chat. Ari asks before it replaces the existing data source.
## Use a CSV from another chat
Ari can load a CSV you attached in any of your chats in this workspace. Name the file. If more than one of your chats holds a file with that name, Ari asks which one you mean.
## Excel workbooks and PDFs
The **Upload** button takes CSV files only.
* To load one sheet of an Excel workbook as data, attach the workbook to a chat and ask Ari to load that sheet as a data source. You can also save the sheet as a CSV and upload it.
* To rebuild a whole workbook model, see [Import an existing spreadsheet model](/guides/import-a-spreadsheet-model).
* To keep a workbook in sync, store it in Google Drive and [connect it](/guides/connect-a-google-sheet).
* To use a PDF, such as a bank statement, attach it to a chat and ask Ari to turn its table into a data source. Check the totals Ari reports against the statement.
## Remove the file
Point at the file's row in the sidebar and select the trash button, **Remove file**, or open its page and select **Remove file**. Confirm **Do you want to remove this file? This cannot be undone.**
## If the file does not load
The row reads **The file couldn’t load**, followed by what was wrong, such as **the header row has 5 columns and row 12 has 6**. Fix the file and select **Update file**. To see what went wrong, ask Ari; it can copy the file you uploaded into the chat and read it. [Connection statuses and actions](/reference/connection-statuses#messages-about-uploaded-files) lists every message.
If your Model shows fewer rows than the file, the saved query may filter them on purpose. See [Check that your numbers match your books](/guides/check-imported-data).
## Related
* [How imported data loads](/reference/imported-data)
* [Attach files to a chat](/guides/attach-files)
* [Connect your accounting system](/guides/connect-accounting)
# Talk to Ari from Slack
Source: https://docs.cfo.ai/guides/use-ari-in-slack
Connect Slack to cfo.ai and chat with Ari in Slack direct messages. Who in your Slack workspace can message Ari, why Ari answers only in DMs and not in channels, and what to do when your Slack email differs from your cfo.ai email.
Use this to ask Ari questions from Slack, and to have Ari send you messages there, such as a weekly cash summary from a Trigger. Ari works in Slack direct messages, as the cfo.ai person whose email matches the Slack account that writes to it.
## Before you start
You need:
* Permission to add connections in your cfo.ai workspace, the same permission you need to connect a data source.
* A Slack workspace you can add apps to. If your Slack workspace requires approval for new apps, a Slack admin may need to approve the install.
* To message Ari, a Slack profile email that belongs to a cfo.ai account in this workspace, usually the email you sign in with. If your Slack email is a different address, see [If your Slack email differs from your cfo.ai email](#if-your-slack-email-differs-from-your-cfoai-email).
## Connect Slack to your workspace
Open the account menu, your name or picture at the bottom of the sidebar, choose **Settings**, and select **Ari**. Find the **Slack** section, which reads **Add Ari to Slack**.
Select **Add to Slack**. A Slack window opens; choose your Slack workspace and approve the app. If nothing opens, allow pop-ups for cfo.ai in your browser and try again.
A notice reads **Slack connected**, and the section shows **Connected to** your Slack workspace's name, with **Ari can now DM you in this Slack workspace.**
A cfo.ai workspace connects to one Slack workspace. The connection serves everyone in your cfo.ai workspace, so each person does not need to connect it again.
## Message Ari in Slack
In Slack, open a direct message with the cfo.ai app and write to Ari as you would in cfo.ai:
> How much cash did we burn last month, and how many months of cash runway do we have?
While Ari works, Slack shows that it **is typing…**. Its reply posts in the same thread when the turn ends. Reply in that thread to continue the same conversation; a new message outside the thread starts a new conversation.
## What you can do in Slack
You can ask Ari anything you can ask in cfo.ai: explain a number, build or change the Model, or schedule a Trigger. In Slack, Ari works in Main unless you name another Scenario.
Some things show only in cfo.ai, not in Slack:
* Question forms, plan cards, approval cards, and download cards. When Ari needs an answer or an approval, open the conversation in cfo.ai.
* Files. Ari cannot read a file or image sent in Slack. Paste the text into your message, or attach the file in a chat in cfo.ai.
* Formatting. Slack shows the text, bold, and links of Ari's replies, and prints tables and headings as plain characters.
A link in Ari's reply to a Page or a table opens it in your browser, under your own access.
## Continue a Slack conversation in cfo.ai
Every conversation you have with Ari in Slack is also a chat in cfo.ai, in your chat list under your other chats. Open it there to see the cards Slack cannot show, answer Ari's questions, or keep working on a larger screen.
## Have Ari message you in Slack
Ari can send you a direct message in Slack, for example the result of a scheduled Trigger:
> Every Monday at 8am, summarize last week's cash in and out and send it to my Slack direct messages.
Ari can message only you, the person who asked, and it finds you in Slack by your cfo.ai email address. See [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger).
## Who can message Ari
Anyone in your Slack workspace whose Slack profile email matches the email of someone in your cfo.ai workspace can message Ari. Ari works as that person, with that person's access, so each person's Slack conversations, memory, and permissions stay their own.
Someone without a matching cfo.ai account gets a reply that Ari could not find a cfo.ai account for their Slack email. To let them use Ari, invite the email address on their Slack profile to your workspace. Once they accept, they can message Ari. See [Invite or remove teammates](/guides/invite-teammates).
## Ari answers only in direct messages
Ari answers only in direct messages. It does not read or answer messages in Slack channels, public or private, and it cannot post to a channel for you. You can add the app to a channel, but if someone mentions Ari there, Ari only replies in that thread that it chats in direct messages. To share an answer with a channel, copy it there, or share the Page it built. See [Share a Page outside your workspace](/guides/share-a-page).
## If your Slack email differs from your cfo.ai email
Ari matches you by the email address on your Slack profile. When it differs from your cfo.ai sign-in email, Ari cannot match you to your account, and it cannot send you Slack messages. The reply to your messages depends on that address:
* If no cfo.ai account uses it, Ari replies that it could not find a cfo.ai account for your Slack email.
* If it belongs to a cfo.ai account outside this workspace, Ari replies that your account does not have access to this workspace's cfo.ai.
Either way, do one of these:
* Change the email on your Slack profile to your cfo.ai sign-in email.
* Invite your Slack email address to the cfo.ai workspace, then accept the invitation from that address. That address is a separate cfo.ai account, with its own chats and memory. See [Invite or remove teammates](/guides/invite-teammates).
## Disconnect Slack
In **Settings → Ari → Slack**, select **Disconnect**, then confirm. Ari can no longer post to that Slack workspace until someone connects it again.
## If something goes wrong
For every message Ari can send you in Slack, and what each one means, see [Chat controls and messages](/reference/chat#ari-in-slack).
## Related
* [How Ari works with you](/concepts/working-with-ari)
* [Settings](/reference/settings)
* [Change your theme, sounds, and notifications](/guides/personalize-cfoai)
# Write a formula
Source: https://docs.cfo.ai/guides/write-a-formula
Write a formula in a Table block yourself: open the editor for a row, a breakdown, or one cell, reference other Variables, and save. Also filter, fix, or repoint a reference, override one cell or remove a row's formula, and copy common formula patterns.
Write a formula when a number should be calculated from other numbers, such as Gross Profit from Revenue and Cost of Goods Sold. A formula names Variables, not cells, so one formula on a row fills every month and every item that row shows. To learn how cfo.ai decides which formula fills a cell, read [How formulas work](/concepts/formulas).
To have Ari write the formula for you, see [Have Ari write, change, or explain formulas](/guides/change-formulas-with-ari).
You need a desktop browser and edit access to the Page.
## Open the editor in the right place
Where you open the editor decides which cells the formula fills. Start with a formula for the whole row. Give one cell its own formula only for a real exception, because that cell stops following its row.
* For a whole row, every period and item it shows: point at the row name and select the **fx** button, **Edit variable formula**. Or right-click the row name and choose **Edit formula**.
* For each item under a breakdown, such as each department under Revenue: right-click the Revenue row and choose **Edit formula for Department**. When there is more than one level below, choose **Edit child formula** and pick the level.
* For one cell only: double-click the cell, or select it and press **Enter**.
The editor's title names the Variable and where the formula applies, such as **All Months**, one item such as **Sales**, or **Any Segment** for every item. Every other place that opens the editor, such as the formula bar or a Dimension's label row, is listed in [Where to open the formula editor](/reference/table-blocks#where-to-open-the-formula-editor).
## Write the formula
Open it from the place that matches the cells you want to fill.
Type the formula with the names you see in cfo.ai:
```text theme={null}
Revenue - `Cost of Goods Sold`
```
Wrap a name in backticks when it contains spaces or punctuation. Names must match exactly, including capital letters. As you type, autocomplete suggests Variables, Dimensions, items, functions, and keywords. Press **Ctrl Space** to open it, and **⌥ F12** (**Alt F12**) to see the syntax and an example of the function under the cursor.
Press **Enter**. If Revenue is 100 and Cost of Goods Sold is 40, Gross Profit shows 60.
A leading `=` is optional, and cfo.ai removes it when it saves: `=Revenue * 1.1` saves as `Revenue * 1.1`. In a cell of a text Variable, start a formula with `=`; without it, cfo.ai saves the words as text.
For every operator and lookup, see [Formula syntax](/reference/formula-syntax). For every function, see [Formula functions](/reference/functions).
## Click or point at a cell to reference it
While the editor is open, you can insert a reference by clicking, the way a spreadsheet does. A reference can go where the cursor sits in an empty editor, or after an operator, a bracket, a comma, or `and`.
* With the cursor in such a place, click another cell, a row name, or a period header. Clicking the previous month's Revenue cell inserts `Revenue[-1]`, and clicking a period header inserts a date such as **Last month**. When the cursor is anywhere else, a click saves the formula and closes the editor.
* In the editor that opens on a cell, put the cursor in such a place and press an arrow key to point at the neighboring cell. Keep pressing to move. **F2** also starts pointing. Press **Enter** to keep the reference, or **Esc** to cancel. Once you type after the reference, the arrow keys move the cursor again.
* Double-clicking a cell opens that cell for editing instead. The editor saves first, without the reference the first click inserted.
Each reference and the cell it reads share a color. The cell gets a dashed outline in that color, so you can see which part of a long formula reads which cell.
When the cell you click has fewer Dimensions than the cell you are editing, such as a rate with no months, cfo.ai inserts a fixed reference. Every month then reads that one cell.
## Filter a reference
A filter limits a reference to some items or dates, such as Revenue for the West region.
1. Select the reference in the editor. Its panel opens.
2. Select **Add filter**, choose a Dimension, and choose the items. The filter reads like a sentence: **Where** Region **is** West.
3. For a date, choose a preset: **Last 3 months of actuals**, **Last 6 months of actuals**, **Last 12 months of actuals**, **Year to date**, **Quarter to date**, or **Custom range**. In a Forecast formula, the "last months of actuals" presets read the months up to **Last close**, which gives you a run rate. See [Date filter presets](/reference/formula-syntax#date-filter-presets).
Press **⌥ ↓** (**Alt ↓**) to open the filters of the reference at the cursor. **Reset** removes the filters, so the reference follows the cell again. The typed form of a filter is `Revenue[Region = "West"]`.
## Fix a reference so it reads one value everywhere
A fixed reference reads the same value in every row and period. Use it for an input such as a single tax rate or a reported figure:
```text theme={null}
`Reported June MRR`$
```
To fix or release a reference, select it and press the pin button in its panel, whose tooltip reads **Fixed: does not follow the current row or period. Only its own filters apply. (F4)**. Or press **F4** with the cursor on the reference. Filters you wrote stay either way. A column of an imported table cannot be fixed.
## Point a reference at something else
1. Select the reference to open its panel.
2. Select the reference's name, and search for the Variable or Dimension you want.
3. Choose a result. It replaces the reference.
**Toggle details** in the search shows which table and Page own the highlighted result. Inside a filter, the same search lists Dimensions only.
## Go to a reference's source
Point at a Variable's reference and select the arrow at its end to go to that Variable's row. The arrow reads **Go to its row in this table**, **Go to its row on this page**, or **Go to source in** and the table's name. Your draft stays open, and **Back to cell** (or **Back to table**) returns you to it. When the source is on another Page, a card holds your draft until you go back.
For an imported Variable that is on no table, the arrow opens **About this variable’s source**, with **Open connection**.
## Save, cancel, or open the larger editor
* **Enter** saves. In the editor that opens on a cell, **Tab** and **Shift Tab** save and move right or left. Clicking elsewhere also saves.
* **Esc** closes the editor without saving. In the larger editor, the close button also discards your changes.
* To get more room, select the expand button (**Open full editor**) in the editor that opens on a cell.
The formula saves in the Scenario shown in the sidebar's Scenario picker. A formula with a mistake still saves. A red line under the editor describes the problem as you type, and the cells show an error code such as `#NAME?` until you fix it. See [Fix a formula that shows an error or a wrong number](/guides/fix-formula-errors). When the server refuses a save, the editor reopens with your draft and a notice says why; see [When a change is not saved](/reference/table-blocks#when-a-change-is-not-saved).
## Override one cell
When you open a cell that has no formula of its own, the editor shows the formula it inherits, dimmed, as a suggestion.
* Point at the dimmed formula to see where it comes from: **Inherited from** and the address that owns it. **Edit source formula** opens that formula.
* Click the dimmed formula, press **→**, or press **Tab** to copy it into this cell and edit it.
* Start typing to replace it.
Saving gives the cell its own formula, called an override. The cell keeps it when the row's formula changes, and a dashed underline marks it.
## Remove an override or a row's formula
To make one cell follow its row again, select the cell and press **Delete** or **Backspace**; see [Clear a value or a cell's own formula](/guides/enter-values#clear-a-value-or-a-cells-own-formula).
To remove a row's own formula, open the row's formula, delete all the text, and press **Enter**. The row then follows the formula it inherits, or its imported values.
To find who added a formula or an override, and when, open version history; see [Undo a change or restore an earlier version](/guides/undo-changes).
## Propagate a formula to the rows above
After you save a formula on a lower row of a breakdown, the editor can ask **Propagate?** with **Would you like to propagate this formula to parent rows?**
* Choose **Yes** to give the cells above it formulas that add up the rows beneath them, with `sum`.
* Choose **No** to leave the rows above as they are.
Press **⌘ Enter** (**Ctrl Enter** on Windows and Linux) while saving to propagate without the question. A breakdown's rows above already add up the rows beneath them unless they have a formula of their own; see [Totals, aggregation, and time rollups](/concepts/totals-and-rollups).
## Show or hide the formula columns
The formula columns start hidden.
* Rest the pointer on the **fx** button, **Show formulas**, at the top of the row-name column to peek at them. They stay while the pointer is on them.
* Click the button to keep them open. Click it again, **Hide formulas**, to hide them. The choice is saved for this browser.
* In a phone browser, open the table's **…** menu and choose **Show formula columns**.
On a table with months, the formula column splits into **Actuals Formula** and **Forecast Formula**. To hide one of them or bring it back, see [Show the Actuals and Forecast formulas side by side](/guides/actuals-and-forecast-formulas#show-the-actuals-and-forecast-formulas-side-by-side).
## Common patterns
Replace the example names with your own Variables and Dimensions. Before you save a pattern, check the Variable it belongs to, where it applies, and whether it is for Actuals, Forecast, or both.
### Gross profit and gross margin
```text theme={null}
Revenue - `Cost of Goods Sold`
```
```text theme={null}
if(Revenue <> 0, (Revenue - `Cost of Goods Sold`) / Revenue, 0)
```
### Change from the previous month
```text theme={null}
Revenue - Revenue[-1]
```
At monthly grain, `[-1]` reads the previous month. The same formula on a quarterly row reads the previous quarter.
### Growth rate
```text theme={null}
iferror(Revenue / Revenue[-1] - 1, 0)
```
### Same month last year
```text theme={null}
Revenue[-12]
```
At monthly grain, `[-12]` reads the same month a year earlier. On a quarterly row it reads twelve quarters back, so use `[-4]` there.
### Running balance
```text theme={null}
`Cash Balance`[-1] + `Net Cash Flow`
```
Give the first month an opening balance of its own, or the balance starts from 0. A balance that reads itself without `[-1]` is a circular reference.
### Total across a Dimension
```text theme={null}
sum(Revenue[Department in any])
```
### Share of the total
```text theme={null}
iferror(Revenue / sum(Revenue[Department in any]), 0)
```
On each department's row, the plain `Revenue` reads that department, so the result is its share.
### Average of chosen items
```text theme={null}
average(Revenue[Region in {"East", "West"}])
```
### Trailing 12 months and trailing average
```text theme={null}
sum(Revenue[Date.Month in [-11]:[0]])
```
```text theme={null}
average(Revenue[Date.Month in [-3]:[-1]])
```
The first sums the current month and the 11 before it. The second averages the three months before the current one.
### Year to date
```text theme={null}
sum(Revenue[Date.Month in startofyear([0]):[0]])
```
### Read a per-item input that has no months
```text theme={null}
`Annual Salary`$[Employee = this.Employee] / 12
```
A salary typed once per employee has no months. A plain `` `Annual Salary` `` in a monthly formula looks for it in each month, finds nothing, and reads 0. Inside the filter, `this.Employee` means the employee of the cell being calculated, so each employee's row reads that employee's salary. For more on `this`, see [Lookups and filters](/reference/formula-syntax#lookups-and-filters).
### Payroll from headcount and salary
```text theme={null}
Headcount * `Average Salary`
```
Use this in Forecast when Actuals should keep reading imported payroll; see [Use different formulas for Actuals and Forecast](/guides/actuals-and-forecast-formulas).
### Tiered rate
```text theme={null}
if(Revenue > 100000, Revenue * 0.05, Revenue * 0.03)
```
### Missing date
```text theme={null}
if(`End Date` = null, 1, 0)
```
This marks people with no end date, such as current employees.
### Period boundaries
```text theme={null}
eomonth(Date.Month, 0)
date(year(Date.Month), 12, 31)
```
The first returns the last day of the month. The second returns December 31 of the period's year.
## Related
* [How formulas work](/concepts/formulas)
* [Formula syntax](/reference/formula-syntax)
* [Formula functions](/reference/functions)
* [Fix a formula that shows an error or a wrong number](/guides/fix-formula-errors)
* [Keyboard shortcuts](/reference/keyboard-shortcuts#formulas-and-filters)
# cfo.ai documentation
Source: https://docs.cfo.ai/index
Learn cfo.ai, get a task done, understand how it works, or look up a fact. Ari, your AI finance coworker, answers business questions by building a Model you can open and change.
cfo.ai gives you Ari, an AI finance coworker. Ask it whether you can afford to hire, why spending went up, or how long your cash lasts. Ari answers by building and updating a financial Model of your business, and you can open that Model, check every number, and change the assumptions yourself.
> Can we hire three engineers in September? Show the effect on payroll, cash, and cash runway.
## New to cfo.ai
Short lessons that take you from sign-up to a Model you have checked and changed.
Describe your business to Ari, then check a number and change an assumption. 15 minutes.
Five short lessons: a forecast, a shareable Page, a Scenario, a review, and a weekly update.
For spreadsheet users: build a small table by hand and write a formula.
## Common tasks
Step-by-step guides for the jobs people do most.
Bring in Actuals from QuickBooks, Xero, NetSuite, or Rillet.
Have Ari rebuild your workbook as a live Model, then check that it ties.
Forecast revenue, costs, and cash from what drives your business.
See what a hire, a price change, or a cut does before you commit.
Send investors or your board a read-only link to a report.
Download a Page as an Excel workbook that keeps its formulas.
See all [How-to guides](/guides).
## Understand how cfo.ai works
Explanations of how the pieces fit together, so you can predict what cfo.ai will do.
How your Model fits together: Variables, Dimensions, formulas, Pages, and Ari.
What Ari can and cannot do, when it changes your Model, and what it needs from you.
Which one to use to try a plan, keep a plan, or undo a change.
When you are charged, what uses AI usage, and which connections take a slot.
See all [Concepts](/concepts).
## Look something up
Exact labels, limits, functions, and prices.
See all [Reference](/reference).
## Connect an AI agent
AI clients such as Claude Code and Codex can work on your Model through the cfo.ai MCP server. To connect one, see [Connect Claude, ChatGPT, Codex, or another AI tool to cfo.ai](/guides/connect-an-ai-client). If you are building an agent, start with [cfo.ai for AI agents](/agents).
## Get help
* Ask Ari in the app. It knows how cfo.ai works and what is in your Model: "How do I connect payroll?" or "Walk me through this Page."
* Press **⌘ ?** (**Ctrl ?** on Windows and Linux) in the app to open the Help center.
* Email the team at [success@cfo.ai](mailto:success@cfo.ai).
# Chat controls and messages
Source: https://docs.cfo.ai/reference/chat
Every control and message in a chat with Ari: starting a chat and mentions, the message box and attachments, what the chat shows while Ari works, question forms, plan, approval, email, and download cards, every stop and error message, the chat list, Ari in Slack, and the Welcome chat.
This page lists what you see in a chat with Ari, in the order you meet it: starting a chat, the message box, the chat while Ari works, the cards Ari shows, the messages that end a reply early, and the chat list. For keyboard shortcuts, see [Keyboard shortcuts](/reference/keyboard-shortcuts#chat-with-ari). For file and size limits, see [Limits and file types](/reference/limits#chat-with-ari).
## Start a chat
| Control | What it does |
| - | - |
| **New chat** | The button at the top of the **Chats** section of the sidebar. Opens an empty chat. |
| **Mention cell in chat** | In a cell's right-click menu, and on the small toolbar above a single selected cell. Adds the cell to the message box of the current chat. |
| **Mention in chat** | In a row or column label's right-click menu, and in a Canvas block's hover toolbar. A blank Canvas block offers **Mention block in chat**. |
| **Mention table in chat** / **Mention canvas in chat** | In a block's menu, opened from the six-dot handle at its left edge. |
| **Mention page in chat** | On a Page in the sidebar. |
| **Mention formula in chat** | The **@** button in the formula editor. Adds the formula you are editing to the message box. |
| **Explain with Ari** | In a block's menu. Starts a new chat that asks Ari what the block shows, how it is built, and how to read it. |
A mention appears in the message box as a chip. Ari reads the exact cell, row, block, or Page it points to, so you do not have to describe it. See [Ask Ari about a number](/guides/ask-about-a-number).
## The empty chat
A new chat shows the heading **How can I help you?** and the placeholder **Ask anything about your business or data...** in the message box. Four chips below it open suggested prompts for four kinds of question: **Explain** (what something means), **Benchmark** (whether a number is good), **Decide** (what to do), and **Pressure-test** (whether you are wrong). Selecting a suggestion puts it in the message box.
## The message box
| Control | What it does |
| - | - |
| **Send** | Sends your message. While Ari is working, its tooltip reads **Steer Ari (Enter) · Queue for after this turn (⌥ Enter)** (**Alt+Enter** on Windows and Linux). |
| **Stop** | Replaces **Send** while Ari is working and the message box is empty. Its tooltip reads **Stop Ari**. **Esc** in the message box does the same. Changes Ari already saved stay in the Model. |
| **Attach files** | Opens a file picker. You can also paste a file, such as a screenshot, or drop it on the message box. A message can carry files with no text. |
| **Data source** / **Chat only** | On a CSV file's tile. **Data source**, the default, also imports the file into cfo.ai when you send. **Chat only** shares it with Ari in this chat alone. |
| **Browse Canvas block library** | The palette icon at the bottom left. Opens templates for Canvas blocks. See [Build a dashboard or board report](/guides/build-a-dashboard). |
| Suggested reply | After Ari answers, the empty message box can show a suggested next message in faint text, followed by **Tab** or **→**. Press either key to place it in the box; it is never sent until you send it. |
| Excerpt | A passage you quoted from one of Ari's answers. See [Reply to part of an answer](#reply-to-part-of-an-answer). |
### Why Send is unavailable
When **Send** cannot be used, its tooltip says why.
| Message | Meaning |
| - | - |
| **No internet connection.** | Your browser is offline. |
| **Sending your last message.** | The previous message is still on its way. |
| **Wait for attachments to finish uploading.** | A file is still uploading. |
| **Resolve attachment upload errors before sending.** | A file failed to upload. Remove it or attach it again. |
| **Write a message to send with this excerpt.** | A quoted excerpt must be sent with a question or comment. |
### Attachment messages
| Message | Meaning |
| - | - |
| **File is too large. Images must be 10MB or smaller.** | The image is over the size limit and was not attached. |
| **File is too large. Documents must be 50MB or smaller.** | A file that is not an image is over the size limit and was not attached. |
| **You can attach up to 5 files per message. Remove a file or select fewer files.** | The files you picked would take the message over five files, so none of them were attached. |
| **Attachment upload failed** | A notice with the file's name and the reason. The file's tile turns red. |
| **cfo.ai did not confirm this upload. Check your connection and attach it again. If only this file keeps failing, a security filter may be blocking its contents. Contact [support@cfo.ai](mailto:support@cfo.ai).** | The upload never finished, for example after a dropped connection. Attaching the same file again replaces the failed tile. |
| **Wait for attachments to finish uploading before sending.** | You tried to send while a file was uploading. |
## While Ari works
Ari keeps working on cfo.ai's servers until it finishes, even if you close the tab or switch chats. When you come back, the chat shows everything Ari did.
| What you see | Where | Meaning |
| - | - | - |
| **Thinking…** | Status line above the message box | Ari has your message and has not taken its first step yet. |
| A short phrase and a meter | Status line | Ari is working. The phrase changes with what Ari is doing, such as reading results or writing its answer, and the meter counts the time and tokens used so far. |
| **Ari hit a temporary connection issue. Retrying now.** | Status line | A connection inside cfo.ai failed for a moment. Ari retries on its own. |
| **Ari hit a temporary model-provider issue. Retrying now.** | Status line | The AI model did not answer for a moment. Ari retries on its own. |
| **Activity** | Under your message | Ari's steps, each with a check mark when done. See [Activity group](#activity-group). |
| A line of text, such as a sync in progress | Above the message box, under the status line | Work running beside the conversation. Select it to open its card. |
| **Ari is working…** | Chat list, and a pill near the bottom of the Page | Ari is still working in that chat. |
### Activity group
Ari's reasoning and actions collect in an **Activity** group under your message. The group is open while Ari works and while it is on your screen. Once you scroll past it, it closes and stays closed. Your own click to open or close it always wins, and groups in an earlier conversation start closed.
A closed group reads as one sentence naming up to three things Ari did, such as "Searched the web, checked memory and integrations", followed by **and N more** for the rest, then the time and tokens the turn used. A group with only reasoning reads **Thought it through**. A step that was stopped reads **Cancelled**.
The group does not report failed steps. A failed step shows the normal check mark and folds into **and N more**, because Ari often recovers by retrying or reading the data another way. Anything you need to know about a failure is in Ari's answer.
### Messages you send while Ari works
| Status | Meaning |
| - | - |
| **Sending to the current turn** | You pressed **Enter**. Ari reads the message at its next step. |
| **Queued for after this turn** | You pressed **⌥ Enter**. Ari reads the message after it finishes. |
| Control | What it does |
| - | - |
| **Steer now** | On a queued message. Sends it to the current turn instead. If Ari already took it, a notice says **Ari already picked this message up**. |
| **Edit pending message** | Takes the message back into the message box so you can change it and send it again. |
| **Delete pending message** | Removes the message before Ari reads it. |
Waiting messages sit above the message box until Ari takes them. A message that started a turn cannot be edited or deleted. Some waiting messages cannot be edited, only deleted, and editing says why:
| Message | What to do |
| - | - |
| **Messages that are still sending can't be edited yet.** | Wait a moment and try again. |
| **Messages with attachments can't be edited yet.** | Delete it and send a new message. |
| **Messages that reply to an excerpt can't be edited yet.** | Delete it and send a new message. |
| **Plan approvals and answers to Ari's questions can't be edited.** | Delete it and answer again. |
| **Finish editing the other queued message first.** | You are already editing one; finish it first. |
| **Clear the current draft before editing a queued message.** | The message box has text in it; clear it first. |
For when to steer, queue, or stop, see [Steer, stop, or add to Ari's work](/guides/steer-ari).
### Edits playing on the Page
When Ari edits a table you are looking at, it plays each edit on the Page. A caption moves to the cell, the editor opens, the new value or formula is typed in, and the changed cells are highlighted. The caption's first line is Ari's note for the change. Its second line says what changed:
| Second line | Meaning |
| - | - |
| `Churn [Starter] 5% → 4%` | A value replaced a value. |
| `set to 4%` | A new value where there was none. |
| `cleared (was $15,000)` | A value was removed. |
| A formula followed by `(was …)` | A formula replaced an earlier one. |
| `Filling 24 cells in Revenue with …` | One formula or value filled many cells. |
While a change is still running, the caption shows Ari's note alone, and adds **· Still working** after about eight seconds.
The first edit of each kind in a turn plays at full speed with a short explanation; repeated edits play at twice the speed. Rest the pointer on the caption to pause. If your system asks for reduced motion, captions and highlights appear without movement.
The caption's **…** button (**Thinking animation options**) offers **Hide for this turn** and **Turn off animation for all turns**. Turning the animation off applies to this browser and is the same switch as **Show Ari's edits as they happen** in [Settings](/reference/settings). With the animation off, edited cells are still highlighted, and the chat shows Page and **Formulas** cards for Ari's changes.
While Ari works, the Page follows it to each table it edits. Scrolling by about a third of the screen stops following; a smaller scroll, a click, or typing only pauses it. A pill near the bottom of the Page then reads **Stopped following · Click to follow Ari** (**Tap** on a touch screen) and then **Ari is working…**. Select the pill to jump to Ari's newest edit and follow again. You can drag the pill to another spot, and the spot is remembered. A text block Ari rewrites while you have its Page open updates in place; if you are typing in that block, your save replaces Ari's version.
## Cards in the chat
### Question form
Ari asks for details it needs as a short form in the chat.
| Control | What it does |
| - | - |
| An option | Chooses that answer. Each option shows a letter; press the letter to choose it. A question that takes one answer moves on to the next question. |
| **Other…** | Type your own answer. While the box is empty, the form says **Type your answer to continue.**, or **Type your answer or attach a file to continue.** when the question takes files. |
| **Attach files to your answer** | Appears when a question takes files. Files sent without text read **See attached files**. |
| **Dismiss** beside a question | Skips that question. The record of your answers marks it **Declined**. **Undo** brings it back before you send. |
| **Dismiss** in the footer | Sends the answers you picked and declines the questions still open. |
| **Next** | Moves to the next question, answered or not. |
| **Submit answers** (**Submit** on a one-question form) | Sends your answers. It stays off until every question is answered or dismissed. |
Your answers appear in the chat as a record of the form, each question above your answer. If you scrolled up when a form arrived, select **Scroll to latest messages** to reach it. When a scheduled Trigger run asked questions at more than one point, they appear together on one form in its chat, and your one reply answers all of them.
### Plan card
For a larger piece of work, Ari shows a plan before it starts. Under **Will create:**, the card lists what Ari intends to build, such as Pages and tables. Under **Assumptions**, it lists each assumption Ari intends to use, with suggested choices.
| Control | What it does |
| - | - |
| **Other** | On an assumption. Type your own value (**Write your own answer**). |
| **Make changes** | Puts text in the message box so you can describe changes the choices cannot express. |
| **Build it** | Approves the plan. Your message reads **Build it — with these selected assumptions:** followed by any assumptions you changed. |
While Ari works through an approved plan, a plan card lists its steps with a count such as **2/5**. An **Assumptions** group under the steps names the decisions the plan rests on.
| Status | Meaning |
| - | - |
| **Answer above to continue** | An assumption Ari marked as required has no answer yet. |
| **Changes requested** | You selected **Make changes**. |
| **Approved** | You selected **Build it**. |
| **Paused** | Ari stopped with steps left, for example to ask you a question. |
| **Blocked** | A step cannot go ahead. |
### Tool-call approval card
When Ari wants to call a service you connected under **Settings → Connections**, such as an MCP server or an API, it pauses and shows **Ari is asking to run "\[tool]" on \[service].**
| Control | What it does |
| - | - |
| **Allow** | Runs this one call. Anyone in the chat can choose it. |
| **Always allow** | Runs the call and lets Ari use this tool on this service without asking again, for the whole workspace. Only people who can manage workspace settings see it. Other cards waiting for the same tool then read **Always allowed**. |
| **Decline** | Skips the call. Ari continues without it. |
After you choose, the card reads **Allowed**, **Always allowed**, or **Declined**, and the chat sends Ari a short message for you, such as **I approved the pending tool request — please go ahead and run it.** Ari then continues on its own.
If the call can no longer be acted on, a notice titled **Could not approve tool call** or **Could not decline tool call** (**Could not approve API call** or **Could not decline API call** for an API) says why:
| Message | Meaning |
| - | - |
| **This tool call was already approved.** | Someone already allowed it. |
| **This tool call was already declined.** | Someone already declined it. |
| **The MCP connection for this tool was removed, so this call can no longer run.** | The connection was removed after Ari asked. |
| **A workspace setting now blocks this tool, so this call cannot be approved.** | The tool was blocked in Settings after Ari asked. |
| **This approval request is not part of this chat.** | The card belongs to another chat. |
If the approval saved but the message to Ari did not go out, a notice reads **Approved, but Ari was not prompted to continue**. Send any message to have Ari run the call.
### View from an MCP server
Some MCP servers bring their own form, such as an issue form. It appears under **View from \[server]: \[tool]**, filled with what Ari proposed, and you finish it there. The note under it reads **Runs in a sandbox. Calls it makes use your \[server] connection and its approvals.**
* A call the view makes that needs approval shows its own card, **The \[server] view is asking to run "\[tool]" on \[server].**, with the call's **Arguments**, **Allow**, and **Decline**. A view's call never offers **Always allow**.
* A link the view wants to open asks first with **The \[server] view is asking to open a page on \[site].**, the full address, **Open link**, and **Cancel**.
* A view from an earlier turn, or after a reload, starts closed with **Open view**. Opening it shows the original call again.
* Views appear only in chats in the cfo.ai web app, not in Slack or in scheduled work.
### Email preview card
Ari emails you at your sign-in address as soon as you ask. An email to anyone else waits for you: Ari shows a card headed **Preview** with the recipients (**To**), **Subject**, and message, and the footer **Review the recipient and message before sending.**
| Control | What it does |
| - | - |
| **Edit** | Opens the draft. Addresses you type become removable chips when you press **Enter** or type a comma. |
| **Preview email** / **Cancel editing** | Saves your edits back to the preview, or drops them. |
| **Send** | Sends the email. Nothing leaves cfo.ai until you press it. |
| Message | Meaning |
| - | - |
| **Enter a valid email address.** | An address you typed is not valid. |
| **Sent** / **Ari sent this message.** | The email was sent. |
| **Email wasn’t sent. Try again.** | Sending failed. |
If the mail provider accepted only some addresses, the card keeps only the addresses it refused, so you can send to them again without emailing the others twice. Scheduled Triggers work the same way, so a run that addresses someone else stops at the preview.
### Email and Slack messages to you
When Ari emails you or messages you on Slack, the step in its activity group has one of these titles.
| Message | Meaning |
| - | - |
| **Notification request accepted** | The email service or Slack took the message. It does not confirm that the message reached your inbox. |
| **Notification accepted on some channels** | One channel took the message, and another refused it or did not answer. |
| **Notification outcome unconfirmed** | The provider did not answer. |
| **Couldn’t send the notification** | No channel took the message. |
| **Prepared an email preview and reported another channel** | An email waits in a preview card for you to send, and the step also reports how another channel did. |
### Download a file Ari made
A file Ari makes for you, such as a workbook, an image, or a document, appears as a card with its name and size directly under the answer that ends the turn. PNG, JPEG, GIF, and WebP images show a preview. When the answer names the file by its exact file name, with the same letter case and extension and outside a code block, that name is a link to the same download.
Selecting a file makes a download address that works for 5 minutes without a sign-in. A link Ari sends you, for example in a scheduled run's message, works for 24 hours for anyone who has it, and stops sooner if the file or its chat is deleted. An expired link opens a page that says **This download link has expired or is not valid.** A file that was not deleted is still in its chat.
### Scenario confirmation card
When you ask Ari to merge, delete, or revert a Scenario and to let you confirm first, Ari posts a card instead of acting. You press its one button yourself.
| Card | Button |
| - | - |
| **Merge \[Scenario]** | **Merge** |
| **Delete \[Scenario]** | **Delete** |
| **Revert \[Scenario]**, or **Revert \[Scenario] and its branches** when its child Scenarios go back too | **Revert** |
The button turns off when the action is no longer possible, for example because the Scenario changed after Ari's summary. See [Scenario and version history controls](/reference/scenarios-and-history).
### Other cards
| Card | What it shows |
| - | - |
| Page card | A Page Ari created or changed, with a button to open it. Shown when the edit animation is off. |
| **Formulas** | Every formula Ari wrote in the turn, closed by default; open it to read each formula. Shown when the edit animation is off. |
| Connection card | Opens the sign-in for a system you are connecting. Ari cannot sign in for you. See [Connect QuickBooks, Xero, NetSuite, or Rillet](/guides/connect-accounting). |
| **Start walkthrough** / **Start tour** | Starts a Guide Ari built or presented. See [Walk someone through a Page with a Guide](/guides/create-a-guide). |
## Reply to part of an answer
A **Reply** button appears above words you select in one of Ari's finished answers. **Reply**, or **⌘ L**, quotes the passage in the message box as an excerpt, for you to send with your question. The sent message shows the excerpt above your words, and selecting an excerpt scrolls to the original passage and highlights it. For the steps, see [Steer, stop, or add to Ari's work](/guides/steer-ari#reply-to-one-part-of-an-answer).
* A message can quote up to 5 excerpts; past that, a notice says **A message can reply to up to 5 excerpts.** A long passage is cut to 2,000 characters.
* **Reply** is not offered while the answer is still being written, when the selection spans two answers, or in a read-only chat.
## When a reply stops early or shows an error
| Message | Meaning | What to do |
| - | - | - |
| **Conversation interrupted - tell Ari what to do differently.** | You stopped Ari, or the reply was interrupted. | Say what you want next. Changes saved before the interruption stay. |
| **Couldn't stop Ari. Please try again.** | The stop did not reach Ari, so it is still working. | Select **Stop** again. |
| **Ari stopped before finishing.** with **Retry** | The reply was cut off. | Select **Retry** to have Ari pick the work back up. |
| **I stopped before finishing this reply. It was interrupted, and repeated attempts to continue it did not finish. Changes I saved before the interruption are kept. Sending the message again starts a new reply.** | cfo.ai tried on its own to continue an interrupted reply, for example after a restart, and could not. | Send the request again. |
| **No internet connection. Reconnect to continue with Ari.** | Your browser is offline. Ari keeps working on the server. | Reconnect. The chat catches up. |
| **Connection lost. Reconnect to continue with Ari.** | The chat lost its connection to cfo.ai. | Reconnect, then select **Retry** if it is offered. |
| **Ari is temporarily unavailable. Please try again in a moment.** | The service that runs Ari failed for a moment or is restarting. | Try again in a moment. |
| **Ari hit an error while reconnecting. Please try again in a moment.** | The chat could not reconnect to a reply in progress. | Try again in a moment. |
| **Ari is having trouble reaching the model provider. This is usually temporary. Please try again in a moment.** | The AI model kept failing after retries. | Try again in a moment. |
| **Ari hit a temporary connection problem and could not recover. Please try again in a moment.** | A connection inside cfo.ai kept failing after retries. | Try again in a moment. |
| **Ari hit an unexpected error. Please try again.** | Another failure. | Send the request again. |
| **Ari could not finish its latest reply. Please try again.** | The reply ended without a visible answer. | Send the request again, or ask for a smaller piece first. |
| **Ari's reply stopped: the model reached its output limit of \[N] tokens after \[M] minutes of reasoning, before it wrote a reply. Your conversation is saved.** | The model spent its whole output on reasoning twice. | Ask for a smaller piece of the work. |
| **This request is too large for Ari to continue. Start a new chat with a shorter request or fewer attachments.** | The chat outgrew what Ari can read in one request. No **Retry** is offered. | Start a new chat with **⌘ /** and restate what you need. Attach a file again only if the new request needs it. |
| **Your session expired. Please sign in again.** | You were signed out. | Sign in again. |
| **Start your free trial to continue.** / **Verify your work email to continue.** | A new workspace has a step left before Ari can continue. | Finish that step. |
For what to try when Ari is slow or stuck, see [When Ari is slow, stuck, or wrong](/guides/ari-is-slow-stuck-or-wrong).
### Billing stops: out of AI usage or over a limit
When billing stops a turn, the reply ends with Ari's own explanation, which starts **I had to stop here**. No **Retry** is offered, because the same request would stop again. Fix the cause, then send a new message. The **AI usage recovery** panel and the low-usage notices that appear above the message box are listed in [Plans and billing](/reference/plans-and-billing#notices-in-the-chat); to add usage, see [Buy AI usage, set a spend limit, or turn on auto-reload](/guides/manage-ai-usage).
| Message | Meaning |
| - | - |
| **I had to stop here: this workspace is out of AI usage. Buy more usage or upgrade the plan in Settings → Billing, then send a new message and I'll pick up where we left off.** | No AI usage left |
| **I had to stop here: this workspace reached its monthly AI spend limit. Raise the limit in Settings → Billing, then send a new message and I'll pick up where we left off.** | Spend limit reached |
| **I had to stop here: this workspace has reached a limit of its current plan. Upgrade the plan in Settings → Billing, then send a new message and I'll pick up where we left off.** | Plan limit reached |
| **I had to stop here: the workspace's last usage purchase failed, so no more AI usage could be added. Update the payment method in Settings → Billing, then send a new message and I'll pick up where we left off.** | A usage purchase failed |
| **I had to stop here while more AI usage is on the way. Send a new message in a moment and I'll pick up where we left off.** | Usage is being added, for example by auto-reload |
| **I had to stop here: this workspace has not started its trial yet. Start the trial, then send a new message and I'll pick up where we left off.** | Trial not started |
| **I had to stop here: this workspace doesn't have an active plan. Choose one — or fix the payment method if a charge has failed — in Settings → Billing, then send a new message and I'll pick up where we left off.** | No active plan |
| **I had to stop here: the workspace's subscription payment is failing, so usage is paused. Update the payment method in Settings → Billing, then send a new message and I'll pick up where we left off.** | Subscription payment failing |
| **I had to stop here: this would not fit the limits of the plan this workspace is switching to. Keep the current plan in Settings → Billing, then send a new message and I'll pick up where we left off.** | A plan change is pending |
| **I had to stop here: the workspace's billing status could not be checked. Wait a moment, then send a new message and I'll pick up where we left off.** | Billing could not be checked |
### A message that never reached Ari
A message that could not be delivered shows **Not sent.** under it, with the reason. **Send again** sends the same text as a new message, and the delete button removes it.
| Message | Meaning |
| - | - |
| **It waited more than a day without reaching Ari.** | It was queued for over a day. |
| **Ari could not start a reply, so this message never reached Ari.** | cfo.ai's attempts to start the reply failed. |
| **This message never reached Ari.** | The reason was not recorded. |
### Earlier messages
A chat opens on its newest messages, and scrolling up loads older ones.
| Message | Meaning |
| - | - |
| **Loading earlier messages…** | Older messages are loading. |
| **Earlier messages didn't load.** with **Load earlier messages** | Older messages failed to load. Select **Load earlier messages** to try again. |
| **This chat's history didn't load.** with **Load history** | The saved chat's history could not be read at all. Select **Load history** to try again. |
## The chat list: find, rename, and archive chats
The **Chats** section of the sidebar lists your chats, newest activity first. A chat moves up only when something happens in it, not when you open it. You cannot pin a chat to the top of the list, and you cannot delete a chat you sent a message in: **Archive chat** hides it and keeps its messages and files. To have data deleted, see [Cancel your subscription or delete your data](/guides/cancel-or-delete).
| Control | What it does |
| - | - |
| Filter | Shows **Active** chats (the default), **Archived** chats, or **All**. |
| **Show more** | The list starts with your newest chats. After you expand it, scrolling to the bottom loads older chats, showing **Loading older chats…**, or **Retry loading chats** if a page fails. |
| **Archive chat** | Hides the chat from the active list. Its messages and files stay stored. |
| **Unarchive chat** | Returns an archived chat to the active list. |
| **Discard chat** | Offered instead of **Archive chat** on a chat you have not sent anything in. |
| Chat name | Double-click it to rename the chat. **Enter** saves the name and **Esc** cancels. Ari sees the new name when it looks back at past chats. |
Changing the filter, or closing the **Chats** section and opening it again, returns the list to its short form.
A mark beside a chat shows its status. Point at it to read the status.
| Status | Meaning |
| - | - |
| **Ari is working…** | Ari is still working in the chat. |
| **Needs your response** | Ari is waiting for you, such as an answer, an approval, or a sign-in. |
| **Unread response** | Ari answered since you last looked. |
| **Something went wrong** | The last turn ended with an error. |
An empty list says **No chats yet.**, **No active chats.**, or **No archived chats.**
## Notifications when Ari finishes
After a turn that ran longer than 20 seconds ends while you are watching, a line above the message box offers **Get a heads-up when Ari finishes** with **Turn on**. Turning it on asks your browser for permission. From then on, when Ari finishes a chat you are not looking at, the browser shows a notification from **Ari** that reads **Replied in “\[chat name]”** or **Needs your response in “\[chat name]”**. Selecting it opens the chat. The switch is per browser; see [Change your theme, sounds, and notifications](/guides/personalize-cfoai).
| Message | Meaning |
| - | - |
| **Notifications aren’t available** | This workspace's server is not set up to send them. |
| **Couldn’t turn on notifications** | Turning them on failed. Try again in a moment. |
## Ari in Slack
Ari chats in Slack direct messages once your workspace is connected in **Settings → Ari → Slack**. See [Talk to Ari from Slack](/guides/use-ari-in-slack).
| Message | Meaning |
| - | - |
| **is typing…** | Ari is working on your message. Its reply posts in the same thread when the turn ends. |
| **I couldn't find a CFO.ai account for your Slack email. Ask an admin to invite you to CFO.ai, then message me again.** | No cfo.ai account uses the email address on your Slack profile. |
| **Your CFO.ai account doesn't have access to this workspace's CFO.ai. Ask an admin for access, then message me again.** | Your cfo.ai account does not belong to the cfo.ai workspace this Slack workspace is connected to. |
| **I can only chat in DMs right now. Send me a direct message and I'll pick this up there.** | You mentioned Ari in a channel. Ari does not read or answer channel messages. |
| **I can't read files or images in Slack yet. Paste the text into a message and I'll take it from there.** | You sent a file or image. Ari reads nothing in that message. |
| **Something went wrong on my end and I couldn't finish that. Please try again.** | Ari's turn failed. |
Slack shows the text, bold, and links of Ari's replies, and prints tables and headings as the characters typed. Question forms, approval cards, plan cards, download cards, and Guides do not appear in Slack; open the chat in cfo.ai to see them. A link to a Page or block opens it in your browser, under your own access.
## The Welcome chat and the starting card
A new workspace opens on a chat named **Welcome**, with Ari's greeting and a starting card docked above the message box.
### Company research and brief
When you verify your email, cfo.ai starts a public web search for the email address and, for a work address, for the company behind its domain. While the search runs, the Welcome chat shows a **Searching the web** step. When it finishes, Ari posts a short brief about you and your company. The brief ends with a **Sources** list: your signup's email domain first, then each public page the research used, linked by its site and labeled with what ties it to you.
| Label | Meaning |
| - | - |
| **shows your full email address** | The page shows your exact email address. |
| **on your email domain** | The page is on your company's own website. |
| **found by searching your email domain** | The page came up in the search for your company. |
| **matches your name only** | The page shares only your name. |
When cfo.ai could not research a company for your email, for example a personal address such as a Gmail address, a page that matches only your name is left out. The brief is researched for the person who created the workspace; a teammate who joins later gets a greeting and the starting card without a brief. To correct what the research found, see [Teach Ari your company's conventions](/guides/teach-ari#correct-what-ari-found-about-your-company).
### Starting card
The card asks one question, and your answer picks one follow-up. Answer and select **Get started** to send your answers. **Dismiss** sends the answers you already picked and declines only the open questions. When your answer can take files, **Attach files to your answer** appears beside **Dismiss** and **Get started**; files sent without text read **See attached files**.
| Question | Options |
| - | - |
| **Where would you like to start?** | **My business** plans the company the research found or that you describe. **Something hypothetical** builds a small sample model to show what Ari can do. **Other…** takes a longer answer in your own words, with files. |
| **What should I work from?** (after **My business**) | **Connect my data** builds from your own records, such as an accounting system or a file. **Upload an existing model** adds the attach button so you can send your workbook. **I want to type or upload context** takes your own description, with files. The note reads **Dismiss this to start from estimates and what you tell me.** |
| **Which business should I use?** (after **Something hypothetical**) | **A business like mine** builds a sample that resembles yours, labeled as a sample. **Another business, I'll choose** takes a name. A name everyone ties to one well-known company means that company; for any other name, Ari builds from what you say about it. |
When nothing has established which business you run, for example a personal email address and no company named, Ari asks what the business does before it builds. A number Ari had to estimate is labeled as an assumption. For a guided first run, see [Build your first Model](/tutorials/first-model).
## Related
* [How Ari works with you](/concepts/working-with-ari)
* [Steer, stop, or add to Ari's work](/guides/steer-ari)
* [Answer Ari's questions and approve its actions](/guides/approve-ari-actions)
* [Attach files to a chat](/guides/attach-files)
* [Keyboard shortcuts](/reference/keyboard-shortcuts)
* [Limits and file types](/reference/limits)
# Connection statuses and actions
Source: https://docs.cfo.ai/reference/connection-statuses
What each connection status in cfo.ai means, such as Syncing, Sync failed, or Needs attention, what the orange dot or red triangle in the sidebar means, and the action that fixes it, plus the exact messages you can see while connecting, syncing, or uploading.
A connection's status says whether its data is current and whether you must act. Every connection shows one status. You see it on the connection's row in the sidebar's **Connections** section, on the card that opens when you point at the row, on the connection's page, and on the source's card in the connection manager. Ari reads the same status when you ask about a connection. To fix a problem step by step, see [Fix a connection that needs attention](/guides/fix-a-connection).
## Sidebar marks
The mark at the right end of a connection's row in the sidebar says whether anything needs you.
| Mark | Meaning |
| - | - |
| No mark | The data is in and current (**Synced**), or an MCP server or API is ready (**Connected**). |
| Blinking orange dot | A sync is running (**Syncing**). |
| Steady orange dot | Something is incomplete but needs nothing from you: the sync resumes by itself, cfo.ai must fix it, or someone else must act. |
| Red triangle | A problem you must act on. The card names it and offers the fix. |
| Gray dot | Setup was not finished, someone stopped the latest sync, or the source was added but not connected. |
With no connections, the section reads **No connections yet**. The **Connections** section shows one row per app. An app you connected more than once, such as one QuickBooks company per entity, gets a parent row with its connections nested under it, and the parent shows the mark of its most urgent connection.
## Statuses
### Synced
The data reached your Model, and the last sync finished. Mark: none. Action: none. The card also shows **Last synced** and **Used in**.
### Connected
An MCP server or an API is ready for Ari to call. Nothing syncs from it, so it has no **Last synced** time. Mark: none.
### Syncing
A sync is running, or a first sync is queued. Mark: blinking orange dot. The connection's page reads **A sync is running now.** No **Reconnect** or **Retry** is offered while it runs. While cfo.ai waits on the provider, the line says how long it has waited:
* **Waiting \[time] so far for \[source]’s first sync to finish.** It may add what the provider last reported, then **If it hasn’t finished by \[date and time], this sync stops at its next check after that.** cfo.ai checks every 10 minutes and waits up to 20 hours.
* **Waiting \[time] so far for cfo.ai’s request allowance at \[source] to reset. The allowance for this organization is used up or nearly used up, and the sync resumes on its own when it resets.** It ends the same way. Xero, for example, limits how many requests an app can make each day.
### Partly synced
The last sync finished without some of the source's tables. The data that arrived is usable. Mark: steady orange dot. Line: **\[Source]’s last sync couldn’t refresh \[tables]. Everything else is up to date, and cfo.ai will try again on the next sync.** It names up to three tables and counts the rest. Action: none; the next sync retries the missing tables.
### Setup not finished
Someone started connecting the source and did not finish. The setup may have failed, been abandoned, or still be open in someone's tab. Mark: gray dot. Line: **Syncing won’t begin until setup is finished.** Actions: **Finish setup** continues the same connection, and **Cancel setup** removes it without asking, because it holds no data yet.
A source whose setup cannot be resumed, such as Stripe (Direct), offers only **Cancel setup** on the card, with **Setup wasn’t finished. Cancel it and connect \[source] again.** On the connection's page, the line says **Remove it** and the button is **Remove connection**. People who cannot manage connections see the line and no button.
### Connection failed
Setup failed before the source linked. Mark: red triangle. Line: the failure's reason, or **Retry to set it up again.** Action: **Retry**.
### cfo.ai lost access
The source revoked cfo.ai's access, for example after a password change or an expired authorization. Mark: red triangle. Line: **Reconnect to start syncing again.** Action: **Reconnect**, then sign in to the same company the connection already holds.
### Missing access in \[source]
The source is not sharing some tables the reports need, such as **Missing access in NetSuite SuiteAnalytics**. Mark: red triangle. Line on the card: **\[Source] isn’t sharing some tables the reports need. Open the connection for the steps to fix it.** The connection's page shows an **Access missing** badge and the steps. For NetSuite they read: **NetSuite isn't sharing some tables cfo.ai needs for your financial statements. Have your NetSuite admin create an access token for the Data Warehouse Integrator role, then reconnect NetSuite with that token. Changing only the role ID won't work. Missing tables: …** Action: fix the access in the source, then **Reconnect**.
### Sync failed
There are two kinds:
* The provider's own sync failed, often for a moment. Mark: steady orange dot. Line: **\[Source]’s sync hit an error. cfo.ai will try again on the next scheduled sync.** When the provider gave a reason, its report sits between the two sentences, for example **BigQuery’s sync hit an error. Fivetran reports: The user cannot access BigQuery tables/project. cfo.ai will try again on the next scheduled sync.** Action: none, or **Retry** to run it now. When the report names something to fix at the source, fix that first.
* cfo.ai could not tell why the sync failed. Mark: red triangle. Line: the source's own reason, or **The last sync couldn’t finish.** Actions: **Retry** and **Investigate with Ari**.
### First sync not ready
The provider had not finished its own first sync of your account 20 hours after cfo.ai began waiting. Mark: steady orange dot. Line: **\[Source]’s data still wasn’t ready when cfo.ai stopped waiting for its first sync, so this sync ended without data. The next scheduled sync tries again.** Action: none, or **Retry** to start a new wait.
### Paused until next sync
The source's daily request allowance ran out. Mark: steady orange dot. Line: **\[Source]’s daily limit was reached. Progress is saved, and syncing picks up on the next scheduled run.** Action: none. Signing in again would not change the limit, so **Reconnect** is not offered.
### Sync stopped
cfo.ai's data warehouse refused the data. Mark: steady orange dot. Line: **Contact cfo.ai support if it isn’t fixed soon.** Action: none; only cfo.ai can fix it.
When several causes apply at once, this one is shown, because nothing else helps until it is fixed.
### Stopped
Someone stopped the latest sync. Nothing broke. Mark: gray dot. Line: **This sync was stopped before it finished.** Action: **Retry**.
### No queries yet
The source's data synced, but no saved query brings it into your Model yet. Mark: red triangle, or a steady orange dot when someone else must set up the queries. Line: **Data synced. No queries saved yet.**, or **Data synced. Someone with access can set up queries with Ari.** Action: **Set up queries with Ari**, for people who can edit the connection.
### No data found
The sync finished, but neither the source nor its queries returned any rows. Mark: red triangle. Line: **The last sync finished but found no data.** Action: **Investigate with Ari**.
### Upload incomplete
A file upload connection has no file attached yet. Upload the file again.
### Needs attention
The general status for a connection that stopped short of your Model. MCP servers and APIs show it with a next step, such as **Reconnect this source to continue.** for a server whose saved sign-in no longer works.
### Not connected
A source was added, for example from a list Ari asked you to connect, but nothing was connected yet. Mark: gray dot.
## Statuses for MCP servers and APIs
In **Settings → Connections** and on the **MCP** and **API** tabs, a server or API reads one of these, followed by its next step when it has one.
| Status | Meaning |
| - | - |
| **Ready for Ari** | Ari can call its tools or endpoints. |
| **Synced** | A sync setup Ari saved has brought its data in. |
| **Connecting** | Setup is still in progress. |
| **Waiting for you** | Someone must finish setup, such as signing in. |
| **Needs attention** | Something broke; the next step says what to do. |
| **Unavailable** | The status could not be read. |
| Next step | When |
| - | - |
| **Reconnect this source to continue.** | The saved sign-in has no usable token. |
| **Finish authorization to continue.** | Sign-in was started and not finished. |
| **Retry authorization to continue.** | Sign-in failed. |
| **Allow at least one API endpoint to continue.** | No endpoint of the API is allowed yet. |
| **Grant access to the missing source tables to continue.** | A synced source, such as NetSuite, hides tables the reports need. See [Missing access in \[source\]](#missing-access-in-source). |
| **Refresh the API definition to continue.** | The API's specification needs to be loaded again. |
| **Verify the available tools to continue.** | The server's tool list needs checking. |
| **Retry the import to continue.** | A data import failed. |
| **Import data to continue.** | Nothing has been imported yet. |
| **Review the imported data to continue.** | The import finished, and what it produced needs a look. |
| **Review the source data to continue.** | The source's data needs a look before it can be imported. |
| **Attach a file to continue.** | A file upload has no file. |
| **No action is needed while setup finishes.** | Setup is running. |
## Connection actions
| Action | What it does | Offered when |
| - | - | - |
| **Finish setup** | Continues the unfinished setup on the same connection. No second connection is created. | Setup was not finished and the source can resume it. |
| **Cancel setup** | Removes an unfinished connection at once. It frees the plan slot the connection held. | Setup was not finished. |
| **Reconnect** | Opens the source's sign-in again for the same connection. | The source revoked access, or a failed sync needs a new sign-in. |
| **Retry** | Runs the import again with the same sign-in, or starts setup again after **Connection failed**. It does not use another plan slot. | A sync failed and only the import needs to run again, a sync was stopped, or setup failed. |
| **Update file** | Replaces an uploaded CSV with a new file. | On a file upload. It is the only action when the file could not be read. |
| **Set up queries with Ari** | Opens a new chat with a request for Ari to review what synced, propose saved queries, and set them up. You review the request before you send it. | **No queries yet** |
| **Investigate with Ari** | Opens a new chat and asks Ari to find out why the sync failed or found nothing, and start it again if that helps. | **No data found**, or a **Sync failed** with no known cause |
| **Remove connection** | Asks **Do you want to remove this connection? This stops future imports and cannot be undone.** Data already in your Model stays, and stops updating. | On the connection's page, for people who can manage connections |
| **Remove file** | Asks **Do you want to remove this file? This cannot be undone.** | On a file upload's row and page |
| **Manage**, **Disconnect** | Open an MCP server's or API's tools and permissions, or disconnect it. Disconnecting a server warns **Ari will no longer be able to use this server's tools until you reconnect.** | MCP servers and APIs |
A connection never offers both **Retry** and **Reconnect**. No action appears while a sync is running, when only cfo.ai support can fix the failure, when cfo.ai has turned the source off, or for people who cannot manage connections. To rename a connection, open its page and select its name.
## The connection's page
Select a connection's row to open its page. Its sections, top to bottom:
| Section | What it shows |
| - | - |
| **Status** | The status, the line under it, any setup warning, and the actions. **Last synced** reads a time such as **Just now**, or **Not yet**. **Used in** lists the Pages that use the connection's data. **Added** gives the date it was connected, and **File** names an uploaded file. |
| **Source tables** | **When each table from this source last synced.** Each row gives the **Table**, its **Last synced** time, and the date its **Data through** reaches. It appears once two or more tables have synced. |
| **Import** | **What Ari imported from this connection.** It appears for Google Sheets and Google Drive connections. |
| **Syncs** | **What Ari copies from this connection into your data.** It appears for MCP servers and APIs with sync setups. |
| **Queries** | **Ari writes these queries to bring this data into your model. They are read-only.** Each saved query shows its last run, such as **Ran 2 hours ago · Completed**, **Queued**, **Running**, or **Never run**, with **Show SQL** and **Copy SQL**. With no queries, it reads **No saved queries yet.** |
At the bottom, people who can manage connections see **Remove connection**, or **Remove file** for an upload.
## Directory cards
The connection manager's **Integrations** tab shows one card per source.
| Label | Meaning |
| - | - |
| **Connect** | Starts connecting this source. |
| **Add another** | The source already has a connection; adds one more, such as a second company. |
| **Upload CSV**, **Add CSV** | Uploads CSV files as data sources. |
| **Finish setup** | Continues a setup that was not finished. |
| **Retry** | Repairs a connection whose setup failed. |
| **Added**, with a connection count | The source has connections. Each connection's own page holds its actions. |
| **\[N] connections max** | The source allows at most that many connections in a workspace. |
| **Limit reached** | The source already has its maximum number of connections. Rillet allows one. |
| **Upgrade plan**, **Choose plan**, **Manage plan** | Your plan has no room for this connection. The button's tooltip says why. |
| **Premium** | A premium source your plan does not include. Its tooltip reads **This integration is only available for premium plan.** |
| **Unavailable** | This source cannot be connected from here. |
| **Temporarily unavailable** | cfo.ai has turned this source off for now. |
## Messages when a new connection is refused
These appear in the plan tooltip on a directory card:
* **Your plan includes \[N] standard integrations and all are in use. Upgrade to add more.** (or premium integrations)
* **Your plan has reached its integration limit. Upgrade to add more.**
* **Integrations aren't included in your plan. Upgrade to connect this one.**
* **Premium integrations aren't included in your plan. Upgrade to connect this one.**
* **Unable to resolve an active plan for this workspace. Choose a plan to connect integrations.**
* **Your plan is scheduled to change to \[plan] on \[date], \[what it includes]. Keep your current plan to connect this one.**
These come from cfo.ai when a setup starts:
* **your plan has no room for another integration of this kind — upgrade your plan to add more**
* **this workspace has reached this integration's connection limit**
* **start the free trial to add integrations**
* **this workspace has no active plan — choose one, or settle the outstanding payment, to add integrations**
* **this workspace has a plan change scheduled, and the upcoming plan has no room for this integration — keep your current plan to add it**
* **this integration is temporarily disabled by cfo.ai; contact support to re-enable it**
Signing in to a company the workspace already holds shows **\[Company] is already connected**, with **Use the existing connection instead of adding another.**, or **It is connected as “\[name]”. Use that connection instead of adding another.**, or **It needs reconnecting. Reconnect it instead of adding another.** with a **Reconnect** button.
## Messages while connecting
| Message | Meaning |
| - | - |
| **\[Source] was not connected**, **The \[source] window closed before authorization finished.** | The sign-in window closed early. **Try again** reopens it on the same unfinished connection. |
| **\[Source] was not reconnected**, **Choose “Relink integration” on the \[source] screen and sign in to \[company]. “Finish” keeps the current authorization.** | On a reconnect, the provider's screen showed **Finish**, which keeps the old authorization. **Try again** and choose **Relink integration**. For Xero, the card adds how to disconnect the app under **Connected apps** in Xero first. |
| **Fivetran setup was interrupted** | Fivetran's setup lost track, for example because you finished it in another tab. The page offers **Go to cfo.ai** or **Return to cfo.ai**. The connection was saved; select **Finish setup** on it. |
| **Rillet rejected this API key. Check the key and try again.** | The Rillet key was wrong or revoked. |
| **Browser blocked the \[provider] authentication popup** | Allow pop-ups for cfo.ai and try again. |
| **Before you connect Rippling** | Rippling needs an API key; see [Rippling](/reference/integrations#rippling). |
The Google flow reads **Opening Google authentication…**, **Choosing in Google…**, **Completing connection…**, and **Connected**. Rillet reads **Creating connection…**, **Verifying API key…**, and **Connected**. Fivetran sources read **Creating connection...** and **Opening Fivetran...**, and a failure reads **Connection failed**.
## Messages about uploaded files
| Message | Meaning |
| - | - |
| **Choose non-empty .csv files up to 50 MB each.** | A file was not a CSV, was empty, or was larger than 50 MB. |
| **\[N] of \[M] CSV files imported. \[K] couldn't be imported. Try \[files] again.** | Some files in a batch failed to upload. |
| **The file couldn’t load: \[what was wrong]. Fix the file and upload it again.** | The loader could not read the file as one table. What was wrong reads **the file has no rows**, **the file has no header row**, **the file has a header row and no data rows**, or **the header row has \[N] columns and row \[M] has \[K]**. |
| **The CSV file could not be processed. Check that it has a valid header row and consistent columns, then retry or upload it again.** | Another problem with the file. |
Google Sheets and Google Drive connections report their import on the row and in the page's **Import** section:
| Message | Meaning |
| - | - |
| **Waiting for the first sync to read this file…** | Connected from the connection manager; the first sync has not run yet. |
| **Ari is importing this file…** | Ari is rebuilding the file in your Model. |
| **Imported to \[Page]. Ari updates the model when the file changes.** | The import finished. **The last sync found no change.** is added when the latest sync found the file unchanged. |
| **Attached to the chat that connected it. Ari imports it there.** | Connected from a chat; that chat handles the import. Afterward it reads **The chat that connected it handles later changes.** |
| **Ari stopped before finishing this import. The next sync tries again.** | A background import stopped; the next sync resumes it. |
| **Ari stopped before finishing this import. Continue in the chat that connected it.** | An import in a chat stopped; continue it there. |
| **This file is too large to import. Make it smaller and the next sync imports it.** | The file is over the limit: 50 MB for a Google Sheet, 10 MB for an Excel or CSV file in Drive. |
## Messages about MCP servers and APIs
| Message | Meaning |
| - | - |
| **A workspace admin must connect this service.** | You cannot manage connections. |
| **The provider must approve cfo.ai's sign-in URL to connect.** | The provider has to allow cfo.ai's sign-in address first. Trying again does not help until it does. |
| **This provider signs in only apps registered with it in advance. It offers neither automatic registration nor client metadata documents, the two ways cfo.ai identifies itself, so sign-in cannot start.** | The server cannot be connected with OAuth. |
| **This server's address, or an address it gave for sign-in, is a non-public address. cfo.ai did not contact it.** | cfo.ai connects only to public addresses. |
| **This MCP server requires sign-in. Disconnect it and add it again with OAuth or a bearer token.** | The server was added without sign-in. |
| **The MCP server rejected authorization (it answered HTTP 401). Reconnect this server and try again.** | The saved sign-in was refused. The status code is the one the server answered. |
| **Could not load tools from the MCP server (it answered HTTP 404). Try again shortly.** | The server did not return its tools. |
| **API saved** | The API's specification loaded. It does not prove the key works. |
| **Retry to finish this setup. A retry keeps any connection already saved.** | An API setup failed partway. |
| **The specification URL is not an https address.** | The OpenAPI address must start with `https://`. |
| **The specification URL answered HTTP 404, not the document.** | The address returned an error status instead of the specification. |
| **The specification document is larger than the 12 MiB this connection accepts.** | The specification is too large. |
| **Could not load the OpenAPI specification. Check its URL or paste the document as JSON.** | The address did not answer. |
## Messages in a chat
When Ari syncs a connection, a line above the message box reads **Syncing** with the connection's name. Selecting it opens a card with the run's two steps, **Sync source data** and **Run saved queries**, its progress, such as **Step 1 of 2**, and **View connection** and **Stop**. A failed run reads **Couldn't finish** and keeps the connection's own status and action. A run you stopped reads **Stopped**.
When a scheduled sync finds that a source you connected stopped updating and a repair needs you, cfo.ai opens a chat titled **\[Connection name] stopped updating** in your chat list.
When a workspace has no active plan, a refresh you ask for answers **data syncing is paused for this workspace — choose a plan, or settle the outstanding payment, to resume syncing**, and the nightly refresh skips the workspace until it has a plan again.
## Related
* [Fix a connection that needs attention](/guides/fix-a-connection)
* [Integrations directory](/reference/integrations)
* [How connected data reaches your Model](/concepts/connected-data)
# Formula errors and warnings
Source: https://docs.cfo.ai/reference/formula-errors
What each formula error code means (#DIV/0!, #VALUE!, #REF!, #NAME?, #NUM!, #CIRC!, #ERR!, #NO_ROLLUP), the formula editor's messages, Ari's save refusals and warnings, and the notices a table shows when it cannot calculate.
When a formula cannot produce a value, its cells show an error code such as `#DIV/0!` or `#REF!`. The formula editor shows messages as you type, and a table that cannot calculate shows a notice instead of values. This page lists each code, message, and notice, and what it means. For the steps to fix a formula, see [Fix a formula that shows an error or a wrong number](/guides/fix-formula-errors).
## Cell error codes
A cell shows an error code when its formula cannot produce a value. Point at the cell to see the message. When the error started in another cell, the message adds lines that begin with **originating in** and **via**, naming the Variable and items where it started and each step it passed through. Right-click the cell and choose **Fix** to start a chat with Ari about it.
An error spreads: a cell that reads a cell with an error shows the same code and message. `iferror` catches an error and returns a fallback; see [`iferror`](/reference/functions#iferror).
When a cell has no message of its own, it reads **Formula evaluation failed. The formula contains an error or references invalid data.**
### #DIV/0!
`#DIV/0!` means the formula divides by zero or by an empty value. The message reads **division by zero**. The `%` remainder operator and a zero raised to a negative power cause it too. Guard the divisor with `if(Revenue <> 0, ..., 0)`, or wrap the division in `iferror`.
### #VALUE!
`#VALUE!` means a value has the wrong type, a function has the wrong number of arguments, or the formula text cannot be read. The message names the problem, for example **IF requires exactly 3 arguments**, **SUM requires between 1 and 10 arguments**, **DATE expects numeric values**, **DATEDIF expects date values**, **DAYSIN expects a date value**, or **Invalid string escape**. A formula with a syntax mistake shows the editor's message for it.
### #REF!
`#REF!` means the formula names a Variable or Dimension that does not exist, for example one that was deleted or a name with a typo. The message begins with **Variable or dimension not found**, or reads **Unsupported reference** or **Expected a property reference**. Point the reference at a Variable that exists.
### #NAME?
`#NAME?` means the formula calls a function cfo.ai does not have, such as `VLOOKUP` or `SUMIF`. The message reads **Unknown function**. See [Excel functions that are not available](/reference/functions#excel-functions-that-are-not-available).
### #NUM!
`#NUM!` means a calculation has no valid number as its answer. The message names the case:
| Message | Cause |
| - | - |
| **0^0 is undefined** | Zero raised to the power of zero |
| **DATEDIF start date is after end date** | `datedif` with its dates in the wrong order |
| **DATE result is out of range** | `date` built a date outside the supported range |
| **invalid rounding significance** | `ceiling` or `floor` with a positive number and a negative significance |
### #CIRC!
`#CIRC!` means a circular reference: the cell's formula reads the cell itself, directly or through other formulas, in the same period. The message reads **Circular reference**. Common causes are a running balance written without `[-1]`, two Variables that read each other in the same month, and a total formula that also runs on the rows beneath it. See [Fix a circular reference](/guides/fix-formula-errors#the-cell-shows-circ-a-circular-reference).
### #ERR!
`#ERR!` means the calculation failed for a reason that has no more specific code. Point at the cell for the message, or right-click it and choose **Fix**.
### #NO\_ROLLUP
`#NO_ROLLUP` appears only in Ari's reads of a table by quarter, half-year, or year. It means no formula calculates the Variable at that grain, so its months are not added into the longer period. The app shows 0 in those cells. Ask Ari to give the Variable a time rollup, such as a sum of its months; see [Why a quarterly view shows zeros](/concepts/totals-and-rollups#why-a-quarterly-view-shows-zeros).
## Formula editor messages
The formula editor checks what you type and underlines problems in red. A formula with a problem still saves, and its cells show an error code until you fix it.
### Syntax messages
| Message | Meaning | Fix |
| - | - | - |
| **missing closing ')' for '(' opened at line 1:3** | A parenthesis, bracket, or brace has no closing partner. The message names where it opened. | Add the closing `)`, `]`, or `}` |
| **unmatched ')'** | A closing parenthesis has no opening partner | Remove it, or add the opening one |
| **expected a value, found ...** | Something is missing, such as the number after `+` or an argument between two commas | Add the missing value |
| **expected ')' or ',', found ...** | Two values sit side by side with no operator or comma | Add the operator or comma |
| **expected end of formula, found ...** | Extra text follows a complete formula | Remove it, or join it with an operator |
| **unexpected character '@'** | The formula has a character formulas do not use | Remove the character. Wrap a name with punctuation in backticks |
| **'and' joins conditions: (condition) and (condition). It cannot be called as a function.** | `AND(...)` was written as a function. The same message exists for `or`. | Write `a and b` |
| **A selector in \[ ] attaches directly to a variable reference, as in Revenue\[-1]. Here it follows ')'.** | A lookup or period offset follows a calculation or a value instead of a Variable's name | Put the selector on each Variable: `Revenue[-1] - Cost[-1]` |
| **expected a reference, found end of formula** | A dot with nothing after it | Finish the name after the dot, or remove the dot |
### Name and argument messages
| Message | Meaning |
| - | - |
| **Unknown variable or dimension name** | No Variable or Dimension has this name. Names match capital letters exactly; a name with spaces needs backticks. |
| **Ambiguous variable or dimension name** | Several Variables share the name. Pick the one you mean from autocomplete, which adds a short `#` code. |
| **Unable to resolve variable or dimension reference** | The reference points at something that cannot be loaded, such as a deleted Variable |
| **Unknown function** | The function name is not one of cfo.ai's functions |
| **SUM requires between 1 and 10 arguments** | The function has too many or too few arguments. Each function names its own limit, such as **IF requires exactly 3 arguments**. |
### Dimension not in the formula's condition
This message is a warning, not an error, and does not block the save. It says a formula reads a Dimension, such as Employee, that its condition does not include. Where the formula runs on a row that has that Dimension, the reference reads that row's item. The formula also runs on the row above the breakdown, where there is no single item, so that row shows one looked-up item's result instead of a total. Give the rows above their own formula, or add the Dimension to where the formula applies.
### Other editor messages
| Message | Meaning |
| - | - |
| **This variable lives on a table you don’t have access to.** | The Variable a reference names is on a table you cannot open, so its source cannot be shown |
| **This variable is in the model but has not been added to a table yet.** | The Variable exists but no table shows it |
| **This is a source record, not a modeled formula cell.** | The row is an imported record shown by drilling in, which has no formula to open |
| **This input doesn't map to one formula address, so it can't be opened here.** | The input row read by drilling in combines several formulas, so no single one opens |
| **Propagate?** and **Would you like to propagate this formula to parent rows?** | Asked after you save a formula on a lower row of a breakdown; see [Propagate a formula to the rows above](/guides/write-a-formula#propagate-a-formula-to-the-rows-above) |
When the server refuses to save a cell or formula, the notice is listed in [Table block controls and messages](/reference/table-blocks).
## Ari's save refusals
A formula you type in a table always saves, even with a mistake. A formula Ari or a connected AI agent writes is checked first, and the write is refused when it has one of these problems. Ari reads the reason and usually corrects the formula itself.
| Refused because | What it means |
| - | - |
| A syntax mistake | A missing parenthesis or quote, or another problem from the editor's syntax messages |
| A name that matches nothing | No Variable or Dimension has the name in that Scenario. A name that differs only in capital letters is refused too, and the refusal names the Variable it matches. |
| A name several Variables share | The write must name the one it means, with its `#` code |
| The wrong number of arguments | A function was called with more or fewer arguments than it takes |
| An Actuals or Forecast formula with no months | A formula for one range must cover periods. A Variable on a table without months has one formula for every period. |
| A number function over text or dates | `sum`, `product`, `average`, `stdev`, `var`, and `median` add up numbers, so they refuse a text or date value. `first`, `last`, and `count` work on any value, and `min` and `max` also take dates. |
| A formula aimed at the rows with no item | The rows a table shows as **None** have no value for a Dimension. They cannot have a formula of their own; the formula on the row above them reaches them. |
When Ari writes several formulas at once, a refusal can stop the whole batch or only the formulas that failed, depending on the request. Ask Ari which changes were saved.
## Ari's save warnings
A warning means Ari saved the formula, but it may not do what the wording suggests. Ari sees each warning in its save receipt. If a number looks wrong right after a change, ask Ari to read back the warnings.
| Warning | What it means |
| - | - |
| An imported Variable is read in Forecast months | The imported Variable has no saved formula, as with some older imports, so it reads its source only through **Last close**. The formula reads 0 in Forecast months, or an empty value for a date or text Variable. Give the Variable a Forecast formula. See [How imported data loads](/reference/imported-data). |
| A filter value matches no item | A condition such as `Step = "Committed"` saves, but no item is spelled that way, so the cells it reaches read 0. The warning shows how the items are spelled. For a Dimension of timestamps, it explains that `date(y, m, d)` is a date, not a timestamp. |
| A read finds no value there | A plain reference reads the input at every Dimension of the cell, months included. An input saved once per employee has no months, so a monthly formula reads 0. The warning names the fixed reference that reaches it, such as `` `Annual Wage`$[Employee = this.Employee] ``. |
| What the first Forecast month read | When the formula reads a previous period or an imported source, the receipt states what it calculated in the first Forecast month and what each input returned there. A roll-forward that starts from an empty last Actuals month shows up here. |
| Two formulas tie | Two saved formulas are equally specific and reach the same cells, so the more recently saved one wins. Saving either again flips the result. The warning names the other formula. |
| A shared formula was replaced | A formula belongs to its Variable, so saving a new one changes every table that shows the Variable. The warning names the formula it replaced. If the new formula reads its own Variable, the cells where that read lands on themselves show `#CIRC!`. |
| Rows with no value for a Dimension are left out | A test such as `this.Department = "Sales"` is empty for rows with no department, so the formula does not reach them. Those rows show as **None**. |
| A formula saved at a coarser grain | A formula written through a table by year, in a monthly Model, is saved at year grain. The year cells calculate it, whatever their months hold, and the month cells do not read it. |
| A row above does not read an override | A formula saved on one lower row is not read by a total above it that calculates from other rows. The warning names those totals. |
| A latest-date filter sees only one month | A filter that picks the latest date with `max` over an imported column, inside a formula with months, sees only that month's rows, so it often matches nothing. Read the Variable that holds the column's latest value instead. |
| The Scenario the write used | When the request named no Scenario, the receipt says which one Ari used: the one you were viewing. |
A receipt also states formulas the save wrote for you, such as the time rollup a new monthly row gets and the formulas that make a breakdown's rows above add up the rows beneath them. See [Totals, aggregation, and time rollups](/concepts/totals-and-rollups).
## Table calculation notices
When a table cannot calculate, it shows a notice over or above the grid instead of values. Nothing is wrong with your formulas unless the notice says so.
| Notice | Meaning | Action |
| - | - | - |
| **Calculation capacity was full when this calculation ran. We're looking into it — try again in a moment.** | The shared calculation capacity was used up | **Retry**, or wait |
| **Calculation results are temporarily busy; this failure is transient. We're looking into it — try again in a moment.** | The results store was busy for a moment | **Retry** |
| **Calculations are temporarily unavailable. We're looking into it — try again in a moment.** | No calculation service answered | **Retry**, or wait |
| **This calculation stopped before it finished because the worker running it ended. The calculation service ends a worker whose calculation runs past its time or memory limit.** | The calculation is too large. Retrying runs into the same limit. | Narrow the table, for example by filtering the items it shows, or ask Ari why the table is large |
| **This table didn't load because the connection to cfo.ai failed.** | Your network dropped or no answer arrived. The table and formulas are fine. | **Retry**. The table also reloads when your connection returns. |
| **We couldn't calculate this table because of a model misconfiguration.** | Something in the Model's setup stops the calculation | **Fix** starts a chat with Ari about the table |
| **We couldn't calculate this table.** | The calculation failed for another reason | **Retry**, or ask Ari |
| **We couldn't load this table**, with a sentence that names the problem and **If you recently added an item in the sidebar, try removing it.** | The table's setup cannot be calculated as it is | Remove the item you last added to the table's setup, then select **Retry** |
One setup problem names an imported source that has no date column. The table cannot place the source's rows in periods. Tell Ari which column dates the rows, or add a date column at the source and sync again; see [Check what a connection brought in](/guides/check-imported-data).
## Related
* [Fix a formula that shows an error or a wrong number](/guides/fix-formula-errors)
* [Formula syntax](/reference/formula-syntax)
* [Formula functions](/reference/functions)
* [Review Ari's formula changes](/guides/review-ari-formula-changes)
# Formula syntax
Source: https://docs.cfo.ai/reference/formula-syntax
The syntax of a cfo.ai formula: how to name a Variable, filter by a Dimension item, read last month or a fixed period, and write dates, operators, and conditions, plus how cfo.ai formulas differ from Excel.
A cfo.ai formula calculates a Variable's values from Variables, numbers, and functions, such as `Price * Quantity` or `Revenue[-1] * 1.1`. This page lists each part of the formula language and what it means. For the functions, see [Formula functions](/reference/functions). For the steps to write one, see [Write a formula](/guides/write-a-formula). To learn how cfo.ai picks which formula fills a cell, read [How formulas work](/concepts/formulas).
## Names of Variables and Dimensions
| You write | It means |
| - | - |
| `Revenue` | The Variable or Dimension named Revenue, read for the cell being calculated |
| `` `Cost of Goods Sold` `` | A name in backticks. Use backticks when a name has spaces, punctuation, or a word the formula language uses, such as `and` |
| `Revenue#a3f` | One of two Variables that share a name. Use the shortest code after `#` that the editor or Ari offers |
| `this.Revenue` or `Revenue[]` | The same as `Revenue` |
Names match exactly, including capital letters: `` `Deferred revenue` `` does not find a Variable named Deferred Revenue. A formula stores the Variable itself, not its name, so renaming a Variable updates every formula that uses it.
## Numbers, text, and empty values
| You write | It means |
| - | - |
| `1000`, `1000.5`, `.5` | A number. A leading `0` is optional |
| `50%` | 0.5. A `%` right after a number makes it a percentage |
| `-500` | A negative number |
| `"West"` or `'West'` | Text. Either quote style works. Inside text, `\"`, `\'`, and `\\` stand for a quote, an apostrophe, and a backslash, and `\n` and `\t` for a new line and a tab |
| `null` | The empty value |
A cell that no formula or value covers has a default. A number reads 0, so sums keep working. A date or text Variable reads empty, so `` `End Date` = null `` is true for a person with no end date.
Arithmetic treats an empty value as 0: `null + 1` is 1. Dividing by an empty value is a division by zero. A minus sign in front, and `abs`, keep an empty value empty. A comparison with an empty value, other than `= null`, is itself empty, so `if` takes its third argument.
## Operators
| Group | Operators | Example |
| - | - | - |
| Arithmetic | `+` add, `-` subtract, `*` multiply, `/` divide, `%` remainder, `^` power | `Price * Quantity` |
| Comparison | `=` equal, `<>` not equal, `<`, `<=`, `>`, `>=` | `Revenue >= 100000` |
| Logical | `and`, `or`, `not` | `Revenue > 0 and Margin > 0.2` |
| Membership | `in`, `not in`, with a set `{...}` or a range `a:b` | `if(Tier in 1:3, 1, 0)` |
`%` between two numbers is the remainder, so `17 % 5` is 2. `%` right after a number is a percentage.
### Order of operations
From first to last, cfo.ai applies operators in this order:
1. Parentheses and function calls
2. A minus sign in front of a value
3. `^`
4. `%` as a percentage
5. `*`, `/`, and `%` as a remainder
6. `+` and `-`
7. Comparisons: `<`, `<=`, `>`, `>=`, `=`, `<>`
8. `in` and `not in`
9. `not`
10. `and`
11. `or`
`^` works from right to left, unlike in Excel: `2 ^ 3 ^ 2` is 2 to the power of 9, which is 512. Use parentheses when the order matters.
When `and` or `or` already has its answer, an error on the other side does not matter: `false and` an error is false, and `true or` an error is true.
## Lookups and filters
A lookup in brackets after a name changes which items the reference reads. Every Dimension you do not name still follows the cell being calculated.
| You write | It reads |
| - | - |
| `Revenue[Department = "Sales"]` | Sales, for the cell's other Dimensions |
| `Revenue[Region = "West", Department = "Sales"]` | West and Sales together |
| `Revenue[Department in {"Sales", "Marketing"}]` | Both departments |
| `Revenue[Department not in {"Engineering"}]` | Every department except Engineering |
| `Revenue[Department in any]` | Every department |
| `Revenue[Date.Month < "2026-07"]` | Months before July 2026. `<>`, `<`, `<=`, `>`, and `>=` also work in a lookup |
| `Revenue[Department in any] where Revenue > 10000` | Every department whose revenue is over 10,000 |
| `` `List Price`$[Plan = this.Plan] `` | The list price for the plan of the cell being calculated |
When a lookup reads several items, wrap it in a function that says how to combine them, such as `sum`, `average`, or `count`: `average(Revenue[Department in any])`. See [Aggregation functions](/reference/functions#aggregation-functions).
Inside `where`, a plain name refers to the item being tested, and `this.` to the cell being calculated: `sum(Revenue[Department in any] where Revenue > this.Revenue)` adds the departments with more revenue than this one.
A lookup or offset attaches to a Variable's name, not to a calculation. `(Revenue - Cost)[-1]` is an error; write `Revenue[-1] - Cost[-1]`.
### Lookups through a Dimension mapping
When a mapping assigns each item of one Dimension to an item of another, such as accounts to reporting lines, a dot follows the mapping: `Account.Bucket` is the line the cell's account belongs to. A lookup on the mapped Dimension gathers every item the mapping puts there: `sum(Amount[Bucket = "COGS"])` adds every account in COGS. See [Dimension mappings and shared Dimensions](/concepts/dimension-mappings).
## Fixed references
A `$` right after a name makes a fixed reference. It reads only the items you name and does not follow the cell being calculated.
| You write | It reads |
| - | - |
| `` `Tax Rate`$ `` | The same value in every cell |
| `Revenue$[Department = "Sales"]` | Sales, with no other Dimension from the cell |
| `Revenue$[-1]` | A fixed reference with a period offset |
In the formula editor, a fixed reference shows a pin in place of its icon. **F4** or the pin button fixes or releases the reference at the cursor, and the saved text is the same as typing the `$`. A column of an imported table cannot be fixed.
## Period offsets
A number in brackets reads a nearby period, at the cell's own grain.
| You write | It reads |
| - | - |
| `Revenue[-1]` | The previous period: the previous month on a monthly row, the previous quarter on a quarterly row |
| `Revenue[1]` | The next period |
| `Revenue[-12]` | Twelve periods back: the same month last year on a monthly row |
| `Revenue[Department = "Sales"][-1]` | Sales in the previous period |
| `Revenue[Date.Month = [-1]]` | The previous month, written as a Date filter |
| `Revenue[Department = "Sales"]Date.Quarter[-1]` | Sales in the previous quarter, with the grain written out |
A period offset is how a running balance reads its own previous value: `` `Cash Balance`[-1] + `Net Cash Flow` ``. Without the offset, the formula reads itself and the cell shows `#CIRC!`.
## Dates
### Date grains
`Date` with a grain names the period a formula works in. Write the grain with a capital letter.
| You write | The period |
| - | - |
| `Date.Day` | A day |
| `Date.Week` | A week |
| `Date.Month` | A month |
| `Date.Quarter` | A calendar quarter |
| `Date.Half` | A half-year |
| `Date.Year` | A year |
### Fixed periods and date text
| You write | It reads |
| - | - |
| `Revenue[Date.Month = "2026-01"]` | January 2026 |
| `Revenue[Date.Quarter = "2026-Q1"]` | The first quarter of 2026 |
| `Revenue[Date.Year = "2026"]` | 2026 |
Date text names the start of a period. These forms work: `2026`, `2026-01`, `2026-01-31`, `2026-Q1`, `2026-H2`, `July 2026`, `Jul 1, 2026`, `1 July 2026`, `7/1/2026`, and `2026-07-01T13:30:12Z`. In `7/1/2026` the month comes first, unless the first number is over 12, as in `13/7/2026`. These do not work: `Q1 2026`, `Sept 2026`, `July` with no year, and two-digit years such as `7/1/26`.
`Revenue["2026"]` is not a filter for 2026. Write `Revenue[Date.Year = "2026"]`.
### Date ranges
A range includes both ends. A range written from the later date to the earlier one matches nothing.
| You write | It reads |
| - | - |
| `sum(Revenue[Date.Month in "2026-01":"2026-12"])` | Every month of 2026 |
| `sum(Revenue[Date.Month in [-12]:[-1]])` | The 12 months before the cell's month |
| `sum(Revenue[Date.Month in "2026-01":[-1]])` | January 2026 through the month before the cell's month |
| `sum(Revenue[Date.Month in startofyear([0]):[0]])` | Year to date |
| `sum(Revenue[Date.Month in startofquarter([0]):[0]])` | Quarter to date |
### Date filter presets
When you add a date filter to a reference in the formula editor, the presets write a range for you. What a preset covers depends on whether you are editing an Actuals formula.
| Preset | In an Actuals formula | In any other formula, such as a Forecast formula |
| - | - | - |
| **Last 3 months of actuals** | The cell's month and the two before it. When the reference reads the formula's own Variable, the three months before the cell's month. | The three months that end with the **Last close** month, the same months for every cell |
| **Last 6 months of actuals**, **Last 12 months of actuals** | As above, for 6 or 12 months | As above, for 6 or 12 months |
| **Year to date** | `startofyear([0]):[0]`: January through the cell's month | The same |
| **Quarter to date** | `startofquarter([0]):[0]`: the quarter's first month through the cell's month | The same |
| **Custom range** | Dates you pick, fixed or relative to the cell's period | The same |
On a reference by quarter, half-year, or year, the presets offer the last 2, 3, or 4 of those periods instead, such as **Last 4 quarters of actuals**.
For example, a Forecast formula that averages Revenue with the **Last 3 months of actuals** filter reads a run rate: every Forecast month uses the average of the last three closed months.
### Dates against imported timestamps
An imported Dimension can hold timestamps, such as one item per sync at `2026-07-01 00:00:00+00`. Where you write a date decides what it matches:
* In a lookup, `date(2026, 7, 1)` or a date Variable selects the item at midnight of that day, as long as every item of the Dimension comes from the source.
* In a formula's condition, a date never equals a timestamp item, so `[synced_month = date(2026, 7, 1)]` reaches no cell.
* Date text such as `"2026-07-01"` reaches the item at that moment in both places.
* ``synced_month.Month = startofmonth(`Snapshot Month`$)`` compares two dates, so it matches every item in that month.
## Conditions: where a saved formula applies
Every saved formula has a condition that says which cells it fills. You usually set it by choosing where to open the editor, or by telling Ari, rather than typing it. The formula editor's title shows the condition as pills.
| Condition | The formula fills | In the editor's title |
| - | - | - |
| `[]` | Every cell of the Variable | **Any Segment** |
| `[Department in any]` | Every department's cells, including finer breakdowns under them | **All Departments** |
| `[Department = "Sales"]` | Sales cells, including finer breakdowns under Sales | **Sales** |
| `[Date.Month in any]` | Every month of the Variable | **All Months** |
| `[Department = "Sales", Date.Month in any]` | Sales in every month | **Sales**, **All Months** |
| `$[Department = "Sales"]` | Only cells broken down by exactly Department, at Sales | **Sales** |
| `$[]` | Only the cell with no Dimensions | No table opens this condition |
Ari's formula cards in the chat show the same condition a little differently. A Dimension that applies to every item shows its name, such as **Department**. A **+ any** pill marks a condition that names a Dimension and also applies to finer breakdowns, which is every such condition without a leading `$`. `[]` reads **Any Segment** and `$[]` reads **Empty Segment**.
A `$` in front of a condition limits it to exactly the Dimensions it names. A `$` after a name in a formula makes a fixed reference. They are different things.
When several formulas could fill a cell, the more specific one wins; see [How formulas work](/concepts/formulas).
## Comments
```text theme={null}
Revenue * 1.1 // 10% uplift for the new price
/* A note that spans
more than one line */
```
`//` starts a comment that runs to the end of the line. `/* */` wraps a comment of any length.
## A leading equals sign
You can start a formula with `=`, as in a spreadsheet. cfo.ai removes it when it saves, so `=Revenue * 1.1` saves as `Revenue * 1.1`. In a cell of a text Variable, start a formula with `=`; without it, cfo.ai saves the words as text.
Inside a formula, `=` compares two values. Docs and Ari sometimes write a relationship with `=` to say which Variable a formula belongs to:
```text theme={null}
`Ending Customers` = `Beginning Customers` + `New Customers` - `Churned Customers`
`Subscription Revenue` = Customers * `Average Subscription Price`
```
Enter only the part after `=` as the formula of the Variable before it. Typed as one formula, the whole line would be a comparison.
## Preferred spellings
Older formulas may use other spellings of some operators. They still work, but write the preferred ones in new formulas.
| Preferred | Older spelling that still works |
| - | - |
| `^` | `**` |
| `=` | `==` |
| `<>` | `!=` |
Older function names, such as `avg` for `average`, are listed in [Formula functions](/reference/functions#older-spellings).
## How cfo.ai formulas differ from Excel and other spreadsheets
* A formula names Variables, not cells. There are no addresses such as `B7`; use a period offset such as `Revenue[-1]` for the period before.
* `and`, `or`, and `not` are operators, not functions. `AND(a, b)` shows the message **'and' joins conditions: (condition) and (condition). It cannot be called as a function.**
* `if` needs all three arguments.
* `^` works from right to left.
* There is no `&` operator and no text function. Keep text as the values of a text Variable or as the items of a Dimension.
* Some spreadsheet functions have no cfo.ai equivalent. See [Excel functions that are not available](/reference/functions#excel-functions-that-are-not-available).
## Related
* [Formula functions](/reference/functions)
* [Formula errors and warnings](/reference/formula-errors)
* [Write a formula](/guides/write-a-formula)
* [How formulas work](/concepts/formulas)
# Formula functions
Source: https://docs.cfo.ai/reference/functions
Every function you can use in a cfo.ai formula, such as sum, average, if, and eomonth, with its arguments, what it returns, its limits, and an example. Includes older names and what to write instead of Excel functions cfo.ai does not have, such as SUMIF, VLOOKUP, and TODAY.
cfo.ai formulas have 38 functions, listed here by category. Function names are not case-sensitive, so `SUM` and `sum` are the same function; write them in lowercase in new formulas. Each argument can be any expression, including a lookup, another function, or a condition. For operators, lookups, and filters, see [Formula syntax](/reference/formula-syntax).
In the formula editor, press **⌥ F12** (**Alt F12**) to see the syntax and an example of the function under the cursor.
## All functions at a glance
| Function | Returns | Arguments |
| - | - | - |
| [`sum`](#sum) | The total of the values | 1 to 10 |
| [`product`](#product) | The values multiplied together | 1 to 10 |
| [`average`](#average) | The mean of the values | 1 to 10 |
| [`min`](#min) | The smallest value, or the earliest date | 1 to 10 |
| [`max`](#max) | The largest value, or the latest date | 1 to 10 |
| [`count`](#count) | How many values are not empty | 1 to 10 |
| [`first`](#first) | The first value that is not empty | 1 or more |
| [`last`](#last) | The last value that is not empty | 1 or more |
| [`median`](#median) | The middle value | Exactly 1 |
| [`stdev`](#stdev) | The sample standard deviation | 1 to 10 |
| [`var`](#var) | The sample variance | 1 to 10 |
| [`abs`](#abs) | The value without its sign | Exactly 1 |
| [`sqrt`](#sqrt) | The square root | Exactly 1 |
| [`ceiling`](#ceiling) | The number rounded up to a multiple | Exactly 2 |
| [`floor`](#floor) | The number rounded down to a multiple | Exactly 2 |
| [`round`](#round) | The number rounded to a number of digits | Exactly 2 |
| [`if`](#if) | One of two values, depending on a condition | Exactly 3 |
| [`iferror`](#iferror) | A value, or a fallback when it is an error | Exactly 2 |
| [`year`](#year) | The year of a date | Exactly 1 |
| [`month`](#month) | The month of a date, 1 to 12 | Exactly 1 |
| [`day`](#day) | The day of the month, 1 to 31 | Exactly 1 |
| [`quarter`](#quarter) | The calendar quarter, 1 to 4 | Exactly 1 |
| [`half`](#half) | The half-year, 1 or 2 | Exactly 1 |
| [`weekday`](#weekday) | The day of the week as a number | 1 or 2 |
| [`weeknum`](#weeknum) | The week of the year | 1 or 2 |
| [`isoweeknum`](#isoweeknum) | The ISO 8601 week of the year | Exactly 1 |
| [`startofday`](#startofday) | The start of the day | Exactly 1 |
| [`startofweek`](#startofweek) | The Monday that starts the week | Exactly 1 |
| [`startofmonth`](#startofmonth) | The first day of the month | Exactly 1 |
| [`startofquarter`](#startofquarter) | The first day of the quarter | Exactly 1 |
| [`startofhalf`](#startofhalf) | January 1 or July 1 | Exactly 1 |
| [`startofyear`](#startofyear) | January 1 of the year | Exactly 1 |
| [`dateadd`](#dateadd) | A date moved by a number of units | Exactly 3 |
| [`datedif`](#datedif) | The difference between two dates | Exactly 3 |
| [`daysin`](#daysin) | The number of days in a period | Exactly 2 |
| [`date`](#date) | A date built from a year, month, and day | Exactly 3 |
| [`edate`](#edate) | A date moved by whole months | Exactly 2 |
| [`eomonth`](#eomonth) | The last day of a month | Exactly 2 |
A call with the wrong number of arguments shows `#VALUE!` in its cells, and the editor names the limit, such as **SUM requires between 1 and 10 arguments**. A name that is not on this list shows `#NAME?`. See [Formula errors and warnings](/reference/formula-errors).
## Aggregation functions
Aggregation functions combine several values into one, such as every department's revenue. The values usually come from a lookup that selects several items, such as `Revenue[Department in any]`. Empty values are skipped. Most take 1 to 10 arguments; `median` takes exactly one.
For example, when Revenue is 50 for Sales, 30 for Marketing, and 20 for Engineering:
| Formula | Result |
| - | - |
| `sum(Revenue[Department in any])` | 100 |
| `average(Revenue[Department in any])` | 33.33 |
| `min(Revenue[Department in any])` | 20 |
| `max(Revenue[Department in any])` | 50 |
| `median(Revenue[Department in any])` | 30 |
| `count(Revenue[Department in any])` | 3 |
| `product(Revenue[Department in any])` | 30,000 |
| `stdev(Revenue[Department in any])` | 15.28 |
| `var(Revenue[Department in any])` | 233.33 |
### sum
`sum` adds numbers and always returns a number: a sum with no values returns 0.
```text theme={null}
sum(value, value2, ...)
sum(Revenue[Department in any])
```
It takes 1 to 10 arguments and adds the values of all of them: ``sum(Revenue[Region in any], `Other Income`[Region in any])``.
### product
`product` multiplies numbers. It returns 0 when there are no values.
```text theme={null}
product(value, value2, ...)
product(`Growth Factor`[Date.Month in [-2]:[0]])
```
It takes 1 to 10 arguments.
### average
`average` returns the arithmetic mean of numbers, or an empty value when there are none. `avg` is an older spelling that still works.
```text theme={null}
average(value, value2, ...)
average(Revenue[Department in any])
```
It takes 1 to 10 arguments. With several arguments, it averages all their values together, so each value counts once.
### min
`min` returns the smallest number, or an empty value when there are none.
```text theme={null}
min(value, value2, ...)
min(Revenue[Department in any])
```
It takes 1 to 10 arguments. With one argument that holds dates, it returns the earliest date; when values mix numbers and dates, the numbers decide. With several arguments, every argument must be a number.
### max
`max` returns the largest number, or an empty value when there are none.
```text theme={null}
max(value, value2, ...)
max(`Observed On`[Employee in any])
```
It takes 1 to 10 arguments. With one argument that holds dates, it returns the latest date, so the example returns the newest observation. When values mix numbers and dates, the numbers decide. With several arguments, every argument must be a number.
### count
`count` returns how many values are not empty, of any type. It works like Excel's COUNTA, not Excel's COUNT.
```text theme={null}
count(value, value2, ...)
count(`Deal Amount`[Deal in any])
```
It takes 1 to 10 arguments. A value of 0 counts.
### first
`first` returns the first value that is not empty, in the order of the series, or an empty value when there is none.
```text theme={null}
first(value, source2, ...)
first(Revenue[Date.Month in any])
```
Over an imported table's rows, the order is the source's date column. Rows on the same date keep the source's order. A source with no date column is read in the source's order.
`first` takes several arguments only when each one is an imported column or a plain reference to a Variable or Dimension, with no arithmetic around it. It then returns the value from the input whose row is earliest. A dated row wins over an undated one, and when dates tie, the earlier argument wins.
### last
`last` returns the last value that is not empty, in the order of the series, or an empty value when there is none. Use it for a balance or a headcount, where the period's value is its latest one.
```text theme={null}
last(value, source2, ...)
last(`Cash Balance`[Date.Month in any])
```
Over an imported table's rows, `last` returns the row with the latest date in the source's date column. Rows on the same date keep the source's order. A source with no date column has no dates to compare, so `last` returns the last row in the source's order. A reference to an imported column with no function around it reads the same way as `last`.
`last` takes several arguments only when each one is an imported column or a plain reference to a Variable or Dimension, with no arithmetic around it. It then returns the value from the input whose row is latest. A dated row wins over an undated one, and when dates tie, the later argument wins. Each input keeps its own filters, so ``last(`Vendor A`, `Vendor B`)`` returns whichever vendor has the latest entry. This is how a Dimension combined from two accounting sources reads; see [Dimension mappings and shared Dimensions](/concepts/dimension-mappings).
### median
`median` returns the middle number, or an empty value when there are none. With an even count of values, it returns the mean of the two middle ones.
```text theme={null}
median(value)
median(`Annual Salary`[Employee in any])
```
It takes exactly one argument.
### stdev
`stdev` returns the sample standard deviation, which divides by the count minus one. It returns an empty value when there are fewer than two values. `stddev` is an older spelling that still works.
```text theme={null}
stdev(value, value2, ...)
stdev(Revenue[Region in any])
```
It takes 1 to 10 arguments and pools all their values into one result.
### var
`var` returns the sample variance, which divides by the count minus one. It returns an empty value when there are fewer than two values. `variance` is an older spelling that still works.
```text theme={null}
var(value, value2, ...)
var(Revenue[Region in any])
```
It takes 1 to 10 arguments and pools all their values into one result.
## Math functions
### abs
`abs` returns a number without its sign, so -5 becomes 5. An empty value stays empty.
```text theme={null}
abs(value)
abs(Actual - Budget)
```
### sqrt
`sqrt` returns the square root of a number. Use it on zero or a positive number, and guard a value that can be negative.
```text theme={null}
sqrt(value)
if(`Spread` >= 0, sqrt(`Spread`), 0)
```
### ceiling
`ceiling` rounds a number up to the nearest multiple of a significance, as Excel's CEILING does.
```text theme={null}
ceiling(number, significance)
ceiling(`Unit Price`, 0.05)
```
A positive number with a negative significance returns `#NUM!`.
### floor
`floor` rounds a number down to the nearest multiple of a significance, as Excel's FLOOR does.
```text theme={null}
floor(number, significance)
floor(`Unit Price`, 0.05)
```
A positive number with a negative significance returns `#NUM!`.
### round
`round` rounds a number to a number of digits, as Excel's ROUND does. A negative digit count rounds to the left of the decimal point.
```text theme={null}
round(number, num_digits)
round(Revenue, -3)
```
The example rounds to the nearest thousand.
## Conditional functions
### if
`if` returns one value when a condition is true and another when it is false. It needs all three arguments; unlike Excel, the third one is not optional.
```text theme={null}
if(condition, value_if_true, value_if_false)
if(Revenue > 0, `Gross Profit` / Revenue, 0)
```
A condition that compares with an empty value is not true, so `if` returns the third argument. To test for an empty value, compare with `null`: ``if(`End Date` = null, 1, 0)``. You can nest calls: `if(Revenue > 100, "high", if(Revenue > 50, "medium", "low"))`.
### iferror
`iferror` returns a value, or a fallback when that value is an error such as a division by zero.
```text theme={null}
iferror(value, value_if_error)
iferror(`Gross Profit` / Revenue, 0)
```
It takes exactly two arguments. An empty value is not an error, so `iferror` returns it unchanged.
## Date-part functions
Date-part functions return one part of a date as a number. Each takes exactly one date, except `weekday` and `weeknum`.
### year
`year` returns the four-digit year of a date, as Excel's YEAR does.
```text theme={null}
year(Date.Month)
```
### month
`month` returns the month of a date as a number from 1 to 12.
```text theme={null}
month(Date.Month)
```
### day
`day` returns the day of the month as a number from 1 to 31.
```text theme={null}
day(Date.Day)
```
### quarter
`quarter` returns the calendar quarter of a date as a number from 1 to 4.
```text theme={null}
quarter(Date.Month)
```
### half
`half` returns 1 for a date from January to June and 2 for a date from July to December. It returns 2 when the date is empty.
```text theme={null}
half(Date.Month)
```
### weekday
`weekday` returns the day of the week as a number, as Excel's WEEKDAY does. By default Sunday is 1 and Saturday is 7.
```text theme={null}
weekday(date, return_type)
weekday(Date.Day, 2)
```
The optional return type sets the numbering: 1 (Sunday is 1, the default), 2 (Monday is 1), 3 (Monday is 0), or 11 to 17 (the week starts on Monday through Sunday).
### weeknum
`weeknum` returns the week of the year, from 1 to 54, as Excel's WEEKNUM does. Except with return type 21, week 1 is the week that contains January 1.
```text theme={null}
weeknum(date, return_type)
weeknum(Date.Week, 21)
```
The optional return type sets the first day of the week: 1 (Sunday, the default), 2 (Monday), or 11 to 17 (Monday through Sunday). Return type 21 uses ISO 8601 weeks.
### isoweeknum
`isoweeknum` returns the ISO 8601 week of the year, from 1 to 53. It is the same as `weeknum(date, 21)`.
```text theme={null}
isoweeknum(Date.Week)
```
## Date-truncation functions
Date-truncation functions return the first day of the period that contains a date. Each takes exactly one date. Use them to line up dates from a source with the Model's periods.
### startofday
`startofday` returns the start of the day that contains a date.
```text theme={null}
startofday(Date.Day)
```
### startofweek
`startofweek` returns the Monday that starts the week containing a date.
```text theme={null}
startofweek(Date.Week)
```
### startofmonth
`startofmonth` returns the first day of the month containing a date.
```text theme={null}
startofmonth(Date.Month)
```
### startofquarter
`startofquarter` returns the first day of the calendar quarter containing a date.
```text theme={null}
startofquarter(Date.Month)
```
### startofhalf
`startofhalf` returns January 1 or July 1 of the half-year containing a date.
```text theme={null}
startofhalf(Date.Month)
```
### startofyear
`startofyear` returns January 1 of the year containing a date.
```text theme={null}
startofyear(Date.Month)
```
## Date arithmetic and construction
### dateadd
`dateadd` moves a date by a signed number of units.
```text theme={null}
dateadd(date, amount, "unit")
dateadd(Date.Month, -1, "month")
```
The unit must be in quotes: `"day"`, `"week"`, `"month"`, `"quarter"`, `"half"`, or `"year"`. The amount is a whole number; a negative amount moves back.
### datedif
`datedif` returns the whole number of units between a start date and an end date, as Excel's DATEDIF does.
```text theme={null}
datedif(start_date, end_date, "unit")
datedif(`Start Date`, Date.Month, "M")
```
The unit is `"Y"`, `"M"`, `"D"`, `"MD"`, `"YM"`, or `"YD"`. The count leaves out the end day, so add 1 to count both ends. A start date after the end date returns `#NUM!`, and a value that is not a date returns `#VALUE!`.
### daysin
`daysin` returns the number of days in the period that contains a date.
```text theme={null}
daysin(date, "unit")
daysin(Date.Month, "month")
```
The unit is one of the `dateadd` units, in quotes. Use it to spread an amount by calendar days, such as `` `Annual Fee` / 365 * daysin(Date.Month, "month") ``.
### date
`date` builds a date from a year, a month, and a day, as Excel's DATE does.
```text theme={null}
date(year, month, day)
date(year(Date.Month), 12, 31)
```
A month or day outside its range rolls into the next or previous month or year, so `date(2026, 13, 1)` is January 1, 2027. A year from 0 to 1899 has 1900 added. Unlike Excel, February 29, 1900 does not exist. A result outside the supported range returns `#NUM!`, and a value that is not a number returns `#VALUE!`.
### edate
`edate` moves a date by whole months and keeps the day inside the new month, as Excel's EDATE does.
```text theme={null}
edate(date, months)
edate(`Start Date`, 3)
```
A fraction of a month is dropped, so 2.7 moves two months.
### eomonth
`eomonth` returns the last day of the month a signed number of months from a date, as Excel's EOMONTH does.
```text theme={null}
eomonth(date, months)
eomonth(Date.Month, 0)
```
Use 0 for the date's own month, 1 for the next month, and -1 for the previous month. A fraction of a month is dropped.
## Older spellings
These names still work in existing formulas. Write the preferred name in new ones.
| Older spelling | Preferred name |
| - | - |
| `avg` | [`average`](#average) |
| `stddev` | [`stdev`](#stdev) |
| `variance` | [`var`](#var) |
## Excel functions that are not available
cfo.ai has no function by these names. A formula that uses one shows `#NAME?`. Write the cfo.ai form instead.
| Excel function | What to write in cfo.ai |
| - | - |
| `SUMIF`, `SUMIFS` | A filtered `sum`: `sum(Revenue[Region = "West"])`, or with a test, `sum(Revenue[Department in any] where Revenue > 10000)` |
| `COUNTIF`, `COUNTIFS` | A filtered `count`: ``count(`Deal Amount`[Deal in any] where `Deal Amount` > 0)`` |
| `AVERAGEIF`, `AVERAGEIFS` | A filtered `average`: `average(Revenue[Region in {"East", "West"}])` |
| `VLOOKUP`, `XLOOKUP`, `INDEX` and `MATCH` | A lookup in brackets, such as `` `List Price`$[Plan = this.Plan] ``, or a Dimension mapping; see [Map one Dimension's items to another](/guides/map-dimensions) |
| `AND()`, `OR()`, `NOT()` | The operators `and`, `or`, and `not`: `Revenue > 0 and Margin > 0.2` |
| `IFS`, `SWITCH`, `CHOOSE` | Nested `if` calls, or a separate formula for each item of a Dimension; see [Write a formula](/guides/write-a-formula#open-the-editor-in-the-right-place) |
| `TODAY()`, `NOW()` | No current-date function. A formula calculates for every period, and `Date.Month` is the period being calculated. |
| `OFFSET`, or a reference to the cell above | A period offset: `Revenue[-1]` |
| `MOD` | The `%` operator between two numbers: `17 % 5` is 2 |
| `POWER` | The `^` operator: `2 ^ 3` is 8 |
| `ROUNDUP`, `ROUNDDOWN` | For a number of 0 or more, [`ceiling`](#ceiling) or [`floor`](#floor) with a significance, such as `ceiling(Price, 1)`. For a negative number, swap them: `floor` rounds away from zero, as `ROUNDUP` does, so `floor(-2.5, 1)` is -3, and `ceiling` rounds toward zero, as `ROUNDDOWN` does. |
| `SUMPRODUCT` | A Variable for the product on each item, then a `sum`: ``sum(`Line Revenue`[Product in any])`` where `` `Line Revenue` `` is `Units * Price` |
| Text functions such as `CONCAT`, `LEFT`, or `TEXT` | No text functions. Keep text as the values of a text Variable or as the items of a Dimension. |
| Financial functions such as `NPV`, `IRR`, or `PMT` | No financial functions. Build the schedule with period formulas, such as a balance that reads `[-1]`, or ask Ari to calculate the figure. |
## Related
* [Formula syntax](/reference/formula-syntax)
* [Formula errors and warnings](/reference/formula-errors)
* [Write a formula](/guides/write-a-formula)
# Glossary
Source: https://docs.cfo.ai/reference/glossary
Short definitions of every cfo.ai product term, and the cfo.ai term for everyday words people use, such as assumptions, breakdowns, categories, tabs or sheets, month-end, versions of the plan, export, credits, and add a user. Each entry links to the page that explains it.
This glossary defines each cfo.ai term in a sentence or two and links to the page that explains it. Entries for everyday words, such as **Assumption**, **Month-end**, or **Sheet or tab**, name the cfo.ai term they mean. Entries are in alphabetical order.
## Actuals
The periods up to and including Last close, which usually read your accounting system. A Variable can use a different formula in Actuals than in the Forecast. See [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast).
## Address
Where one value lives: a Variable, an item from each of its Dimensions, and a period, such as Revenue for Sales in March 2026. It plays the part of a cell reference such as B7 in a spreadsheet. See [Variables and Dimensions](/concepts/variables-and-dimensions).
## Administrator
A member of a workspace. Every member is an administrator, who can see and edit every Page, invite others, and manage billing and settings. cfo.ai has no view-only or other lower role; to let someone read Pages without editing, use a [share link](#share-link). See [Sharing and access](/concepts/sharing-and-access).
## Aggregation
How a Variable that reads a connected system combines the source's rows in one period into one value, such as their sum or the latest row. See [Totals, aggregation, and time rollups](/concepts/totals-and-rollups).
## AI usage
What Ari's work draws on: your chats with Ari, background work it starts, every scheduled Trigger run, and images Ari creates. Each plan includes an amount for each billing cycle. You can buy more, cap what extra usage costs in a month with a spend limit, or turn on auto-reload. A sync uses none. Many people call it credits. See [Plans, AI usage, and connection slots](/concepts/plans-and-usage).
## API key
cfo.ai issues no API keys or access tokens. AI tools, and software you build, connect through the cfo.ai [MCP server](#mcp-server), and a person approves the connection in a browser. See [Is there a cfo.ai API or API key?](/guides/connect-an-ai-client#is-there-a-cfoai-api-or-api-key)
## Ari
cfo.ai's AI finance coworker. Ari answers questions by reading your Model and connected data, and it builds what an answer needs into the Model, where you can inspect and change it. See [How Ari works with you](/concepts/working-with-ari).
## Assumption
An input you choose rather than observe, such as a price, a growth rate, or a hire's start date. In cfo.ai an assumption is a Variable, and Ari labels every value it estimated as an assumption. See [Variables and Dimensions](/concepts/variables-and-dimensions).
## Block
A part of a Page: a Table block, a Canvas block, or a Text block. A Page can also hold a chart block made earlier; new charts are Canvas blocks. See [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks).
## Breakdown
A Variable split into rows or columns by the items of a Dimension, such as Revenue by Product. People also use the word for the Dimension itself. See [Break a number down](/guides/break-down-a-number).
## Breakdown total
A parent row that adds up the rows directly beneath it, such as Revenue above its products. It is a formula cfo.ai saves with the table, and you can replace it with your own, such as a ratio. See [Totals, aggregation, and time rollups](/concepts/totals-and-rollups).
## Canvas block
A block that draws a custom chart, scorecard, or interactive visual from your Model's numbers. See [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks).
## Catch-all
The item a Dimension mapping assigns to every source item that no assignment matches, such as Other. See [Dimension mappings and shared Dimensions](/concepts/dimension-mappings).
## Category
What many people call a Dimension, such as Department or Region. See [Dimension](#dimension).
## Cell comment
cfo.ai has no comments on cells. To keep a note beside your numbers, add a Text block, or ask Ari for a text Variable, such as Notes, on a table that lists records. See [What a table cannot do](/reference/table-blocks#what-a-table-cannot-do). A formula can hold its own comment; see [Formula syntax](/reference/formula-syntax#comments).
## Chart
New charts are Canvas blocks: ask Ari for a chart, because the **Add block** menu has no chart option. A chart block a Page already has can still be restyled. See [Change a chart or Canvas block](/guides/change-a-visual).
## Circular reference
A formula that needs its own result to calculate, shown in the cell as `#CIRC!`. See [How formulas work](/concepts/formulas).
## Connection
A link to another system, such as QuickBooks Online, Rippling, or a Google Sheet, that brings its data into your Model and refreshes it. Also called an integration. See [How connected data reaches your Model](/concepts/connected-data).
## Connection slot
One of the connections a workspace's plan lets it hold. Each source that syncs data into your Model takes a standard or premium slot; CSV uploads, and the MCP servers and APIs that Ari calls live, take none. Settings calls them integration slots. See [Plans, AI usage, and connection slots](/concepts/plans-and-usage).
## Credits
Another name for AI usage. See [AI usage](#ai-usage).
## Dashboard
A Page of the numbers you review regularly, such as burn, cash, and cash runway, usually built by Ari from Canvas blocks, Table blocks, and Text blocks. See [Build a dashboard or board report](/guides/build-a-dashboard).
## Dimension
A list of categories that breaks Variables down, such as Department, Region, Product, or Date. People also call these breakdowns or categories. See [Variables and Dimensions](/concepts/variables-and-dimensions).
## Dimension item
One entry in a Dimension, such as Sales in Department or West in Region. Called an item for short. See [Variables and Dimensions](/concepts/variables-and-dimensions).
## Dimension mapping
A one-way lookup that assigns each item of one Dimension to an item of another, such as GL accounts to P\&L lines. See [Dimension mappings and shared Dimensions](/concepts/dimension-mappings).
## Drill in
To open, under a calculated row, the rows its formula reads. Drill-in rows have a hatched background, and **Clear drill-in** closes them. See [Break a number down](/guides/break-down-a-number#see-the-inputs-behind-a-calculated-number-drill-in).
## Empty value
The value of a text or date cell that holds nothing, written `null` in a formula. A number cell that holds nothing reads 0 instead. See [How formulas work](/concepts/formulas).
## Export
cfo.ai's export is **Download spreadsheet** in the Page header, which saves the Page's tables as an Excel workbook. For a PDF or a CSV, ask Ari. See [Export a Page to Excel](/guides/download-a-spreadsheet).
## Expression
The part of a formula that says what to calculate, such as `Revenue - Expenses`. See [How formulas work](/concepts/formulas).
## Fiscal year
cfo.ai has no fiscal-year setting: quarters, half-years, and years follow the calendar. Ari can add fiscal-year totals for a year that starts in another month. See [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast).
## Fixed reference
A reference written with `$` after a Variable's name. It reads the same value wherever the formula runs, instead of following the cell being calculated. See [Formula syntax](/reference/formula-syntax).
## Forecast
The periods after Last close, calculated from your assumptions and formulas. A forecast is not a separate model: it is the later part of the same Model's timeline. See [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast).
## Formula
What sets a Variable's values where it applies: an expression, its scope, and, when it covers only Actuals or only Forecast, a formula range. A value typed into a cell is saved as that cell's own formula. See [How formulas work](/concepts/formulas).
## Formula column
A column beside a table's row names that shows each row's formula. It can split into one column for each formula range, such as Actuals and Forecast. See [Use different formulas for Actuals and Forecast](/guides/actuals-and-forecast-formulas).
## Formula range
A span of the timeline that can have its own formula. Actuals and Forecast are the two formula ranges, split by Last close. See [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast).
## Grain
The length of the periods a table shows, such as months, quarters, or years. **Raw** shows each date without grouping it into periods. Settings calls it date granularity. See [Table block controls and messages](/reference/table-blocks#table-header).
## Guide
An interactive walkthrough that highlights Pages, blocks, or cells and explains what someone should notice. See [Skills, Triggers, and Guides](/concepts/automation).
## Integration
Another name for a connection. See [Connection](#connection).
## Last close
The end of the last period whose books you have closed, your month-end. It splits Actuals from the Forecast, and it moves only when you or Ari move it. See [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast).
## Line item
What many people call a Variable, such as Revenue or Rent. See [Variable](#variable).
## Main
The Scenario that holds your plan, which other Scenarios branch from. Pages show Main unless you pick another Scenario. See [Scenarios, Snapshots, and version history](/concepts/scenarios).
## MCP server
A way for AI tools to work with a service. cfo.ai's MCP server, at `https://api.cfo.ai/mcp`, lets AI tools such as Claude, ChatGPT, or Codex read your Model and make the changes you ask for, as you. In **Settings → Connections**, you can also give Ari other services' MCP servers. See [Connect Claude, ChatGPT, Codex, or another AI tool to cfo.ai](/guides/connect-an-ai-client).
## Mention
A chip in the message box that points Ari at an exact cell, row, column, block, Page, or formula, so you do not have to describe it. See [Chat controls and messages](/reference/chat#start-a-chat).
## Model
Your company's shared financial model: its Variables, Dimensions, formulas, and Scenarios, which you, Ari, and connected AI tools all work on. Each workspace has one Model. See [How cfo.ai works](/concepts/how-cfoai-works).
## Month-end
What many teams call Last close: the end of the last month whose books are closed. See [Last close](#last-close).
## Multi-entity
Reporting several legal entities, such as a US company and a UK subsidiary, and their group total in one Model. See [Model several entities and consolidate them](/guides/consolidate-entities).
## Original table
The table where a Variable or Dimension was created. Removing its last row or column there deletes it from the Model; removing it from any other table only takes it off that table. See [Table block controls and messages](/reference/table-blocks#removing-and-deleting).
## Override
A value or formula saved for one cell or one item, which replaces the formula it would otherwise take from its row. A dashed underline marks a cell with its own value or formula. See [How formulas work](/concepts/formulas).
## Page
A report built from blocks: Table blocks, Canvas blocks, and Text blocks. People sometimes call Pages sheets or tabs. A Page shows the Model's numbers; it does not hold copies of them. See [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks).
## Period
One bucket of time at the Model's base grain, such as March 2026 in a monthly Model. See [Variables and Dimensions](/concepts/variables-and-dimensions).
## Plan
The word means two things in cfo.ai: your financial plan, which lives in Main and its Scenarios, and your workspace's subscription plan, which sets what the workspace includes. See [Main](#main) and [Plans and billing](/reference/plans-and-billing).
## Refresh
A connection's update, which brings the source's latest data into your Model. Also called a sync. Every integration refreshes once a day, by default in a nightly run that starts at 02:00 UTC, and you can ask Ari to refresh one now. See [Refresh data or change when it refreshes](/guides/refresh-data).
## Saved query
The query that brings one table from a connection into your Model. See [How connected data reaches your Model](/concepts/connected-data).
## Scenario
A branch of your Model where you can try a different plan, such as a hiring freeze, without changing Main. People also call these versions of the plan. See [Scenarios, Snapshots, and version history](/concepts/scenarios).
## Scope
Where a formula applies: every cell of its Variable, one item, one formula range, or one cell. See [How formulas work](/concepts/formulas).
## Segment
A slice of a Variable named by one or more Dimension items, such as Revenue for Sales in the West. See [Variables and Dimensions](/concepts/variables-and-dimensions).
## Segment by
The table action that shows a Variable's segments as rows or columns, one for each item of a Dimension. See [Break a number down](/guides/break-down-a-number).
## Segmentation and Mapping
The two roles a Dimension can play on a table, set with **Use as**. **Segmentation** breaks Variables down by the Dimension's items. **Mapping** shows which item each row belongs to. See [Dimension mappings and shared Dimensions](/concepts/dimension-mappings).
## Share link
A link that lets someone outside your workspace read chosen Pages, in one Scenario, without a cfo.ai account. It can require a password, and it never allows editing. The Share panel calls it an external link. See [Sharing and access](/concepts/sharing-and-access).
## Sheet or tab
What spreadsheet users often call a Page. A Page holds blocks, and its Table blocks hold the rows and columns of numbers. See [Page](#page).
## Skill
A saved workflow that teaches Ari how your team does a recurring kind of work. See [Skills, Triggers, and Guides](/concepts/automation).
## Snapshot
A copy of Main or a Scenario pinned to one moment in its history, which no later sync or edit reaches. You can still type over its values. See [Scenarios, Snapshots, and version history](/concepts/scenarios).
## Summary column
A total column at the end of a group of columns on one table, headed **SUM**, **LAST**, or **TOTAL**, for example. It summarizes the cells shown in its group and saves no formula. See [Totals, aggregation, and time rollups](/concepts/totals-and-rollups).
## Sync setup
A saved setup that copies the results of one read-only tool or endpoint of an MCP server or API into your data on each sync. Ari creates and edits sync setups. See [How imported data loads](/reference/imported-data#sync-setups-for-mcp-servers-and-apis).
## Table block
A spreadsheet-like block that shows and edits Variables, usually across time. It is the closest thing in cfo.ai to a worksheet. See [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks).
## Text block
A block of written notes on a Page. See [Pages, Table blocks, and Canvas blocks](/concepts/pages-and-blocks).
## Theme
Your own colors for cfo.ai, which only you see. A workspace brand replaces your theme in that workspace. Light or dark mode is a separate setting. See [Change your theme, sounds, and notifications](/guides/personalize-cfoai).
## Time rollup
A Variable's own formula for quarters, half-years, and years: the sum, last value, or average of its months. A Variable without one shows 0 in quarter and year cells. See [Totals, aggregation, and time rollups](/concepts/totals-and-rollups).
## Total
A total is one of two things in cfo.ai. A [breakdown total](#breakdown-total) is a parent row that adds up the rows beneath it. A [summary column](#summary-column), which a table's menu calls a total column, summarizes a group of columns. They calculate differently; see [Totals, aggregation, and time rollups](/concepts/totals-and-rollups).
## Trigger
A scheduled instruction that has Ari run a recurring task at the time you choose. See [Skills, Triggers, and Guides](/concepts/automation).
## Undo
**⌘ Z** undoes the last change you made to the Model in the current browser tab. To undo a change Ari made, or one from an earlier visit, use version history. See [Undo a change or restore an earlier version](/guides/undo-changes).
## User
cfo.ai calls the people in a workspace administrators. To add a user, invite them as an administrator. See [Administrator](#administrator).
## Variable
A named line of your Model, such as Revenue, Headcount, Cash, or Price, with one value for each period and each item it is broken down by. People also call these line items or assumptions. See [Variables and Dimensions](/concepts/variables-and-dimensions).
## Variance
The difference between a table's values and another Scenario or an earlier period, shown in **Variance** and **Variance %** comparison columns. Green means better and red means worse, as set by the Variable's **Variance colors**. See [Compare Scenarios side by side](/guides/compare-scenarios).
## Version
People use the word for three things in cfo.ai: a [Scenario](#scenario), which is a different plan; a [Snapshot](#snapshot), which is a plan pinned at one moment; and an entry in [version history](#version-history), which you can restore.
## Version history
The record of every change to the Model, yours and Ari's, where you can undo one change or restore an earlier version. See [Scenarios, Snapshots, and version history](/concepts/scenarios).
## Workspace
Your company's home in cfo.ai: its Model, Pages, Scenarios, connections, chats with Ari, saved Skills and Triggers, and team. Some people call a workspace a project. See [How cfo.ai works](/concepts/how-cfoai-works).
## Workspace brand
A workspace's saved look: its name, colors, heading typeface, corner shape, and logo. Everyone who opens the workspace sees it, including visitors who follow a share link. See [Brand your workspace](/guides/brand-your-workspace).
# How imported data loads
Source: https://docs.cfo.ai/reference/imported-data
The rules cfo.ai follows when it loads synced and uploaded data: CSV format and date parsing, how columns become Variables and Dimensions, starting formulas, accounting statement columns, saved query rules and limits, when data refreshes, how much history loads, and sync setup limits.
This page lists the rules that decide how a source's rows become numbers in your Model: how a CSV is read, what each column becomes, when data refreshes, and how much history loads. A saved query is the query that brings a source's rows into your Model; see [Saved queries](#saved-queries). For the path data takes, read [How connected data reaches your Model](/concepts/connected-data). For a file's or a connection's messages, see [Connection statuses and actions](/reference/connection-statuses).
## CSV files
A CSV you upload as a data source loads as one table.
| Rule | Detail |
| - | - |
| File type | `.csv` only, not empty, up to 50 MB per file |
| First line | Column names. The loader reads the table from the first line of the file. |
| Separator | Comma |
| Rows | Every row has as many fields as the first line has column names |
| Repeated column name | Still loads. Names are compared without letter case, and the later column is stored under its name and its own column number, so a second `Amount` in the fourth column becomes `Amount (column 4)`. |
| Blank column name | Stored as `COLUMN_` and the column's number, such as `COLUMN_4` for the fourth column |
| Each file | Becomes its own data source, named after the file until you rename it |
| Refresh | Never. A new upload of the file replaces its rows. |
A report exported from an accounting, payroll, or payments tool often has title lines above the table and totals below it. The loader does not skip those lines, so such a file fails with **The file couldn’t load**. To load it, delete those lines or let Ari clean up a copy; see [Upload a CSV as a data source](/guides/upload-a-csv).
The **Upload** button does not take Excel workbooks or PDFs. Attach those to a chat and ask Ari to load the table you need.
## Dates in CSV files
cfo.ai decides how to read dates for a whole column at once.
* It reads every value in the column and picks the one day and month order that reads them all. A column that holds `13/04/2026` is read day-first throughout.
* A column whose values all read either way, such as `03/04/2026`, is read month-first.
* A column that needs both orders is not a date column, and it loads as text.
* A date with a clock, such as `2026-09-25 00:56`, loads as a timestamp.
* Plain digit strings, such as `2024` or `20240229`, stay numbers. Only a column whose name is exactly `yyyyMMdd` reads compact dates such as `20260131`.
A column named `Date` does not place the rows on the calendar by itself. Tell Ari which column dates the rows, such as an invoice date or a period column, and Ari saves it as the source's date column. A table that shows a source with no date column by month cannot calculate it; see [Table calculation notices](/reference/formula-errors#table-calculation-notices).
## How columns become Variables and Dimensions
Each column a saved query returns becomes a Variable or a Dimension when it first loads.
| Column | Becomes |
| - | - |
| Numbers | A Variable that adds up, unless its name marks it as an identifier |
| Numbers with an identifier name | A text Dimension, so a leading zero in `00400` and every digit of a long ID stay as written |
| Text | A Dimension |
| Dates | Dates |
| Text from a database query (`TEXT`, `VARCHAR`, `STRING`, or `CHAR`) | A text Dimension, whatever its name and even when every value looks like a number. A cast in the saved query, such as `to_varchar(stage_order)`, decides the role before the first load. Text whose values are all dates still loads as dates. |
A name marks an identifier when its last word is "id", including a camel-case ending such as `dealId`, or when it ends in "account number" or "account code". `Customer ID`, `deal_id`, `P&L Account Number`, and `SalesDefaultAccountCode` load as Dimensions. `Amount Paid`, `Account Balance`, and `Number of Accounts` still add up.
The first load fixes each column's role and stored type, and later loads keep them. To change a column's role, ask Ari, for example "Load Store Code as a Dimension instead." Ari switches it in place, so formulas, tables, and filters that use the column keep working. The stored type does not change, so a number column that becomes a Dimension has number items; to keep leading zeros, the query must return the column as text under a new name. A column that feeds a Dimension other queries also feed cannot switch roles for one query.
## Starting formulas
When a column first loads, its Variable or Dimension gets a saved starting formula, which you can open, edit, or delete like any other formula:
| Loads as | Starting formula |
| - | - |
| Variable | `sum` over the source column |
| Dimension | `last` over the source column: the value with the latest date when the source has a date column, or the last value in the source's order when it has none |
| A roster field from an HR source, such as pay rate | Each employee's latest value in each period. A period with no value carries the previous one forward, and an explicit zero replaces it. Department and company totals add each employee's latest value once. |
A starting formula has no [formula range](/reference/glossary#formula-range), so it reads source rows in every period, including months after [Last close](/reference/glossary#last-close). A Variable with no saved formula, because it loaded before starting formulas existed or someone deleted its formula, reads its source only through Last close, and its type's default after that, 0 for a number or an empty value. See [How formulas work](/concepts/formulas).
## Accounting statement columns
| Source | Saved queries | Columns |
| - | - | - |
| QuickBooks Online | **QuickBooks Profit & Loss**, **QuickBooks Balance Sheet** | Amounts split into **Revenue**, **Cost of Goods Sold**, **Expense**, **Other Income**, **Other Expense**, and **Unclassified P\&L Amount**, with the account, **Vendor**, **Entity**, and **Currency** |
| Xero | **Xero Income Statement** or **Xero Income Statement by Tracking Category**, **Xero Balance Sheet** | Amounts by account, with **Entity** and **Currency**, and tracking categories when you use them |
| NetSuite SuiteAnalytics | **NetSuite Income Statement**, **NetSuite Balance Sheet** | Amounts by account. Income Statement rows also carry **Subsidiary** and **Currency Name**. |
| Rillet | **Rillet Income Statement**, **Rillet Balance Sheet** | One set of rows per subsidiary, with **Entity** and **Currency** |
An amount whose account has no classification loads into the statement's unclassified amount, which stays part of the statement's total. Normal activity is positive within its category, and refunds and reversals reduce it.
**Entity** holds the company's name, so each company's rows stay apart. When a second connection of the same integration loads for the first time, such as a second QuickBooks Online company, its compatible Dimensions, such as **Entity**, **Currency**, and the account, join the ones the first connection created, so both companies share one Entity Dimension. Dimensions from different integrations, such as QuickBooks Online and Xero, do not join on their own. See [Dimension mappings and shared Dimensions](/concepts/dimension-mappings) and [Consolidate entities](/guides/consolidate-entities).
## Source logos
A Variable or Dimension shows its integration's logo on table row and column labels and in formula reference chips when every reference in its formulas reads imported columns of one integration. Two connections of the same integration count as one. A formula that mixes integrations, or reads another Variable, shows no logo. An MCP server or API added by its address shows its company's logo when the address is a public company domain.
## Saved queries
| Rule | Detail |
| - | - |
| Who writes them | cfo.ai adds them for supported sources, and Ari writes the rest. You see them read-only on the connection's page under **Queries**. |
| Scenarios | Saved queries belong to Main. Every ordinary Scenario reads Main's latest query and result. A Snapshot keeps the query and result it was pinned with. You cannot test a query change in one Scenario alone. |
| Planning time limit | When a query is saved, the warehouse runs it once to count its rows and read its columns, for at most 40 seconds. A query that runs longer is refused and nothing is saved. |
| Fewer rows than the source | Can be on purpose: a query may filter the source to what you asked for. Ask Ari what the query keeps. |
| Updates to cfo.ai's own queries | A query cfo.ai installed that nobody edited moves to cfo.ai's current version at the next sync. A query you or Ari edited keeps its text. An update never drops a column the query already loaded. |
| Removing a query | Ari first lists what the removal retires: the Variables and Dimensions the query owns and the formulas and tables that read them. It deletes the query only after you confirm. The connection and its other queries stay. If the query is loading, the load finishes first. |
| Changing a query | An edit that would drop columns your Model uses is refused until you approve the exact list of what it retires. An edit cannot change the type of a column that already loaded. |
## Refresh schedule
| Source | When it refreshes |
| - | - |
| Integrations | Once a day, in a shared nightly run that starts at 02:00 UTC (7 PM Pacific the evening before during daylight saving time, 6 PM in winter). The run reruns each connection's saved queries after its data arrives. |
| A connection with its own refresh time | Once a day at that time of day, in its time zone, keeping that clock time across daylight saving changes. A new time applies from the next nightly run. |
| Google Sheets and Google Drive | Nightly and when you ask Ari. An unchanged file starts no update. |
| CSV uploads | Never |
| MCP servers and APIs | Live when Ari calls them. A saved sync setup refreshes nightly, like an integration. |
| Any source, on request | When you ask Ari to refresh it. There is no **Sync now** button. |
The nightly refresh and refreshes you ask for work the same on every active plan, including a trial. A workspace with no plan, or whose billing is locked, does not refresh until it has an active plan again. A connection's last loaded time counts only syncs that finished; a failed or canceled sync does not move it.
## How much history loads
| Source | History |
| - | - |
| QuickBooks Online, Xero | From January 1 of last year through today. The three most recent complete months load first, and the rest follows in the background. |
| QuickBooks Online, Xero, full-history import | From a year you name, 2015 or later. The months it imports stay through later refreshes. A QuickBooks import also takes the balance sheet of the month before that year. |
| QuickBooks Online, nightly refresh | The last three months: the current month and the two before it. The whole range when the connection has not synced for more than a month. |
| Xero, nightly refresh | The whole range |
| Sources through Fivetran | The full history of each selected table. A newly added table loads its full history. |
| Stripe (Direct) | Every object on the first sync, then Stripe's changes since the last sync |
## Sync setups for MCP servers and APIs
A sync setup copies the results of one read-only tool or endpoint into your data on each sync. Ari creates and edits them; the connection's page lists them under **Syncs** as **Enabled** or **Paused**, with **Last warehouse sync** or **Not synced to the warehouse yet.**
| Type | What it keeps |
| - | - |
| **Replace · Keeps the latest result** | The latest complete result |
| **Snapshot · Keeps each capture** | Every successful capture, stamped with when it was taken |
| **Upsert · Adds and updates matching records** | New records, and updates to records it already holds. It does not remove records a later response leaves out. |
| **Append · Adds new records and keeps existing ones** | New records only. A record it already holds never changes. |
| Limit | Value |
| - | - |
| Replace and snapshot, without a changes-since filter | 10,000 pages, 16 MiB per page |
| Upsert, append, or any sync with a changes-since filter | 100 pages, 20,000 records, and 16 MiB in all |
| One page from an MCP server | 2 MiB |
| One sync | 10 minutes |
A sync that reaches a limit stops without saving a partial result, and the data from the last complete sync stays. The tool or endpoint must be set to **Allow**.
## Related
* [How connected data reaches your Model](/concepts/connected-data)
* [Check that your numbers match your books](/guides/check-imported-data)
* [Refresh data or change when it refreshes](/guides/refresh-data)
* [Upload a CSV as a data source](/guides/upload-a-csv)
# Reference
Source: https://docs.cfo.ai/reference/index
Exact facts about cfo.ai to look up while you work: product terms, the interface, Settings, keyboard shortcuts, chat and table controls, limits, formula syntax and functions, integrations, connection statuses, plans, privacy, and example prompts.
Reference pages describe cfo.ai exactly: every control, setting, message, limit, and function, laid out the way the product shows them. Look things up here while you work. For steps, see the [How-to guides](/guides); for why things work the way they do, see [Concepts](/concepts).
## Terms
The [Glossary](/reference/glossary) defines every product term in a sentence or two. It also maps everyday words, such as assumptions, breakdowns, tabs, and month-end, to the cfo.ai term.
## Using cfo.ai
The app's screens, menus, and messages, one page for each part of the product.
* [The cfo.ai interface](/reference/interface): sign-in, the sidebar, the account menu, the Triggers page, the Help center, workspace search, the Page header, sharing, Canvas and chart blocks, and phones.
* [Settings](/reference/settings): every Settings section and field, and what Settings has no setting for.
* [Keyboard shortcuts](/reference/keyboard-shortcuts): shortcuts for search, chat, Pages, table cells, formulas, and undo.
* [Chat controls and messages](/reference/chat): the message box, what the chat shows while Ari works, Ari's cards, and every stop and error message.
* [Table block controls and messages](/reference/table-blocks): table menus, formats, colors and marks, and the notices when a change is not saved.
* [Scenario and version history controls](/reference/scenarios-and-history): the Scenario picker, the Snapshot and merge dialogs, and the version history panel.
* [Limits and file types](/reference/limits): every size, count, and time limit.
## Formulas
The formula language: how to write names, filters, and dates, what each function does, and what each error means.
* [Formula syntax](/reference/formula-syntax): names, filters, operators, dates, and how cfo.ai formulas differ from Excel.
* [Formula functions](/reference/functions): every function, with its arguments and an example.
* [Formula errors and warnings](/reference/formula-errors): error codes, formula editor messages, and table calculation notices.
## Data
What you can connect, what each connection status means, and how cfo.ai loads what it imports.
* [Integrations directory](/reference/integrations): every system you can connect, and what each one syncs.
* [Connection statuses and actions](/reference/connection-statuses): every status a connection can show, the action that fixes it, and its messages.
* [How imported data loads](/reference/imported-data): file parsing, column types, starting formulas, and refresh times.
## Account
Plans, billing, privacy, and your data.
* [Plans and billing](/reference/plans-and-billing): plans, the free trial, AI usage, billing settings, and every billing message.
* [Privacy, security, and legal documents](/reference/privacy-and-legal): the Privacy Policy and Terms, how cfo.ai uses your data, and security answers.
## Examples
Ready-made text to copy and adapt.
* [Prompt library](/reference/prompt-library): prompts that get good results from Ari, by goal.
* [Sample Skills](/reference/sample-skills): ready-to-save Skills with full instructions.
# Integrations directory
Source: https://docs.cfo.ai/reference/integrations
Which apps cfo.ai can connect to and what data comes in from each, such as QuickBooks, Xero, NetSuite, Stripe, Gusto, HubSpot, and databases: how each one connects, whether it is standard or premium, what syncs, how much history loads, its limits, and what you need first. Also what to do when your system is not listed.
cfo.ai pulls data from accounting, HR and payroll, CRM, payments, database, and file sources, and it lets Ari call other services through MCP servers and APIs. This page lists every source in the in-app directory, with the facts you need before you connect one. To connect a source, follow [Connect your accounting system](/guides/connect-accounting) or [Connect payroll, HR, CRM, payments, or a database](/guides/connect-other-sources). cfo.ai only reads from these systems; see [What cfo.ai does not do with your sources](#what-cfoai-does-not-do-with-your-sources).
## Check whether your system is supported
These are the systems customers ask about most. Search the rest of this page, or the **Search integrations** box in the app, for any other name.
| You use | In cfo.ai |
| - | - |
| QuickBooks Online | Supported: [QuickBooks Online](#quickbooks-online), standard |
| QuickBooks Desktop or QuickBooks Enterprise | Not available. Export reports to CSV and [upload them](/guides/upload-a-csv), or attach them to a chat for Ari. |
| Xero | Supported: [Xero](#xero), standard |
| NetSuite | Supported: [NetSuite SuiteAnalytics](#netsuite-suiteanalytics), premium |
| Sage Intacct | Supported: [Sage Intacct](#sage-intacct), premium |
| Acumatica, Visma, Microsoft Dynamics 365 Business Central, Exact Online | Supported through Fivetran, premium. See [Other accounting, billing, and commerce systems](#other-accounting-billing-and-commerce-systems). |
| Rillet | Supported: [Rillet](#rillet), standard |
| Zoho Books, FreshBooks, e-conomic | Supported through Fivetran, standard |
| bill.com, Quicken, Puzzle, Tripletex | Not available |
| Pennylane | Available as an [API for Ari](#apis), not as a synced integration |
| Gusto, Justworks | Supported: [Gusto](#gusto), [Justworks](#justworks), standard |
| ADP Workforce Now, Paychex, Hibob, UKG Pro, UKG Ready, BambooHR, Deel, Workday | Supported through Merge. See [Other HR and payroll systems](#other-hr-and-payroll-systems). Workday is premium. |
| Rippling | Supported: [Rippling](#rippling), standard. Also available as an MCP server and an API for Ari. |
| HubSpot, Salesforce | Supported: [HubSpot](#hubspot), [Salesforce](#salesforce), standard |
| Stripe | Supported: [Stripe (Direct)](#stripe-direct), standard. Also an MCP connector and an API for Ari. |
| Shopify | Supported: [Shopify](#shopify), standard |
| Square, PayPal, Braintree, Chargebee, Recurly, Zuora | Supported through Fivetran, standard |
| Toast | Not available |
| Mercury | Available to Ari as an [MCP connector](#mcp-connectors) and an [API](#apis) for live lookups, not as a synced integration |
| Other banks | No direct bank feed. Bank balances reach cfo.ai through your accounting system's balance sheet, or upload a CSV of bank transactions. |
| Brex, Ramp | Supported through Fivetran, standard. Also MCP connectors for Ari. |
| Snowflake, BigQuery, Databricks | Supported: [Snowflake](#snowflake), [BigQuery](#bigquery), [Databricks](#databricks), premium |
| PostgreSQL, MySQL, SQL Server | Supported, premium. See [PostgreSQL](#postgresql) and [Other databases and data warehouses](#other-databases-and-data-warehouses). |
| Amazon Redshift | Not available |
| Supabase | Available as an [MCP connector](#mcp-connectors) for Ari, not as a synced integration |
| Google Sheets | Supported: [Google Sheets](#google-sheets), standard |
| Excel workbooks | Connect one through [Google Drive](#google-drive), attach it to a chat for Ari, or [import it as a model](/guides/import-a-spreadsheet-model). The **Upload** button takes CSV files only. |
| Airtable | Supported through Fivetran, standard. Also an MCP connector for Ari. |
| Notion, GitHub | Supported through Fivetran, standard. Notion is also an MCP connector for Ari. |
| SharePoint, OneDrive, Box, Dropbox | Supported through Fivetran for files, standard |
| Carta | Available as an [MCP connector](#mcp-connectors) for Ari |
## The directory in the app
The **Integrations** tab of the connection manager holds the same catalog. Open it from **Connections** in the sidebar with **+** (**Add integration**).
| Control | What it does |
| - | - |
| **Search integrations** | Finds a source by name |
| **Upload** | Uploads CSV files as data sources |
| **Filter integrations** | Shows **All integrations**, or one type: **Accounting**, **CRM**, **Data upload**, **Data Warehouse**, **HR & Payroll**, **Recruiting**, **Support & Ticketing**, or **Other** |
| **Sort integrations** | **Relevance**, **A–Z**, or **Z–A**. **Relevance** shows a **Featured** group first: QuickBooks Online, Xero, Rillet, Gusto, Rippling, Google Sheets, Justworks, HubSpot, and Salesforce. The other sources follow. In each group, sources you already connected come first; after them, the **Featured** group keeps the order above and the other sources are alphabetical. |
| **List view**, **Grid view** | Changes how the cards are laid out |
[Connection statuses and actions](/reference/connection-statuses#directory-cards) explains each card's button.
## If your system is not listed
You can still bring its data in:
* Export a report from the system, such as a general ledger, a monthly profit and loss, or a payroll register, and [upload it as a CSV](/guides/upload-a-csv). An upload does not refresh, so upload a new export when the numbers change.
* Attach the export or workbook to a chat and ask Ari to load it or [import it as a model](/guides/import-a-spreadsheet-model).
* If the data sits in a database cfo.ai supports, such as PostgreSQL, connect the database.
* If the system has an MCP server or an API, [give Ari access to it](/guides/give-ari-another-service), so Ari can look things up live.
To ask for a system that is not listed, email [success@cfo.ai](mailto:success@cfo.ai) and name the system.
## Standard and premium integrations
Each integration that syncs data into your Model is standard or premium, and each one uses a connection slot on your plan. Your plan sets how many standard and premium connections you can hold; see [Plans and billing](/reference/plans-and-billing).
| Class | Sources | Plans |
| - | - | - |
| Standard | Every integration on this page that is not marked premium, including QuickBooks Online, Xero, Rillet, Stripe (Direct), Gusto, Rippling, HubSpot, Salesforce, Shopify, and Google Sheets | Every plan that includes integrations |
| Premium | NetSuite SuiteAnalytics, every database and data warehouse, and the other sources marked "(premium)" on this page, such as Sage Intacct, Acumatica, Visma, Workday, Microsoft Dynamics 365 Business Central, and Amazon S3 | The Enterprise plan. A directory card for a premium source your plan does not include shows a **Premium** badge. |
These connections do not use a slot:
* CSV uploads.
* MCP servers and APIs that Ari calls live. If Ari saves a sync setup that copies an MCP server's or an API's data into your Model, that server or API then uses one standard slot.
Two rules change the count:
* Every Google Sheets connection in a workspace shares one slot. Each Google Drive connection uses its own.
* Each connection to a system uses its own slot, so a second QuickBooks company uses a second slot. A connection keeps its slot while it is broken or unfinished, until you remove it.
## How each kind of source connects
Most sources connect through Merge or Fivetran, outside services that copy a source's data for cfo.ai. A few connect directly.
| Method | Sources | What you do |
| - | - | - |
| Sign in through Merge | QuickBooks Online, Xero, Gusto, Justworks, and the other HR and payroll systems that connect through Merge | Sign in to the system in a window cfo.ai opens, choose the company, and approve access. |
| Fivetran setup window | NetSuite SuiteAnalytics, Rippling, HubSpot, Salesforce, databases, and the other sources this page lists as syncing through Fivetran | Enter the credentials or sign-in the source needs in Fivetran's setup window. Some sources then show a table picker. |
| API key | Rillet | Paste a Rillet API key into the card. |
| Stripe app | Stripe (Direct) | Sign in to Stripe and install cfo.ai's Stripe app, which asks for read-only access. |
| Google sign-in and file picker | Google Sheets, Google Drive | Sign in with Google and choose one file. |
| File upload | CSV / Raw File Upload | Choose CSV files. Nothing to sign in to. |
When a sign-in asks for a permission you do not have, the person who administers that system must connect it, or grant you access first. See each source below for what it needs.
## Accounting
The app's **Accounting** filter also holds payment and commerce sources, such as Stripe (Direct) and Shopify, so they are listed here. In each table, a saved query is the query that brings a synced table into your Model; see [Saved query](/reference/glossary#saved-query).
### QuickBooks Online
| Fact | Detail |
| - | - |
| How it connects | Sign in to QuickBooks Online in a window cfo.ai opens (through Merge) and choose the company. |
| Standard or premium | Standard |
| What syncs | Monthly profit and loss and balance sheet, the chart of accounts, vendors, invoices, and journal entries |
| Saved queries cfo.ai adds | **QuickBooks Profit & Loss** and **QuickBooks Balance Sheet**. Each carries **Entity** and **Currency** columns. |
| History | From January 1 of last year through today. Ask Ari for a full-history import to reach back to a year you name, 2015 or later. A full-history import also takes the balance sheet of the month before that year. |
| First sync | Your three most recent complete months load first. The rest of the history loads in the background. |
| Nightly refresh | Re-reads the last three months, or the whole history when the connection has not synced for more than a month |
| Limits | One connection per company. Connect each company you own as its own connection. |
| You need first | A login with admin access to the company in QuickBooks Online |
| Writes back | No |
### Xero
| Fact | Detail |
| - | - |
| How it connects | Sign in to Xero in a window cfo.ai opens (through Merge) and choose the organisation. |
| Standard or premium | Standard |
| What syncs | Journals by month, the chart of accounts, tracking categories, contacts, and organisation details |
| Saved queries cfo.ai adds | **Xero Income Statement**, or **Xero Income Statement by Tracking Category** when you use tracking categories, and **Xero Balance Sheet**. Each carries **Entity** and **Currency** columns. |
| History | From January 1 of last year through today. Ask Ari for a full-history import to reach back to a year you name, 2015 or later. |
| First sync | Your three most recent complete months load first. The rest loads in the background. Xero limits how many requests an app can make for an organisation each minute and each day, so a large ledger can take several hours or more than a day. |
| Nightly refresh | Re-reads the whole history window |
| Limits | One connection per organisation. Connect each organisation as its own connection. |
| You need first | A login with admin access to the organisation in Xero |
| Writes back | No |
### NetSuite SuiteAnalytics
| Fact | Detail |
| - | - |
| How it connects | Fivetran setup window, with your NetSuite account and an access token |
| Standard or premium | Premium |
| What syncs | The NetSuite tables the financial statements need, such as accounts, accounting periods, transactions and their lines, subsidiaries, customers, and vendors. You can ask Ari to add other tables later. |
| Saved queries cfo.ai adds | **NetSuite Income Statement** and **NetSuite Balance Sheet** |
| History | The full history of each table |
| First sync | Can take many hours, because Fivetran copies every table's full history first |
| Table choice | Setup shows a table picker. Required tables are locked. Tables for features your account does not use, **Multiple Currencies**, **Multiple Calendars**, or **Multi-Book Accounting**, start unchecked. Tables that already sync cannot be removed. |
| You need first | A NetSuite admin creates an access token for the **Data Warehouse Integrator** role. Entering a different role ID with an existing token does not change what cfo.ai can see. |
| Writes back | No |
If NetSuite hides a table the statements need, the connection reads **Missing access in NetSuite SuiteAnalytics**. See [Fix a connection](/guides/fix-a-connection#netsuite-is-missing-access).
### Rillet
| Fact | Detail |
| - | - |
| How it connects | API key. Find or create one in Rillet under **Organization Settings → API access**. |
| Standard or premium | Standard |
| What syncs | Accounts, subsidiaries, custom fields, the income statement and balance sheet reports, customers, contracts, and invoices |
| Saved queries cfo.ai adds | **Rillet Income Statement** and **Rillet Balance Sheet**, one set of rows per subsidiary, with **Entity** naming the subsidiary and **Currency** taken from each row |
| Limits | One Rillet connection per workspace. Adding up the subsidiaries does not reproduce Rillet's consolidated view, because Rillet applies intercompany eliminations only there. |
| You need first | A Rillet API key |
| Writes back | No |
### Stripe (Direct)
| Fact | Detail |
| - | - |
| How it connects | Sign in to Stripe and install cfo.ai's Stripe app, which asks for read-only access |
| Standard or premium | Standard |
| What syncs | Customers, subscriptions and their items and schedules, invoices with their lines and payments, charges, refunds, disputes, payment intents, balance transactions, payouts, products, plans, prices, coupons, discounts, credit notes, payment methods, and setup intents. An object Stripe deleted stays as a row marked deleted, so an invoice's customer is still there to look up. |
| History | The first sync lists every object in the account. Later syncs apply Stripe's changes since the last one. |
| First sync | Can take a day or more for an account with a long history. Stripe lists payment methods one customer at a time, so for an account with many customers that table fills over several syncs. |
| Permissions | A table that needs a permission your installed version of cfo.ai's Stripe app does not have still syncs what other objects carry, and the sync completes. The table fills in full after an owner of the Stripe account approves an app version that includes the permission. Signing in again does not change the app's permissions. |
| You need first | A Stripe login that can install apps on the account |
| Writes back | No |
### Sage Intacct
| Fact | Detail |
| - | - |
| How it connects | Fivetran setup window |
| Standard or premium | Premium |
| What syncs | The tables you choose in the table picker, up to 100 |
| Saved queries | Ari writes them after the first sync |
| You need first | Sage Intacct credentials, which you enter in Fivetran's setup window |
| Writes back | No |
### Shopify
| Fact | Detail |
| - | - |
| How it connects | Fivetran setup window for your store |
| Standard or premium | Standard |
| What syncs | The tables you choose in the table picker, up to 100. Ari recommends orders and their lines, refunds, adjustments, discounts, shipping, taxes, tags, fulfillments, customers, products, gift cards, and the shop. Payment transactions are left out of that recommendation because they slow the first sync the most. |
| Saved queries | Ari writes them after the first sync |
| Writes back | No |
### Other accounting, billing, and commerce systems
These sources sync through Fivetran. Each one shows a table picker during setup, where you choose up to 100 tables, and Ari writes the saved queries that bring the data into your Model after the first sync. None of them writes back. Sources marked "(premium)" are premium; the rest are standard.
Acumatica (premium), Adyen, Afterpay, Anaplan (premium), Anvyl, Aumni, AWS Cost and Usage Reports (premium), BigCommerce, BillingPlatform, Braintree, Braintree Sandbox, Brex, Brightpearl, Chargebee Product Catalog 1.0, Chargebee Product Catalog 2.0, Checkout.com, Cin7 Core, Clubspeed, Column, Coupa, Donus, e-conomic, EasyPost, eBay, Exact Online (premium), ExpenseIn, Expensify, EZOfficeInventory, Flexport, FourKites, FreshBooks, GoCardless, Invoiced, Klarna, Lightspeed Retail, Mambu, Maxio Chargify, Maxio SaaSOptics, Microsoft Dynamics 365 Business Central (premium), Microsoft Dynamics 365 Finance and Operations (premium), Navan, Open Exchange Rates, Oracle Fusion Cloud Applications - FSCM(ERP & SCM) (premium), Ordway, PayPal, PayPal Sandbox, Pigment (premium), Pipe17, Planful, Ramp, ReBound Returns, Recharge, Recurly, Revel Systems, RevenueCat, Rithum, Salesforce Commerce Cloud (premium), SAP Business ByDesign (premium), SAP Concur (premium), Sharetribe, ShipHero, Shopware, Splash, Square, Squarespace, Statistics Netherlands CBS, sticky.io, Subscript, Tremendous, Unicommerce, Visma (premium), Walmart Marketplace, Workday Financial Management (premium), Workday Strategic Sourcing (premium), Xactly, Xsolla, Younium, Zip, Zoho Books, Zoho Inventory, Zuora, Zuora Sandbox
## HR and payroll
### Gusto
| Fact | Detail |
| - | - |
| How it connects | Sign in to Gusto in a window cfo.ai opens (through Merge) |
| Standard or premium | Standard |
| What syncs | Employees and their jobs and pay, groups such as departments and teams, payroll runs with each employee's gross and net pay, pay period, and check date, and benefits |
| Saved query cfo.ai adds | **HRIS Employees** |
| First sync | cfo.ai waits for Merge's first sync of your account, which can take hours. See [First sync not ready](/reference/connection-statuses#first-sync-not-ready). |
| Limits | One connection per company. Signing in again to a company the workspace already holds is refused. |
| You need first | Admin access to the company in Gusto |
| Writes back | No |
### Justworks
| Fact | Detail |
| - | - |
| How it connects | Sign in to Justworks in a window cfo.ai opens (through Merge) |
| Standard or premium | Standard |
| What syncs | Employees and their jobs and pay, groups, payroll runs, and benefits, as your Justworks account provides them |
| Saved query cfo.ai adds | **HRIS Employees** |
| First sync | cfo.ai waits for Merge's first sync of your account, which can take hours |
| You need first | Admin access to the company in Justworks |
| Writes back | No |
### Rippling
| Fact | Detail |
| - | - |
| How it connects | Fivetran setup window with a Rippling API key. Before the window opens, a dialog titled **Before you connect Rippling** says: **You’ll need a Rippling API key to continue. API access may need to be enabled as a paid feature in your Rippling account.** |
| Standard or premium | Standard |
| What syncs | The seven tables the employee roster reads: workers, users, worker details, departments, teams, team membership, and work locations. There is no table picker. Ari can add other Rippling tables later; these seven stay locked. |
| Saved query cfo.ai adds | **HRIS Employees** |
| You need first | A Rippling API key. If you cannot get one, upload an employee export as a CSV, or connect the Rippling MCP server for Ari. |
| Writes back | No |
### Other HR and payroll systems
These systems connect the same way as Gusto, through Merge, and sync the same kinds of records. Sources marked "(premium)" are premium; the rest are standard.
ADP Workforce Now, Altera Payroll, BambooHR, Breathe, Charlie, ChartHop, ClayHR, Dayforce, Deel, Factorial, Freshteam, Google Workspace, Hibob, HR Cloud, HR Partner, Humaans, Insperity Premier, IntelliHR, JumpCloud, Kallidus, Keka, Lano, Lucca, Microsoft Entra ID, Namely, Nmbrs, Officient, Okta, OneLogin, PayCaptain, Paychex, Paycom, Paycor, Paylocity, PeopleHR, Personio, Proliant, Sage HR (premium), SAP SuccessFactors (premium), Sesame, Simployer, Square Payroll, TriNet, TriNet HR Platform, UKG Pro, UKG Pro Workforce Management, UKG Ready, Workday (premium), Zoho People
These HR, scheduling, and learning tools sync through Fivetran, with a table picker during setup:
15Five, 360Learning, 7shifts, Absorb LMS, Canvas by Instructure, Care Quality Commission, Coassemble, Cornerstone, Culture Amp, D2L Brightspace, Deputy, Docebo, Employment Hero, Google Classroom, Jibble, KeyPay, Lattice, Lessonly, Oracle Fusion Cloud Applications - HCM (premium), Papershift, Persona, Playvox Workforce Management, Proofpoint Security Awareness, Ruddr, Sana, SCORM, Security Journey, SkillsTX, SmartHR, Thinkific, Tymeshift, Udemy Business, When I Work, Workleap Officevibe, WorkRamp
## CRM
### HubSpot
| Fact | Detail |
| - | - |
| How it connects | Fivetran setup window, where you sign in to HubSpot |
| Standard or premium | Standard |
| What syncs | A fixed set of 20 tables: deals, deal pipelines and their stages, owners, contacts, companies, line items, products, and invoices; the links between deals, contacts, companies, line items, and invoices; and the property histories of line items, products, and invoices. A table HubSpot does not expose is skipped. There is no table picker. Ari can add other HubSpot tables later; these 20 stay locked. |
| Saved query cfo.ai adds | **HubSpot Deals** |
| Writes back | No |
### Salesforce
| Fact | Detail |
| - | - |
| How it connects | Fivetran setup window, where you sign in to Salesforce |
| Standard or premium | Standard |
| What syncs | Opportunities, accounts, and users. There is no table picker. Ari can add other Salesforce objects later; these three stay locked. |
| Saved query cfo.ai adds | **Salesforce Opportunities** |
| Writes back | No |
### Other CRMs
These sync through Fivetran, with a table picker during setup. Sources marked "(premium)" are premium; the rest are standard.
AccuLynx, Amazon Selling Partner, Attio, Bigin by Zoho CRM, Boostr, Buildium, Centra, Clari, Clazar, Close, Cloudbeds, Copper, Crossbeam, DoorLoop, Duoplane, Freshsales, Freshsuccess, JobNimbus, Lemlist, Loop, Malomo, Microsoft Dynamics 365 CRM (premium), Odoo Synconics, Oracle Business Intelligence Publisher, Oracle Fusion Cloud Applications - CRM (premium), Outreach, Pipedrive, Planhat, RevOps, Ricochet360, Salesforce Sandbox, Salesloft, SugarCRM, Tive, Veeqo, Zendesk Sell, Zoho CRM
## Databases and data warehouses
Every database and data warehouse is premium. Each one connects through Fivetran's setup window, with your database's connection details and a user that can read the tables you want, and then shows a table picker where you choose up to 100 tables. Ari writes the saved queries that bring the rows into your Model. cfo.ai never writes to your database.
### PostgreSQL
Four PostgreSQL entries cover different hosts: **PostgreSQL** for any reachable server, **Amazon RDS for PostgreSQL**, **Amazon Aurora PostgreSQL**, and **Google Cloud SQL for PostgreSQL**. All four are premium and work the same way.
### Snowflake
Premium, through Fivetran, with a table picker.
### BigQuery
Premium, through Fivetran, with a table picker. If the sync fails with Fivetran's report that the user cannot access BigQuery tables or the project, grant that user access in Google Cloud, then select **Retry**.
### Databricks
Premium, through Fivetran, with a table picker.
### Other databases and data warehouses
Amazon Aurora MySQL, Amazon DocumentDB, Amazon DynamoDB, Amazon RDS for MariaDB, Amazon RDS for SQL Server, Azure Cosmos DB for MongoDB, Azure Cosmos DB for NoSQL, Azure Database for MySQL, Azure Database for PostgreSQL, Azure SQL Database, Azure SQL Managed Instance, CockroachDB, Google Cloud SQL for MySQL, Google Cloud SQL for SQL Server, Heroku PostgreSQL, High Volume Agent Db2 for i, High-Volume Agent SQL Server, HVA SAP ECC on SQL Server, Magento MySQL, Magento MySQL RDS, MariaDB, MongoDB, MongoDB Sharded, MySQL, MySQL RDS, SQL Server
Amazon Redshift is not in the catalog.
## Spreadsheets and file uploads
### CSV / Raw File Upload
| Fact | Detail |
| - | - |
| How it connects | Select **Upload** in the connection manager, or attach a CSV to a chat as a **Data source** |
| Standard or premium | Neither. An upload uses no connection slot. |
| What it loads | One table per file. The file must be a `.csv` up to 50 MB. See [How imported data loads](/reference/imported-data#csv-files). |
| Refresh | Never. Upload a new version to update it. |
### Google Sheets
| Fact | Detail |
| - | - |
| How it connects | Sign in with Google and choose one spreadsheet in Google's file picker. cfo.ai can open only the files you pick. |
| Standard or premium | Standard. Every Google Sheets connection in a workspace shares one slot. |
| What it loads | Ari imports the whole spreadsheet, every tab with its formulas, values, and number formats, and rebuilds it in your Model |
| Refresh | Nightly and when you ask Ari. A changed value or formula updates your Model; an unchanged spreadsheet starts no update and uses no AI usage. |
| Limits | One spreadsheet per connection, up to 50 MB when exported as an Excel workbook. The same spreadsheet cannot be connected twice in a workspace. |
### Google Drive
| Fact | Detail |
| - | - |
| How it connects | Sign in with Google and choose one file: a Google Sheet, an Excel workbook (`.xlsx`), or a CSV file |
| Standard or premium | Standard. Each Google Drive connection uses its own slot. |
| What it loads | Ari imports the file the way it imports an upload |
| Refresh | Nightly and when you ask Ari |
| Limits | A Google Sheet up to 50 MB as an Excel workbook; an Excel or CSV file up to 10 MB |
### Other file sources
These sync files through Fivetran, with a table picker during setup. Sources marked "(premium)" are premium; the rest are standard.
Amazon S3 (premium), Azure Blob Storage (premium), Box, Dropbox, Email, FTP, Google Cloud Storage (premium), OneDrive, SFTP, SharePoint
## Recruiting, support, and other apps
These sources sync through Fivetran, with a table picker during setup. Sources marked "(premium)" are premium; the rest are standard.
### Recruiting
Fountain, Gem, Greenhouse, Lever, Pinpoint, SmartRecruiters, Teamtailor, Workable
### Support and ticketing
Ada, Alchemer, Assembled, Birdeye, CloudTalk, DSV, Fillout, Formstack, Freshchat, Freshdesk, Freshdesk Contact Center, Front, Genesys, GetFeedback, Gladly, Gorgias, Help Scout, Helpshift, Intercom, Ivanti, Khoros Care, Khoros Communities, Konnect Insights, Kustomer, Medallia, Medallia Agile Research, Missive, NICE, Playvox, Qualaroo, Qualtrics, RingCentral, Ringover, ServiceTitan, Simplesat, SurveyMonkey, Survicate, Talkdesk, Typeform, Uservoice, Visit by GES, Vonage Contact Center, Zendesk Chat, Zendesk Sunshine, Zendesk Support, Zingtree, Zoho Desk
### Marketing, product, and other apps
ActiveCampaign, Adjust, Adobe Analytics, Adobe Analytics Data Feed (premium), Adobe Commerce, Adobe Workfront, AdRoll, Affinity, Aha!, Aircall, Airtable, Akamai, Akeneo, Algolia, Amazon Ads, Amazon Attribution, Amazon DSP, Amplitude, Appcues, Appfigures, Apple App Store, Apple Search Ads, AppsFlyer, Asana, Attentive, Auth0, Autodesk BIM 360 & Construction Cloud, AvantLink, Awin, AWS Inventory (premium), Azure DevOps, Backbone PLM, BallotReady, Bazaarvoice, Betterworks, BigMarker, Bitly, Branch, Brave Ads, Braze, Brevo, Brightcove, Buildkite, Buzzsprout, Calabrio, Calendly, CallRail, Campaign Monitor, Campfire, Canny, Castor EDC, Chameleon, ChargeDesk, ChartMogul, Checkr, Chorus, Churnkey, ChurnZero, Cimis, Cin7, CircleCI, CJ Commission Detail, ClickUp, Clockify, Clockodo, Codefresh, Collibra, commercetools, Compliance Checkpoint, Concord, Confluence, Constant Contact, Contrast Security, Criteo, Criteo Retail Media, crowd.dev, Customer.io, Cvent, dbt Cloud, DCL Logistics, Deposco, Destini, Dialpad, Dixa, Drata, Drift, Drip, Dropbox Sign, Electronic Tenant Solutions, Eloqua, Eventbrite, Eventsforce, Everflow, Everhour, Facebook Ads, Facebook Pages, Factbird, FireHydrant, Float, Flywheel Digital, Fone Dynamics, Forj Community, Freightview, Freshservice, Friendbuy, FullStory, G2, Gainsight Customer Success, Gainsight Product Experience, GitHub, GitLab, GlobalMeet, Gmail, Gong, Google Ad Manager, Google Ads, Google Analytics 4, Google Analytics 4 Export (premium), Google Business Profile, Google Calendar, Google Campaign Manager 360, Google Display & Video 360, Google Play, Google Search Ads 360, Google Search Console, Google Tasks, GreenPowerMonitor, Grepsr, GRIN, Guru, HappyFox, Harvest, Healthie, Heap, Hilti On!Track, Hopin, iLevel, Impact Brand, incident.io, Infobip, Insightly, Instagram Business, Integral Ad Science, Integrate, Ironclad, ironSource, Iterable, Jama Software, Jamf, Jira, Jira Align, Jotform, JustCall, Kandji, Kantata, Katana, Kevel, Kisi, Kissmetrics, Klaviyo, LaunchDarkly, Leap CRM, LearnUpon, Liftoff, LINE Ads, Linear, LinkedIn Ad Analytics, LinkedIn Company Pages, LinkSquares, LiveChat Partner, Lob, Looker, Loopio, Luma, Mailchimp, Mailgun, Mailjet, Mandrill, Marin Software, Marketo, Matomo, Megaphone, Mention, Microsoft Advertising, Microsoft Lists, Mixmax, Mixpanel, MNTN, Mode, Moloco, Monday.com, Mux, myosh, Notion, Nylas, Okendo, Ometria, ON24, OnceHub, Open Data DC, Opsgenie, Optimizely, Oracle Moat Analytics, Orbit, Ortto, Outbrain, Packiyo, PagerDuty, PandaDoc, Pardot, Partnerize, PartnerStack Vendor, Pendo, Phoenix Ads, Picqer, Pingdom, Pinterest Ads, Pinterest Organic, Pivotal Tracker, Piwik PRO, Placer.ai, Podio, PostHog, Postmark, PowerReviews Enterprise, Prisma Cloud, Prive, Procore, Productboard, Productive, Quora Ads, Quorum, Rakuten Advertising, Rarible, Reddit Ads, ReferralHero, Reltio, Reply.io, Resource Management by Smartsheet, RetailNext, Retently, REVIEWS.ai, REVIEWS.io, RevX, Rocketlane, Rokt, Rollbar, Rootly, RTB House, Rundeck, SafeBase, SafetyCulture, Sailthru, Salesforce Marketing Cloud (premium), Salsify, Samsara, Segment, SendGrid, Sentry, ServiceNow, ShipNetwork, ShipStation, Short.io, Shortcut, Sigma Computing, Simplecast, SISTRIX, Skilljar, SkuVault, Slab, Smadex, Smartwaiver, Snapchat Ads, Snowplow (premium), Snyk, SolarWinds Service Desk, SonarQube, SparkPost, Split, Spotify Ads, Sprout Social, StarRez, Statsig, Statuspage, Stylight, Swoogo, Tableau, Taboola, Talkwalker, Teads, Teamwork, Tempo, TestRail, TextUs, The Movie Database, The Trade Desk, Ticket Tailor, Tiktok Ads, Toggl Track, Totango, Transcend, Trelica, Trello, Triple Whale, Trisolute, Twilio, Upland Software, UpPromote, Vimeo, Vitally, VTS, VWO, Web Scraper, Webconnex, Wherefour, Wicked Reports, WooCommerce, WordPress, Wrike, X Ads, X Organic, Xandr, Xray, Yahoo Search Ads on Yahoo Japan, Yotpo, YouGov Sport, YouTube Analytics, Zoho Campaigns, Zonka Feedback, Zoom
## What cfo.ai does not do with your sources
* cfo.ai does not write back to a synced source. To change a number in QuickBooks, Xero, or any other system, change it there; the next sync brings it in.
* cfo.ai does not connect bank accounts directly. Use your accounting system's balance sheet, upload bank transactions as a CSV, or connect Mercury's MCP server for live balances.
* The **Upload** button does not take Excel workbooks or PDFs. Attach them to a chat for Ari instead; see [Upload a CSV as a data source](/guides/upload-a-csv#excel-workbooks-and-pdfs).
* There is no **Sync now** button. Ask Ari to refresh a connection; see [Refresh data](/guides/refresh-data).
## Connections for Ari
MCP servers and APIs are tools Ari calls live when you ask a question, such as a bank balance in Mercury. Nothing from them is copied into your Model unless you ask Ari to save a sync setup, and they use no plan slot until then. The integrations above, by contrast, sync data into your Model once a day. Add an MCP server or API from the **MCP** and **API** tabs of the connection manager, or from **Settings → Connections**. See [Give Ari access to another service](/guides/give-ari-another-service).
### MCP connectors
Each connector signs in with OAuth, except Brex, which takes a bearer token.
| Category | Connectors |
| - | - |
| Payments | Stripe, PayPal, Square, Mercado Pago |
| Banking & Spend | Mercury, Ramp, Brex, Expensify, Plaid (Plaid dashboard diagnostics, not bank data) |
| Accounting & Tax | Avalara AvaTax, Avalara Returns, Numeric |
| HR & Payroll | Gusto, Deel |
| Equity & Compliance | Carta, Vanta |
| Market Data | Alpha Vantage, Morningstar |
| CRM & Sales | Intercom, Close, Attio |
| Productivity & Docs | Notion, Linear, Atlassian (Jira & Confluence), Airtable, monday.com |
| Data & Analytics | Supabase, ThoughtSpot |
### Rippling MCP server
Rippling's remote MCP server is not in the catalog; add it with **Add manually**. When the **MCP URL**'s host is `mcpgateway.rippling.com`, the **Add MCP server** dialog shows a **Rippling setup** note. The same note appears beside the server's tool-loading error and on Ari's connect card:
* Copy your Remote MCP server URL from **IT → My IT → MCP connectors → Rippling MCP**.
* Ask a Rippling admin to assign you access in **IT → MCP Gateway → Rippling MCP → Access assignments**.
* Your company needs an active Rippling AI trial or subscription for tools to return data.
### APIs
| API | Sign-in |
| - | - |
| Stripe | API key (bearer) |
| Adyen Transfers, Adyen Management, Adyen Balance Platform | API key in the `X-API-Key` header |
| Increase | API key (bearer) |
| Synctera | API key (bearer) |
| Mercury | API key (bearer) |
| Brex | API key (bearer) |
| Pleo | API key (bearer) |
| Pennylane | API key (bearer) |
| TaxJar | API key (bearer) |
| Rippling | API key (bearer) |
| CoinAPI | API key in the `X-CoinAPI-Key` header |
| Tradier | API key (bearer) |
| Frankfurter | None (exchange rates) |
| U.S. Treasury Fiscal Data | None |
### Any other MCP server or API
Use **Add with Ari** to have Ari research the provider and set it up in a chat, or **Add manually**. An MCP server can sign in with OAuth, a bearer token, or an API key header. An API needs an OpenAPI 3.0 or 3.1 specification and signs in with a bearer token, an API key header, a key as the Basic authentication username, or no authentication. A provider that requires an app registered with it in advance, and an API that signs in with a username and password or with a key in the address, cannot be connected this way.
cfo.ai's own MCP server, which lets an outside AI client use cfo.ai, is a different thing; see [Use cfo.ai from Claude Code, Codex, or another AI client](/guides/connect-an-ai-client).
## Related
* [How connected data reaches your Model](/concepts/connected-data)
* [Connection statuses and actions](/reference/connection-statuses)
* [How imported data loads](/reference/imported-data)
* [Plans and billing](/reference/plans-and-billing)
# The cfo.ai interface
Source: https://docs.cfo.ai/reference/interface
Every part of the cfo.ai app and what it does: the sign-in page and its code messages, the sidebar and its Page list, the account menu and workspace switcher, the Triggers page, the Help center and how to contact support, workspace search, the Page header, Download spreadsheet (export to Excel) and Share messages, Canvas and chart blocks, and the phone and tablet layout.
The cfo.ai app has a navigation sidebar on the left and the chat or Page you are working in beside it. This page describes each part in the order you meet it, starting with signing in, with every label and message. It describes a desktop browser; for phones and tablets, see [Phones and tablets](#phones-and-tablets). For keyboard shortcuts, see [Keyboard shortcuts](/reference/keyboard-shortcuts).
## Sign-in page
The page is titled **Sign in to cfo.ai**.
| Control | What it does |
| - | - |
| **Continue with Google** / **Sign in with Microsoft** | Signs you in with that account. When these are offered, the page reads **Choose how you want to continue.** |
| **Work email** | Your work address, with the note **Use the address where you want to receive cfo.ai workspace updates.** When only email sign-in is offered, the page reads **Enter your work email and we’ll send a six-digit code.** |
| **Send code** | Emails you a six-digit code and opens the code page. |
A Google or Microsoft sign-in that fails shows **We couldn’t complete sign-in. Please try again.**
### Sign-in code page
The page is titled **Enter your code**, with **We sent a six-digit code to \[email].** Its footer reads **Codes expire quickly. You can request a new one if needed.**
| Control | What it does |
| - | - |
| Code boxes | The code is checked as soon as you type the sixth digit. |
| **Continue** | Checks the code. It stays off while the boxes hold a code that was already refused. |
| **Change email** | Goes back to the email step. |
| **Resend code** | Sends a new code and says **We sent a fresh code to \[email].** |
When a Google or Microsoft sign-in matches an account that already exists, a code links the two sign-in methods. The page then says **We sent a six-digit code to \[email] to safely link your sign-in methods.** and **This protects your existing cfo.ai account when adding another sign-in method.**, and offers **Start over** instead of **Change email**.
### Sign-in code messages
| Message | Meaning | What to do |
| - | - | - |
| **That code does not match the one that was sent.** | The code is wrong. | Check the code, or check that you entered the right email. |
| **That code expired. A code works for 10 minutes after it is sent.** | The code is more than 10 minutes old. | Select **Resend code**. |
| **That code was not accepted. \[n] codes were sent to this address in the last 10 minutes, and only the newest one works.** | You typed an older code. | Use the code in the newest email. |
| **That code was already used. A code works once.** | The code was used before. | Select **Resend code**. |
| **Too many wrong codes were entered, so that code no longer works.** | Too many wrong codes were tried. | Select **Resend code**. |
| **We couldn’t find the email for this verification step. Head back and start from the email step again.** | The sign-in lost track of your email. | Select **Start over**. |
For how many codes you can request and check, see [Limits and file types](/reference/limits#workspace-account-and-sign-in).
## Navigation sidebar
From top to bottom:
| Control | What it does |
| - | - |
| Logo | The cfo.ai logo, or your workspace brand's logo, is a link. Pointing at it shows where it goes: **New chat** when you can start a chat, or **Open** followed by the first Page's name when you cannot. **⌘**-click opens it in a new tab. |
| **Collapse navigation** | Folds the sidebar to a narrow strip (**⌘ \\**, or **Ctrl \\** on Windows and Linux). On the strip, **Expand navigation** brings it back; pointing at the strip shows the sidebar over the page, and **Keep navigation open** pins it. Dragging the sidebar's edge resizes it, and dragging it narrow enough folds it. In a chat on a narrow window, the sidebar stays folded and the strip offers **Open navigation**. |
| **Search** | Opens [workspace search](#workspace-search). |
| **Chats** | Your chats, newest activity first, with **New chat** and a filter. See [The chat list](/reference/chat#the-chat-list-find-rename-and-archive-chats). |
| **Model** | Select **Model** to open or close the section. It holds the Scenario picker (**Main Scenario** until you pick another) and the Pages of that Scenario, and **New page** (**+**) creates a Page. The section appears once the workspace has its first Page. See [Scenario and version history controls](/reference/scenarios-and-history) and [Create and organize Pages and blocks](/guides/create-pages-and-blocks). |
| **Connections** | Your connected systems, each with its status, and **Add integration** (**+**). See [Connection statuses and actions](/reference/connection-statuses). |
| **Feedback** | Opens [Share feedback](#share-feedback). |
| **Powered by cfo.ai** | Shown in a workspace with a brand. |
| Your name and picture | Opens the [account menu](#account-menu). |
The sidebar remembers its Scenario for each workspace, so opening a chat does not change which Pages it lists. A Page you pick in the sidebar opens full-screen in the sidebar's Scenario and closes the chat you had open.
### Pages in the sidebar
| Control | What it does |
| - | - |
| A Page row | Opens the Page. Pointing at it shows **New subpage**, **Mention page in chat**, and **Delete page**, and, on a Page with subpages, **Expand subpages** or **Collapse subpages**. |
| Message | Meaning |
| - | - |
| **Ari is building this page** | A mark on a Page that Ari is still building. |
| **No pages in this scenario.** | The Scenario you are viewing has no Pages. |
| **Loading pages…** / **Couldn’t load pages.** with **Retry** | The list is loading, or it could not load. |
| **Creating page…** / **Could not create the page.** | A new Page is being created, or it could not be created. |
To move, nest, or rename Pages with the keyboard, see [Keyboard shortcuts](/reference/keyboard-shortcuts#pages-and-blocks). To organize Pages, see [Create and organize Pages and blocks](/guides/create-pages-and-blocks).
### A workspace with no Pages
A workspace with no Pages opens on **Ready to model, \[your name]?**, with **Create with Ari** and **Add block** (**Table block**, **Text block**, or **Canvas block**). Someone who cannot create Pages sees **No pages yet** and the reason, such as **This workspace has no pages.** or **Pages are read-only in this browser. Use Desktop Site to create a page.**
## Account menu
Select your name and picture at the bottom of the sidebar. From top to bottom:
| Control | What it does |
| - | - |
| Your name and email | Shows which account you are signed in with. |
| **Appearance** | **Auto**, **Light**, or **Dark** for you alone. The current choice shows beside it. |
| **Settings** | Opens Settings. See [Settings](/reference/settings). |
| **Help center** | Opens the [Help center](#help-center) (**⌘ ?**). |
| **Manage memories** | Starts a chat in which Ari shows what it remembers about you and the workspace and helps you change it. See [What Ari remembers](/concepts/ari-memory). |
| **Manage triggers** | Opens the [Triggers page](#triggers-page). |
| **Workspaces** | Every workspace you belong to, with a check beside the one you are in. Select another to switch to it; **⌘**-click opens it in a new tab. |
| **Create workspace** | Opens a **Workspace name** field. Press **Enter** to create the workspace and open it. If it fails, the field says **Unable to create workspace right now.** See [Create a second workspace or switch workspaces](/guides/manage-workspaces). |
| **Log out** | Signs you out. |
The list under **Workspaces** is the workspace switcher; there is no other switcher control. Workspace search also finds your other workspaces by name.
## Triggers page
**Manage triggers** in the account menu opens **Triggers**, marked **Preview**. It lists only your own Triggers. To set one up, see [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger).
| Control | What it does |
| - | - |
| **Write manually** | Opens **New trigger**. |
| **Build with Ari** | Opens a chat that starts with **Help me create a trigger for recurring work. Ask what I want Ari to do and when it should run, then set it up.** |
| A row in **Your triggers** | The Trigger's name, its schedule or **Paused**, and what happened last. |
| **Run now** | Starts one run right away, even when the Trigger is paused. The schedule does not change. |
| **Edit trigger** (pencil) | Opens **Edit trigger**. |
| **Delete trigger** (trash can) | Opens **Delete \[name]?**: **This trigger will stop running on its schedule. Runs already started will continue.** |
| **Schedule** / **Next scheduled** | The schedule in your time zone, and the next run, or **Nothing upcoming**. **Schedule and history are shown in your timezone.** |
| **Instructions** | The saved instructions, with **Show full instructions** and **Show less**. |
| **History** | **Open a run to see its conversation.** Every run, newest first, with its start time, its status, and, for a run that did not complete, its reason. **Show earlier** loads older runs. With none: **No history yet. Ari’s scheduled work will appear here.** |
With no Triggers, the page reads **Put recurring work on a schedule** and **Build a trigger with Ari or write the instructions and schedule manually.**
### Run statuses
| Status | Meaning |
| - | - |
| **Pending** | Queued and waiting to start. |
| **Running** | Ari is working on it. |
| **Finished** | The run reported the saved task done. |
| **Did not complete** | The run reported it could not do the task; its reason shows below. |
| **Ended without a report** | The run ended without reporting done or not done. |
| **Failed** | The run stopped with an error. |
| **Interrupted** | The run stopped, and nothing is working on it. |
| **Needs response** | The run's chat has a question or card waiting for you. |
A row in **Your triggers** shows the latest run's status, or one of these labels about the last scheduled attempt:
| Status | Meaning |
| - | - |
| **Failed to start** | The last scheduled attempt could not start a run. |
| **Skipped: Ari was still busy** | The last scheduled attempt came while an earlier run of the Trigger was still going or waiting, so no run started. |
| **Started** | The last scheduled attempt started a run. |
| **No history yet** | The Trigger has not run. |
When the last scheduled attempt did not start a run, the Trigger's details explain why:
| Message | Meaning |
| - | - |
| **The last scheduled attempt could not start**, with the error, or **Open a recent conversation or ask Ari to help check this trigger.** | An error kept the run from starting. |
| **The last scheduled attempt was skipped**, with **The run that started \[time] was still going. No run has started since then.**, **The run queued \[time] was still waiting to start. No run has been queued since then.**, or **Ari was still working on the previous task.** | An earlier run of the Trigger was still going or waiting, so no new run started. |
### New trigger and Edit trigger
The form has **Name** (placeholder **Weekday cash brief**), **Instructions**, and **Schedule**. **Repeat** offers:
* **Every few minutes**: 1, 2, 3, 5, 10, 15, 20, or 30.
* **Hourly**: every 1, 2, 3, 4, 6, 8, or 12 hours, **At minute**.
* **Daily**, **Weekdays**, or **Weekly** (**On these days**).
* **Monthly**: **Day of month**, including **Last day**.
* **Advanced schedule**: **Edit advanced rule**, **Schedule expression (UTC)**.
The note under the schedule reads **Times in \[your time zone]. Saved in UTC.**, or **Advanced rules use UTC.** A monthly day after the 28th adds **Months without this day are skipped. Choose Last day to run every month.** **Edit trigger** also has an **Enable trigger** switch (**Enabled** or **Disabled**). The buttons are **Create trigger** or **Save changes**, and **Cancel**.
### Trigger messages
| Message | Meaning |
| - | - |
| **Add a name, instructions, and schedule.** | A field is empty. |
| **Use up to 200 characters for the name and 10,000 for instructions.** | A field is too long. |
| **This trigger already has a queued or active run. Open its latest conversation.** | You selected **Run now** while a run is going. |
| **This trigger is no longer available.** / **This trigger is no longer available. Close this dialog and refresh the list.** | The Trigger was deleted. |
| **Could not save the trigger. Please try again.** / **Could not start the trigger. Please try again.** | The change or the run failed. |
| **Could not load triggers.** with **Retry triggers**; **Could not load history.** with **Retry history** | The page could not load. |
| **Creating a trigger requires permission to create chats in this workspace.** | You cannot create Triggers in this workspace. |
## Help center
Open it with **Help center** in the account menu, or press **⌘ ?** (**Ctrl ?**). It lists short interactive walkthroughs of the app. **Search help** at the top narrows the list as you type; with no match it says **No help found** and links to these docs. The sections, from top to bottom:
| Section | What it holds |
| - | - |
| **Recently viewed** | Walkthroughs and explanations you ran since the app last loaded, including ones Ari made. Each has a button that asks Ari to refresh it with your latest numbers. |
| **Getting started** | **Get to know navigating your workspace**, **Ask Ari to change your model**, **Start from a Canvas template**, **Find your way around the model**, and **Ask a human**. |
| **Tables & formulas** | **Get to know table blocks**, **Ask Ari about any number**, **Compare scenarios side by side**, and **See how a number is calculated**. |
| **Collaboration** | **Share pages & see who has access** and **Find and undo a change**. |
| **Data & integrations** | **Connect your data sources**. |
| **Keyboard shortcuts** | The common shortcuts. |
| **Additional resources** | **cfo.ai Docs** (these pages) and **System status** (service availability and incidents). Both open in a new tab. |
**Ask Ari to change your model** opens a new chat with suggested prompts, such as **Explain my cash** and **Plan next year’s hiring**, and puts the one you pick in the message box. **Start from a Canvas template** points at the palette button under the message box and opens the Canvas block library.
An entry added in the last 30 days shows **New** until you complete it, and a completed entry shows a check. Selecting an entry shows what it covers, its number of steps, and **Start walkthrough**. If the walkthrough needs something the workspace lacks, the button is off and a line says what to add, such as **This needs a table. Add a table to a page to try this.**, **This needs a page. Add a page to your model, then start this again.**, or **Open a page with a table, then start this again.** Add it, then select **Check again**.
Guides your team saved are not listed in the Help center; ask Ari to start one. See [Walk someone through a Page with a Guide](/guides/create-a-guide).
### Contact support
You can reach a person at cfo.ai three ways.
| Control | What it does |
| - | - |
| **Help center → Getting started → Ask a human** | Opens [Ask a human](#ask-a-human), which sends a message straight to the cfo.ai team. |
| **Feedback** in the sidebar | Sends product feedback to the cfo.ai team. |
| Email [success@cfo.ai](mailto:success@cfo.ai) | The **Support** link at the top of these docs. |
### Ask a human
The dialog is titled **Ask a human**, with **Tell us what's going on — this goes straight to our team.**
| Control | What it does |
| - | - |
| **What can we help with?** | Your message, up to 4,000 characters. |
| Screenshots | Up to 3 PNG, JPG, or WebP files of 5 MB or less each. Paste them, drop them on the dialog, or select **Attach screenshots**. |
| **Send** (**⌘ Enter**) / **Cancel** | Sends the message, or closes the dialog. |
| Message | Meaning |
| - | - |
| **Message sent** | The message was sent to the cfo.ai team. |
| **Could not send your message**, with the reason | The message was not sent. |
| **Could not attach screenshot**, with **Attach up to 3 screenshots.**, **Screenshots must be PNG, JPG, or WebP files.**, or **Screenshots must be 5 MB or smaller.** | The screenshots you added were not attached: the message would have more than 3, a file is another type, or a file is larger than 5 MB. |
### Share feedback
**Feedback** in the sidebar opens **Share feedback**, with the placeholder **Write your feedback and include any links here**, the same screenshot rules and messages as **Ask a human**, and **Submit feedback** (**⌘ Enter**). It confirms with **Feedback sent**, or shows **Could not send feedback** with the reason.
## Workspace search
Press **⌘ K** (**Ctrl K**) or select **Search** in the sidebar. Press **⌘ K** again or **Esc** to close it; a touch screen shows a close button. The box reads **Search pages, chats, and more**, and the window is marked **Preview**.
Search covers the Scenario the sidebar shows and lists only what you can open. It is not offered while you view a past version, or before you finish signing up.
### Before you type
The window lists the Pages you opened recently in this browser, under **Today**, **Yesterday**, **Past week**, and **Past 30 days**. The Page you have open is left out.
### Results
Results are grouped, up to eight in each group. The group with the best match comes first.
| Group | What it finds |
| - | - |
| **Pages** | Pages by title, and from two characters also by the words in their text. A Page found by its text shows the passage. |
| **Blocks** | Named tables, and titled charts and Canvas blocks |
| **Variables** | Variables that a table on one of your Pages shows |
| **Dimensions** | Dimensions that a table on one of your Pages shows |
| **Dimension items** | Items, such as a customer or a vendor, whose Dimension a table shows on its rows. Each names its Dimension and, for imported items, the connection it came from. |
| **Workspaces** | Your other workspaces, by name |
| **Chats** | Your chats by title. When none of the chats already loaded match, search also looks through your older chats once you pause typing. |
### How words match
* Every word you type must match.
* Case, accents, and punctuation are ignored: `paid in` finds Paid-In Capital.
* A word matches the start of a word: `rev` finds Revenue.
* From three letters, a word also matches inside a word: `count` finds Headcount.
* From five letters, a small typo is forgiven: `salry` finds Salary.
### Search keys and what a result opens
| Shortcut | What it does |
| - | - |
| **↑** / **↓** | Move through results |
| **Enter** | Open the result |
| **⌘ Enter**, or **⌘**-click | Open the result in a new browser tab |
| **Esc** | Close search |
A Page opens full-screen in the sidebar's Scenario. A block opens its Page, scrolls to the block, and flashes it. A Variable or a Dimension item opens the table that shows it and marks its row. A Page found by its text scrolls to the passage. A workspace switches to that workspace. A chat opens.
### Search messages
| Message | Meaning |
| - | - |
| **Type to search your workspace.** | Nothing typed and no recent Pages |
| **No results for “…”** | Nothing matched every word |
| **Loading pages…** | The Page list is still loading |
| **Couldn’t load pages. Try again from the sidebar.** | The Page list failed to load |
| **Searching dimension items…** / **Searching chats…** | A slower search is still running |
| **Couldn’t search dimension items.** / **Couldn’t search chats.** | That part of the search failed; the other results still show |
## Page header
The bar above a Page, from left to right:
| Control | What it does |
| - | - |
| Scenario picker and **/** | Shown only when a chat is open beside the Page. Switches the Page's Scenario. |
| Page icon | Pick or change the Page's icon. |
| Page name | Opens **Pages**, the list of Pages in this Scenario, with **Search pages** and **New page**. |
| **Merge** | Shown when a chat is open beside a Page in a Scenario other than Main. See [Merge a Scenario into Main](/guides/merge-a-scenario). |
| **Building…** | Ari is building this Page. |
| **Download spreadsheet** | The download icon, on a Page with a Table block. See [Download spreadsheet](#download-spreadsheet). |
| **Share** | Opens the [Share panel](#share-panel). Shown to people who can share the Page. A link icon on the button means an external link to the Page is on. |
| **Version history** | The clock icon. Opens the version history panel. See [Scenario and version history controls](/reference/scenarios-and-history). |
| **New chat** | On a full-screen Page with no chat open. Opens a floating chat over the Page. |
| **Collapse model pane** | When the Page is beside a chat. Folds the Page to a narrow rail; **Expand model pane** opens it again. |
Below the header, the Page's title row holds the **Page title**, which you can click to rename the Page, and, on a Page with a table, a chart, or a Canvas block with data, the **Page date range**. Changing the date range sets it on every table, chart, and Canvas dataset on the Page.
### A chat and a Page together
| Control | What it does |
| - | - |
| **Expand model pane** / **Collapse model pane** | In a chat, shows or folds the Page beside the conversation. |
| **Show page navigation** / **Hide page navigation** | In a chat, shows or hides a list of Pages beside the Page. |
| **Expand chat** | On a floating chat, docks it beside the Page. |
| **Minimize chat** / **Close chat** | On a floating chat. Minimize keeps the conversation; a chat with nothing sent yet offers **Close chat**. |
## Download spreadsheet
**Download spreadsheet**, the download icon in the Page header, exports the Page's tables as an Excel workbook. Its tooltip reads **Download tables as a spreadsheet**. cfo.ai has no button that downloads a Page as a PDF or a CSV; ask Ari for one. For what the workbook contains, see [Export a Page to Excel](/guides/download-a-spreadsheet); for a PDF, see [Get a PDF report or slide deck](/guides/get-a-pdf-or-slides).
While an export runs, the button spins and its tooltip shows the stage:
| Message | Meaning |
| - | - |
| **Waiting for another spreadsheet to finish** | Another export in this workspace is running; yours starts when it ends. |
| **Spreadsheet queued** | Your export is waiting to start. |
| **Preparing spreadsheet** | Your export is running. |
When it finishes:
| Message | Meaning |
| - | - |
| **Spreadsheet download started**, with the file name | The workbook downloaded. |
| **Spreadsheet download started**, with **\[file]: \[n] cells hold values instead of a formula. The Export notes sheet lists them.** and **Ask Ari to fix** | Some formulas could not be written as Excel formulas. **Ask Ari to fix** opens a chat that asks Ari to export the Page again and write a working formula into each of those cells. |
| **Could not download spreadsheet**, with **\[Page]: \[reason]** | The export failed. |
A failed export's reason has up to three parts:
* **The export failed while \[stage] and produced no workbook.** The stage is **starting**, **loading the saved model**, **reading the model calendar**, **calculating the page tables**, **calculating the inputs the page tables read**, **writing the workbook**, or **storing the workbook**.
* The cause, such as a formula whose input has no cell in the workbook, a formula that could not be written for Excel, **The export reached its time limit**, or a Dimension whose list of items was larger than the export could read.
* When it is known, whether to try again: **The failure is temporary: a new export of the same pages can succeed.** or **A new export fails the same way while the saved model is unchanged.**
Two reasons stand alone:
| Message | Meaning |
| - | - |
| **Add a table to this page before downloading a spreadsheet.** | The Page's last table was removed during the download. |
| **The export was cancelled before it produced a workbook.** | The export was cancelled. |
For time and size limits, see [Limits and file types](/reference/limits#tables-pages-sharing-and-exports).
## Share panel
**Share** in the Page header opens the panel, titled **Share \[Page]**, or **Share \[n] pages** when the link opens several, with **Close**. From top to bottom:
| Part | What it does |
| - | - |
| **Pages to share** | Lists the Page you started from, marked **Opens here**, and the other Pages in this Scenario you can share. Select Pages to add them to the link. Below it: **The first page opens when someone follows the link. Up to 50 pages can be shared.** |
| **External access** | **Create link**, and the links that already exist. With none, it says **No one outside your workspace has link access.** A Page can have up to 100 links. |
| A link | Its address; **Anyone with this link can view. Updated \[time]**; the names of the Pages it opens; **Copy external link**; and **Turn off external link** (the trash icon). |
| **Edit share preview** | On a link without a password. Opens the [Share preview](#share-preview) dialog. |
| **Link settings** | Opens under a new link. **Shared scenario** picks the link's Scenario. **Require password** turns the password on; turning it on fills in a generated password. The password field has **Show password** (**Hide password**) and **Generate password**. **Save changes** appears after a change; saving replaces the link with a new address, and the old address stops working. |
| Footer | **Model data on this page may also be used on other pages.** |
Turning a link off opens **Turn off this external link?**, which says **Anyone who has the link will no longer be able to open it.** The checkbox **Also remove access already granted by this link** is checked, with **Recommended for financial information. Direct invitations stay unchanged.** Select **Cancel** or **Turn off link**.
An old link that opens a Page in every Scenario shows **All scenarios** as its **Shared scenario**, and only someone with full access to the Page can turn it off: **Only a page-wide owner can turn off this legacy link.**
### Share panel messages
| Message | Meaning |
| - | - |
| **Checking external link access…** | The panel is loading the Page's links. |
| **External link access could not be fully verified.** | Not every link could be checked; **Create link** stays off. |
| **External links could not be loaded. Try again later.** | The links failed to load. |
| **Checking page access…** | The panel is checking which Pages you can share. |
| **Access to a selected page could not be verified. Remove it or retry after access refreshes.** | One of the Pages you picked could not be checked. |
| **Could not check access to some other pages. Retry after access refreshes.** | Some Pages are missing from **Pages to share**. |
| **Remove \[n] pages before creating a link.** | More than 50 Pages are selected. |
| **Could not create an external link. Try again.** | Creating the link failed. |
| **Enter a password before saving link settings.** | **Require password** is on with an empty password. |
| **This link password could not be revealed.** / **This link password could not be loaded.** | The saved password could not be read. |
| **Could not update this external link. Its old settings are unchanged.** | Saving settings failed and the old link still works. |
| **Could not copy the external link.** | Your browser refused the copy. |
| **Could not turn off this external link. Try again.** | Turning the link off failed. |
### Share preview
**Edit share preview** opens **Share preview**: **This is how your page appears when someone shares the link.**
| Part | What it does |
| - | - |
| **Preview** | Shows the card's picture, **Built from the content on this page.**, with **Live**, **Ready to publish**, or **Creating artwork…**. **This page changed after the preview was made. Create new artwork to bring it up to date.** appears when the Page changed. **Social apps may take a while to refresh a preview they have already seen.** |
| **Social copy** | **Written from the shared page. Edit it before publishing if you like.** with **Headline** and **Description** |
| **Publish changes** | Saves the card. Confirms with **Preview published.** |
| **Create new artwork** | Makes three new pictures (**Creating three new options from this page.**). They appear under **New artwork**: **Choose an option, then publish it with your social copy.** Selecting one says **Option selected. Publish when it looks right.** If some fail: **Some covers could not be generated. You can use a ready option or generate new ones.** |
| **Public link** | **Anyone with this link can view the page. We do not list it publicly.** with the address and **Copy link** (**Link copied.**) |
| **Memorable address** | A readable address, such as `revenue-plan`, with **Update** (**Public address updated.**). **Optional. Memorable addresses can be guessed. Changing one retires the old address.** |
A memorable address has 3 to 64 lowercase letters, digits, or single hyphens between them; capital letters are made lowercase. Some words, such as `admin`, `docs`, `help`, `login`, `settings`, `share`, and `support`, are reserved.
| Message | Meaning |
| - | - |
| **use 3–64 characters: letters, numbers, or single hyphens between them** | The address breaks the rule above. |
| **this address is reserved** | The address is a reserved word. |
| **this address is already reserved** | Another link uses the address, or used it before it was retired. |
### What a link's reader sees
| Screen | What it shows |
| - | - |
| Opening | **Opening shared link…**, then **Taking you in…** |
| Password | **Enter the link password**, with **Ask the person who shared this page for its password.**, a **Password** field, and **Open shared page**. A wrong password shows **That password does not match** and **Check the password you were given and try again.** |
| Link turned off or wrong | **Link not found**, with **This share link may have been revoked or never existed.** |
| Link expired | **Link expired**, with **Ask the sharer for a fresh link.** |
| A memorable address that cannot load | **This shared page is temporarily unavailable. Please try again.** |
| Other failure | **Something went wrong**, with **Please try again in a moment.** |
| Strip above the Page | For a reader who is not signed in: your brand's logo and name with **Powered by cfo.ai**, or, without a brand, **Like what you see? Build a model like this for your own business.** with **Start your free trial now**, which the reader can dismiss |
| **Keep your access** | A card for a reader who is not signed in: **Sign in to hold on to the pages shared with you.**, with an email field |
A link's preview card in chat and social apps shows **Shared page unavailable** and **This link has expired or is no longer available.** once the link is off.
## Canvas blocks
### Canvas block controls
| Control | What it does |
| - | - |
| **Add** | At the block's top corner, for people who can edit the Page. Opens the Canvas block library to pick components and templates for Ari to add. |
| **Select element** | At the block's top corner. Click one part of the block to add it to the chat as a reference. |
| **Mention in chat** | At the block's top corner. Adds the block to the chat's message box. |
| **Browse library** and **Mention block in chat** | On a blank block, under **Build something custom** and **Chat with Ari to generate custom visuals and interactive tools**. |
A blank block on a deleted Page that you open from version history reads **This Canvas block was empty when the page was deleted.**, and a blank block you cannot edit reads **This Canvas block is empty.**
A button inside a Canvas block can open another Page or block, put a message for Ari in the message box for you to review and send, or start a new chat that sends its message at once, such as a request to start a Guide. A button that hands work to Ari shows a trailing arrow. No button changes the Model.
A Canvas block can open your browser's own dialogs: a print button prints that block alone, and a block can ask you to confirm a step or type a short answer. These dialogs come from the block, not from cfo.ai. A Canvas block cannot show images from the web or from a file.
### Canvas block messages
| Message | Meaning | What to do |
| - | - | - |
| **This Canvas block didn't start because its libraries from esm.sh failed to load.** | The block's software did not load. | Select **Retry**. |
| **This Canvas block didn't start because its runtime stopped with an error before it was ready.** | The block's software stopped before the block started. | Select **Retry**. |
| **This Canvas block was still loading its libraries from esm.sh after 15 seconds.** | The block's software is loading slowly. | Select **Retry**, or wait. |
| **This block failed to render.**, with the error | The block stopped with an error while drawing. | Select **Fix** to hand the block to Ari, or **Mention in chat**. |
| **This block's "\[data]" data didn't load because the connection to cfo.ai failed.** | Your connection to cfo.ai failed. | Wait. The block asks again when your connection returns. |
| **This block's "\[data]" data failed to load.** | The data the block reads failed to load. | Select **Mention in chat** and ask Ari to check the data the block reads. |
A block that stopped with an error tries again by itself once after 2 seconds, and any block that did not start tries again when your connection to cfo.ai returns.
## Chart blocks
New charts are Canvas blocks; the **Add block** menu has no chart option. A chart block a Page already has can still be restyled:
| Control | What it does |
| - | - |
| **Show chart setup** / **Hide chart setup** | At the left of the chart's header. Opens the chart's sidebar. |
| Chart title | Type to rename the chart. |
| **Compare** | In the chart's header. **None** or **Scenarios**, and whether colors follow the **Dimension item** or the **Scenario**. |
| **Setup** tab | The chart type (**Line**, **Bar**, **Bar Stacked**, **Bar Stacked Normalized**, **Pie**, **Combo**, **Donut**, **Nightingale**, **Area**, **Area Stacked**, **Area Stacked Normalized**, **Waterfall**); **X-Axis** with the date grain; **Y-Axis** (or **Values** on a pie, donut, or Nightingale chart) with each Variable's **Pick color**, **Segment by**, and **Remove**; a combo chart's line or bar choice per series; a waterfall's **Positive**, **Negative**, and **Total** colors. |
| **Display** tab | **Show chart title** and **Chart title text**; **Show legend** and its **Position**; **Show X axis**, **Show X axis title**, and **X axis title**; **Show Y axis**, **Show horizontal grid lines**, **Show Y axis title**, and **Y axis title** |
| **Open Chart Options** (**…**) | **Reset chart** |
## Phones and tablets
A phone or tablet browser opens the same workspace in a reading layout, one screen at a time. A tablet gets the desktop layout only when its browser asks for the desktop site.
| Control | What it does |
| - | - |
| **Open navigation** | Opens a drawer with the logo, **Search**, your chats, the **Model** section with its Pages, and **Connections**. Picking a chat, a search result, or **New chat** closes it; so does **Close navigation**. |
| **Open page panel** | In a chat's header. Opens the Page beside the conversation. |
| **Back to chat** | On a Page opened from a chat. Returns to the conversation. |
| **New chat** | On a Page. Starts a chat. |
Pages are read-only in this layout, whatever your access. You can read tables and Canvas blocks, and a table's **Table block settings** menu offers **Show formula bar** and **Show formula columns**, but you cannot edit cells, add rows or blocks, or create Pages or Scenarios. The Page header shows only the Page's name: **Share**, **Download spreadsheet**, and **Version history** are not offered. Empty states say **Pages are read-only in this browser. Use Desktop Site to create a page.** and **Scenarios are read-only in this browser. Use Desktop Site to create one.**
To edit on a phone or tablet, ask your browser for its desktop site. You get the full desktop app, editing included.
## Related
* [Find a Page, number, or file](/guides/find-things)
* [Keyboard shortcuts](/reference/keyboard-shortcuts)
* [Chat controls and messages](/reference/chat)
* [Settings](/reference/settings)
* [Sharing and access](/concepts/sharing-and-access)
# Keyboard shortcuts
Source: https://docs.cfo.ai/reference/keyboard-shortcuts
Every keyboard shortcut in cfo.ai: workspace search, the Help center, chat with Ari, Pages and blocks, table cells, formulas, and undo and redo, including whether Ctrl+Z undoes Ari's changes.
This page lists every keyboard shortcut in cfo.ai, grouped by where you use it. On Windows and Linux, use **Ctrl** where this page shows **⌘**, and **Alt** where it shows **⌥**. Press **⌘ ?** in the app to see the common shortcuts in the Help center.
## Search, help, and the sidebar
| Shortcut | What it does |
| - | - |
| **⌘ K** | Open or close workspace search |
| **↑** / **↓** in search | Move through the results |
| **Enter** in search | Open the highlighted result |
| **⌘ Enter** in search, or **⌘**-click a result | Open the result in a new browser tab |
| **Esc** in search | Close search |
| **⌘ ?** | Open the Help center |
| **⌘ \\** | Fold the navigation sidebar to a narrow strip, or pin it open again |
| **⌘ Enter** in **Ask a human** or **Share feedback** | Send the message |
## Undo and redo
| Shortcut | What it does |
| - | - |
| **⌘ Z** | Undo the last change you made to the Model in this browser tab |
| **⌘ Shift Z** | Redo the change you undid |
Undo and redo work anywhere in the app except while you are typing in a text field or the formula editor. They cover only changes you made in this tab since it opened. To undo a change Ari made, or one from an earlier visit, use version history; see [Undo a change or restore an earlier version](/guides/undo-changes).
| Message | Meaning |
| - | - |
| **Changes undone** / **Changes redone** | The undo or redo finished. |
| **Couldn’t undo changes** / **Couldn’t redo changes**, with **Try again.** | The undo or redo failed, and the change stays as it was. |
## Chat with Ari
| Shortcut | What it does |
| - | - |
| **⌘ /** | Start a new chat. With a cell, block, or Page selected, the new chat starts about it. |
| **⌘ L** | Mention the selected cell, row, column, or block in the current chat. With text selected in one of Ari's answers, quote that text in your reply. |
| **Enter** | Send your message. While Ari is working, the message reaches Ari at its next step. |
| **⌥ Enter** | While Ari is working, queue your message for after Ari finishes. When Ari is not working, start a new line. |
| **Shift Enter** or **⌘ Enter** | Start a new line in the message box |
| **Esc** in the message box | Stop Ari while it is working |
| **Tab** or **→** in an empty message box | Use Ari's suggested reply |
| **A**, **B**, **C**, and so on | Choose an option on one of Ari's question forms, when the cursor is not in a text box |
| **↑** at the start of your draft | Move to the status line above the message box, such as a running sync. **↓** returns to the draft. |
## Pages and blocks
| Shortcut | What it does |
| - | - |
| **↑** / **↓** | Move through Pages in the sidebar, or through blocks on a Page |
| **→** / **←** | Expand or collapse a Page in the sidebar |
| **⌥ ↑** / **⌥ ↓** | Move the selected Page up or down among its siblings |
| **⌥ →** | Nest the selected Page under the Page above it |
| **⌥ ←** | Move the selected Page out one level |
| **F2** | Rename the selected Page |
| **Shift F10** | Open the selected block's menu |
| **Enter** | Enter the selected block |
| **Esc** | Return from inside a block to the block |
## Table cells
| Shortcut | What it does |
| - | - |
| **Enter** | Edit the selected cell. While editing, save and move down. |
| **Shift Enter** | Save and move up |
| **Tab** / **Shift Tab** | Save and move right or left |
| **⌘ Enter** | Save and stay on the cell |
| **⌥ Enter** | Start a new line inside the cell |
| **Esc** | Cancel editing and return to the table |
| **Delete** / **Backspace** | Clear the selected cells' own values |
| **⌘ C** / **⌘ X** / **⌘ V** | Copy, cut, or paste the selected cells |
| **⌘ Shift V** | Paste values only, without formulas |
| **⌃ ⌘ C** (**Ctrl Alt C** on Windows and Linux) | Copy the value a cell displays |
| **⌘ A** | Select every cell in the table |
| **Shift F10** or the **Menu** key | Open the right-click menu of the selected cell or header |
| **Shift**-click a row's or column group's chevron, or **⌘**-click on a Mac | Expand or collapse every sibling row or column group at that level. On Windows and Linux, use **Shift**-click; **Ctrl**-click does not do this. |
## Formulas and filters
| Shortcut | What it does |
| - | - |
| **Ctrl Space** | Open formula autocomplete |
| **⌥ F12** | Show the syntax and an example for the function under the cursor |
| **F2** | Start selecting a cell or row to reference |
| **Arrow keys** | While selecting a reference, point at the neighboring cell |
| **Enter** | Insert the selected reference. When the editor is closed, save the formula. |
| **Esc** | Cancel reference selection, or close the editor without saving |
| **⌥ ↓** | Open the filters on the reference at the cursor |
| **F4** | Fix or release the reference at the cursor |
| **⌘ Enter** | In a row's formula editor, save and apply the formula to the parent rows too |
## Related
* [The cfo.ai interface](/reference/interface)
* [Table block controls and messages](/reference/table-blocks)
* [Chat controls and messages](/reference/chat)
# Limits and file types
Source: https://docs.cfo.ai/reference/limits
Every size, count, and time limit in cfo.ai: chat attachments and file types, uploads, connections and syncs, tables and Pages, share links, exports, Skills, Triggers, Guides, connected AI tools, the free trial, and sign-in codes.
This page lists every size, count, and time limit in cfo.ai, grouped by part of the product. Each section ends with links to the pages that explain its limits. For what each plan includes, such as integration slots and AI usage, see [Plans and billing](/reference/plans-and-billing).
## Chat with Ari
| Limit | Value |
| - | - |
| Files attached to one message | 5 |
| Size of one image file | 10 MB |
| Size of one other file, such as a PDF, Excel workbook, CSV, or text file | 50 MB |
| Quoted excerpts in one message | 5, each up to 2,000 characters |
| Size of a text file or CSV that Ari reads in the conversation | 64 KiB; a larger one opens in Ari's private workspace instead |
| Recipients of one email Ari drafts | 10 |
| Size of a page of conventions you ask Ari to remember | 32 KiB of text, saved as up to 12 separate facts at a time |
| Files Ari delivers at once | 10, each up to 50 MB, and 250 MB in all |
| Time a download link for a file in a chat works | 5 minutes after you select the file; select it again for a new link |
| Length of one chat | No fixed limit; when a chat grows too large for Ari to read in one request, the chat says so, and you start a new chat |
See [Attach files to a chat](/guides/attach-files) and [Chat controls and messages](/reference/chat).
## Connections, syncs, and uploads
| Limit | Value |
| - | - |
| Size of a CSV uploaded as a data source | 50 MB, in a `.csv` file |
| Size of a connected Google Sheet | 50 MB, read as an Excel workbook |
| Size of an Excel or CSV file read from Google Drive | 10 MB |
| Tables one connection syncs | 100, for sources with a table picker |
| Time cfo.ai waits for a provider's own first sync | 20 hours, checking every 10 minutes |
| Time to plan a saved query | 40 seconds |
| History in the first load of QuickBooks Online or Xero | From January 1 of the previous year; the 3 most recent complete months arrive first |
| Months the nightly refresh of QuickBooks Online reads again | The last 3; ask Ari for a full-history import to correct older months |
| History in a full-history import of QuickBooks Online or Xero | Back to 2015 at the earliest |
| Rillet connections in one workspace | 1 |
| Start of the nightly refresh of connected sources | 02:00 UTC |
| OpenAPI specification for an API connection | An `https` address that answers within 20 seconds, with a document of 12 MiB or less |
| Time one run of a sync setup for an MCP server or API can take | 10 minutes |
| Results one run of a sync setup can save | 10,000 pages of up to 16 MiB each for a replace or snapshot sync without a changes-since filter; 100 pages, 20,000 records, and 16 MiB in all for any other sync |
| Size of one page an MCP server returns to a sync setup | 2 MiB |
See [How imported data loads](/reference/imported-data) and [Integrations directory](/reference/integrations).
## Tables, Pages, sharing, and exports
| Limit | Value |
| - | - |
| Items on one table axis before Ari notes that the table may be slow | About 1,000 |
| Scenarios one Canvas block can read | 3 |
| External links on one Page | 100 |
| Pages one share link opens | 50 |
| Length of a share link's memorable address | 3 to 64 characters: lowercase letters, digits, and single hyphens |
| Pages in one workbook Ari exports | 64 |
| Spreadsheet exports a workspace prepares at once | 1 |
| Time one spreadsheet export can run | 45 minutes |
| Length of a formula in an exported workbook | 8,192 characters, Excel's limit; a longer formula is exported as its value |
See [Export a Page to Excel](/guides/download-a-spreadsheet), [Share a Page outside your workspace](/guides/share-a-page), and [Table block controls and messages](/reference/table-blocks).
## Skills, Triggers, and Guides
| Limit | Value |
| - | - |
| Saved Skills for one person, across all their workspaces | 100 |
| Length of a Skill's name | 64 characters: lowercase letters, digits, and single hyphens |
| Length of a Skill's description | 1,024 characters |
| Length of a Skill's instructions | 49,152 characters |
| Length of a Trigger's name | 200 characters |
| Length of a Trigger's instructions | 10,000 characters |
| Precision of a Trigger's schedule | 1 minute; the schedule is stored in UTC, so a local time can shift by an hour when daylight saving time changes |
| Time a download link in a scheduled run's message works | 24 hours |
| Saved Guides in one workspace | 200 |
| Length of a Guide's name | 64 characters: lowercase letters, digits, and hyphens |
See [Save a workflow as a Skill](/guides/save-a-skill), [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger), and [Walk someone through a Page with a Guide](/guides/create-a-guide).
## AI tools connected to cfo.ai
| Limit | Value |
| - | - |
| Calls to one cfo.ai tool | 60 a minute for each person in each workspace; past that, the tool answers `rate limit exceeded for` and the tool's name |
| Size of one request | 1 MiB; a larger request gets **The MCP request is too large.** |
| Time the browser page where you approve a connection works | 60 minutes after it opens |
See [Connect Claude, ChatGPT, Codex, or another AI tool to cfo.ai](/guides/connect-an-ai-client).
## Workspace, account, and sign-in
| Limit | Value |
| - | - |
| Length of the free trial | 14 days, once per workspace |
| Amount of one extra AI usage purchase or one auto-reload | \$5 to \$10,000 |
| Length of a welcome note in an invitation | 600 characters, with up to 3 questions of 200 characters each |
| People Ari invites in one request | 50 |
| Sign-in codes for one email address | 5 sent and 10 checked in 15 minutes; past that, sign-in answers **Too many requests** until the 15 minutes pass |
| Time a sign-in code works | 10 minutes |
| Length of an **Ask a human** message | 4,000 characters, with up to 3 PNG, JPG, or WebP screenshots of 5 MB or less each |
| Length of a **Share feedback** message | 20,000 characters, with up to 3 screenshots |
| Workspace search results | 8 in each group |
| Length of a workspace brand's name | 40 characters |
| Size of a workspace brand's logo | 1 MB and 2,000 pixels on each side, in a PNG, JPEG, or WebP image |
See [Plans and billing](/reference/plans-and-billing), [The cfo.ai interface](/reference/interface), and [Brand your workspace](/guides/brand-your-workspace).
# Plans and billing
Source: https://docs.cfo.ai/reference/plans-and-billing
cfo.ai's Starter, Growth, and Enterprise plans and every billing rule and message: when the free trial charges your card, monthly and annual billing, changing or canceling a plan, invoices and receipts, Settings → Billing and Usage, banners, locked workspaces, and AI usage notices.
cfo.ai has three plans: Starter, Growth, and Enterprise. A plan is bought for a workspace, not per person, and its price is shown in the app. Billing lives in two Settings sections, **Billing** and **Usage**, which every member can open because every member is an administrator. A workspace billed outside the app shows neither. Ari can read your plan, usage, balance, spend limit, and connection slots, but it cannot change billing; it tells you which Settings section to use. For how plans work, see [Plans, AI usage, and connection slots](/concepts/plans-and-usage).
## Plans
The plan cards show each plan's current price. Starter and Growth come in monthly and annual versions, and include no premium integrations. For which integrations are standard and which are premium, see the [integrations directory](/reference/integrations#standard-and-premium-integrations). The cfo.ai MCP server works in any workspace that has started its trial or a plan, whichever plan it is; see [Connect Claude, ChatGPT, Codex, or another AI tool](/guides/connect-an-ai-client).
### Starter
The card reads **Build and run the finance model.** It lists:
* Standard AI usage that resets monthly
* Up to 5 standard integrations
* Up to 500K synced rows per month
* The MCP server and Slack app
* Proactive alerts and Triggers
* Unlimited Models and Scenarios
* 1 dedicated workspace
### Growth
The card reads **Scale the workflows that are already running in Starter.** It lists:
* Everything in Starter
* A multiple of Starter's AI usage, shown on the card
* Up to 8 standard integrations
* Up to 2M synced rows per month
### Enterprise
The card reads **Connect a complex stack and add hands-on finance support.** It lists:
* A custom price and AI credits
* Premium integrations, including NetSuite and data warehouses
* Multiple workspaces
* SSO / SAML and priority support
* A dedicated Forward Deployed Finance Partner and a shared Slack channel
Enterprise is not chosen from its card: **Talk to sales** opens a scheduler to book a call. If the scheduler cannot open, a notice reads **Couldn’t open scheduling**.
## Monthly and annual billing
| Rule | Detail |
| - | - |
| The switch | **Monthly** and **Annual** above the plan cards, shown when annual plans are offered. **Annual** names its saving as **Annual — save** followed by a percentage, or **Annual — save up to** a percentage when plans save different amounts. |
| Which side it opens on | **Annual** when you choose your first plan. In **Settings → Billing**, the cadence of the plan you have. |
| Annual prices | Shown per month, with **billed annually** and the yearly amount. An annual plan is charged for the year at the start of each term. |
| Changing cadence | Moving between monthly and annual on the same plan is scheduled for your next renewal. Moving to a higher plan and changing cadence in one step is not offered. |
## Free trial
A free trial lasts 14 days and needs a card to start. When it ends, the paid plan starts and charges the card on file, unless you cancel before the end date.
| Rule | Detail |
| - | - |
| Length | 14 days |
| How many | One per workspace. A workspace that had a trial, including a canceled one, pays from the first day of its next plan. |
| Card | Required to start. Nothing is charged when the trial starts. |
| When it ends | The paid plan starts on its own and charges the card on file. |
| Canceling | Cancel any time before the end and nothing is charged; the trial still runs to its end date. |
| Changing plans | A plan change during a trial applies right away at no charge, and the trial keeps its end date. |
| AI usage | The trial includes its own AI usage. Once it is used up, **Start paid plan** lets you end the trial early, pay, and reset your included usage. Before then, the plan cannot be started early. |
| Message | Where |
| - | - |
| **Try cfo.ai free for 14 days** / **\$0 today. Cancel anytime before it ends and you won’t be charged, and you still get all 14 days free.** | Plan page, first plan |
| **Choose a plan to continue** / **Your free trial has already been used. Paid plans charge immediately.** | Plan page, when the workspace had a trial |
| **We ask for a card to confirm you’re a real person and keep** \[plan] **active after the trial. Cancel in Settings before** \[date] **and you won’t be charged. You still get all 14 days free.** | **Add a payment method**, starting a trial |
| **This plan has no free trial. Starting it charges your card immediately.** | **Add a payment method**, plan with no trial |
| **By starting a plan, you agree to the Standard Terms and Conditions and acknowledge the Privacy Policy.** | Plan page; see [Privacy, security, and legal documents](/reference/privacy-and-legal) |
## Plan chooser
**Change** next to your plan in **Settings → Billing** opens the plan chooser, headed **Choose your plan**, or **Change your trial** during a trial. With no plan, the button reads **Choose a plan**.
| Button on a plan card | Meaning |
| - | - |
| **Start free trial** | Starts a 14-day trial of the plan. |
| **Subscribe to** \[plan] / **Choose** \[plan] | Starts the plan and charges it today. |
| **Switch to** \[plan] | Changes your plan. A confirmation shows the cost first. |
| **Your current plan** / **Your current trial** | The plan you have, marked **Current plan** or **Current trial**. |
| **Keep** \[plan] | Calls off a scheduled change or a cancellation. |
| **Keep and switch to** \[plan] | Calls off a cancellation and switches plan. |
| **Starts** \[date] / **Change scheduled** | The plan you are switching to at renewal. |
| **Contact support to change plan** | Your plan is a custom plan that cannot be changed here. |
| **Talk to sales** | Enterprise. |
A card you cannot choose is grayed out, and pointing at its button says why:
| Reason | Text |
| - | - |
| More integrations connected than the plan allows | \[plan] **includes** \[number] **standard integrations; you have** \[number] **connected. Disconnect integrations to switch.** For a class the plan lacks: \[plan] **doesn't include premium integrations; you have** \[number] **connected.** |
| A higher plan on the other cadence, from annual | **Your annual plan runs until** \[date]**. Switch to** \[plan] **on annual billing to upgrade now, or move to monthly billing when your plan renews.** |
| A higher plan on the other cadence, from monthly | **Switch to** \[plan] **on monthly billing first. You can move to annual billing when your plan renews on** \[date]**.** |
| Not offered for this workspace | \[plan] **isn't available for this workspace — contact support.** |
### Confirm plan change
The dialog **Confirm plan change** states the cost before anything is charged:
| Change | Dialog text | Button |
| - | - | - |
| First paid plan | **Your** \[plan] **plan starts today, billed at** \[amount]**.** **Your plan will renew for** \[amount] **on** \[date]**.** | **Subscribe to** \[plan] |
| Higher plan | **Your plan will switch to** \[plan] **immediately. You will be billed** \[amount] **today to upgrade from** \[plan]**, covering the rest of your current plan term.** | **Switch to** \[plan] |
| Higher plan at the same price | **Your plan will switch to** \[plan] **immediately at no extra charge.** | **Switch to** \[plan] |
| Lower plan, or the same plan on the other cadence | **Your current** \[plan] **plan will remain active until** \[date]**, when it will change to** \[plan] **at** \[price]**.** | **Schedule change** |
| Any change during a trial | **Your trial switches to** \[plan] **today, at no charge.** **Your trial still ends on** \[date]**, when** \[price] **begins.** | **Switch to** \[plan] |
| Starting a trial | **Your 14-day** \[plan] **trial starts today and runs until** \[date]**.** | **Start free trial** |
| Starting a trial, with no card on file | **Starting your free trial needs a payment method on file — it’s what your plan charges when the trial ends.** | **Add a payment method** |
| A plan that charges, with no card on file | \[Switching to or Subscribing to] \[plan] **costs** \[amount] **today. Add a payment method to continue.** | **Add a payment method** |
| Switching while a cancellation is booked | **Your plan will no longer end on** \[date] **— confirming keeps it and switches you to** \[plan]**.** | **Keep and switch to** \[plan] |
If the cost cannot be worked out, the dialog reads **We couldn’t work out what switching to** \[plan] **will cost right now. Please close this and try again.** and cannot be confirmed.
| Notice | Meaning |
| - | - |
| **You're on** \[plan] | The new plan applies now. |
| **Your** \[plan] **trial has started** | The trial started. |
| **Plan change scheduled for** \[date] | The change applies at renewal. |
| **Plan change canceled** / **You’ll stay on** \[plan]**.** | A scheduled change was called off. |
| **Couldn’t change plan** / **Couldn’t cancel the plan change** | The change failed; the reason follows. |
## Settings → Billing
### Current plan
The plan's name, with a **Trial** or **Canceled** badge when one applies, and one line about what happens next:
| Line | Meaning |
| - | - |
| **You will be charged** \[amount] **when your trial ends on** \[date]**.** | In a trial. The paid plan starts on that date. |
| **Your plan renews on** \[date] **for** \[amount] | A paid plan renewing as usual. |
| **Your** \[plan] **plan is active until** \[date]**, then changes to** \[plan] **at** \[price]**.** | A change is scheduled. **Keep** \[plan] calls it off. |
| **Your plan was canceled and won’t renew — it ends on** \[date]**.** | Canceled. For a trial, it reads **Your trial was canceled**. **Keep** \[plan] restores it. |
| **Renewal processing. Your invoice is being prepared, and your credits will refresh after payment.** | A renewal is being charged. |
| **Choose a plan to start using Ari.** | No plan. |
| **Only workspace admins can change the plan.** | You cannot change billing. |
Buttons: **Change** or **Choose a plan**; **Keep** \[plan] while a change or cancellation is booked; and **Start paid plan** in a trial whose included AI usage is used up.
### Payment methods
| Control | What it does |
| - | - |
| **Add** | Opens **Add a payment method**. Enter a card and select **Save payment method**. |
| A card row | The card, its last four digits, and its expiry, with **Default**, **Expired**, or **Payment failed** when they apply. |
| **Set as default** | On a card's menu. Renewals and automatic top-ups charge the default card. |
| **Replace…** | Opens **Replace payment method** to swap in a new card. |
| **Remove** | Removes the card. Unavailable for the only card on a plan, with the reason. |
| Message | Meaning |
| - | - |
| **No payment methods saved yet. Add one to enable automatic top-ups and plan charges.** | No card is saved. |
| **A workspace on a plan needs a payment method on file — it is what your renewal charges. Add another payment method to replace this one.** | You cannot remove the last card while you have a plan. With auto-reload on, the message reads **…it is what your renewal and your automatic top-ups charge…** |
| **You need a payment method on file while automatic top-ups are on. Turn off auto-reload first, or add another payment method.** | You cannot remove the last card while auto-reload is on. |
| **Payment method added** / **Payment method replaced** | Saved. |
| **Couldn’t save the payment method** / **Couldn’t replace the payment method** / **Couldn’t remove the payment method** / **Couldn’t set the default payment method** | The change failed; the reason follows. |
The card dialogs do not close when you click outside them, so a card you typed is not lost. Close them with **Cancel** or **Esc**.
### Billing history
Each row shows the date, **Credit top-up** for a usage purchase, the amount, and a status: **Paid**, **Failed**, **Processing**, **Due**, **Pending**, **Refunded**, or **Void**. **View** opens the invoice or receipt in a new tab. **View all** opens the full list when there are more rows. With no rows, it reads **Nothing here yet. Invoices and credit purchases will appear here.**
### Cancel subscription
Shown while you have a plan that is not already ending. It reads **Your plan stays active until the end of your current plan term on** \[date]**.**, or in a trial **Your trial stays active until** \[date]**. You won’t be charged when it ends.**, with **Cancel**.
| Part of the dialog | Text |
| - | - |
| Title | **Cancel subscription** |
| Paid plan | **Your** \[plan] **plan will remain active until** \[date]**, the end of your current plan term. You won’t be charged again. You can undo this cancellation any time before** \[date]**.** |
| Trial | **Your** \[plan] **trial will remain active until** \[date]**. You won’t be charged when it ends. You can undo this cancellation any time before** \[date]**.** |
| **Why are you cancelling?** | **Select a reason**: **I don’t want to be charged yet** (trials only), **I didn’t see enough value**, **It was too difficult to get started**, **Pricing wasn’t right for me**, or **Other — please tell us why**, which needs a short explanation. |
| Waiting for a reason | **Cancel subscription is available once a reason is selected.** or **Cancel subscription is available once the explanation is filled in.** |
| Buttons | **Keep subscription** closes the dialog; **Cancel subscription** confirms. |
| Notice | Meaning |
| - | - |
| **Your** \[plan] **plan was canceled** / **You keep access until** \[date]**.** | Canceled. |
| **Cancellation reversed** / \[plan] **renews as normal.** | **Keep** \[plan] undid the cancellation. |
| **Couldn’t cancel the plan** / **Couldn’t reverse the cancellation** | The change failed; the reason follows. |
## Settings → Usage
| Row | What it shows |
| - | - |
| **Included plan usage** | The share of this cycle's included AI usage used, such as **42% used**, and **Resets** \[date]. **No included usage granted yet** before the first grant. |
| Trial usage used up | **You’ve used all the AI usage included with your trial. Start your paid plan early to reset your monthly AI usage. Your payment method will be charged now.** with **Start paid plan**. |
| **Free AI credits** | Only when the workspace still has one-time free credits: \[amount] **remaining** and **One-time credits. They do not replenish.** |
| **Extra usage** | **Credits used after your included usage runs out**: the monthly spend limit, the purchased balance, and auto-reload, below. |
| **Integrations** | **Standard** \[used]/\[limit] and **Premium** \[used]/\[limit], or **Premium not included**, then each counted connection with its class and status. Google Sheets connections show together with their count. **Updated** \[time] and a refresh button sit beside the heading. |
### Extra usage
| Control | What it does |
| - | - |
| **Monthly spend limit** | The most extra usage may cost per billing cycle, or **No limit**, with the amount spent this cycle. **Adjust limit** opens a box: type an amount and select **Save** (**Takes effect immediately.**), or select **Remove limit**. |
| **Purchased usage balance** | \[amount] **available**, with **Auto-reload on**, **off**, or **paused**. When paused: **A payment failed. Update the payment method or buy credits to resume automatic reloads.** |
| **Buy credits** | Opens the purchase page, below. |
| **Auto-reload** | A switch. When on: **When the balance falls below** \[amount] **, add** \[amount], and **Save**. Needs a default card in **Settings → Billing**. |
| Message | Meaning |
| - | - |
| **Spend limit updated** / **Spend limit removed** | Saved. |
| **Auto-reload updated** / **Auto-reload turned off** | Saved. |
| **Each reload adds this amount to your balance.** | The reload amount is fine. |
| **The minimum reload amount is \$5.** / **The maximum reload amount is \$10,000.** / **The reload amount must be greater than \$0.** | The reload amount is out of range. |
| **Add a payment method in Billing before turning this on.** | No default card. |
| **Charges use** \[card]**.** | The card auto-reload charges. |
| **Only workspace admins can manage usage.** | You cannot change usage settings. |
| **Couldn’t update the spend limit** / **Couldn’t update auto-reload** | The change failed; the reason follows. |
### Buy credits
The page reads **Choose an amount. Credits never expire.**
| Control | What it does |
| - | - |
| **\$100**, **\$250**, **\$1,000**, **Other** | The amount. **Other** takes a whole-dollar amount. |
| **Payment method** | Your saved cards, with **Default** marked, and **Use a new payment method**, which adds a card here with **Use this payment method**. |
| **Pay** \[amount] **now** | Charges the card. **By clicking Pay now, you allow cfo.ai to charge the selected payment method for the amount selected.** |
| Message | Meaning |
| - | - |
| **The minimum top-up is \$5.** / **The maximum top-up is \$10,000.** | The amount is out of range. |
| **That’s more than your remaining monthly limit of** \[amount]**.** | The purchase would pass your spend limit. The link opens the limit. |
| **Add a payment method to buy credits.** | No card is saved. |
| **Added** \[amount] **of usage** | Bought. With **(clamped by your spend limit)**, less than you asked for was bought, to stay within the limit. |
| **Couldn’t complete the purchase** | The purchase failed; the reason follows. |
## Payment messages
| Message | Meaning |
| - | - |
| **The bank that issued this card declined it and gave no reason. Nothing was charged.** | The card was declined. Try another card or ask your bank. |
| \[the card provider's reason] **Nothing was charged.** | The card provider gave a reason. |
| **Please check your payment details and try again.** | Another payment problem. |
| **The payment form is still loading. Please try again.** / **Could not load the payment form. Close it and try again.** | The card form did not load. |
| **Payment received** | A payment went through. Your plan and usage update a moment later. |
| **Payment failed** | Starting the paid plan early failed; the reason follows. |
## Banners above the workspace
| Banner | When | Action |
| - | - | - |
| **Your trial ends on** \[date]**. Add a payment method to keep access. Without one, this workspace locks when the trial ends.** | A trial with no usable card. Teammates who cannot change billing see **Ask a workspace admin to add a payment method so you keep access.** | **Add payment method** |
| **Your trial ends on** \[date]**. Your paid subscription starts that day, and we’ll charge the card on file.** | Three days or less before a trial with a card ends. | Dismiss with the ✕. |
| **You’ve used all the AI usage included with your trial. Start your paid plan now to refresh it, or keep using purchased usage until the trial ends on** \[date]**.** | A trial's included usage is used up. Dismissing hides it for the rest of the trial; **Start paid plan** stays in Settings. | **Start paid plan** |
| **We couldn’t process your last payment. We’ll keep retrying, or you can update your card and pay now.** | A renewal payment failed and is being retried. Teammates who cannot change billing see **We couldn’t process this workspace’s last payment. Ask a workspace admin to update billing.** | **Update payment method**, **Pay now** |
| **Need a hand with cfo.ai?** | During a trial, until the first invoice is paid. Dismissing hides it until the next day. | **Talk to our Forward Deployed team** |
## When the workspace locks
A workspace locks when it has no plan, such as after a canceled plan ends, or when a renewal payment still fails after retries. While it is locked, nobody can open its Pages or chat with Ari, and its connected sources stop refreshing.
| Screen | Who sees it | What it says |
| - | - | - |
| Plan chooser | Administrators, when there is no plan | The plan cards, with a heading such as **Choose a plan to continue**. Choosing a plan unlocks the workspace. |
| **We couldn’t charge your card** | Administrators, when payment failed | **Your subscription payment didn’t go through. Update your card and we’ll try again straight away. Access comes back as soon as it succeeds.**, your payment methods, and **Pay now**. |
| **This workspace is unavailable** | Members who cannot change billing | **A workspace admin needs to update billing before anyone can get back in.** |
Each screen offers **Log out** and the [help@cfo.ai](mailto:help@cfo.ai) address. When the workspace has connected sources, the screen says when they last refreshed: **Your connected source was last refreshed on** \[date]**. Your connected source is not being refreshed while this workspace has no plan.**, or **while this workspace’s billing is locked.** With several sources, it gives the newest refresh of any of them.
## Notices in the chat
When billing stops Ari, its reply ends with a message that starts **I had to stop here**; every one is listed in [Chat controls and messages](/reference/chat#billing-stops-out-of-ai-usage-or-over-a-limit). The **AI usage recovery** panel above the message box then names the problem and offers a fix:
| Panel message | Button |
| - | - |
| **This workspace is out of AI usage.** | **Usage settings**, and a purchase button such as **Add \$100** |
| **This workspace reached its monthly AI spend limit.** | **Adjust limit** |
| **Automatic top-up failed — the card on file was declined.** | **Update payment method** |
| **More AI usage is still being added to this workspace.** | **Usage settings** |
| **This workspace's plan isn't active.** | **Review billing** |
| **The workspace's subscription payment is failing.** | **Update payment method** |
| **This workspace hasn't started its trial yet.** | **Open billing** |
| **This workspace reached a limit of its current plan.** | **Open billing** |
| **This wouldn't fit the limits of the plan this workspace is switching to.** | **Open billing** |
| **This workspace's billing status couldn't be checked.** | **Open billing** |
After a purchase, the panel reads **Usage added. This workspace will update in a moment.** and may suggest **Turn on auto-reload to avoid running out of AI usage next time.** with **Enable auto-reload**. In a trial whose usage is used up, it offers **Start plan & save**. Teammates who cannot buy usage see **Ask a workspace admin to add usage.**
While auto-reload buys usage, the chat shows **Adding AI usage to your workspace…**, then **AI usage added to your workspace.**
Before usage runs out, one notice at a time can appear above the message box:
| Notice | When | Button |
| - | - | - |
| \[percent] **of your workspace's monthly AI usage remains.** | Included usage falls to 50%, 25%, 10%, and 5% left. Below 1%, it reads **Less than 1%**. | **View usage** |
| **Your workspace reached its monthly top-up limit.** | Purchases this cycle reached the spend limit. | **Adjust limit** |
| **AI usage is low, but the monthly top-up limit keeps auto-reload from adding more.** | Auto-reload is blocked by the spend limit. | **Adjust limit** |
Dismissing a notice hides it until the next threshold or the next billing cycle.
## Rules and limits
* Every member is an administrator, so every member can change the plan, buy usage, and change the spend limit and auto-reload. Adding people does not change the price.
* Canceling keeps the plan until the end of the term you paid for, and a trial until its end date. Under the Standard Terms and Conditions, fees already paid are not refunded.
* You can undo a cancellation any time before it takes effect.
* A subscription cannot be paused.
* A workspace cannot be deleted from Settings. See [Cancel your subscription or delete your data](/guides/cancel-or-delete).
* Adding an integration needs a started trial or an active plan with a free slot. For the messages, see [Connection statuses and actions](/reference/connection-statuses).
* For the trial length and purchase limits with every other limit, see [Limits and file types](/reference/limits).
## Related
* [Start a trial or choose a plan](/guides/choose-a-plan)
* [Buy AI usage, set a spend limit, or turn on auto-reload](/guides/manage-ai-usage)
* [Cancel your subscription or delete your data](/guides/cancel-or-delete)
* [Plans, AI usage, and connection slots](/concepts/plans-and-usage)
* [Settings](/reference/settings)
# Privacy, security, and legal documents
Source: https://docs.cfo.ai/reference/privacy-and-legal
cfo.ai's Privacy Policy, Terms, subprocessor list, and privacy choices, with answers to common questions: does cfo.ai train AI on your data, who can see your data, where it is stored, who to ask about security certifications such as SOC 2, DPAs and contracts, uploading salaries, and deleting your data.
This page answers common questions about your data in cfo.ai, such as whether cfo.ai trains AI on it, who can see it, and how to get it deleted. The answers point to cfo.ai's legal documents, which are published on its website; where a document and this page differ, the document wins. For a security questionnaire, a Data Processing Agreement, or a contract, write to [legal@cfo.ai](mailto:legal@cfo.ai).
## Legal documents
| Document | Address | What it covers |
| - | - | - |
| Privacy Policy | [cfo.ai/privacy](https://cfo.ai/privacy) | The personal information cfo.ai collects, how it uses and shares it, international transfers, security, retention, your rights, and how to reach cfo.ai about privacy |
| Standard Terms and Conditions for Orders | [cfo.ai/terms](https://cfo.ai/terms) | Your agreement with cfo.ai: who owns your data and how cfo.ai may use it, confidentiality, data security practices, fees, and what happens to your data when the agreement ends |
| Terms of Use | [cfo.ai/tou](https://cfo.ai/tou) | Use of the cfo.ai website and your account, including how to close your account |
| Service Providers and Subprocessors | [cfo.ai/subprocessors](https://cfo.ai/subprocessors) | The service providers that process customer data for cfo.ai, what each one does, and the country of each provider |
| Privacy choices | [cfo.ai/privacy-choices](https://cfo.ai/privacy-choices) | Cookie and tracking choices for your browser, and the data request form |
| Legal documents | [cfo.ai/legal](https://cfo.ai/legal) | Every legal document, with its earlier versions |
When you start a plan in cfo.ai, you agree to the Standard Terms and Conditions and acknowledge the Privacy Policy. The Standard Terms and Conditions, together with your order, form your agreement with cfo.ai.
## Does cfo.ai train AI on my data?
Under the Standard Terms and Conditions, you own your data (section 3.4). You give cfo.ai a license to use it to provide the service to you, including to train AI models specific to your company. cfo.ai may also compile aggregated, anonymized statistics from your use of the service, including what it learns from those company-specific models (sections 1 and 2.5). Those statistics must not identify you or your confidential information.
Ari runs on AI models from outside providers, listed under AI services on the [Subprocessors](https://cfo.ai/subprocessors) page. For the terms that apply to those providers, write to [legal@cfo.ai](mailto:legal@cfo.ai).
## Who can see my data?
* Everyone in your workspace is an administrator and can see every Page, the Model, connections, and settings. Each person's chats with Ari are visible only to that person. See [Sharing and access](/concepts/sharing-and-access).
* Someone you send a share link to sees only the Pages the link names, read-only. See [Share a Page outside your workspace](/guides/share-a-page).
* The Standard Terms and Conditions include confidentiality obligations (section 5), and the Privacy Policy says cfo.ai trains its employees on privacy and security (section 6). For who at cfo.ai can access your workspace and how that access is controlled, write to [legal@cfo.ai](mailto:legal@cfo.ai).
* The service providers that process customer data for cfo.ai, such as hosting, data integration, and AI model providers, are listed on the [Subprocessors](https://cfo.ai/subprocessors) page with what each one does.
## Where is my data stored?
The Privacy Policy (section 9) says cfo.ai is based in the United States, and that personal information may be transferred to and processed in the United States and other countries where cfo.ai and its service providers operate. Transfers from the European Economic Area, the UK, or Switzerland rely on the European Commission's Standard Contractual Clauses, with the UK Addendum where it applies. The [Subprocessors](https://cfo.ai/subprocessors) page gives each provider's country. For anything more specific, write to [legal@cfo.ai](mailto:legal@cfo.ai).
## Security and certifications
* The Privacy Policy (section 6) says cfo.ai uses industry-standard security technologies and procedures, and that cfo.ai reports a personal data breach to the relevant authority within 72 hours where feasible, and tells you without undue delay when a breach is likely to put you at high risk.
* The Standard Terms and Conditions (section 5.4) say cfo.ai keeps internal policies on privacy, data security, business continuity, and disaster recovery, and that copies of its certifications and policies are available on request.
To ask whether cfo.ai holds a certification such as SOC 2, for copies of its security policies, or to send a security questionnaire, write to [legal@cfo.ai](mailto:legal@cfo.ai).
## Contracts and Data Processing Agreements
The Standard Terms and Conditions for Orders, with your order, are your agreement with cfo.ai. The [Subprocessors](https://cfo.ai/subprocessors) page also refers to a Data Processing Agreement. To get one, or to ask for a signed or negotiated contract, write to [legal@cfo.ai](mailto:legal@cfo.ai).
## Is it safe to upload salaries or other sensitive data?
What you upload is your data under the Standard Terms and Conditions, covered by the confidentiality and security terms above. Before you upload pay for each person, think about who will see it:
* Every member of your workspace can see every Page and table. cfo.ai cannot hide one table from a teammate.
* Anyone with a share link sees what the shared Pages show.
If some people should not see individual pay, keep them out of the workspace and send them a share link to a summary Page, or bring in pay totals by department instead of by person.
## Deleting your account or data
cfo.ai has no button for deleting a workspace or an account. To ask for deletion, write to [legal@cfo.ai](mailto:legal@cfo.ai) from the address you sign in with and name the workspace; the Terms of Use name this address for closing and deleting an account (section 4.1). For a request about your personal information, you can also use the data request form on [Privacy choices](https://cfo.ai/privacy-choices). See [Cancel your subscription or delete your data](/guides/cancel-or-delete).
What the documents say about timing:
* The Privacy Policy (section 7) says that when you cancel your account or ask cfo.ai to delete your information, cfo.ai limits its access to what it needs to do what you asked, and deletes the information it can access within 30 days. Section 10 says some information may be kept where the law requires it.
* The Standard Terms and Conditions (section 8.3) say that, once your agreement ends, cfo.ai keeps your data for 15 days if you paid for your first term, and may delete it after that. Within those 15 days you can ask for paid help moving your data to yourself or another provider (section 8.4).
Before you leave, you can download your Pages as spreadsheets; see [Download a Page as a spreadsheet](/guides/download-a-spreadsheet).
## Contacts
| For | Write to |
| - | - |
| Security questionnaires, certifications, Data Processing Agreements, contracts, and deleting a workspace or account | [legal@cfo.ai](mailto:legal@cfo.ai) |
| Questions about the Privacy Policy and requests about your personal information | [security@cfo.ai](mailto:security@cfo.ai), as the Privacy Policy gives, or the data request form on [Privacy choices](https://cfo.ai/privacy-choices) |
| Help using cfo.ai | **Support**, at the top of these docs |
## Related
* [Sharing and access](/concepts/sharing-and-access)
* [Cancel your subscription or delete your data](/guides/cancel-or-delete)
* [Plans and billing](/reference/plans-and-billing)
# Prompt library
Source: https://docs.cfo.ai/reference/prompt-library
What you can ask Ari, with what you get back: example requests grouped by goal, from what to ask first to understanding a number, forecasts, Scenarios, month-end reviews, charts, sharing, and recurring work, plus a table for choosing the right chart.
These are requests you can type to Ari, grouped by what you want to get done, with what you get back from each. Replace the words in brackets with your own. Ari's wording and layout change from run to run, so the results describe what you get, not exact output. For how to frame a question, see [Get better answers from Ari](/guides/get-better-answers).
## Get started
| Prompt | What you get |
| - | - |
| "What can you build for me?" | A short list of what Ari can do with your workspace as it stands, and what to connect or describe first. |
| "We sell \[product] to \[customers] for about \[price]. We have \[N] employees and about \[\$X] in the bank. Build a 12-month model so I can see how long our cash lasts." | A first Model built from your description, with every number you did not give labeled as an assumption. |
| "Connect QuickBooks Online so we can build a monthly profit and loss statement." | A card that opens the connection's sign-in for you to approve. Ari cannot sign in for you. |
| "Rebuild this spreadsheet in cfo.ai." (with the workbook attached) | Your workbook's structure, names, and numbers rebuilt as Pages, Variables, and formulas, with a note on anything Ari could not carry over. |
| "Does this tie to my workbook?" (with the workbook attached) | With the built-in **Model migration tie-out** Skill turned on, a tie-out Page that compares your Model with the workbook and explains every difference. |
| "Walk me through this Page." | An explanation of what the Page you have open shows and how its numbers connect. |
| "What should I build next?" | Suggestions based on what your Model has and lacks, such as headcount from payroll or pricing detail. |
| "How do I connect payroll?" | The steps for your payroll system, from these docs, and an offer to start the connection. |
| "How can you help me understand where our cash goes?" | A plan for what to build or connect to answer that, such as expenses by vendor and department. |
| "Add a chart showing the last 12 months of revenue above this table." | A chart placed on the Page you have open. |
## Understand a number
| Prompt | What you get |
| - | - |
| "Where does this number come from? Show me the formula and the source." (with the cell added through **Mention cell in chat**) | The formula behind the cell and where its inputs came from: your data, a file, something you said, or an estimate. |
| "Why did this move so much from March to April?" | The change broken down into what drove it. |
| "Which source rows make up this number?" | The rows from your connected data behind the value. |
| "Where did the \[35% fringe rate] come from?" | The source of that assumption, or a note that Ari estimated it. |
| "List every assumption behind this forecast and which ones you estimated." | Each assumption with its source, estimates marked. |
| "What depends on \[Headcount]? List the formulas, tables, and Pages that read it." | Everything in the Model that reads the Variable. |
| "List every Variable and Dimension in the Model." | The full list, including ones no table shows. |
| "Which formula applies to this cell?" | The formula that calculates the cell, and why that one applies there. |
| "Why is this row blank?" | The reason the value does not calculate, such as a missing mapping or a formula that does not cover that period. |
| "Explain why gross margin fell in March. Don't change anything." | An answer from what the Model already holds, with nothing changed. |
See [Ask Ari about a number](/guides/ask-about-a-number).
## Forecast and plan
| Prompt | What you get |
| - | - |
| "Build a 12-month revenue forecast by customer segment. Use our history where we have it and label any assumptions." | The Variables, Dimensions, and formulas behind the forecast, each assumption named so you can change it. |
| "Build out our forecast. Calculate revenue from \[new customers, pricing, and churn] and costs from \[headcount and our biggest vendors]." | A forecast driven by the inputs you named, placed where you can see and change them. |
| "Build a headcount plan by department with loaded payroll cost." | Employees and departments, compensation, start dates, and planned hires over time. |
| "Build a P\&L, balance sheet, and cash flow statement that tie together." | A linked set of statements built from your Model and accounting data, with assumptions disclosed. |
| "Forecast monthly cash for the next 18 months and show when cash falls below six months of operating expenses." | A cash forecast with the month the threshold is crossed. |
| "Add average contract value to the model." | A new Variable for average contract value, with its formula, that the rest of the Model can use. |
| "Break revenue out by region." | A Dimension applied to Revenue, so it calculates for each region and adds up to the total. |
| "Write a formula for \[gross margin] and check it, but don't save it yet." | A check of the formula's syntax and references, with nothing saved and no values calculated. |
## Test a decision
| Prompt | What you get |
| - | - |
| "Can we hire three engineers in September? Build a hiring Scenario and show the effect on payroll, cash, and cash runway." | A Scenario with the hires modeled and their effect on each number. |
| "Create a Hiring Freeze Scenario, pause new hires through December, and compare payroll and ending cash with Main." | A Scenario and a side-by-side comparison with Main. |
| "What's different between this Scenario and Main?" | A summary of what the Scenario changed. |
| "Is this Scenario ready to merge into Main? Let me confirm before you merge." | What merging would change, and a **Merge** button you press yourself. |
| "Review my Model without changing it. Which assumptions look weakest compared with our history?" | A review of weak or missing pieces, with nothing changed. |
See [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario).
## Review the month
| Prompt | What you get |
| - | - |
| "Compare last month's Actuals with the Forecast and explain the three biggest differences." | The variances and what caused them. |
| "Compare last month's operating expenses with our budget by department. Explain the five largest differences." | A budget-versus-actuals breakdown by department. |
| "Why did software spend increase last month? Break the change down by vendor and tell me whether any new contract explains it." | The breakdown, the largest contributors, and the records behind them. |
| "Update the assumptions behind those differences and show me each change." | The changed assumptions, listed. |
| "Check my model before the board sees it." | A review of formulas, mappings, statements, and ties to your books. With the built-in **Model audit** Skill turned on, Ari also writes a short audit Page; see [Turn on a built-in Skill](/guides/turn-on-built-in-skills). |
| "Where are we this month?" | With the built-in **Flash report** Skill turned on, a read of the current month before the books close, with actuals, commitments, and estimates kept apart. |
## Choose the right visual
Ari builds charts and other visuals as Canvas blocks on a Page. Name the shape you want: "show me revenue" leaves the shape to Ari, and "show me revenue by month as a line" does not.
| When your question is | Ask Ari for |
| - | - |
| How has this moved over time? | A line chart |
| How do these categories compare? | A bar chart |
| How much accumulated, and when? | An area chart |
| What is this total made of, month by month? | A stacked bar or stacked area chart |
| Is our mix shifting, whatever the size? | A 100% stacked chart |
| What was the split in one period? | A pie or donut chart |
| Where do we lose people between stages? | A funnel chart |
| What explains the change from A to B? | A waterfall chart |
| How does a total compare with a rate? | A combo chart, with bars and a line |
| How close are we to a target? | A gauge, or a scorecard with a change figure |
| What are the few numbers I check every day? | A scorecard grid |
| What if this assumption changed? | A chart with a control, such as a slider |
| I need the actual numbers in rows and columns | A Table block, not a chart |
## Build charts and visuals
| Prompt | What you get |
| - | - |
| "Show me revenue by month as a line." | A line chart of Revenue across months, in the Scenario you are viewing. |
| "Plot revenue and expenses on the same chart." | One chart with two series and a shared time axis. |
| "Show revenue as bars with gross margin percent as a line." | A combo chart, revenue on one scale and margin on its own. |
| "Show our revenue mix as stacked bars by customer segment." | Each segment as its own series, stacking to the total. |
| "Same chart, but show each segment as a share of the total." | A 100% stacked chart, so the mix shows even while the total grows. |
| "What was our revenue split by region last quarter?" | A pie or donut chart for that one period. |
| "Build a waterfall explaining the change in revenue from Q1 to Q2." | The opening total, the changes that moved it in order, and the closing total. |
| "Show our sales funnel from lead to closed won." | A funnel with each stage's volume, in order. |
| "Give me a row of our five key numbers with change since last month." | A scorecard grid, each figure with its own change. |
| "Put a slider on this so I can try different hiring dates." | A control wired to that assumption, with the result updating as you move it. |
| "Build a board summary Page with our five key numbers, a cash runway gauge, and a slider for hiring dates." | A Page of Canvas blocks built from live Model data. |
What shapes the result:
* A pie or donut chart shows one period. For a mix over time, ask for stacked bars or a stacked area chart.
* A waterfall bridges two totals with the changes between them. For the totals themselves, ask for bars.
* A change figure is colored by what it means for the business, not by its sign, so a cost under plan reads as good news.
* A visual reads your Model and never writes to it. Moving a slider previews a result without changing a saved assumption. To change the Model, ask Ari to update the assumption, or edit the value in a Table block.
For ideas, select **Browse Canvas block library** in the message box. See [Build a dashboard or board report](/guides/build-a-dashboard).
## Change a visual or its period
| Prompt | What you get |
| - | - |
| "Make that quarterly instead of monthly." | The chart recalculated by quarter. You can ask for daily, weekly, monthly, quarterly, half-yearly, or yearly. |
| "Only show the last 12 months." | The window narrowed and the values recalculated for it. |
| "Drop the time axis and show the total by product." | A chart of categories with no date axis. |
| "Start the axis at zero." | Fixed axis bounds. |
| "Format the axis in thousands and label it." | Shorter axis labels and a title; the values themselves stay exact. |
| "Show the value on each bar." | A label with each bar's value. |
| "Turn that line chart into stacked bars." | The same data redrawn in the new shape. |
| "Narrow that chart to enterprise customers only." | The same chart updated in place, not a second chart. |
| "Change the title of this chart." | The title updated, with the data left alone. |
| "Move the cash chart above the expense table." | The block moved on the Page. |
| "I need the actual numbers behind this chart." | A Table block with the values in rows and columns. |
| "Show this side by side for Main and the Downside Scenario." | A Table block with Scenario comparison columns. |
See [Change a chart or Canvas block](/guides/change-a-visual).
## Share and report
| Prompt | What you get |
| - | - |
| "Prepare a board-reporting Page with revenue, gross margin, cash, headcount, and the largest changes since last quarter." | A reporting Page built from live Model data. |
| "Create a view-only link to the Board summary Page." | A read-only link's address. See [Share a Page outside your workspace](/guides/share-a-page). |
| "Export this Page to Excel." | A workbook of the Page as a download card in the chat. See [Download a Page as a spreadsheet](/guides/download-a-spreadsheet). |
| "Invite \[[name@company.com](mailto:name@company.com)] to the workspace." | An invitation to that person. See [Invite or remove teammates](/guides/invite-teammates). |
| "Make this workspace look like \[our website]." | A workspace brand with your colors, typeface, and logo. See [Brand your workspace](/guides/brand-your-workspace). |
| "Build a Guide walking a new analyst through this Page." | A saved Guide whose steps point at specific blocks, rows, or cells. See [Walk someone through a Page with a Guide](/guides/create-a-guide). |
## Automate recurring work
| Prompt | What you get |
| - | - |
| "Every Monday at 8am \[your time zone], summarize last week's cash in and out and flag any unusual expense. Send it to my Slack direct messages." | A Trigger on that schedule that sends you the result. See [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger). |
| "On the 5th of every month, compare last month's Actuals with the Forecast and explain the three biggest differences." | A monthly Trigger with that review. |
| "List my Triggers." | Your saved Triggers with their names and schedules. |
| "Save this as our monthly close review." | A Skill with when it applies and the steps to follow, which Ari uses next time. See [Save a workflow as a Skill](/guides/save-a-skill). |
| "Here is what I still do by hand each month: \[list]. Which of these can you do as a Skill or a Trigger? Set up the first one." | A recommendation and the first Skill or Trigger set up. |
## Teach Ari
| Prompt | What you get |
| - | - |
| "Remember that I prefer ending cash shown in thousands." | A fact saved in your own memory. |
| "For the whole workspace, remember that our fiscal year starts in February." | A fact saved for everyone in the workspace. |
| "What do you remember about me?" | The facts Ari saved for you. |
| "Forget that I prefer quarters." | That fact removed. |
See [Teach Ari your company's conventions](/guides/teach-ari).
## Related
* [Get better answers from Ari](/guides/get-better-answers)
* [How Ari works with you](/concepts/working-with-ari)
* [Chat controls and messages](/reference/chat)
# Sample Skills
Source: https://docs.cfo.ai/reference/sample-skills
Four complete Skills you can save in your workspace as they are or adapt: three-statement-buildout, forecast-simulator, headcount-from-capacity, and interactive-profitability-bridge, each with its description and full instructions.
These are four complete Skills you can save as they are, or change to fit how you work. A Skill is a saved workflow that Ari follows whenever a request matches its description. Each sample has a name, a description that tells Ari when to use it, and full instructions.
To save one, copy its name, description, and instructions into a new Skill in **Settings → Skills**, or paste them into a chat and ask Ari to save the Skill. For the steps, see [Save a workflow as a Skill](/guides/save-a-skill).
| Name | What it does |
| - | - |
| [`three-statement-buildout`](#three-statement-buildout) | Builds an Income Statement, a Cash Flow Statement, and a Balance Sheet as Table blocks on one Page, from the Variables, formulas, and mappings you already have, and checks that they tie. |
| [`forecast-simulator`](#forecast-simulator) | Builds an interactive forecast Page with sliders, where the simulated numbers match your Model exactly at the default settings and the sliders never change the Model. |
| [`headcount-from-capacity`](#headcount-from-capacity) | Plans headcount as a ratio to what your business grows on, such as accounts or stores, instead of a monthly growth rate that compounds out of control. |
| [`interactive-profitability-bridge`](#interactive-profitability-bridge) | Builds a Canvas block that walks from revenue to net income using your own income statement lines, with preview sliders that never change the Model. |
## three-statement-buildout
Builds an Income Statement, a Cash Flow Statement, and a Balance Sheet as Table blocks on one Page, from the Variables, formulas, and mappings you already have, and checks that they tie.
**Description**
```text wrap theme={null}
Build a saved page with native Income Statement, Cash Flow Statement, and Balance Sheet tables using the workspace's existing variables, formulas, and mappings. Use when creating or updating the full three-statement model, or when auditing subtotals, cash continuity, and the balance check.
```
```markdown expandable wrap theme={null}
# Three statement buildout
Build a saved page titled "9. Financial Statements" with three native model tables:
1. Income Statement
2. Cash Flow Statement
3. Balance Sheet
Use the workspace's existing variables, formulas, dimensions, mappings, source data, accounting conventions, currency, signs, scenario, and Last close. Do not assume USD, tax rates, fiscal years, or literal variable names. Resolve the closest verified existing variables and formulas first. Reuse existing model objects instead of creating duplicates.
Use the current scenario unless another is specified. Preserve unrelated pages, tables, variables, formulas, mappings, scenarios, and time settings. If the page or tables already exist, update them in place.
Use variables on rows and the system Date dimension by month across columns. Use a visible window from January 2025 through December 2030 unless another window is requested. Preserve the scenario's existing Last close and label Actuals and Forecast according to that boundary.
## Income Statement rows
Use verified organizational equivalents where necessary:
- Revenue
- Cost of Revenue
- Gross Profit
- Gross Margin
- Technology and Infrastructure
- Sales and Marketing
- General and Administrative
- Total Operating Expenses
- Operating Income
- Operating Margin
- Interest Expense Net
- Other Income Net
- Pre-Tax Income
- Income Tax Expense
- Net Income
- Depreciation and Amortization
- Stock-Based Compensation Expense
- Other Non-GAAP Adjustments
- Adjusted EBITDA
- Adjusted EBITDA Margin
- Adjusted Operating Income
- Adjusted Operating Margin
- Adjusted Net Income
## Cash Flow Statement rows
- Net Income
- Depreciation and Amortization
- Stock-Based Compensation Expense
- Customer Prepayments Received
- Deferred Revenue Recognized
- Change in Deferred Revenue
- Working Capital Intensity of Revenue Growth
- Other Working Capital Change
- Operating Cash Flow
- Cash Capital Expenditures
- Free Cash Flow
- Net Debt Draws
- Equity Issuance
- Financing Cash Flow
- Net Change in Cash
- Cash Balance
## Balance Sheet rows
- Cash Balance
- Property and Equipment Net
- Net Working Capital and Other Assets
- Lease ROU Capitalized per Leased MW
- Operating Lease ROU Assets
- Total Assets
- Total Debt
- Deferred Revenue Balance
- Operating Lease Liabilities
- Total Liabilities
- Stockholders Equity
- Balance Check
## Before writing
Inspect:
- The current page and its existing blocks
- Existing saved tables and layouts
- Variables, formulas, source metadata, formats, currencies, and preferred time bindings
- Dimensions, items, reporting grain, ranges, and Last close
- Existing account mappings and statement formulas
- Calculated values at monthly grain
## Modeling requirements
- Use native editable tables, not read-only Canvas tables.
- Use the system Date axis, not a source date column.
- Preserve source signs exactly.
- Preserve currencies, units, decimal precision, and number formats.
- Preserve Actuals and Forecast logic.
- Keep meaningful business dimensions and mappings.
- Use existing statement subtotals when available.
- Do not double-count source rollups and their children.
- Use correct monthly aggregation for flows.
- Use closing-value behavior for balance-sheet stocks such as Cash Balance.
- Calculate margins only when the denominator exists and is nonzero.
- Do not invent tax rates or unsupported relationships.
- Keep observed source data, formulas, and editable assumptions distinct.
- If a requested line is unavailable, omit it and explain why.
- Only create a clearly labeled editable assumption when it is necessary and useful to complete the report.
## After saving
Verify:
- All three tables exist on the page.
- The monthly Date axis and requested window are correct.
- Actuals and Forecast periods align with Last close.
- Income statement subtotals and margins calculate correctly.
- Cash flow subtotals and Cash Balance continuity are supported.
- Balance Sheet totals and Balance Check reconcile.
- Early, middle, and late periods have been checked.
- Unexpected zeros, blanks, and unchanged values have been traced to their formula origins.
- Any missing mappings, source gaps, omitted rows, formula gaps, assumptions, or reconciliation residuals are reported.
## Final response
Include:
- Links to the saved page and tables
- Selected scenario and reporting window
- Currency and formatting basis
- The verified variable used for each statement line
- Omitted lines and reasons
- Assumptions, mappings, formula gaps, and residuals
- Whether Actuals/Forecast boundaries were verified
- Whether the balance check and cash-flow reconciliation passed or remain unverified
Do not create a Canvas visualization unless separately requested.
```
## forecast-simulator
Builds an interactive forecast Page with sliders, where the simulated numbers match your Model exactly at the default settings and the sliders never change the Model.
**Description**
```text wrap theme={null}
Build an interactive forecast tool whose scenario ties exactly to the model baseline. Use for what-if pages, scenario explorers, or any page with controls over model variables.
```
```markdown expandable wrap theme={null}
# Forecast simulator
## The requirement that matters
At default slider settings the simulated series must sit **exactly** on the baseline. If
it drifts, every delta the user reads is contaminated by model error and the tool loses
credibility.
Two ways to achieve it, in order of preference:
**A. Re-run the recurrence.** Rebuild the series in component code using the model's own
inputs and the scenario control. At the control's default value this reproduces the model
exactly because it is the same recurrence:
const mwS = [];
for (let i = 0; i < n; i++) {
if (i === 0) { mwS.push(mwB[0] || 0); continue; }
const contPrev = (contB[i-1] || 0) * Math.pow(live.contract, i-1);
mwS.push(mwS[i-1] + Math.max(0, contPrev - mwS[i-1]) * live.act);
}
**B. Ratio against baseline.** Where a recurrence is impractical, scale the baseline by
the ratio of scenario to default:
const g = ((1 + live.traffic) * (1 + live.price)) / ((1 + D.traffic) * (1 + D.price));
const sim = base[i] * Math.pow(g, i);
At defaults `g === 1`, so `sim === base`.
Read cost rates, life, and load factors from a `rates` dataset rather than hardcoding, so
the component stays in sync when the model changes.
## Dataset rules
- **One breakdown per dataset.** Multiple variables each broken down by the same dimension
in a single dataset collapses the duplicate child labels — you get the totals and one set
of children, and the rest render blank. Split into one dataset per variable.
- Max 8 datasets per block. Check `dataset_evidence` in the write receipt: `series_count`
and `series_names` tell you what actually came back.
- A series with empty `segments` is the variable **total**; populated `segments` are
breakdown items. Filter to `segments.length` before stacking, or the total
double-counts against its children.
- `layout.rows` become series; `layout.columns` (Date) become plotted points.
## Component contract
- `Block({ data, state, emit })`. React and its hooks are lexical bindings — no imports,
no `window.React`.
- `Slider` fires `onChange(v)` for a local draft and `onCommit(v)` for an accepted change.
**Persist only on commit** via `emit({ type: "setState", patch: { key: v } })`.
- Sliders must never write to the model. The scenario is a local preview.
- Provide a reset that restores defaults and clears saved state (`patch` values to `null`).
- Show a status pill so the viewer always knows whether they're looking at baseline or a
scenario.
## After writing
Saving a Canvas block does not check that it renders. Always run `validate_code_blocks` on the
block id — it renders in Chromium in light and dark and returns real runtime errors. Fix
anything it reports before calling the work done.
## Chart selection
Before shipping charts, check whether any two are the same curve rescaled. If per-unit
economics are uniform across a dimension, then "units by dimension", "revenue by
dimension", and "profit by dimension" are one shape drawn three times. Replace duplicates
with **ratios that diverge** — cost as % of revenue, support headcount per 100 units,
profit per employee. A quotient of two things growing at different rates carries
information a level chart cannot. Every chart must have an explicit, descriptive title
inside the chart configuration.
```
## headcount-from-capacity
Plans headcount as a ratio to what your business grows on, such as accounts or stores, instead of a monthly growth rate that compounds out of control.
**Description**
```text wrap theme={null}
Model headcount as a ratio to the business activity that sets it, not a growth rate. Use for hiring plans, workforce forecasts, personnel cost models, or org plans.
```
```markdown expandable wrap theme={null}
# Headcount from capacity
## Why not a growth rate
A monthly hiring rate compounds. Fitting one to a fast-growth year and running it 48
months produces absurd totals — a 7.7%/month rate that matches one year of real hiring
yields 30,000 employees four years out.
Anchor headcount to whatever the business actually scales on: megawatts, stores,
shipments, accounts, ARR.
## Structure
`Headcount per Unit` [Department] — input, seeded at the first period
`Headcount` [Department] = `Headcount per Unit` * `Total Units`$[Date.Month = this.Date.Month]
`Average Salary` [Department] — input constants
`Personnel Cost` [Department] = `Headcount` * `Average Salary` / 12 * `Payroll Load Multiplier`
`Headcount` needs no separate parent and child formulas — the parent computes correctly as parent-ratio ×
units. `Personnel Cost` does: parent is a sum, children are the explicit product.
Note the `$[Date.Month = this.Date.Month]` on the units read. The unit total lives outside
Department space; without it, the exact-reach seed on the spine won't reach a
Department-coordinated cell and every row reads zero.
## Let the ratio move
A flat ratio understates early scaling and overstates late. Give the ratio its own
decaying trend:
`Ratio Growth` = `Ratio Growth`[-1] * `Ratio Decay` // seeded positive, decay ~0.94
`Headcount per Unit` [Dept] = `Headcount per Unit`[-1] * (1 + `Ratio Growth`$[Date.Month = this.Date.Month])
This reproduces a hiring surge early and flattens into operating leverage later, without
exploding. Tune the seed to hit year-1 actuals and the decay to keep the terminal ratio sane.
## Diagnostics worth surfacing
- **Revenue per employee** — sanity check against comparables.
- **Units per employee** — the productivity trend.
- **Support ratio** (corporate heads per 100 units) — whether the org is outgrowing what
it serves.
- **Profit per corporate employee** — returns on overhead headcount. This one often
declines, and saying so is more useful than hiding it.
## Reconcile to the P&L
If the P&L carries labor as a % of revenue, either drive that line from this build
(`Units * Cost per Unit`) or add an explicit reconciliation row showing the implied
percentage. Never let a bottom-up headcount plan sit beside a top-down labor percentage
with no tie between them.
```
## interactive-profitability-bridge
Builds a Canvas block that walks from revenue to net income using your own income statement lines, with preview sliders that never change the Model.
**Description**
```text wrap theme={null}
Build a reusable interactive Canvas visual from the organization's existing income statement, with local preview controls that never write back to the model. Use for what-if profitability views, scenario walkthroughs of the income statement, or margin bridges.
```
```markdown expandable wrap theme={null}
# Interactive profitability bridge
Create a custom workspace skill called `interactive-profitability-bridge`.
The skill should create or update a reusable interactive Canvas visual based on the organization's existing income statement. It should work across different organizations and must not assume specific line-item names such as Subscription Revenue, Total COGS, Gross Profit, EBIT, or Net Income.
## When the skill runs
1. Inspect the current page, saved income statement tables, variables, formulas, dimensions, mappings, reporting grain, currency, and Last close.
2. Identify the organization's available equivalents for:
- Revenue or total income
- Direct costs or cost of sales
- Gross profit or contribution profit
- Operating expenses
- Operating profit or EBIT
- Pretax profit
- Tax expense
- Net income or net profit
3. Prefer variables already used by the organization's saved income statement.
4. Preserve the organization's actual labels, formulas, accounting signs, currency, number formats, dimensions, and Actuals/Forecast logic.
5. If a stage is unavailable, omit it instead of inventing a value. Do not use generic tax rates, hardcoded currencies, or assumed financial categories.
## Canvas block
Create a saved Canvas block titled "Interactive Income Statement Funnel" or an equivalent organization-specific title.
The Canvas visual should:
- Connect to a live dataset containing the supported statement variables and system Date.
- Allow the user to select an available reporting period at the statement's existing grain.
- Identify the selected period as Actuals or Forecast using Last close.
- Show supported profitability stages in order:
- Top-line revenue or income
- Gross or contribution profit
- Operating profit
- Pretax profit
- Net income or net profit
- Display each stage's organization-specific label, value, percentage of the top-line measure, proportional bar, and change from baseline when a preview is active.
- Distinguish missing values from zero.
- Display losses and negative values honestly.
- Show a useful empty state when no supported periods or values are available.
## Preview controls
Add local, non-persisted preview controls only when the required model stages exist:
- Increase revenue or income
- Reduce direct costs
- Reduce operating expenses
The controls should:
- Start at zero.
- Use bounded sliders with clear labels.
- Use the organization's terminology.
- Recalculate downstream supported stages using the same relationships as the income statement.
- Preserve non-operating income and expense unless a specific control exists for them.
- Preserve the selected period's tax treatment when tax is available.
- Omit unsupported controls or outputs.
- Include a reset-to-baseline action.
- Never write values, formulas, assumptions, or scenario changes to the model.
Show local impact summaries for supported outputs, such as top-line, gross or contribution profit, operating profit, and net income. Clearly label these as local preview impacts, not approved forecasts or model changes.
## Components and state
Use cfo.ai Canvas components and theme tokens. Use injected components such as Select, Slider, Button, Card, Text, NumberText, Delta, and appropriate chart or visualization components. Do not use hardcoded USD formatting, a 21% tax rate, FY2026, company-specific variable names, replacement color palettes, or native HTML controls where an injected component is available.
Store only presentation state in Canvas state, such as the selected period and preview-control values. Keep actual model data read-only.
## Validation
After saving, validate the Canvas block in light and dark themes. Test:
- Baseline values with all controls at zero
- Period selection
- Actuals and Forecast labeling
- Each enabled preview control
- Negative profit or loss
- Missing optional stages
- Missing values versus zero
- Reset behavior
- Confirmation that no model values or formulas changed
## Final report
In the final response, report the saved Canvas block, the variables mapped to each profitability stage, omitted stages, preview assumptions, and any unresolved ambiguity or data gap.
```
## Related
* [Save a workflow as a Skill](/guides/save-a-skill)
* [Skills, Triggers, and Guides](/concepts/automation)
* [Turn on a built-in Skill](/guides/turn-on-built-in-skills)
# Scenario and version history controls
Source: https://docs.cfo.ai/reference/scenarios-and-history
Every control, label, and message for Scenarios (versions of your plan), Snapshots, and version history: the Scenario picker and its actions menu, the Take snapshot and merge dialogs, and the version history panel where you undo a change or restore an earlier version.
Scenarios, Snapshots, and version history each have their own controls: the Scenario picker in the sidebar, the **Take snapshot** and merge dialogs, and the version history panel. This page lists every control and message, in the order the app shows them. For what each one is for, see [Scenarios, Snapshots, and version history](/concepts/scenarios).
## Scenario picker
The Scenario picker is the **Main Scenario** control in the sidebar's **Model** section, above the list of Pages. It shows the Scenario you are viewing. Selecting another Scenario switches every Page to it and changes the address bar, so you can bookmark the address or share it with a teammate.
| Control | What it does |
| - | - |
| A Scenario's row | Switches to that Scenario. |
| **Open actions for \[Scenario]** (gear icon on a row) | Opens the [Scenario actions menu](#scenario-actions-menu). |
| **Take snapshot** | Opens the [Take snapshot dialog](#take-snapshot-dialog) for the Scenario you are viewing. Not shown while you view a Snapshot. |
| **Create scenario** | Adds a row where you type the new Scenario's name. **Enter** creates it from Main and switches to it. |
| **Merge** (beside the picker, while you view a Scenario) | Opens the [merge dialog](#merge-dialog). Its tooltip reads **Merge all changes into \[target]**, **No changes to merge**, or **Checking for changes…**. Not shown for Main or a Snapshot. |
You must have permission to create Scenarios to see **Take snapshot** and **Create scenario**. Neither is offered on a phone.
### Scenario actions menu
| Control | What it does |
| - | - |
| **Version history** | Opens the Scenario's [version history panel](#version-history-panel). |
| **Last close** | Sets the Scenario's Last close date, the boundary between Actuals and Forecast. |
| **Rename** | Renames the Scenario. Main cannot be renamed. |
| **Delete** | Deletes the Scenario. Main, and a Scenario that has child Scenarios, cannot be deleted. |
Items you do not have permission for are not shown.
## Take snapshot dialog
The dialog is titled **Take snapshot**, with **Keep a version for an approved budget or board meeting.** It says what it pins: **Pins \[Scenario] at its latest saved version.**, or **Pins \[Scenario] at \[time].** when you open it from a version history entry. Then it reads **Later changes and integration syncs will stop flowing into this snapshot. You can still edit the snapshot manually.**
| Control | What it does |
| - | - |
| **Snapshot name** | The new Snapshot's name. It starts as **\[Scenario] snapshot**. |
| **Create snapshot** | Creates the Snapshot. Reads **Creating…** while it saves. |
| **Cancel** | Closes the dialog. |
## Merge dialog
The dialog is titled **Merge "\[Scenario]" into \[target]?**. The target is the Scenario this one branched from, usually Main. It reads **Every change in "\[Scenario]" moves into \[target]:**, followed by a summary of the changes across the Model, and the note **"\[Scenario]" is deleted after the merge.**
| Control | What it does |
| - | - |
| **Merge \[n] changes** (or **Merge scenario**) | Merges the Scenario. Stays off while the summary refreshes, and when there is nothing to merge. |
| **Cancel** | Closes the dialog. |
| Message | Meaning |
| - | - |
| **scenario cannot be merged while it has \[n] child scenario**, followed by the children's names | The Scenario has editable child Scenarios, so the merge is refused. Snapshots taken from the Scenario do not block a merge. |
## Confirmation cards in the chat
When you ask Ari to merge, delete, or revert a Scenario and to let you confirm first, Ari posts a card with a button you press yourself. See [Scenario confirmation card](/reference/chat#scenario-confirmation-card).
## Version history panel
The version history panel lists every change to the Model, yours and Ari's. Open it with **Version history**, the clock icon in the Page header, or from a Scenario's actions menu.
| Control | What it does |
| - | - |
| **Filter version history** | Shows or hides changes by person; by **Ari**, **Data syncs**, and **Imports**; and by kind: **Formulas and values**, **Variables and dimensions**, **Pages and blocks**, **Scenarios**, and **Settings**. |
| **Jump to date** | Moves the list to a day. |
| **Close version history** | Closes the panel. |
| Scenario line | Names the Scenario and shows **Live**, or the version you are viewing. |
| **Show Main Scenario changes** | In a Scenario other than Main, also lists the changes it inherited from Main. |
Each entry names who made the change, when, and what it touched. A formula entry names the Variable. For a formula with a condition, the entry also names the condition and its reach: **includes additional dimensions** when it also covers cells that carry more Dimensions, or **exact grain** when it matches exactly the Dimensions it names.
### Entry actions
Point at an entry and select its **…** button (**Actions for \[entry]**).
| Control | What it does |
| - | - |
| **Undo this change** | Reverses this change and keeps everything after it. Asks **Undo this change?** when later changes depend on it. Not offered for a change the Scenario inherited from Main. |
| **Redo this change** | Shown instead of **Undo this change** on an entry that undid a change. |
| **Restore this version** | Returns the Scenario to this point and rolls back every later change. Needs full access to the Scenario; otherwise its tooltip reads **Only people with full access to this scenario can restore it.** |
| **Take snapshot here** | Opens the [Take snapshot dialog](#take-snapshot-dialog), pinned at this entry. |
| **Copy link to version** | Copies a link that opens the Model as it was at this entry. |
**⌘ Z** undoes only your own last change in the current browser tab; see [Keyboard shortcuts](/reference/keyboard-shortcuts#undo-and-redo).
### Viewing a past version
Selecting an entry shows the Model as it was at that moment, read-only, with a banner that offers **Restore this version** and **Back to current**. The restore dialog previews the result.
| Message | Meaning |
| - | - |
| **Loading restore preview…** | The preview is loading. |
| **This is already the current version.** | Restoring would change nothing. |
| **Changes made in Main Scenario stay as they are.** | Shown in a Scenario other than Main. Restoring leaves the changes made in Main as they are. |
| **Restored to \[time]** | The restore finished. |
| **Couldn’t restore this version** | The restore failed. |
## Related
* [Scenarios, Snapshots, and version history](/concepts/scenarios)
* [Test a decision in a Scenario](/guides/test-a-decision-in-a-scenario)
* [Merge a Scenario into Main](/guides/merge-a-scenario)
* [Save a plan as a Snapshot](/guides/save-a-snapshot)
* [Undo a change or restore an earlier version](/guides/undo-changes)
# Settings
Source: https://docs.cfo.ai/reference/settings
Every section and field in cfo.ai Settings, in the order the app shows them: General, Workspace, Profile, Ari, Connections, Skills, Billing, and Usage. Covers the workspace name and brand, Last close and date defaults, light or dark mode, inviting teammates, who sees each section, its messages, and what Settings has no setting for, such as a fiscal year or an API key.
Settings holds the workspace's options and your own preferences. To open it, select your name or picture at the bottom of the sidebar, then **Settings**. Settings opens on **General**, with the sections listed down the left side in this order.
| Section | Who sees it | What it holds |
| - | - | - |
| **General** | Everyone | The workspace name and brand, date defaults and Last close, and your appearance |
| **Workspace** | Administrators | Inviting, removing, and listing administrators |
| **Profile** | Everyone | Your name and sign-in email |
| **Ari** | Everyone | Ari's thinking animation, sound, notifications, and Slack |
| **Connections** | Everyone, once the workspace has started its trial or a plan | The cfo.ai MCP server address, and the MCP servers and APIs Ari can call |
| **Skills** | Everyone | Your own Skills, wherever they apply, and the workspace's optional built-in Skills |
| **Billing** | Workspaces billed in the app | Plan, payment methods, billing history, and cancellation |
| **Usage** | Workspaces billed in the app | AI usage, extra usage, spend limit, auto-reload, and integration slots |
Every member of a workspace is an administrator, so everyone sees **Workspace**. A workspace billed outside the app has no **Billing** or **Usage** section.
## General
### Workspace name
The **Workspace** section of **General** holds the workspace's name.
| Control | What it does |
| - | - |
| **Name** | The workspace's name. **Save** saves a new name. Renaming also changes the workspace's web address: bookmarks to the old address stop working, and share links keep working. |
| Message | Meaning |
| - | - |
| **Workspace renamed** / **Your workspace is now “\[name]”.** | The new name is saved. |
| **Couldn’t rename workspace** | The rename failed; the reason follows. |
| **Only admins can change the name.** | You cannot change the name. |
| **I’m finding a better name for your workspace — you can pick one yourself anytime.** | cfo.ai is naming a new workspace from your company's details. |
### Brand
A brand gives the workspace a name, logo, colors, and heading typeface that everyone who opens it sees, including people reading a shared Page. See [Brand your workspace](/guides/brand-your-workspace).
| Control | What it does |
| - | - |
| **Website address** and **Build from a website** | Shown with no brand. Opens a chat in which Ari builds a brand from the website you typed. |
| **Set up by hand** | Shown with no brand. Opens the brand form. |
| Brand form | **Name** (up to 40 characters), **Logo**, **Logo for dark mode**, **Light mode colors** and **Dark mode colors** (**Page surface**, **Panel surface**, **Text**, **Accent**), **Heading typeface** (**cfo.ai default**, **Source Serif**, **Newsreader**, **Fraunces**, **Zilla Slab**, or **DM Sans**), and **Corner shape** (**Square**, **Soft**, or **Round**), with **Save** and **Cancel**. |
| **Edit** | Shown with a brand. Opens the brand form. |
| **Copy to other workspaces** | Shown with a brand. Copies it to workspaces you choose. |
| **Remove brand** | Shown with a brand. After you confirm, everyone sees their own theme again. |
Someone who cannot change the brand sees **This workspace uses the** \[name] **brand. Workspace admins set the brand.**
### Date and time
The section is headed **Date & Time**. Its settings belong to the Scenario you are viewing in the sidebar's Scenario picker. A Scenario follows Main's settings until someone changes them in that Scenario. See [Set Last close and date defaults](/guides/set-dates-and-last-close).
| Control | What it does |
| - | - |
| **Default date granularity** | The period new tables show: **Raw**, **Day**, **Week**, **Month**, **Quarter**, **Half**, or **Year**. **Month** unless changed. Last close snaps to the end of a period at this granularity. |
| **Default date range** | The first and last dates new tables show. A new workspace starts from January 1 of the year six months ago through December 31 of the year six months ahead. Existing tables keep their own range. |
| **Last close** | The end of the last period you closed, your month-end. Periods up to it are Actuals, and later periods are Forecast. Pick a period; cfo.ai saves its last day. A new workspace starts at the last day of the previous calendar month, and Last close never moves on its own. See [Actuals, Forecast, and Last close](/concepts/actuals-and-forecast). |
There is no fiscal-year setting: quarters, half-years, and years follow the calendar year, January to December. See [Fiscal years](/concepts/actuals-and-forecast#fiscal-years-quarters-and-years-follow-the-calendar).
| Message | Meaning |
| - | - |
| **Loading…** / **Couldn’t load** | Shown in **Last close** while it loads, or when it cannot be read. |
| **Failed to save date settings** | The change was not saved; the settings return to what is saved. |
| **Failed to load date settings** | The settings could not be read. Reopen Settings. |
### Appearance
| Control | What it does |
| - | - |
| **Color mode** | Light or dark mode: **Auto**, **Light**, or **Dark**. **Auto** follows your device's setting. Saved in this browser. The account menu's **Appearance** item sets the same thing. |
| **Theme** | **Warm paper**, cfo.ai's own theme, **Prism**, **Pid Girl**, **Hot Dog Stand**, **50s CFO**, **Columnar Pad**, **Spreadsheet**, or **Custom**. Saved to your account and seen only by you. |
| **Create a custom theme** box and **Personalize** | Shown when **Theme** is **Custom**. **Personalize** opens a chat where Ari builds a theme from what you typed. See [Change your theme, sounds, and notifications](/guides/personalize-cfoai). |
In a workspace with a brand, **Theme** is replaced by **This workspace uses the** \[name] **brand. Your own theme applies in workspaces without a brand.** If a theme change cannot be saved, a notice reads **Failed to save theme**.
## Workspace
### Invite admin
**Invite admin** adds a teammate to the workspace. The section reads **Admins can see and edit all pages in the workspace, invite others, and manage billing and other settings.** See [Invite or remove teammates](/guides/invite-teammates).
| Control | What it does |
| - | - |
| **Email address** | The address to invite. |
| **Add admin** | Creates the invitation and emails it. The form then reads **Admin invitation created for** \[address]. Adding an address that already has a pending invitation sends the email again. An address that already belongs to the workspace is refused. |
cfo.ai has no other role to invite someone to: every invitation is for an administrator. For read-only access, use a share link; see [Share a Page outside your workspace](/guides/share-a-page).
### Admins
| Control | What it does |
| - | - |
| **Ask Ari** | Starts a chat in which Ari lists the people in the workspace and the pending invitations, then asks what you want to change. Ari changes nothing until you answer. |
| A row | A person's name and email, marked **(you)** for you, with **Admin** for someone who joined or **Invited** for a pending invitation. |
| **Remove member** | On an **Admin** row's menu. Opens **Remove member?**: **They will lose access to this workspace. Any pending invitations for this email will also be cancelled. Their other workspaces are unaffected.** Your own row has no menu. |
| **Cancel invitation** | On an **Invited** row's menu. Opens **Cancel invitation?**: **They will no longer be able to join through this invitation. If they have already joined, their membership stays unchanged.** |
| Message | Meaning |
| - | - |
| \[email] **was removed from this workspace.** | The person was removed, and the list reloaded. |
| \[email] **invitation was cancelled.** | The invitation was cancelled, and the list reloaded. |
| **Couldn’t load the admins of this workspace. Please try again.** | The list could not load. |
## Profile
| Control | What it does |
| - | - |
| **Profile** | Your picture, from the account you sign in with. |
| **Name** | Your name. **Edit** opens the field, and **Save** saves it. |
| **Email** | The email you sign in with. **Edit** opens **Change email**, with **New email** and **Send verification code**, then a **Verification code** field under **Verify your new email** for the code sent to the new address. cfo.ai may also ask for a **Sign-in code** under **Verify it’s you**. Your email changes only after the new address is verified. |
| Message | Meaning |
| - | - |
| **Name updated** / **Could not update name** | The name change was saved, or it failed. |
| **Email updated** | The new email is verified and saved. |
## Ari
The sections appear in this order.
### Thinking animation
| Control | What it does |
| - | - |
| **Show Ari's edits as they happen** | On by default. **Ari points at each change on the page and explains it in a short note. When off, only the finished edits are marked.** Saved in this browser. See [Review Ari's formula changes](/guides/review-ari-formula-changes). |
### Sound
| Control | What it does |
| - | - |
| **Play sound when Ari is done** | Plays a short sound when Ari finishes a reply. |
| Volume | **Quiet**, **Normal**, or **Louder**. |
| **Alert sound** | **Default** or **Beep Me**. |
Each change plays the sound. These settings are saved in this browser.
### Notifications
Shown in browsers that support notifications.
| Control | What it does |
| - | - |
| **Notify me when Ari finishes in this browser** | Shows a browser notification when Ari replies, or needs your answer, in a chat you are not looking at. **Applies to this browser only. Ari stays quiet while you’re looking at the chat.** |
When the browser blocks notifications, the switch is unavailable and reads **Notifications are blocked for cfo.ai. Allow them in your browser’s site settings to turn this on.** For the notifications themselves, see [Chat controls and messages](/reference/chat#notifications-when-ari-finishes).
### Slack
| Status | What the section shows |
| - | - |
| Not connected | **Add Ari to Slack** with **Add to Slack** and **Enable messages to and from Ari via DM.** While Slack's sign-in window is open, the button reads **Cancel**. |
| Connected | **Connected to** \[Slack workspace] and **Ari can now DM you in this Slack workspace.**, with **Disconnect**. |
| Disconnecting | **Disconnect "\[Slack workspace]"?** with **cfo.ai Agent will no longer be able to post to this workspace until you reconnect.** |
Anyone whose Slack profile email matches a member of the workspace can message Ari in Slack, and Ari works as that person, in direct messages only. See [Talk to Ari from Slack](/guides/use-ari-in-slack).
## Connections
| Control | What it does |
| - | - |
| **Connect via MCP** | The cfo.ai MCP server address, `https://api.cfo.ai/mcp`, with **Copy cfo.ai MCP URL**. AI tools such as Claude or Codex connect to it and sign in through the browser; cfo.ai issues no API key. See [Connect Claude, ChatGPT, Codex, or another AI tool to cfo.ai](/guides/connect-an-ai-client). |
| **Add connections** | Tabs **MCP** and **APIs** for other services whose tools Ari can call. Each tab offers **Add manually** and **Add with Ari**. See [Give Ari access to another service with MCP or an API](/guides/give-ari-another-service). |
While Ari cannot be used, for example when the workspace is out of AI usage, **Add with Ari** is unavailable, and a line reads **Add with Ari is unavailable.** followed by the reason. Data sources such as QuickBooks are connected from **Connections** in the sidebar; see [Connect QuickBooks, Xero, NetSuite, or Rillet](/guides/connect-accounting).
## Skills
The section reads **Teach Ari workflows you repeat. Your skills go with you to every workspace you work in.** Your Skills belong to you: every workspace where you are a member shows the same list, and nobody else sees it. See [Save a workflow as a Skill](/guides/save-a-skill).
| Control | What it does |
| - | - |
| **Write manually** | Opens the Skill editor, headed **New skill**. |
| **Build with Ari** | Opens a new chat that starts with **Help me build a reusable skill.** Unavailable while Ari cannot be used. |
| **Search skills** | Filters both lists by name and description. |
### Your skills
Each of your Skills shows its name, its description, where it applies, **Edit**, and **Delete**. The list includes Skills limited to your other workspaces. With none saved, the list reads **No skills yet. Build one with Ari by describing a workflow, pasting instructions, or attaching a Markdown file.** The heading **Your skills** shows when the workspace offers built-in Skills below it.
| Where it applies | Meaning |
| - | - |
| **All your workspaces** | Ari follows the Skill in every workspace where you are a member. |
| **Only in** \[workspace] | Ari follows the Skill only in that workspace. |
| **Not in use: you're no longer a member** | The Skill is limited to a workspace you left. Ari follows it there again if you rejoin. You can still edit it, change **Use in**, or delete it. |
| Editor field | Note in the editor |
| - | - |
| **Name** | **Lowercase letters, digits, and hyphens. The name can't be changed later.** |
| **Description** | **Phrase it as an activation trigger ("Use when…") — Ari uses it to decide when the skill applies.** |
| **Instructions** | **Markdown. The full workflow, steps, and conventions Ari should follow when the skill activates.** |
**Use in** offers **All your workspaces**, the default for a new Skill, and **Only in** one workspace: the Skill's own workspace when it already has a limit, otherwise the workspace you are in. The editor saves with **Create skill** for a new Skill and **Save** for an existing one. **Delete** opens **Delete** \[name]**?** with **Ari will stop using this skill in all your workspaces.** or, for a Skill limited to one workspace, **Ari will stop using this skill in** \[workspace]**.**
| Message | Meaning |
| - | - |
| **Name must be 1-64 lowercase letters or digits, with single hyphens between them (e.g. "board-report").** | The name breaks the naming rule. |
| **Description is required.** / **Instructions are required.** | A field is empty. |
| **Description must be at most 1024 characters.** / **Instructions must be at most 49,152 characters.** | A field is too long. |
| **A skill with this name already exists** | One of your Skills that applies here uses the name, or it is a built-in Skill's name. |
| **You have another skill with this name in another workspace. Choose another name, or keep this one in one workspace.** | A Skill for all your workspaces cannot share a name with one of your Skills limited to a workspace. |
| **You can have at most 100 skills. Delete one first.** | You have 100 Skills across all your workspaces. |
| **Only members of a workspace can save skills there** | The Skill is limited to a workspace you are not a member of. |
| **Failed to load skills** / **Failed to delete skill** | The list or a deletion failed. Try again. |
### Built-in skills
The section reads **Enable ready-made workflows for everyone in this workspace. Skills stay up to date with improvements from cfo.ai.** Each optional built-in Skill has a switch: **Model audit**, **Flash report**, and **Model migration tie-out**. A switch applies to everyone in the workspace. Built-in Skills cannot be edited. See [Turn on a built-in Skill](/guides/turn-on-built-in-skills).
## Billing and Usage
**Billing** shows **Current plan**, **Payment methods**, **Billing history**, and **Cancel subscription**. **Usage** shows **Included plan usage**, **Extra usage**, and **Integrations**. Every field and message is on [Plans and billing](/reference/plans-and-billing).
## What Settings has no setting for
* A fiscal year that starts in a month other than January. See [Fiscal years](/concepts/actuals-and-forecast#fiscal-years-quarters-and-years-follow-the-calendar).
* A view-only or other lower role, access to single Pages for one person, or user groups. Use a share link for read-only access; see [Sharing and access](/concepts/sharing-and-access).
* An API key or access token for cfo.ai. AI tools connect to the MCP server and sign in through the browser; see [Is there a cfo.ai API or API key?](/guides/connect-an-ai-client#is-there-a-cfoai-api-or-api-key)
* Deleting a workspace or an account. See [Cancel your subscription or delete your data](/guides/cancel-or-delete).
* Pausing a subscription. See [Plans and billing](/reference/plans-and-billing#rules-and-limits).
* Moving Last close automatically. Ask Ari, or schedule a Trigger that does it; see [Schedule a recurring report with a Trigger](/guides/schedule-a-trigger).
* Folders or groups for workspaces. See [Create a second workspace or switch workspaces](/guides/manage-workspaces).
## Related
* [Plans and billing](/reference/plans-and-billing)
* [The cfo.ai interface](/reference/interface)
* [Limits and file types](/reference/limits)
# Table block controls and messages
Source: https://docs.cfo.ai/reference/table-blocks
Every control in a Table block: the header, the Table block settings menu, the setup panel, the right-click menus for Variables, Dimensions, items, and cells, format options (currency, percentages, decimal places) and date formats, total columns, what colors and marks mean, what you can type into a cell, where to open the formula editor, every notice when a change is not saved, and what a table cannot do, such as cell comments or numbers in thousands.
This page lists every control, menu action, option, and message of a Table block, in the order the table shows them. For steps, see the guides linked in each section. Keyboard shortcuts are in [Keyboard shortcuts](/reference/keyboard-shortcuts#table-cells).
Editing controls appear only when you can edit the Page, in a desktop browser. A phone, a past version opened from version history, and a Page you can only read show the table without them.
## Table header
The strip above the grid, from left to right.
| Control | What it does |
| - | - |
| **Show table setup** / **Hide table setup** | Opens or closes the [table setup panel](#table-setup-panel). Whether it is open is remembered in your browser. |
| Table title | Click to rename the table. An unnamed table reads **New Table Block**. A new table gets a suggested title when you first add rows or columns. |
| **Regenerate title** | A sparkle button that appears when you point at the title. Asks cfo.ai for a new title from what the table shows. |
| **Deleted property reference** | A warning triangle. The table still lists a Variable or Dimension that was deleted; its tooltip reads **This table references a property that no longer exists.** With several, it reads **\[N] deleted property references** and its tooltip counts them. Select it to open the setup panel, where each such row reads **Deleted property**. |
| **Recomputing** | Shows while the table recalculates after a change. |
| Date range button | Shows the first and last period. Opens a calendar; a complete range applies at once. **Today** jumps the calendar to the current period. |
| Grain button | **Raw**, **Daily**, **Weekly**, **Monthly**, **Quarterly**, **Half-yearly**, or **Yearly**. Reads **Mixed** when the table's Date axes use different grains. **Raw** shows each date without grouping it into periods. |
| **Compare** | Shows other Scenarios or an earlier period beside the table's values. See [Compare Scenarios side by side](/guides/compare-scenarios). |
| **Mention table in chat** | Adds the table to the current chat. See [Chat controls and messages](/reference/chat). |
| **Table block settings** (**…**) | Opens the [settings menu](#table-block-settings-menu). |
On a phone, the header shows the title, the date range, **Mention table in chat**, and **Table block settings**.
## Table block settings menu
The **…** button at the right of the table header.
| Control | What it does | Saved |
| - | - | - |
| **Show formula bar** | Shows a strip above the grid with the selected cell's formula and where it applies. Select it to edit that formula. | In your browser |
| **Show total columns** | Shows the [total column](#total-columns) at the end of each column group. On when a table opens. | Not saved |
| **Pin total columns** | Keeps total columns beside the row names while you scroll. Available while total columns show. | Not saved |
| **Show dimension headers** | Shows a label row, such as **DEPARTMENT**, above each Dimension's items. On by default. | With the table, for everyone |
| **Auto-fit all columns** | Sizes every column to its widest content. | With the table, for everyone |
| **Show formula columns** | On a phone only: shows the formula columns. | In your browser |
## Table setup panel
Opened with **Show table setup**. It has four sections, from top to bottom: Layout, Formula, Columns, and Rows.
### Layout section
| Control | What it does |
| - | - |
| **Open Layout Options** (**…**) → **Force refresh** | Calculates the table's values again. |
| **Open Layout Options** (**…**) → **Show all formulas** | Opens **All Formulas**, which lists the formulas saved in the Model, each with its Variable, where it applies, and when it was created and last changed. **Delete formula** removes one. An empty list reads **No formulas found**. |
### Formula section
| Control | What it does |
| - | - |
| **Show all formula columns** / **Hide all formula columns** | Opens or closes every formula column. |
| **Pin formula column** / **Unpin formula column** | Keeps the formula column beside the row names. |
| **Formula settings** → **Show formula bar** | The same setting as in **Table block settings**. |
| **Formula settings** → **Show formula columns** | Shows the formula columns. Unavailable, with **Formula columns require Variables in Rows or at the root of a Column group.**, when the layout cannot show them. |
| **Formula settings** → one checkbox per range, such as **Actuals** and **Forecast** | Shows or hides that range's formula column. |
Which formula columns are open is remembered in your browser. See [Use different formulas for Actuals and Forecast](/guides/actuals-and-forecast-formulas).
### Columns and Rows sections
Each section lists what is on that axis as a tree. Drag an item to reorder it, to nest it under another, or to move it between **Columns** and **Rows**.
| Control | What it does |
| - | - |
| **+** (**Add Column**, **Add Row**) and **Add** | Opens the [Create or search field](#add-rows-and-columns-the-create-or-search-field) for that axis. |
| **…** (**Open Column Options**, **Open Row Options**) → **Reset columns** / **Reset rows** | Removes every column, or every row, from the table. |
| Eye button, **Hide \[name]** / **Show \[name]** | Hides or shows that item for everyone who opens the table. Hidden items keep their settings. |
| **Sort** | **None**, **Ascending**, **Descending**, or **Manual**, for a Dimension other than Date. |
| Funnel button, **Filter \[name]** | Opens the item list: **All Items**, the items, **Cancel**, and **Save**. For a Dimension other than Date, in rows or columns. |
| **Segment by** | Adds a breakdown level under that item. |
| **Date granularity** | For a Date axis: **Inherit**, **Raw**, **Day**, **Week**, **Month**, **Quarter**, **Half**, or **Year**. |
| **Remove from table** | Removes the item from this table. On a Variable's original table it reads **Delete variable** (or **Delete dimension**) instead; see [Removing and deleting](#removing-and-deleting). For a deleted Variable or Dimension it reads **Remove deleted property from table**. |
At rest, each item shows small marks for what is set on it: an eye with a line through it when hidden, the sort, a funnel with the number of items the filter keeps, a branch with the number of breakdown levels, and the date grain. Right-click an item for the same [right-click menus](#right-click-menus) as in the grid; here the remove action reads **Remove item**, or **Remove item and drill-ins** for an item with levels under it.
## Controls in the grid
| Control | Where | What it does |
| - | - | - |
| **Build your table** | A new, blank table | Hint: **Add a row below or a column above**. |
| **Add row** | Last row of the table | Opens the [Create or search field](#add-rows-and-columns-the-create-or-search-field) for rows. |
| **Add column** | **+** at the right end of the column headers | Opens the Create or search field for columns. |
| **Insert row below** | **+** at a row's left edge | Opens a new row with **Type a name or / to search**. A name and **Enter** creates a Variable there, or an item when the row above is an item. **/** opens the search. |
| **Reorder row or open row menu** | Grip at a row's left edge | Drag to reorder; click for the row's menu. Dragging a Dimension's items sets its sort to **Manual**. |
| **Edit variable formula** | **fx** button when you point at a row name or column header | Opens the formula for the whole row or column. |
| **Segment by** | Branch button when you point at a row name or column header | Breaks the row or column down by a Dimension. On a row that already has a breakdown, opens **Segment \[name] by** with **Add**, **Remove level**, and drag to reorder levels. |
| **Drill in** / **Clear drill-in** | Magnifier button when you point at a calculated row's name | Opens or closes the rows its formula reads. |
| **More header actions** | Button when you point at a column header | Opens the column's right-click menu. |
| **Edit formula for \[Dimension]** | Button when you point at a Dimension's label row | Opens the formula every item at that level shares. |
| **Create \[Dimension] item** | **+** at the end of a Dimension's items | Adds an item. Reads **Create \[Dimension] item under \[parent]** inside a nested breakdown. |
| **Show formulas** / **Hide formulas** | **fx** button at the top of the row-name column | Peeks at, opens, or closes the formula columns. |
| Arrow beside a row name | A row with rows under it | Expands or collapses them. **Shift**-click, or **⌘**-click on a Mac, does it for every sibling row at that level. Your browser remembers rows you open or close. |
| Arrow in a column group's header | A group of columns, such as a region over its months | Collapses the group to its total column, or expands it. **Shift**-click, or **⌘**-click on a Mac, does it for every sibling group at that level. |
| **LC** | Header of the Last close period | **Edit last close** changes Last close for the Scenario you are viewing. See [Set Last close and date defaults](/guides/set-dates-and-last-close). |
| **Space** | A blank spacer row, when you point at it | Select it to replace the space with a row. |
| Bottom edge | Below the last row | Drag to resize the table. Double-click, or press **Enter**, to fit the table to its content. The height is saved in your browser. |
| **Mention cell in chat** and **Open context menu** | Buttons beside one selected cell | Add the cell to the current chat, or open the cell menu. |
Select a range of cells to see a bar at the lower right of the Page: **Count:**, **Sum:**, and **Avg:** for the numbers in the selection, rounded to two decimals. Cells without a number are not counted.
## Add rows and columns: the Create or search field
**Add row**, **Add column**, **Insert above** and its neighbors, and the setup panel's **Add** open the same search field, **Create or search**. Type to find Variables, Dimensions, and items; select one to add it.
| Control | What it does |
| - | - |
| **Create variable: \[name]** | Creates a Variable with that name and adds it here. It holds numbers. Not offered on an axis where the other axis already holds Variables. |
| **Create dimension: \[name]** | Creates a Dimension with that name. |
| **Create \[Dimension] item: \[name]** | In a new row under an item: creates an item of that Dimension. |
| **List dimension items** | For a Dimension: one row or column per item, a breakdown. |
| **Add as mapping** | For a Dimension: one cell per row that shows which item the row belongs to. |
| **Back** | Returns from the choice above to the search. |
| **Filter** → **Include**: **Variables**, **Dimensions**, **Dimension items** | Limits what the search lists. |
| **Filter** → **Show disabled results** | Also lists Variables and Dimensions the search leaves out because they are already on the table. |
## Right-click menus
Right-click (or **Control**-click) a row name, a column header, a Dimension's label row, or a cell. With a cell or name selected, **Shift F10** opens the same menu. Each menu shows only the actions that apply. **All tables** beside an action means it changes every table that shows that Dimension.
### Variable row or column menu
A row or column for a Variable, such as Revenue, or a column that shows each row's item, such as Department.
| Control | What it does |
| - | - |
| **Edit formula** | Opens the formula for the whole row or column. |
| **Edit formula for \[Dimension]** | On a row with a breakdown: the formula for the items one level down. |
| **Edit child formula** | The same, when there are several levels below; pick one. |
| **Segment by** | Breaks the row down by a Dimension. |
| **Drill in** / **Clear drill-in** | Opens or closes the rows its formula reads. |
| **Go to original table** | Opens the Page and table where the Variable was created. Shown on other tables only. |
| **Copy** | Copies the row or column name. |
| **Mention in chat** | Adds the row or column to the current chat. |
| **Show formula column** / **Hide formula column** | On a column that heads a group: opens or closes its formula column. |
| **Filter** | On a column that shows each record's item, such as Status: filters the rows by its value. |
| **Pin column** / **Unpin column** | Columns only. Keeps the column beside the row names while you scroll, like a frozen column in a spreadsheet, for everyone. |
| **Format and display** | Opens the [format options](#format-and-display-options). |
| **Use as** | For a Dimension shown as a mapping column: **Segmentation** or **Mapping**. |
| **Insert above** / **Insert below** | Opens the Create or search field for a new row at that place. On a column: **Insert left** / **Insert right**. |
| **Insert space** | Rows only. Adds a blank spacer row below. |
| **Expand child rows** / **Collapse child rows** | Opens or closes the rows under it. |
| **Remove item and drill-ins** | Removes the row, and any rows under it, from this table. On the Variable's original table, removing its last row reads **Delete variable**. |
### Dimension item menu
A row or column for one item, such as Sales.
| Control | What it does |
| - | - |
| **Copy** | Copies the item's name. |
| **Mention in chat** | Adds the item to the current chat. |
| **Segment by** | Breaks this item down by another Dimension. |
| **Drill in** / **Clear drill-in** | Opens or closes the rows the item's formula reads. |
| **Filter out this item** | Hides the item on this table. Not offered on a read-only table. |
| **Expand child rows** / **Collapse child rows** | Opens or closes the rows under it. |
| **Format and display** | Opens the [format options](#format-and-display-options). |
| **Rename** | Renames the item. **All tables.** Unavailable, with **Only items you created can be renamed**, for items from connected data. |
| **Delete \[Dimension] item** | On the Dimension's original table only: deletes the item. **All tables.** Unavailable, with **Only items you created can be deleted**, for items from connected data. |
| **\[Dimension] dimension settings** | Opens the [Dimension menu](#dimension-menu). |
### Dimension menu
Opens from a Dimension's label row, such as **DEPARTMENT**, or from **\[Dimension] dimension settings** on any of its items.
| Control | What it does |
| - | - |
| **Go to original table** | Opens the table where the Dimension was created. Shown on other tables only. |
| **Default for every \[Dimension]** | Opens the formula every item at this level shares. A nested breakdown offers one entry per level. |
| **Edit dimension formula** | Rows only. Opens the formula that defines the Dimension's items. |
| **Segment by** | Breaks every item at this level down by another Dimension. |
| **Filter items** | Rows only. Chooses which items show. Shows the number of items kept when filtered. |
| **Filter** | On a list of records: filters the rows by this column's value. |
| **Sort** | **None**, **Ascending**, **Descending**, or **Manual**. Not offered for Date. |
| **Date granularity** | For Date: **Inherit**, **Raw**, **Day**, **Week**, **Month**, **Quarter**, **Half**, or **Year**. |
| **Use as** | **Segmentation** or **Mapping**. |
| **Segment generation** | **Show only matching segments** shows the combinations that have data. **Expand to create all segments** shows every combination; for a Dimension of numbers, set **Start Value** and **End Value**. |
| **Format and display** → **Dimension color** | The color of the Dimension's item pills. **All tables.** |
| **Format and display** → **Display as** | **Pill** or **Plain text**, on this table, in your browser. |
| **Create \[Dimension] item** | Adds an item. **All tables.** Unavailable, with **Clear the filter to create a new item.**, while the Dimension is filtered. |
| **Insert above** / **Insert below** | Opens the Create or search field at that place. On a column: **Insert left** / **Insert right**. |
| **Insert space** | Rows only. Adds a blank spacer row. |
| **Pin column** / **Unpin column** | Columns only, not Date. |
| **Remove dimension** | Removes the Dimension from this table. On its original table it reads **Delete dimension**, **All tables**. |
For actions that change the layout, such as **Filter items**, **Sort**, and **Insert above**, the Dimension's level must be saved on this table. A level that **Segment by** added under one item only offers none of them. On a read-only table, the menu keeps **Display as**.
### Cell menu
| Control | What it does |
| - | - |
| **Fix** | On a cell that shows an error: starts a chat that asks Ari to fix it. |
| **Edit formula** | Opens the formula editor for this cell. |
| **Format and display** | Format options for this cell only. |
| **Copy formula** | Copies the cell's formula. Shown when it has one. |
| **Copy value** | Copies the value as the cell shows it. |
| **Paste** | Pastes into the cell. |
| **Mention cell in chat** | Adds the cell to the current chat. |
With several cells selected, the menu reads **\[N] items selected** and offers **Format and display**, for each selected cell, and **Paste**.
### Menu for several selected rows
Select a range that includes row names, or press **⌘ A**, and right-click a row name.
| Control | What it does |
| - | - |
| **Copy \[N] row labels** | Copies the names, one per line. |
| **Filter selected items** | Hides the selected items on this table. |
| **Format \[N] variables** | Format options for every selected row. Reads **Format and display** when the selection mixes Variables and items. |
| **Segment by** | Breaks every selected row down by a Dimension. |
| **Drill in** / **Clear drill-in** | Opens or closes the inputs of every selected row. |
| **Expand child rows** / **Collapse child rows** | For every selected row. |
| **Remove selected items and drill-ins** | Removes the selected Variable rows from this table. On their original table it reads **Delete variables**. |
| **Remove \[Dimension]** | Removes the Dimension of the selected items from this table, or reads **Delete dimension** on its original table. |
| **Remove \[N] rows** | In the table setup panel: removes the selected rows from this table. |
| **Remove all drill-in rows** | Closes every drill-in row. |
| **No actions available** | Nothing applies to the whole selection. |
### Total column header menu
Right-click a total column's header and choose its summary: **None**, **Sum**, **Average**, **Minimum**, **Maximum**, **Count**, or **Last**. See [Total columns](#total-columns).
### Formula column header menu
| Control | What it does |
| - | - |
| **Pin column** / **Unpin column** | Keeps the formula column beside the row names. |
| **Hide column** | Hides this formula column. On a split column, hides that range only. |
| **Show \[range] column** | Brings back a hidden range, such as **Show Forecast column**. |
| **Hide all formula columns** / **Show all formula columns** | For a split formula column. |
### Drill-in row and spacer row menus
| Control | What it does |
| - | - |
| **Show variable name** / **Hide variable name** | Labels each drill-in row with its Variable. Remembered in your browser. |
| **Remove all drill-in rows** | Closes every drill-in row on the table. |
| **Remove space** | Removes a spacer row. |
## Removing and deleting
Each Variable and Dimension you create on a table has that table as its original table.
| You remove | What happens |
| - | - |
| A row or column from any other table | It leaves that table only. |
| Its last row or column on its original table | The action reads **Delete variable**, **Delete dimension**, or **Delete variable or dimension**, and deletes it from the Model and every table. |
| Its original table, with **Delete block**, or that table's Page | Kept when another table still shows it, and one of those tables becomes its original table. Deleted when no other table shows it. |
When a delete would break something, cfo.ai asks first:
| Dialog | Text | Buttons |
| - | - | - |
| **Delete variable or dimension?** (**Delete variables or dimensions?** for several) | **Deleting \[name] will break \[counts].** The counts can include formulas, blocks, Canvas blocks, property settings, and items or pins, and end with **including \[N] dependents you can’t see** when you cannot open some of them. | **Cancel**, **Delete anyway** |
| **Delete item?** | **“\[item]” is still used. Deleting it everywhere will break:** and a list. | **Cancel**, **Delete anyway** |
## Format and display options
Right-click a row name, a column header, or a cell and choose **Format and display**. On a read-only table, the menu has none of these.
| Option | Choices | Applies to |
| - | - | - |
| **Background** | A set of colors, and **No fill** | This table, at the row, column, or cell you clicked |
| **Text style** | **Bold**, **Italic**, **Underline**, **Strikethrough** | This table, at the place you clicked |
| **Indent** / **Outdent** | Moves a row name one step in or out | This table, that row |
| **Format** | **Auto**, **Number**, **Currency**, **Percentage**, **Integer**, **Multiple**, **Date**, **Reset** | This table, at the place you clicked. **Date** instead makes the Variable a date Variable on every table; it is not offered for cells or the Date axis. |
| **Currency** | **USD (\$)**, **EUR (€)**, **JPY (¥)**, **GBP (£)**, **CNY (CN¥)**, **AUD (A\$)**, **CAD (CA\$)**, **CHF**, **HKD (HK\$)**, **NZD (NZ\$)** | The Variable, on every table. Appears after you choose **Currency**. |
| **Date format** | See [Date formats](#date-formats) | This table. Appears for date headers and date Variables. |
| **Precision** | Decimal places: **Default**, **1**, **1.0**, **1.00**, **1.000**, **1.0000**, **1.00000** | This table, at the place you clicked |
| **Variance colors** | **Higher is better**, **Lower is better** | The Variable, on every table. See [What colors and marks mean](#what-colors-and-marks-mean). |
| **Reset formatting** | Removes everything set at that place | This table |
A cell's own format wins over its column's, and a column's over its row's. A Variable can also carry a default format that Ari sets; a table's own format wins over it. A format never changes how a Variable adds up. When several selected rows differ, an option shows **Mixed**.
The [Dimension menu](#dimension-menu) has its own **Format and display** with **Dimension color** and **Display as**.
## Date formats
How each **Date format** shows a period at each grain. The examples use September 2, 2026, the week of August 24, 2026, and the third quarter of 2026.
| Format | Day | Week | Month | Quarter | Half-year | Year |
| - | - | - | - | - | - | - |
| **Abbreviated** (default) | `Sep-02-26` | `W35-26` | `Sep-26` | `Q3-26` | `H2-26` | `2026` |
| **ISO standard** | `2026-09-02` | `2026-W35` | `2026-09` | `2026-Q3` | `2026-H2` | `2026` |
| **Full month** | `September 2, 2026` | `Week 35 2026` | `September 2026` | `Q3 2026` | `H2 2026` | `2026` |
| **Compressed year** | `Sep 2 ’26` | `W35 ’26` | `Sep ’26` | `Q3 ’26` | `H2 ’26` | `’26` |
| **Year-first slash** | `2026/09/02` | `2026/W35` | `2026/09` | `2026/Q3` | `2026/H2` | `2026` |
| **Period-separated** | `2026.09.02` | `26.W35` | `26.09` | `26.Q3` | `26.H2` | `2026` |
| **Day-first formal** | `02-Sep-2026` | `W35-2026` | `Sep-2026` | `Q3-2026` | `H2-2026` | `2026` |
| **Descriptive** | `02-Sep-26` | `WE 30-Aug-26` | `Sep FY2026` | `Q3 FY2026` | `H2 FY2026` | `FY2026` |
Weeks are numbered Monday to Sunday, and **WE** shows the Sunday that ends the week. **Raw** dates use the Day form.
## Total columns
A total column, also called a summary column, sits at the end of each column group and summarizes the cells shown in the group. It saves no formula. It is not the same as a parent row that adds up the rows beneath it; see [Totals, aggregation, and time rollups](/concepts/totals-and-rollups#summary-columns-total-columns-at-the-end-of-a-group).
| Summary | Header | Shows |
| - | - | - |
| **Sum** | **SUM** | The group's cells added up |
| **Average** | **AVG** | Their average |
| **Minimum** | **MIN** | The smallest |
| **Maximum** | **MAX** | The largest |
| **Count** | **COUNT** | How many have a value |
| **Last** | **LAST** | The last one |
| **None** | **TOTAL** | The Variable's own value for the whole group, such as its year value beside its months |
A table starts with **Sum**, or **Last** when it shows a Dimension's values. Text and true-or-false values offer only **None**, **Count**, and **Last**; dates add **Minimum** and **Maximum**. The summary is saved with the table.
## What colors and marks mean
| What you see | Meaning |
| - | - |
| A dashed underline under a value | The cell holds its own value or formula, saved at that exact cell, in place of its row's formula. A picked item or date shows no underline. Clear it with **Delete**; see [Enter, paste, and clear values](/guides/enter-values). |
| A green or red number with **+** or **-** | A change in a **Variance** or **Variance %** comparison column: green is good and red is bad, by the Variable's **Variance colors**. Plain values are never colored by sign. |
| Colored pills | Items of a Dimension, in the Dimension's color. |
| Colored references and outlined cells while you edit a formula | Each reference in the formula gets its own color, in order, and the cells it reads take the same color. The colors only tell references apart. See [Write a formula](/guides/write-a-formula). |
| A logo beside a row name | The Variable reads a connected system, such as QuickBooks. |
| A link mark beside a row name, when you point at the row | The Variable appears on other tables too. Point at the mark for **\[N] synced instances** and **Original:** with its table and Page, or **No access** when you cannot open it. |
| A hatched background | Drill-in rows: the inputs of a formula, or source records. |
| **LC** and a vertical line | Last close. Actuals are to the left, Forecast to the right. |
| A funnel in a column header | The rows are filtered by that column's value, such as **Filtered: Status is Active**. A filter the list cannot show reads **Filtered by a custom condition on Status**. |
| **No items** | The Dimension has no items yet. |
| An error code such as `#DIV/0!` | See [Formula errors and warnings](/reference/formula-errors). |
## What you can type into a cell
| You type | cfo.ai saves |
| - | - |
| `1,000` | 1000 |
| `$1,250.75` or `1,250.75 €` | 1250.75, and shows the cell as **Currency** |
| `50%` | 0.5, and shows the cell as **Percentage** |
| `(500)` | -500 |
| `-` | 0 |
| `1e3` | 1000 |
| `=Revenue * 1.1`, or `Revenue * 1.1` in a number cell | The formula `Revenue * 1.1` |
| `Finance assistant`, `N/A`, or `2026-01-01` in a text cell | That text |
| `=Level * 2` in a text cell | The formula `Level * 2` |
| A function call such as `if(Level = "L5", "Senior", "Junior")` in a text cell | That formula, without an `=` |
Recognized currency signs, before or after the number: `$`, `€`, `£`, `¥`, `₹`, `A$`, `C$`, and `NZ$`. A number cannot carry both a currency sign and `%`. A pasted cell follows the same rules, and a formula copied from a cfo.ai table pastes as that formula. **⌘ Shift V** pastes values only.
## Where to open the formula editor
| Where | The formula covers |
| - | - |
| Double-click a cell, or select it and press **Enter** | That cell |
| Right-click a cell → **Edit formula** | That cell |
| A date or item cell → **Edit formula** | That cell |
| **Edit variable formula** (**fx**) on a row name or column header | The whole row or column |
| Right-click a row name → **Edit formula** | The whole row |
| Right-click a row with a breakdown → **Edit formula for \[Dimension]** or **Edit child formula** | The items one level down |
| A Dimension's label row → **Edit formula for \[Dimension]**, or right-click → **Default for every \[Dimension]** | Every item at that level |
| Right-click a Dimension's label row → **Edit dimension formula** | The formula that defines the Dimension's items |
| The formula bar | The selected cell |
For writing and saving formulas, see [Write a formula](/guides/write-a-formula).
## When a change is not saved
A change saves in the background. When cfo.ai refuses it, the cells go back to what they showed and a notice appears. Its title names what you did, and its text says why.
| Message | Meaning |
| - | - |
| **Could not save the formula** | A value or formula you typed into a cell or a formula editor was not saved. The editor reopens with your entry. |
| **Could not save "\[value]"** | An item or a date you picked from a cell's list was not saved. |
| **Could not paste** | A paste was not saved. |
| **Could not fill the cells** | Dragging the fill handle did not save. |
| **The cells could not be cleared** | Pressing **Delete** or **Backspace**, or pasting a cut, which clears its source, did not save. |
| **Could not update the parent rows** | Choosing **Yes** at **Propagate?** did not save. The text can read **The formula was saved, but not to the parent rows.** |
| **Couldn't apply that change** | A change to the table's rows or columns was refused. The table goes back to its earlier layout. See [Why a layout change was undone](#why-a-layout-change-was-undone). |
### Why a change was not saved
The text under the notice titles names the reason.
| Message | Meaning |
| - | - |
| **These cells come from an integration. Change the value in the source system.** | The cells read data imported from a connection, such as a pay rate from your payroll system. Only the source can change them. |
| **These cells don't all take the same formula. Edit one cell at a time.** | The selection spans cells with different formulas, such as Actuals read from an integration and Forecast from a formula. |
| **Each of these cells holds one item. Pick one from the list.** | The column shows a Dimension's items, and the value is not one of them. |
| **The table changed while you were editing. Check the cells, then try again.** | A row or column you edited moved or disappeared while you worked. |
| **You can't edit this table. Ask a page owner for edit access.** | You can read the Page but not edit it. |
| **These cells can't take this change.** | cfo.ai refused the change for another reason. |
| **Try again.**, **Check the cells, then try again.**, **Their values are still in place.**, or **Your edit is still open. Try again.** | The save failed for no stated reason, such as a lost connection. |
| **Your earlier draft is ready to retry.** with **Open draft** | A formula failed to save after you had opened another editor. **Open draft** reopens it. If you have moved to another workspace or Scenario, it reads **Return to the original workspace and scenario to open this draft.** |
In an open formula editor, the same reasons appear under the editor after **Could not save the formula.**
### Why a layout change was undone
The text of **Couldn't apply that change**:
| Message | Meaning |
| - | - |
| **"\[A]" and "\[B]" are both variables, and a table can only have variables in rows or in columns — not both. We restored your previous table.** | Variables went on both axes. |
| **"\[A]" and "\[B]" are both variables, and a row or column can only include one variable. We restored your previous table.** | One row or column path held two Variables. |
| **"\[Dimension]" is segmenting both rows and columns, and a dimension can only segment one of them. We restored your previous table.** | A Dimension broke down both axes. |
| **This change isn't a valid table setup, so we restored your previous table.** | Any other layout cfo.ai cannot calculate. |
## Other notices
| Message | Meaning |
| - | - |
| **A dimension can only segment rows or columns, not both** | You chose a Dimension that already breaks down the other axis. |
| **Date can only be added as segmentation** | You tried to add Date as a mapping. |
| **Only dimensions can be added as segmentation** | You tried to break a row down by a Variable. |
| **Variable or dimension is no longer available** | The search result was deleted while the search was open. |
| **Could not create variable** / **Could not create dimension**, with **Try again or contact support if the issue persists.** | Creating from **Add row** or **Add column** failed. |
| **Failed to create variable** / **Failed to create dimension**, with the same text | Creating from another search, such as **Segment by**, failed. |
| **Something went wrong when trying to create new row** | Inserting a row failed. |
| **Item created** | An item was added. |
| **Could not create item**, **Could not create item here**, or **Something went wrong when trying to create new dimension item** | Adding an item failed. |
| **Items cannot be created when a Dimension has conflicting values in the same hierarchy.** | The **+** to create an item is unavailable there. |
| **At least one dimension item must remain visible** | You chose **Filter out this item** on the last item showing. |
| **Couldn’t filter out dimension item** / **Couldn’t filter out selected dimension items** | **Filter out this item** or **Filter selected items** failed. |
| **Renamed to “\[name]” · updated \[N] formulas** (and filters, drill-ins, or charts) | A rename also changed the formulas and other settings that use the item. |
| **An item named “\[name]” already exists.** | The new name is taken. |
| **“\[item]” now comes from connected data and can't be renamed.** | The item became an imported item. |
| **Could not rename** / **Could not rename property** | Renaming an item, or a Variable from a column header, failed. |
| **Deleted “\[item]”** with **Undo** | An item was deleted. **Undo** brings it back. |
| **Can't delete “\[item]”: \[what uses it]** | Something still uses the item. |
| **“\[item]” comes from connected data and can't be deleted.** | The item is imported. |
| **“\[item]” no longer exists. Refresh the table to see the current items.** | Someone else deleted it first. |
| **“\[item]” can't be deleted on its own here.** | The item exists only paired with other items. |
| **Could not delete “\[item]”** | The delete failed for another reason. |
| **Unable to move block to page** / **Unable to create page** | **Move to page** or its **New page** failed. |
| **Could not update last close** | A change from the **LC** badge failed. |
| **Failed to delete formula** | **Delete formula** in **All Formulas** failed. |
| **This segmentation can only have one top-level property** | A drop in the **Segment by** editor that the breakdown cannot hold. |
When a table cannot load or calculate, its notices, such as **We couldn't load this table**, are listed in [Formula errors and warnings](/reference/formula-errors#table-calculation-notices).
## Why a table looks empty
| What you see | Why | What to do |
| - | - | - |
| No numbers in any date column | The table's date range does not cover the periods that hold values. Typing or importing dated values never moves the range. | Change the range with the date range button. |
| Quarter or year columns show 0 | The Variable has no time rollup, which Variables created by hand lack. | Ask Ari to give the Variable a time rollup. See [Why a quarterly view shows zeros](/concepts/totals-and-rollups#why-a-quarterly-view-shows-zeros). |
| An item missing from a breakdown | An item whose values are all zero or empty across the date range is left out of a breakdown under a Variable. A filter can also hide it. | Set the level to **Expand to create all segments** with **Segment generation**, or ask Ari to show empty items on the table. To clear a filter, see [Filter and sort a table](/guides/filter-and-sort-a-table#clear-a-filter-so-every-item-shows-again). |
| Blank cells for some items only | The formula that fills the row applies to other items, so these read empty. | See [How formulas work](/concepts/formulas). |
## Where each setting is saved
| Saved | Settings |
| - | - |
| With the table, for everyone | Rows, columns, and breakdowns; filters and sorts; hidden rows and columns; the date range and grain; comparisons; **Format**, **Precision**, **Date format**, **Background**, **Text style**, and **Indent**; total column summaries; column widths; pinned columns; **Show dimension headers** |
| On the Variable or Dimension, for every table | Values and formulas; the name; the currency; the **Date** type; **Variance colors**; **Dimension color** |
| In your browser only | Open formula columns; the formula bar; whether the setup panel is open; the table height; **Display as**; which rows you expanded or collapsed; **Show variable name** |
| Not saved | **Show total columns**; **Pin total columns**; drill-in rows |
Version history does not list changes to column widths or pinned columns.
## What a table cannot do
| A table cannot | Instead |
| - | - |
| Hold a comment or note on a cell | Add a Text block beside the table, or ask Ari for a text Variable, such as Notes, on a table that lists records. |
| Color the cells you type into automatically | They carry a dashed underline. Select them and set a **Background**. |
| Sort rows by the numbers in a column | **Sort** orders items by name. Set a **Manual** order, or ask Ari to order the items by value. |
| Show numbers in thousands or millions | Ask Ari for a Variable that divides by 1,000. |
| Filter or sort dates | Change the date range. Dates always run in date order. |
| Show two grains on one Date axis, such as quarters and years | Duplicate the table with **Duplicate block** and give each copy its own grain. |
| Hold Variables in both the rows and the columns | Keep every Variable on one side. |
| Show the same Variable or Dimension twice in the columns | Use a second table. |
| Transpose itself with one button | Ask Ari to flip it, or drag items between **Rows** and **Columns** in the setup panel. |
| Create a text Variable by hand | Ask Ari. By hand, a new Variable holds numbers, and **Format** → **Date** makes it a date. |
| Take new items by hand in a Dimension of numbers, dates, or true-or-false values | Only text Dimensions take new items by hand. For a Dimension of numbers, use **Segment generation** → **Expand to create all segments** with a **Start Value** and **End Value**. |
| Hide a row from the grid's menu | Use the eye button in the table setup panel. |
| Start a breakdown collapsed for everyone | Which rows are expanded or collapsed is saved only in each person's browser, so each reader opens or closes rows for themselves. |
| Lock cells so teammates cannot edit them | Every member of a workspace can edit every Page. To let someone read without editing, share a link; see [Sharing and access](/concepts/sharing-and-access). |
| Choose a Variable's original table | A Variable keeps the table it was created on, until that table is deleted while another table shows it. |
| Be edited on a phone | Ask the phone's browser for its desktop site, or use a computer. See [Phones and tablets](/reference/interface#phones-and-tablets). |
To add a chart, ask Ari; the block menu has no chart block. See [Create and organize Pages and blocks](/guides/create-pages-and-blocks#add-a-block-to-a-page).
## Related
* [Build a Table block by hand](/guides/build-a-table)
* [Enter, paste, and clear values](/guides/enter-values)
* [Format and arrange a table](/guides/format-a-table)
* [Filter and sort a table](/guides/filter-and-sort-a-table)
* [Break a number down](/guides/break-down-a-number)
* [Keyboard shortcuts](/reference/keyboard-shortcuts)
* [Formula errors and warnings](/reference/formula-errors)
# Build your first Model
Source: https://docs.cfo.ai/tutorials/first-model
A 15-minute lesson for new users: tell Ari about your business, watch it build a Model, check a number, change an assumption, and take the change back.
In this lesson we build a first Model of your business with Ari, your AI finance coworker. By the end you will have a set of Pages that forecast your next 12 months, and you will have traced one number to its source, changed one assumption, and taken the change back.
You need about 15 minutes, a payment card for the free trial, and a few facts you know about your business: what you sell, roughly what you charge, how many people you employ, and how much cash you have. You can start from a description, from your current spreadsheet, or from your accounting system; a description is fastest and works for everyone.
Already signed up and the starting card is gone? Press **⌘ /** (**Ctrl /** on Windows and Linux) to start a new chat, and paste the message from step 2 into the message box.
Go to [app.cfo.ai](https://app.cfo.ai) and sign up with **Continue with Google**, **Sign in with Microsoft**, or your work email and the six-digit code cfo.ai sends you.
When you sign up, cfo.ai asks you to start the 14-day free trial: choose a plan, select **Start free trial**, and add a card. Nothing is charged until the trial ends, and you can cancel any time before then. See [Start a trial or choose a plan](/guides/choose-a-plan).
A new workspace opens on a chat named **Welcome**. Notice three things: Ari's greeting at the top, a short brief about your company once Ari finishes its research, and a starting card docked above the message box. The card asks **Where would you like to start?**
On the starting card, choose **My business**. The card then asks **What should I work from?** Pick the tab that matches what you have. The rest of the lesson works the same way for all three.
Choose **I want to type or upload context**, and type a few sentences like these, with your own details:
> We sell \[an annual software subscription] to \[small accounting firms] for about \[\$6,000 a year]. Last month revenue was about \[\$80,000]. We have \[9] employees and about \[\$1.2 million] in the bank. Build a 12-month model so I can see how long our cash lasts.
You do not need exact numbers. Ari labels every number you did not give it as an assumption you can change later. Select **Get started**.
Choose **Upload an existing model**, attach your workbook, such as an Excel file, and select **Get started**. Ari rebuilds it in cfo.ai. For a large workbook, see [Import an existing spreadsheet model](/guides/import-a-spreadsheet-model).
Choose **Connect my data** and select **Get started**. Ari opens the list of sources; pick your accounting system and approve access in the window that opens. You need administrator access to that system. Ari builds your first Model when the data has loaded, which can take from minutes to hours for a long history, and tells you in this chat when it is ready. See [Connect your accounting system](/guides/connect-accounting).
Notice that your answers appear in the chat as your message, and Ari starts working on them.
To try cfo.ai with an example business instead of your own, choose **Something hypothetical** on the starting card, then **A business like mine**, or type the kind of business you want. The rest of the lesson works the same way.
Ari's steps collect in an **Activity** group under your message. For a larger build, Ari first shows a plan card listing the assumptions it intends to use. Read them, change any that are wrong, and select **Build it**.
Notice the sidebar. A **Model** section appears with the first Page Ari builds, and more Pages join it one at a time. You can open the first Page while Ari works on the next.
Select the first Page in the sidebar. A Page is a report made of blocks. Most of what you see are Table blocks: rows such as Revenue and Cash, with months across the top.
Notice that the numbers are live: the Model calculates each one from your assumptions, so a changed assumption updates every number that uses it.
Find a number you already know, such as last month's revenue or your cash balance. Right-click the cell and choose **Mention cell in chat**. The cell appears in the message box. Ask:
> Where does this number come from? Show me the formula.
Ari explains where the value came from, such as your message, your workbook, or your accounting data, and shows the formula behind any number calculated from it. If a number is wrong, tell Ari the right one.
Ask Ari to change something you might really decide:
> Hire two more engineers starting in \[March] at \[\$150,000] a year each, and show what happens to cash.
Watch the Page. Notice that Ari's edit plays out on the table, and every number that depends on the new hires changes: payroll, expenses, and the cash balance in each later month.
Select the clock icon in the Page header to open **Version history**. Every change to the Model is listed, newest first, with who made it and when. Ari's hiring change is at the top.
Point at that entry, select its **…** actions button, and choose **Undo this change**. Notice that the two hires disappear and cash returns to what it was. Nothing you or Ari try in the Model is permanent by accident.
## What you built
You have a Model of your business: Pages built from your own description, a number you traced to its source, and an assumption you changed and took back. That loop of asking, checking, and changing is how you will work with Ari every day.
## Next steps
* Swap Ari's estimates for your books: [Replace estimates with your real numbers](/guides/switch-to-real-numbers).
* Already have a model in a spreadsheet? [Import it](/guides/import-a-spreadsheet-model).
* Keep going with [Your first week](/tutorials/first-week): one short lesson a day.
* Learn how the pieces fit together in [How cfo.ai works](/concepts/how-cfoai-works).
# Build a table yourself
Source: https://docs.cfo.ai/tutorials/first-table-by-hand
A 15-minute lesson for spreadsheet users: build a small Table block by hand, type values, write a formula, and watch the Model recalculate.
Everything Ari builds, you can also build yourself. In this lesson we build a small revenue table for an imaginary coffee cart, without Ari, so you can see how cfo.ai's Variables and formulas differ from spreadsheet cells. By the end you will have a Page with a table that calculates revenue from cups sold and price, and recalculates when you change a number.
You need about 15 minutes and a desktop browser. Pages cannot be edited in a phone browser.
In a workspace with no Pages yet, the sidebar has no **Model** section. On the empty screen, choose **Add block**, then **Table block**. cfo.ai creates a Page with a table on it. Rename the Page from its title, then continue from step 3.
In the sidebar's **Model** section, select the **+** button labeled **New page**. cfo.ai creates a Page named **New Page** and opens it.
Click the **New Page** title at the top of the Page, type `Coffee cart`, and press **Enter**.
On the empty Page, choose **Table block**.
Notice the hint **Build your table**. A new table is blank until you add rows or columns.
Select **Add row** at the bottom of the table. In the search field, type `Cups Sold`, then select **Create variable: Cups Sold**.
Notice that cfo.ai adds a row of months across the top, or of your workspace's default period, such as quarters. A Variable is a named line of the Model, and it has one value for each period.
Select the first month's cell in the **Cups Sold** row and type `1000`. Press **Tab** to move to the next month, type `1200`, press **Tab**, type `1500`, and press **Enter**.
Notice the dashed underline under each number. It marks a cell that holds its own value.
Add another row and create a Variable named `Price per Cup`. Point at its row name and select the **fx** icon, **Edit variable formula**.
The editor has two sections, **Actuals** and **Forecast**: the months up to your workspace's Last close, and the months after it. Type `4` in **Actuals**, select **Forecast** to open it, type `4` there too, and press **Enter**.
Notice that every month now shows 4, with no underline. The row's formula fills every month for you; in a spreadsheet you would have copied the value across.
Add a third row and create a Variable named `Cart Revenue`. Open its formula editor the same way, and type this in both sections:
```text theme={null}
`Cups Sold` * `Price per Cup`
```
As you type, autocomplete suggests the Variable names. Backticks wrap a name that contains spaces. Press **Enter**.
Notice that **Cart Revenue** fills in for every month: 4,000 in the first month, 4,800 in the second, and 6,000 in the third. Later months show 0, because **Cups Sold** has no value there yet. The formula refers to Variables by name, not to cells such as `B7`, so it works in every month at once.
Select the first month of **Cups Sold**, type `2000`, and press **Enter**.
Notice that **Cart Revenue** for that month changes to 8,000 right away. Every number that depends on a value you change recalculates.
Point at the **Cart Revenue** row name and select the magnifier icon, **Drill in**.
Notice the shaded rows that open beneath it: **Cups Sold** and **Price per Cup**, the inputs of its formula. Select **Clear drill-in** to close them.
## What you built
You built a working piece of a Model by hand: three Variables, typed values, a formula that applies to the whole row, and a calculation that updates when an input changes. The tables Ari builds for you work the same way, so you can open any of them, read its formulas, and change them.
## Next steps
* Learn what Variables and Dimensions are and how formulas pick their inputs: [Variables and Dimensions](/concepts/variables-and-dimensions) and [How formulas work](/concepts/formulas).
* Split a row by product or region: [Break a number down](/guides/break-down-a-number).
* Format numbers as currency and arrange the table: [Format and arrange a table](/guides/format-a-table).
* Look up every formula function: [Formula functions](/reference/functions).
* Delete the practice Page when you are done: point at it in the sidebar and select **Delete page**.
# Your first week with cfo.ai
Source: https://docs.cfo.ai/tutorials/first-week
Five short lessons that build on your first Model: a forecast you can explain, a Page you can share, a decision tested in a Scenario, a review of the Model's weak spots, and a weekly update from Ari.
Your first Model is a starting point. Over the next five days, we turn it into something you use every week: a forecast you can explain, a Page you can share, a decision you can test before you make it, and a weekly update you do not have to ask for. Each lesson takes about ten minutes.
Before you start, finish [Build your first Model](/tutorials/first-model). Each lesson begins in a new chat: press **⌘ /** (**Ctrl /** on Windows and Linux). Replace the words in brackets with your own.
## Day 2: Build out your forecast
Today we make the forecast explain itself, so you can say which assumptions move it.
> Build out our forecast. Calculate revenue from \[new customers, pricing, and churn] and costs from \[headcount and our biggest vendors]. Put the assumptions where I can see and change them.
Notice the assumptions Ari adds, such as a churn rate or a price. They sit in the Model where you can see and change them.
Right-click a revenue cell in a future month, choose **Mention cell in chat**, and ask:
> How is this calculated? Which assumptions move it the most?
Notice that Ari shows the formula behind the number and names the assumptions it reads.
Point at the left edge of a table, select its six-dot handle, and choose **Explain with Ari**.
Notice that a new chat opens with a request to explain the table. Ari answers with what the table shows, how it is built, and how to read it.
You are done when you can name the two or three assumptions that move your forecast most.
## Day 3: Build a Page you would share
Today we turn the Model into a Page other people can read, and create a link to share it.
> Build a \[board / monthly] summary Page with a row of our five key numbers and their change since last month, a cash runway gauge, and a slider so I can try different hiring dates and see the effect on cash.
The gauge and the slider are Canvas blocks: custom visuals Ari writes for you from live Model data. Move the slider and notice that the chart changes but your saved assumptions do not. A slider previews; it never changes the Model.
Select **Browse Canvas block library**, the palette icon at the bottom left of the message box, pick a template that looks useful, and select **Add (1)**. The template joins your message as a chip. Send the message, and Ari builds it.
Select **Share** in the Page header. Under **External access**, select **Create link**, then select the copy button on the new link, **Copy external link**.
Paste the link into a private or incognito browser window. Notice that the Page opens without a sign-in, and that you can read it but not change it. Anyone you send the link to sees the same.
You are done when your Page opens through its link.
## Day 4: Test a decision in a Scenario
Today we see what a decision costs before we make it, without changing your plan.
> Create a Scenario called \[Hire two engineers in Q1]. \[Add two engineers at \$X per year starting in January.] Compare cash and cash runway with Main, and add the comparison to my \[board] Page.
Notice the Scenario picker in the sidebar, above your Pages. Main is your plan; the new Scenario holds only the hires. On the Page, the comparison shows both versions of cash side by side.
You are done when your Page shows what the decision does to cash runway. To learn how Scenarios stay in step with Main, read [Scenarios and Snapshots](/concepts/scenarios).
## Day 5: Pressure-test your Model
Every company is different, so today we ask Ari where your Model is weakest.
> Review my Model without changing it. Which assumptions look weakest compared with our history? What is missing, and what should I build next?
Because you said not to change anything, Ari reads and reports. Nothing in the Model moves.
Pick the suggestion that matters most, such as checking a growth rate against your Actuals or [building headcount from payroll](/guides/build-a-headcount-plan), and ask Ari to do it.
Notice that the Pages that show the change update as Ari saves it.
You are done when you have fixed or built one thing Ari flagged.
## Day 6: Let Ari keep watch
Today we set up a weekly update, so you hear what matters without opening the app.
A Trigger runs a prompt on a schedule and sends you the result:
> Every Monday at 8am \[your time zone], summarize last week's cash in and out, show our current cash runway, and flag any expense that looks different from our recent pattern. Include a link to my \[board] Page.
Ari asks whether to send results to your Slack direct messages or your email. Choose one.
> List my Triggers.
Notice the name and schedule Ari saved.
You are done when you have one Trigger saved.
## What you built
In one week you went from a first draft to a working setup: a forecast you can explain, a Page you can share, a decision tested in a Scenario, a Model you have pressure-tested, and a weekly update that arrives on its own.
## Next steps
* Invite a teammate to work in the Model with you. Ask Ari, "Invite \[[name@company.com](mailto:name@company.com)] to the workspace," or see [Invite or remove teammates](/guides/invite-teammates).
* After your books close each month, compare your Actuals with your Forecast and update the assumptions that turned out wrong: [Update your plan after you close the month](/guides/close-the-month).
* Teach Ari a process you repeat, such as your month-end review: [Save a workflow as a Skill](/guides/save-a-skill).
* Look up more prompts in the [Prompt library](/reference/prompt-library).