# Load a CSV

> Put a file straight into your warehouse with no connector and no schedule. sanda reads the header, types every column and lands the rows in a table.

A CSV upload is the quickest way to get a file into your warehouse: a budget, a price list, a lookup table, an export from a system sanda does not connect to. There is no connector and no schedule. sanda reads the header row, works out a type for each column and lands the rows as a table in your warehouse's `raw` schema.

Owners, admins and members can load a CSV. Your warehouse has to be ready first. See [Provision your warehouse](https://docs.sanda-os.com.au/warehouse/provision).

## Load a file

:::steps
1. **Open the upload page.** Go to **Data · Connections** and press **Load a CSV**. You can also reach it from the first step of **New connection**.

2. **Choose the file.** Drop it on the panel that reads **Drop a CSV here, or click to choose**, or click to browse. The panel notes what is accepted: **Comma, semicolon or tab separated · header row required · up to 8 MB**.

3. **Name the table.** **Table name** starts as the file's name without its extension. The hint underneath shows where the rows will land, for example **Lands as raw.csv__monthly_budget in your warehouse.**

4. **Say what to do if the table exists.** **If the table already exists** offers **Replace it** and **Append to it**. See [Replace or append](#replace-or-append).

5. **Choose a destination.** If you have more than one warehouse ready, **Destination warehouse** appears. With one, there is nothing to choose.

6. **Press Load it.** sanda reads the file, types it and lands the rows. When it finishes, the page reports how many rows landed and lists the columns with their types.
:::

The result page offers **Load another file**, and **Learn from my data**, which asks cherry to read the new table and propose what it means for you to accept. See [Learn from your data](https://docs.sanda-os.com.au/semantic-fluid/learn).

## What the file has to look like

| | Limit |
|---|---|
| File types | The file chooser offers `.csv`, `.tsv` and `.txt` |
| Size | 8 MB |
| Rows | 250,000 |
| Columns | 250 |
| Header | The first row must name the columns |
| Delimiter | Comma, semicolon or tab. sanda reads it from the header row |
| Encoding | UTF-8 |

A few things that real files do are handled for you. Quoted fields can contain the delimiter, line breaks and doubled quotes. Both Unix and Windows line endings are read. A byte order mark at the start is ignored. Rows shorter than the header are padded with empty cells. Completely blank lines are skipped.

For a file over the limits, use the [S3](https://docs.sanda-os.com.au/connectors/s3) or [SFTP](https://docs.sanda-os.com.au/connectors/sftp) connector, which read files from storage on a schedule.

## Column names

sanda cleans every header into a name that is safe to query. It turns letters into lowercase, replaces each run of characters other than `a` to `z` and `0` to `9` with one underscore, and trims underscores from the ends. It does not keep the original.

| In the file | Lands as |
|---|---|
| `Revenue ($)` | `revenue` |
| `2024 total` | `c_2024_total` |
| A blank header | `column_5`, numbered by its position |
| A second `date` beside the first | `date_2` |

Accented and other non-English letters are replaced too, so `Ünïcode` becomes `n_code`. Columns are limited to 58 characters. Rename anything important in the file before you load it.

## How types are chosen

sanda reads the whole file, not a sample, and gives each column the strongest type that every value in it supports. One value that does not fit makes the column text, so nothing is ever lost and a late surprise cannot fail a load halfway through.

| If every non-empty value is | The column is |
|---|---|
| A whole number of up to 18 digits | `bigint` |
| A number, with an optional decimal point or exponent | `numeric` |
| `true` or `false`, in any case | `boolean` |
| A date written `2026-09-30` | `date` |
| Timestamps such as `2026-09-30 14:30` or `2026-09-30T14:30:00Z`, alone or mixed with plain dates | `timestamptz` |
| Anything else | `text` |

Empty cells land as nulls, not as empty strings. A column with nothing in it at all is text.

The rules are strict, so a few common formats stay as text:

- Numbers with thousands separators or currency symbols, such as `1,200` or `$12.50`.
- Dates written `30/09/2026` or `30 Sep 2026`.
- `yes` and `no`. A column of `1` and `0` becomes `bigint`, not `boolean`.

And one goes the other way: a column of values with leading zeros, such as postcodes or account numbers, is read as whole numbers, so `0800` lands as `800`. If the zeros matter, put a character in front of every value so the column is read as text, or load the table through an assistant, which can declare a column's type.

If you want a number or a date, write it plainly in the file: `1200`, `12.50`, `2026-09-30`. You can also cast text columns in a [view](https://docs.sanda-os.com.au/modelling/views) after loading.

## Where the table lands

The table is named `raw.csv__<name>`. sanda builds the name from the **Table name** you gave it: lowercase, non-alphanumeric characters turned into underscores, a `.csv`, `.tsv` or `.txt` ending removed, and cut at 40 characters. A name that starts with a digit gets `t_` in front, so `2026 budget` lands as `raw.csv__t_2026_budget`.

The `csv__` prefix keeps hand-loaded tables apart from tables a connection lands, so a CSV can never collide with a sync's table.

Every table gets one extra column of sanda's own, `_becca_loaded_at`, holding when the row was loaded. Like the other loading columns, it stays out of your semantic fluid. If your file has a column with that name, sanda renames it `becca_loaded_at`.

A CSV table counts toward your warehouse's storage in the same way a synced table does, and the storage limit applies to it. The load is recorded in **Overview · Recent activity** as **CSV loaded into warehouse**.

## Replace or append

**If the table already exists** decides what happens when a table with that name is already there.

- **Replace it** swaps the table's rows for the file's. The old rows are gone. The whole load is one transaction, so a file that fails to load leaves the existing table as it was.
- **Append to it** adds the file's rows to what is there. The columns must match the table's: the same names, none missing and none extra. If they do not, nothing loads and the message names the differences, for example **These rows don't match raw.csv__budget (new columns region; missing owner). Load with Replace, or use another table name.** Append also creates the table when it does not exist yet.

Append casts each value into the type the existing column already has. A value that cannot be cast, such as text in a `bigint` column, fails the whole load.

### Replacing a table that is in use

A table nothing reads yet is dropped and made again in the file's shape. Once something reads it, such as your semantic map or a view of your own under **Data · Modelling**, sanda keeps the table and replaces its rows in place, so the map and your views go on working.

If the new file's columns differ from the table's:

- **A column the file adds** goes on the end of the table. The map keeps showing the columns it had, so a new column is in the table but not yet on the map.
- **A column the file no longer has** comes off the table, and **a column whose type changed** takes its new type. If the map reads that column, sanda rebuilds the map's view over the new columns for you. Anything on the map built on a column that has gone stops working until you change it.
- **A view of your own that reads a column the file drops or retypes** stops the load, because sanda never removes your views. Nothing changes, and the message names the view, for example **derived.budget_by_region reads owner of raw.csv__budget, and this file leaves that column out or gives it a different type. Keep it as it was, or change that view under Data · Modelling, then load again.**

If sanda cannot rebuild the map's view after a replace, the rows still land and the page says so: questions on the table fail until the view is rebuilt. Load the file again, or ask sanda support.

## Let an assistant add rows

The checkbox **Open it for intake** turns the table into a place an assistant can keep adding to. A connected assistant holding the intake permission may then add rows to this table, matching its columns, and do nothing else to it: it cannot replace the table, delete rows or touch any other table. It suits a log someone keeps by talking, such as a daily habit list, a form or a running list of jobs.

Only owners and admins can open a table for intake. If a member ticks the box, the rows still land, and the page says only an owner or admin can open the table. Close a table for intake at any time in the **Intake tables** panel, under **Settings · Integrations**. Closing it keeps the rows and stops the assistant adding more.

Someone with the right permission can also land a table, and add to one, by talking to an assistant that is connected to sanda. See [Agent tokens](https://docs.sanda-os.com.au/mcp/agent-tokens) and [MCP tools](https://docs.sanda-os.com.au/mcp/tools).

## When a load fails

| Message | What to do |
|---|---|
| The file is empty. | Check you chose the right file |
| The first row must name the columns. | Add a header row |
| There are no data rows under the header. | The file has a header and nothing else |
| That's 300 columns, and uploads cap at 250. | Split the file or drop columns |
| That's 300,000 rows, and uploads cap at 250,000. For bigger drops, use the S3 or SFTP connector. | Use a connector, or split the file |
| Couldn't parse the CSV: a quoted field never closes, so the file may be truncated. | Reopen the file and check it was saved whole |
| This warehouse is at its storage ceiling, so ingest is paused. | Raise the storage limit or clear space. See [Usage and suspension](https://docs.sanda-os.com.au/warehouse/usage-and-suspension) |
| derived.… reads … of raw.csv__…, and this file leaves that column out or gives it a different type. | A view of yours reads that column. See [Replacing a table that is in use](#replacing-a-table-that-is-in-use) |
| Loading failed: … | The rest of the message says why |

For anything else, see [Troubleshoot connections](https://docs.sanda-os.com.au/connections/troubleshooting).

:::links
- [Learn from your data](https://docs.sanda-os.com.au/semantic-fluid/learn): Have cherry propose what the new table means.
- [Explorer](https://docs.sanda-os.com.au/warehouse/explorer): Look at the table and its columns.
- [Add a connection](https://docs.sanda-os.com.au/connections/add-a-connection): For a system that keeps producing rows.
:::
