Skip to content

Reference / Uploads

create_file_upload

Start uploading a CSV or Excel file. Returns a one-time URL and the exact curl command, so the file never passes through the conversation.

Starts an upload of a CSV or Excel (.xlsx) file into a workspace, for a dashboard built from a spreadsheet rather than from a warehouse. The call does not take the file's contents. It returns a one-time upload URL and the exact curl command to run in the agent's own shell, so the file goes from the user's disk straight to Dashies and never passes through the conversation.

A client with no shell cannot run the command, so it cannot upload.

What an upload can be used for

Uploading a file works as described here, and so does AUTHORING against one: once an upload is ready, pass the workspace's uploaded-file source as connection and the upload id as upload to introspect_schema, explore_data and validate_cube_sql. PUBLISHING a dashboard from an uploaded file also works: name that same connection in the spec's source.connection and the upload id in source.upload, and the dashboard reads that one file and refreshes from it on its schedule. get_file_upload reports where an upload stands, and returns the catalog once the file is converted. An upload can still stop once the file arrives, with a failure saying conversion is not available for that workspace, which is final for that upload: uploading the same file again will not change it.

Behaviour hints for clients: not read-only, not idempotent, not open-world.

Parameters

ParameterTypeRequiredNotes
filenamestringnoThe file's name, for example sales.csv. At most 255 characters, with no control characters. Required for an upload and refused-as-missing without it; omit it only when passing reconvert_of, which takes the original's name. It is not merely a label: a CSV's table is named from it, so converting the same file under a different name would rename the tables.
bytesintegernoThe file's exact size in bytes. Worth passing: a file over the limit is refused before anything is sent, and an upload whose body is a different size is refused before it is stored.
reconvert_ofstringnoConvert an upload you already made again, with overrides, instead of uploading a file. Nothing is uploaded, the same stored bytes are read again, and the original upload is untouched. The answer has no url and no curl: it returns a new upload id to poll with get_file_upload.
overridesobjectnoHow to read the file differently, for a re-convert only - passed on its own it is refused, because a first upload has no census to check an override against. Keys: types (an object keyed by the column's header, valued with a type spelled exactly as the catalog spells one - BIGINT, DECIMAL(12,2), VARCHAR, DATE), dateformat, timestampformat, nullstr, thousands, delim, quote, header, skip, names, sheets, range, encoding. Each is validated against the census this conversion takes, never against what the first conversion concluded: your overrides change how the file is read, so a fresh reading can legitimately disagree with the catalog you chose the override from. An override the data does not support fails that conversion and names your value rather than silently dropping data.
workspacestringnoWorkspace slug, only for a legacy connection authorized for no workspace.

filename reads no in the Required column, and the reason is worth a sentence rather than an asterisk: this tool takes two shapes. An upload needs a filename and is refused by name without one; a re-convert takes the original's name from the ledger and must not re-declare it. A schema-level required list cannot say "required on one of two shapes", so the list is empty and the tool does the refusing.

Returns

Upload 0b6f0c1e-4c1d-4f55-9a57-2a1f0f7d9e21 created for "sales.csv" in the "acme-co" workspace.

Run this command exactly as printed, replacing <path> with the path to the file. Its ticket works once and expires at 2026-09-17T18:15:00Z:

curl --fail-with-body -sS -T "<path>" -H "authorization: Bearer <ticket>" -H "content-type: application/octet-stream" "https://dashies.ai/api/uploads/0b6f0c1e-4c1d-4f55-9a57-2a1f0f7d9e21"

The file goes straight to Dashies: do not read it into this conversation. The largest file accepted is 95000000 bytes. When the command succeeds, call get_file_upload with upload "0b6f0c1e-4c1d-4f55-9a57-2a1f0f7d9e21" and wait_seconds 45, and call it again until the status is ready or failed.

BEGIN_JSON
{"mode":"upload","upload":"0b6f0c1e-...","connection":"5d2c...","url":"https://dashies.ai/api/uploads/0b6f0c1e-...","expires_at":"2026-09-17T18:15:00Z","max_bytes":95000000,"curl":"curl --fail-with-body ..."}
END_JSON

mode says which of two shapes the answer is. A re-convert answers mode: "reconvert" and has no url and no curl, because nothing is uploaded:

Converting upload 11111111-... again as 0b6f0c1e-..., with the overrides you named.
- The same stored bytes are read again. Nothing is uploaded and the original upload is untouched.
- Status: converting. Call get_file_upload with 0b6f0c1e-... and wait_seconds until it is ready or failed.

BEGIN_JSON
{"mode":"reconvert","upload":"0b6f0c1e-...","connection":"5d2c...","reconvert_of":"11111111-...","status":"converting","max_bytes":95000000}
END_JSON

The <ticket> in a real response is a one-time secret. It is stored only as a hash, it works once, and it expires at expires_at.

Running the command

The command PUTs the file to the URL. -T makes curl send the file with a content-length, which the upload requires, and --fail-with-body makes a refusal exit non-zero while still printing the sentence explaining it.

AnswerMeaning
200The file arrived. The body says whether its conversion started; follow it with get_file_upload.
404No upload is waiting at this address for this ticket: it was already used, it expired, or it never existed. Every one of those gets the same answer. Create a new upload.
411The request carried no content-length. Run the command as printed.
413The file is over the limit, or it is not the size the upload was created for.
400The file sent was empty. Check the path.
503Dashies could not take the file just now. The body says whether to run the same command again or create a new upload.

Limits

  • The largest file accepted is 95,000,000 bytes (95 MB). Split larger data into several files.
  • A ticket works once and expires; the response gives the exact time.
  • A workspace can have only a few uploads in progress at once; the refusal names the number.
  • Calls are rate-limited per user.

Errors

Refusals arrive as tool errors, each with a sentence naming what to do:

  • a file over the limit, or a missing or invalid filename, before anything is created;
  • You hold a view-only role in this workspace, so you cannot upload files to it. when the caller has a viewer seat;
  • A paid plan is required to upload a file. on a workspace with no paid plan or trial;
  • a sentence naming the data-connection limit when the workspace has no connection slot left for its uploaded files;
  • a sentence naming how many uploads are already in progress.

Scope

Which workspace the file belongs to comes from how this MCP connection was authorized. A workspace's uploads share one data connection, labelled Uploaded files, which the first upload creates; it uses one of the workspace's connection slots.