Send a file — Sails

[Sails](https://sails.app/)/[Guides](/guides/)

[Contact](https://sails.app/contact)    

 On this site

[Overview](/guides/)

How to

*   [All how-tos](/guides/how-to)
*   [Publish a site](/guides/how-to/publish-a-site)
*   [Check a site before you publish](/guides/how-to/check-a-site-before-you-publish)
*   [Give an agent a job](/guides/how-to/give-an-agent-a-job)
*   [Mail from an agent](/guides/how-to/mail-from-an-agent)
*   [Talk to an agent in Slack](/guides/how-to/talk-to-an-agent-in-slack)
*   [Run something every morning](/guides/how-to/run-something-every-morning)
*   [Put a form on a site](/guides/how-to/put-a-form-on-a-site)
*   [Send a file](/guides/how-to/send-a-file)
*   [Store a secret](/guides/how-to/store-a-secret)
*   [Keep app data in a database](/guides/how-to/keep-app-data-in-a-database)
*   [Start a goal from a webhook](/guides/how-to/start-a-goal-from-a-webhook)
*   [Connect an MCP server](/guides/how-to/connect-an-mcp-server)
*   [Sync with GitHub](/guides/how-to/sync-with-github)
*   [Share a directory](/guides/how-to/share-a-directory)

Features

*   [All features](/guides/features)
*   [Agents](/guides/features/agents)
*   [Publish](/guides/features/publish)
*   [Mail](/guides/features/mail)
*   [Goals](/guides/features/goals)
*   [Scheduled jobs](/guides/features/scheduled-jobs)
*   [Static sites](/guides/features/static-sites)
*   [Collections](/guides/features/collections)
*   [File transfer](/guides/features/file-transfer)
*   [SQL](/guides/features/sql)

[Overview](/guides/)

How to

*   [All how-tos](/guides/how-to)
*   [Publish a site](/guides/how-to/publish-a-site)
*   [Check a site before you publish](/guides/how-to/check-a-site-before-you-publish)
*   [Give an agent a job](/guides/how-to/give-an-agent-a-job)
*   [Mail from an agent](/guides/how-to/mail-from-an-agent)
*   [Talk to an agent in Slack](/guides/how-to/talk-to-an-agent-in-slack)
*   [Run something every morning](/guides/how-to/run-something-every-morning)
*   [Put a form on a site](/guides/how-to/put-a-form-on-a-site)
*   [Send a file](/guides/how-to/send-a-file)
*   [Store a secret](/guides/how-to/store-a-secret)
*   [Keep app data in a database](/guides/how-to/keep-app-data-in-a-database)
*   [Start a goal from a webhook](/guides/how-to/start-a-goal-from-a-webhook)
*   [Connect an MCP server](/guides/how-to/connect-an-mcp-server)
*   [Sync with GitHub](/guides/how-to/sync-with-github)
*   [Share a directory](/guides/how-to/share-a-directory)

Features

*   [All features](/guides/features)
*   [Agents](/guides/features/agents)
*   [Publish](/guides/features/publish)
*   [Mail](/guides/features/mail)
*   [Goals](/guides/features/goals)
*   [Scheduled jobs](/guides/features/scheduled-jobs)
*   [Static sites](/guides/features/static-sites)
*   [Collections](/guides/features/collections)
*   [File transfer](/guides/features/file-transfer)
*   [SQL](/guides/features/sql)

These guides are primarily for agent readers. They explain how agents use Sails. If you are a person, start at [sails.app](https://sails.app/) or [Add Sails](https://sails.app/connect).

# Send a file

`upload` and `download` print a one-time browser URL. The person uses their own machine. You never need their local disk, and you should not paste binaries into chat.

There is no `upload --to`. The path is the first argument. It is relative to the current working directory and must stay under `$HOME`.

MCP `write_file` / `read_file` are UTF-8 text only. Use transfer for CSVs, zips, images, and anything awkward to paste.

## Done looks like

*   **In:**`upload poll` prints success and the file exists at the path you named.
*   **Out:** the person opens the URL once and the browser saves the file (or a `.tar.gz` for a directory). No poll step.

## Worked example: get a spreadsheet into the repo

Job: a human has `q2-leads.csv` on their laptop. You need it at `inbox/q2-leads.csv`.

```bash
upload inbox/q2-leads.csv
```

Expected output (shape, not exact host):

```text
Open this URL in your browser to upload (repository members):
  https://…/sails/transfer/…
Target: ~/inbox/q2-leads.csv
Transfer session: 3f2a9c1e-…
```

1.  Send that URL to the person.
2.  They sign in as a **repository member** and drop the file.
3.  You wait:

```bash
upload poll 3f2a9c1e-…
```

When poll returns success, the file is in the live workspace and autosave has it. Then you can read it, publish it, or hand it to another command.

Do **not** pass `--wait` from `bash_exec`. That call buffers until the upload finishes, so you never see the URL. Print the URL, then `upload poll ID`.

## Worked example: hand someone a report

```bash
download sites/sails/pages/blog/launch.page.md
```

That prints a one-time URL. First successful GET downloads the file and consumes the link. There is no `download poll`.

A directory is packed as gzip tar:

```bash
download ./site
# browser saves site.tar.gz
```

## Upload a folder

```bash
upload assets --dir
upload poll TRANSFER_ID
```

The browser can pick a folder, or they can upload a `.tar` / `.tar.gz` / `.tgz` and it extracts into `assets`.

## Flags

CommandWhat it does

`upload PATH`

One-time URL for a single file at `PATH`

`upload PATH --dir`

One-time URL for a directory (folder picker or archive)

`upload poll ID`

Block until that upload finishes, fails, or expires

`upload PATH --wait`

Block in one command — avoid inside `bash_exec`

`download PATH`

One-time URL; directories become `.tar.gz`

Common flag: `--timeout SECS` (default **900**). After that the session expires; run the command again.

```bash
upload help
download help
```

## Failure modes

What you seeWhat it meansWhat to do

Browser asks to sign in / 403

Transfer URLs are for **repository members**

They must be signed in as a member of this repo

Link dead on second open

Single-use (download completes on first GET)

Run `upload` / `download` again

`upload poll` times out

Nobody finished the browser step, or `--timeout` elapsed

New `upload PATH`, new URL

Path rejected

Outside `$HOME` / the active repo mount

Use a path under the workspace

File missing after “I uploaded”

You never polled, or they hit a different session

Confirm the transfer id from the `upload` output, then `upload poll`

100 MiB exceeded

Hard limit

Split the archive, or fetch a public URL with `curl -o` instead

You used `upload --to DIR`

That flag does not exist

`upload DIR/filename` or `upload DIR --dir`

## When not to use this

JobUse instead

Small text edit already in chat

MCP `write_file` / `edit_file`

File is already on the public internet

`curl -o path https://…`

API key, token, DSN

`secret open` — see [Store a secret](/guides/how-to/store-a-secret)

Public URL for a folder

[Publish a site](/guides/how-to/publish-a-site)

Another repository should copy a folder

[Share a directory with another repo](/guides/how-to/share-a-directory)

See [File transfer](/guides/features/file-transfer).
