# Files

> Upload files for use in tasks.

**URL:** https://inference.sh/docs/api/rest/files
**Last updated:** 2026-10-12

---

Upload files for use in tasks.

See [REST overview — response format](/docs/api/rest/overview#response-format).

---

## Upload File

Uploading is two requests: `POST /files` creates the file record and returns a presigned `upload_url`; the bytes are then `PUT` to that URL. The SDKs (`client.files.upload`) and `belt file upload` do both steps.

### 1. Create the record

`POST /files`

`Content-Type: application/json`

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `files` | array | Yes | One entry per file |
| `files[].uri` | string | Yes | Send `""`; the server assigns it |
| `files[].filename` | string | No | File name |
| `files[].content_type` | string | No | MIME type |
| `files[].size` | number | No | Size in bytes |
| `files[].path` | string | No | Custom storage path |
| `category` | string | No | Storage category |

### Response

An array of file DTOs, one per entry:

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | File ID |
| `uri` | string | File URI for use in inputs |
| `upload_url` | string | Presigned URL to `PUT` the bytes to |
| `filename` | string | File name |
| `content_type` | string | MIME type |
| `size` | number | Size in bytes |

```json
[
  {
    "id": "file_abc123",
    "uri": "https://cloud.inference.sh/t/abc123/image.png",
    "upload_url": "https://storage.example/...signed...",
    "filename": "image.png",
    "content_type": "image/png",
    "size": 102400
  }
]
```

### 2. Upload the bytes

`PUT {upload_url}` with the raw body and a `Content-Type` header. No inference.sh auth header; the URL is presigned and short-lived. A `2xx` means the file is ready and `uri` can be used in task inputs.

---

## cURL Example

```bash
# 1. create the record
curl -X POST https://api.inference.sh/files \
  -H "Authorization: Bearer inf_your_key" \
  -H "Content-Type: application/json" \
  -d '{"files":[{"uri":"","filename":"image.png","content_type":"image/png","size":102400}]}'

# 2. upload the bytes to upload_url from the response
curl -X PUT "https://storage.example/...signed..." \
  -H "Content-Type: image/png" \
  --data-binary @/path/to/image.png
```

CLI equivalent: `belt file upload <path>` (add `--json` for `{filename, size, type, uri}`; supports multiple paths in one command).

---

## Downloading files (CLI)

`belt file download`, `belt app run --download`, and other CLI paths that fetch file bytes use the same HTTP client. URLs come from API responses (presigned storage URLs, task output `url` fields, skill or knowledge file links) or from arguments you pass on the command line.

### Allowed URLs

- **Schemes:** `https` everywhere; `http` only for loopback hosts (`localhost`, `127.0.0.1`, `::1`) or for the **same origin** as your configured API endpoint (typical for a local API on `http://127.0.0.1:3020`).
- **Host required:** Relative paths, `file://`, `ftp://`, and other schemes are rejected.

Presigned third-party URLs (for example Cloudflare R2 or S3) must use `https`.

### API credentials on download

When the download URL points at the configured inference API (same scheme and host as `INFERENCE_API_KEY` / login target, with a matching API path prefix), the CLI sends your session or API key (`Authorization`, `X-Team-ID`, and cookies when present).

For any other host — including presigned object storage — those headers are **not** sent. Matching uses the parsed URL origin, not a string prefix on the URL, so lookalike hosts such as `https://api.inference.sh.evil.example` do not receive your credential.

If the server responds with a redirect, the CLI follows up to **five** hops. Each hop is checked with the same URL rules. When a redirect leaves the **original** request origin, `Authorization`, `Cookie`, and `X-Team-ID` are stripped before the next hop (presigned URLs should not rely on inference auth).

### Common errors

| Message | Typical cause |
|---------|----------------|
| `unsupported scheme` | Non-HTTP(S) URL, or a relative path passed where a full URL is required |
| `https required` | Plain `http` to a non-loopback, non-API host (for example `http://example.com/file`) |
| `stopped after 5 redirects` | Redirect loop or a chain longer than five hops |
| `missing host` | Malformed URL |

For command syntax and examples, see [CLI — Files](/docs/extend/cli-setup#files-cli) and [CLI — Output files](/docs/extend/cli-setup#running-apps).

---

## List Files

`POST /files/list`

Cursor-paginated list of files in your workspace. `GET /files` accepts the same query parameters.

### Request body

| Field | Type | Description |
|-------|------|-------------|
| `limit` | number | Page size (default 10) |
| `cursor` | string | Pagination cursor from a previous response |
| `direction` | string | `next` or `prev` |
| `filters` | array | Field filters (for example `content_type` with `like` operator) |
| `search` | object | `{ "term", "fields" }` for substring match (CLI `--search` / `-q` searches `filename`) |

### Response

Standard cursor list shape: `items` (array of file DTOs), `has_next`, `has_previous`, `next_cursor`, `prev_cursor`.

CLI equivalent: `belt file list` (add `--json` for the raw payload; `--type image` filters by MIME category; `--search photo` or `-q avatar` filters by filename).

---

## Delete File

`DELETE /files/{id}`

Permanently delete a file by ID. Requires `files:write` scope.

CLI equivalent: `belt file delete <id>` (add `--force` or `--yes` to skip the confirmation prompt).

To find the IDs of the files behind a task's input and output URLs, use [`GET /tasks/:id/files`](/docs/api/rest/tasks#list-task-files). To delete all of a task's files in one call, use [`DELETE /tasks/:id/files`](/docs/api/rest/tasks#delete-task-files).

---

## Using Uploaded Files

Use the returned `uri` in task inputs:

```bash
curl -X POST https://api.inference.sh/run \
  -H "Authorization: Bearer inf_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "app": "image-processor",
    "input": {
      "image": "inf://files/abc123/image.png"
    }
  }'
```

---

## Supported Types

Common MIME types:
- `image/png`, `image/jpeg`, `image/webp`
- `audio/mpeg`, `audio/wav`
- `video/mp4`
- `application/pdf`
- `text/plain`
