Guides
Build a dashboard from a file
Upload a CSV or Excel file through your AI tool, author against the catalog it produces, and publish a dashboard that reads that one file.
Not every set of numbers lives in a warehouse. If yours is a CSV or an .xlsx
workbook on your disk, you can hand that file to Dashies once and build a
dashboard on it, with the same spec, the same tiles and the same versioning as a
warehouse dashboard.
The file goes from your disk straight to Dashies. It is not read into your conversation with your AI tool, and it is not pasted into a spec.
Before you start
| You need | Why |
|---|---|
| A paid plan or a trial on the workspace | Uploading is gated the same way connecting a warehouse is. See Plans and what is gated. |
| A creator seat | A viewer seat may read the workspace's dashboards and may not add data to it. |
| A free connection slot, for the first upload only | A workspace's uploads share one data connection, created by the first upload and spending one of the workspace's slots. Later uploads reuse it and need no slot. See Connections and scope. |
| An AI client that can run a shell command | The bytes are sent by a curl line your AI tool runs. |
A client with no shell cannot upload
The upload is a one-time URL plus a curl command, run by the agent in its own
shell. That is what keeps the file out of the conversation, and it is also the
one requirement that cannot be worked around today: an AI client with no shell
cannot take this path at all.
There is no upload form in the Dashies web app, on any plan. The app has no authoring surface of any kind; a dashboard, and now a file, arrives through your AI tool.
What happens to the file
-
Your AI calls
create_file_upload.It passes the filename and, worth doing, the exact byte size. Nothing is sent yet. The answer is a one-time URL and the exact
curlcommand to run. The ticket in it works once and expires; the response prints the exact time. -
Your AI runs the command it was given.
The file is
PUTto Dashies. Only a few uploads may be in progress in one workspace at a time; if you are over that, the refusal names the number. -
Dashies converts the file, once.
A CSV becomes one table. A workbook becomes one table per visible worksheet that has rows: hidden sheets, chart sheets and empty ones are skipped, each with a warning in the catalog saying which and why. Name a hidden sheet in the
sheetsoverride to read it anyway.Each column's type is decided by reading the whole file rather than a sample, and the verdict is written down as a catalog.
-
Your AI polls
get_file_uploaduntil the status isreadyorfailed.A
readyanswer carries the catalog: every table, every column, its declared type, and the findings that kept a column as text. It also carries the SHA-256 of the bytes Dashies stored, which you can compare againstshasum -a 256on your own copy. -
Your AI authors against the upload.
introspect_schema,explore_dataandvalidate_cube_sqlall take the uploaded-file connection asconnectionand the upload id asupload. -
Your AI publishes.
The spec's
source.connectionis the uploaded-file connection andsource.uploadis the upload id. See Source.
What the SQL looks like
The statement returns records, not a summary
Your statement returns one row per underlying record, carrying the columns each number is worked out from, and each number is declared in the spec rather than worked out in the SQL. Dashies holds those rows and works the numbers out when a reader opens the page, so a statement that has already aggregated gets aggregated a second time.
Nothing refuses the wrong shape. A plain total survives the wrong grain, so a statement that groups reads as correct until somebody adds a count, and then the count is the number of groups you made rather than the number of records.
This is the held shape, the one a statement takes against a warehouse connection or the workspace's uploaded-file source, and an uploaded file is always on that side. So the four in-file modes on Datasets and the four modes are not a choice here, and a publish that asks for one is refused. Designing the cube has the shape's rules in full, and A warehouse dashboard is served states it on the warehouse side.
Your SQL runs against the tables the conversion made, so two things differ from every page under Connect a warehouse. Neither of them is the shape.
The table name comes from the file, not from you. A CSV's table is named
after the filename without its extension, folded down to a-z0-9_: Q3 Sales.csv
is queried as q3_sales. A workbook's tables are named from the sheet names the
same way, and because two sheet names can fold to one table name, a collision
gets a numeric suffix.
The folding has more rules than are worth carrying in your head: accents fold to
the base letter rather than to _, runs of punctuation collapse to a single _,
a name starting with a digit gains a t_ prefix, and a long one is truncated. So
the catalog is the authority - read the table name out of get_file_upload
rather than deriving it here.
Column names are kept exactly as the header spelled them, spaces and capitals included, so quote them and alias to the key grammar the spec wants:
select
"Order Date" as order_date,
"Region" as region,
"Net Amount" as net_amount
from q3_sales
That statement has no GROUP BY and no aggregate, and that is the record
grain the shape asks for rather than a simplification made for the example.
net_amount arrives as the column it is on each record, and the spec is where
you say to total it.
Four things about a header row are refused at conversion, each with a sentence
naming the column's position: a blank header, an over-long one, a
duplicate, and two headers that differ only in capitals. That last one is
the exception to headers being kept verbatim: your capitals are preserved, but
Region and region in one file cannot be told apart once quoted, so the file
is refused rather than guessed at.
Changing the data means a new upload
An upload is immutable, and nothing replaces a file in place. There is no
"update this file" call, and re-running the curl command against a spent
ticket does not work.
To publish newer numbers: create a new upload, send the new file, and republish
the spec with the new id in source.upload.
Dashies never picks the newest upload for you
source.upload is required on a file source and a publish without it is
refused. That is deliberate: if the newest upload won by default, republishing a
byte-identical spec would move the numbers on the page with nothing in the
document saying why, and no diff anywhere would show it. Naming the upload makes
a data change a document change.
If you are arriving from Tableau, this is the rule its own web upload works by as well: a file uploaded by hand is not refreshed for you, and new data means uploading a new version of the file.
A schedule does not fetch a new file
A cadence other than manual is accepted on a file source, with a warning,
rather than refused. It is worth knowing exactly what it does: a scheduled
refresh re-runs your SQL over the same converted file. That only changes the
numbers if the SQL is time-relative, such as a trailing window or anything
reading current_date. It cannot pick up a new file.
schedule: manual is the honest default here.
Getting the read wrong, and fixing it without re-uploading
If the conversion typed a column in a way you did not want, or read the CSV with
the wrong delimiter, encoding or header row, you do not upload the file
again. Pass reconvert_of with the original upload id and an overrides object
to create_file_upload: the same
stored bytes are read again under the new instructions, and the original upload
is left untouched.
Each override is checked against a census of the same stored bytes read under your new instructions, so an override the data does not support fails that upload and names your value instead of silently dropping rows.
The window for this is bounded. A failed upload's stored bytes are kept for a while and then dropped, and re-converting works only while they are there; after that you upload the file again. Reading a failed upload's status does not extend that window.
What is refused
| Refused | What you get |
|---|---|
A legacy .xls workbook, or a password-protected workbook | the file is a legacy .xls workbook or a password-protected workbook. Save it as .xlsx without a password. |
Another Office file, such as .docx or .xlsb | a sentence saying it is not an .xlsx, and to save it as one |
| A file that is not a spreadsheet at all, such as a PDF or an image | not a sentence about the format. See the note below. |
| A file over the size cap | refused before anything is sent when the upload declares its size, and at the PUT otherwise. See Limits. |
| A workspace with no paid plan or trial | A paid plan is required to upload a file. Uploaded files are available on paid plans and during a trial. |
| A viewer seat | You hold a view-only role in this workspace, so you cannot upload files to it. A workspace admin can change your role. |
| A workspace with no connection slot left | a sentence naming the data-connection limit |
A failed upload is not the end of it, and the sentence beside the failure token
decides what to do. Where it names an override, re-convert the same stored bytes
with overrides. Where the file itself is wrong, fix it and create a new upload.
Sending the wrong file does not get you a sentence about formats
Dashies decides what a file is from its first bytes, never from its name or
its content type. Only two shapes are recognised: a zip, which is where an
.xlsx lives, and a legacy Office container. Anything else is attempted as
text.
So a PDF or an image is not refused as a PDF or an image. It is read as text, fails on the first byte that text cannot contain, and comes back saying the file is not valid UTF-8 or carries a NUL byte, usually suggesting an encoding override. That advice is right for a mis-encoded CSV and useless for a PDF, so read the filename you sent before you reach for an override.
A file that genuinely is text but is not tabular, such as JSON or a log, is not
refused at all for being the wrong kind of thing. It either fails on columns or
rows, or converts into a table that is real and meaningless. Check the catalog
get_file_upload returns before you build on it.
The size cap is smaller than a file-import BI tool's, deliberately
FILE_UPLOAD_MAX_BYTES on Limits is the
whole of it: there is no multipart upload and no chunking, so a file over the
cap cannot be sent in pieces. Splitting the data across several files works, and
each one is its own upload.
Dashies does not claim parity here with a BI tool whose headline feature is importing a large file. Data past this cap belongs in a warehouse connection, which has no source-file ceiling at all.
How long an upload is kept
Ordinarily a reference keeps an upload alive. A converted upload that a live dashboard reads, or that a retained version names, is not collected while that reference exists. One that nothing references is removed after a grace period during which nothing has referenced or used it, so the copies left behind by iterating on a file do not accumulate forever.
That is why the version-retention rules matter here in a way they do not for a warehouse dashboard: pruning an old version can be what releases the last reference to an old upload. See Version history.
A failed upload is outside that rule: it is dropped on a shorter clock of its own whether or not anything names it. That costs you nothing, because a failed upload cannot be published in the first place.
Deleting the connection deletes the files, references and all
On every other data source, deleting the connection withdraws a credential and
the dashboards built on it keep their numbers. Here the connection is the data.
Deleting the workspace's Uploaded files connection moves every upload under it
to deleted, the ones live dashboards are reading included, and nothing refuses or
warns first.
The web app cannot do this, because the uploaded-file connection is not on the
Data sources page and there is nothing there to press. What can do it is a
tool call or a script naming the connection id, which is the same id
list_connections reports beside your uploads. Treat that id as the
workspace's spreadsheet data rather than as a connection you can drop and
re-add.
There is no undo. Recovering means uploading each file again and republishing every dashboard that read it against the new upload.
A workspace whose plan has ended has its uploaded files removed along with the rest of its stored data. That one is not silent: you are told before anything goes and the notice names the date. See Plans and what is gated.
Check it worked
Ask your AI tool for the workspace's connections. The uploaded-file row is
labelled Uploaded files, and it is the only row that carries an uploads
block: your recent uploads, each with its id, filename and status.
Ask your AI rather than the web app, because the app does not show this connection at all. It is hidden from the Data sources page, which lists the connections you create there. That is the app being consistent about having no upload surface rather than anything being wrong; see Connections and scope.
Then open the published dashboard and compare one tile against the same figure in the spreadsheet.
Next
Author a dashboard for the rest of the authoring loop, or Connections and scope for how the uploaded-file connection sits beside your warehouse ones.