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

# Uploads

> Send a large markdown or HTML file with curl and pass its upload_id to a write tool, instead of pasting the whole body into a tool argument.

Hand a document or page body to Sidecars by reference. The host uploads the
file with a shell command, then passes a short `upload_id` to
[`create_document`](/reference/documents#create_document),
[`edit_document`](/reference/documents#edit_document),
[`create_page`](/reference/pages#create_page) or
[`write_page`](/reference/pages#write_page) in place of the content.

Inline content is fine for a section or a short page. Use an upload when the
content is already a file on disk or is larger than about 8 KB: an inline
argument makes the model re-type every byte (a 100 KB file is roughly 25,000
output tokens), and past a few kilobytes some hosts need several chunked
calls or refuse the argument outright. An upload sends the same file in
seconds.

| Tool | Scope | Kind |
| - | - | - |
| [`request_upload_url`](#request-upload-url) | `documents:write` | write |

## request\_upload\_url

**Scope:** `documents:write` (step-up) · **Kind:** write · **Annotations:** `readOnlyHint: false`, `destructiveHint: false`, `idempotentHint: false`, `openWorldHint: false`

> Upload a markdown or HTML file by reference instead of pasting it into a
> tool argument. Use it when the content is already a file on disk or is
> over \~8 KB and you can run a shell command: call this, run the returned
> `curl` command with your file's path (expect HTTP 200), then pass
> `upload_id` to create\_document / edit\_document (format markdown) or
> create\_page / write\_page (format html) in place of the content.

### Parameters

<ParamField body="format" type="'markdown' | 'html'" required>
  `markdown` for `create_document` / `edit_document`, `html` for
  `create_page` / `write_page`.
</ParamField>

### Returns

<ResponseField name="upload_id" type="string">Pass as `upload_id` to the write tool.</ResponseField>
<ResponseField name="upload_url" type="string">Single-use `PUT` URL on the server's `/uploads/` route.</ResponseField>

<ResponseField name="method" type="'PUT'" />

<ResponseField name="content_type" type="string" />

<ResponseField name="max_bytes" type="number">400,000 for both formats.</ResponseField>
<ResponseField name="url_expires_at" type="string">ISO time; the URL stops accepting a body after 5 minutes.</ResponseField>
<ResponseField name="upload_expires_at" type="string">ISO time; an uploaded file waits 30 minutes for the write that uses it.</ResponseField>
<ResponseField name="curl" type="string">A ready-to-run command; replace `<path>` with the file's path and keep the double quotes, so a path with spaces stays one argument.</ResponseField>

### Uploading

```bash theme={null}
curl -sS --fail-with-body -X PUT \
  -H 'Content-Type: text/markdown; charset=utf-8' \
  --data-binary "@my report.md" '<upload_url>'
```

A successful upload answers `200` with `{"upload_id", "byte_size", "sha256"}`.

| Status | `error` | Cause |
| - | - | - |
| 400 | `empty` | Empty or whitespace-only body |
| 404 | `not_found` | Unknown upload or wrong URL secret |
| 410 | `already_used` / `expired` | The URL was used once already, or is older than 5 minutes — request a new one |
| 413 | `too_large` | Over `max_bytes` |
| 415 | `not_utf8` | The body is not UTF-8 text |

### Using the upload

Pass `upload_id` instead of `content_markdown` (documents) or `html` (pages) —
never both. Everything else about the write is unchanged: the same role
checks, `expected_version` rules, human-edit protection, checkpoints and
audit. An uploaded markdown body is written whole, up to 400,000 bytes,
rather than stopping at the 60,000-character inline cap.

The write that lands uses the upload up. A refused write — a version
conflict, a busy document — leaves it in place, so retry with the same
`upload_id`.

| `error` | Cause |
| - | - |
| `upload_not_found` | No upload with that id belongs to you (another person's id answers the same way), or it was already used |
| `upload_not_received` | The `PUT` did not complete |
| `upload_in_use` | Another write is applying this upload. Don't upload again yet: wait for that write's result. If it was refused, retry with the same `upload_id`; upload again only once it ended without landing and the document lacks the content |
| `upload_expired` | Older than 30 minutes |
| `upload_wrong_format` | A `markdown` upload passed to a page tool, or `html` to a document tool |

### Permissions and side effects

* The upload URL only stores bytes. It cannot create or change anything on
  its own; the write call does, authenticated as you.
* An upload is bound to the person and workspace that requested it, is
  single-use, and deletes itself (with its bytes) when used or after 30
  minutes.
* At most 20 unused uploads per person at a time (`too_many_uploads`).
* Uploaded HTML is rendered exactly like inline HTML: scripts, handlers and
  embeds are removed at render, in a sandboxed frame.

### Limitations

* Needs a host that can run a shell command (Claude Code, Codex, Cursor's
  agent). Hosts without one send content inline.
* Hosts that sandbox network access need the Sidecars server's domain on
  their allowlist for the `curl` to reach it.


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