> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cfo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# How imported data loads

> 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.md). For a file's or a connection's messages, see [Connection statuses and actions](/reference/connection-statuses.md).

## 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.md).

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.md#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.md#formula-range), so it reads source rows in every period, including months after [Last close](/reference/glossary.md#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.md).

## 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**. Both are QuickBooks' accrual-basis reports, as QuickBooks returned them when a sync last wrote each month; [How much history loads](#how-much-history-loads) says which months a refresh rewrites. The balance sheet dates each month's balances by the month's last day, so the month in progress keeps one **Period** from its first sync until it closes. |
| Xero | **Xero Income Statement** or **Xero Income Statement by Tracking Category**, **Xero Balance Sheet** | Amounts by account, with **Entity** and **Currency**. The tracking-category statement has one row per journal line and up to three tracking categories as numbered column pairs. See [Xero](/reference/integrations.md#xero). |
| 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.md) and [Consolidate entities](/guides/consolidate-entities.md).

## 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.** A setup saves the tool's definition as the server described it when Ari saved the setup, and each sync page checks the server's current definition against it. If the server changes the tool, the sync stops without saving a result, and Ari must save the setup again. Ari cannot save an enabled setup while the server's tool list cannot be read.

| 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 tool call in a sync page from an MCP server | 45 seconds. Connecting to the server, listing its tools, and the call each get 45 seconds. A time limit the tool's own description names is the server's own; cfo.ai's applies either way. |
| 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.md)
* [Check that your numbers match your books](/guides/check-imported-data.md)
* [Refresh data or change when it refreshes](/guides/refresh-data.md)
* [Upload a CSV as a data source](/guides/upload-a-csv.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.