# Files

Upload, list, read, download, keep and delete files.

A file is something a task reads or makes: a price list you upload for a worker to use, or a report a worker writes. Every file of a workspace is in one store, whether you uploaded it or a task made it.

Give a task an uploaded file by naming it in the task's `resources`, as `{ "type": "file", "file_id": "file_..." }`. A file is at most 500 MB (500,000,000 bytes), and a workspace keeps at most 1 TB (1,000,000,000,000 bytes) of files. [Files](/concepts/files) explains how a file reaches a task and how to get one back.

How a file was made decides when it goes:

| How the file was made | `scope` | When it is deleted |
| --- | --- | --- |
| Uploaded with [Upload a file](#upload-a-file) | `null` | Only by [Delete a file](#delete-a-file), or at its `expires_at` when you set one. |
| Made by a task | `{ "type": "task", "id" }` | With the task's history: when you delete the task, or when the free plan's 7 days pass. Not if you keep it first. |
| Made by work that belongs to no task | `null` | With the history of the work that made it, when the free plan's 7 days pass. Not if you keep it first. |
| Made by a task or by work with no task, then kept with [Keep a file](#keep-a-file) | `null` | Like an upload: only by [Delete a file](#delete-a-file). |

> [!NOTE]
> The shape is Claude's Files API. Here an upload can be downloaded, and a file a task made can be kept. Claude's uploads cannot be downloaded, and it has no keep.

## The file object

Every route on this page but download and delete answers a file, or a list of them. Times are RFC 3339 in UTC, to the millisecond.

- `type` (string): Always `file`.

- `id` (file id): The file's id: `file_` and 32 hex digits.

- `filename` (string): The file's name. An upload keeps the name its form gave, trimmed and cut to 255 characters, or `file` when it gave none.

- `mime_type` (string): The file's content type, such as `text/csv`. An upload that named none is `application/octet-stream`.

- `size_bytes` (integer): The file's size in bytes.

- `created_at` (time): When the file was made.

- `downloadable` (boolean): Whether the file's content can be downloaded. Every file is `true` today, an upload included.

- `expires_at` (time, or null): When the file expires: the time of the upload plus `expires_in_seconds`. `null` when it does not expire. An expired file cannot be downloaded or given to a task, and its details can still be read for 30 days.

- `scope` (object, or null): The task that made the file, as `{ "type": "task", "id" }`. `null` for an upload, for a file you kept, and for a file made by work that belongs to no task.

  - `type` (string): Always `task`.

  - `id` (task id): The task that made the file.

```json
{
  "type": "file",
  "id": "file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35",
  "filename": "price-list.csv",
  "mime_type": "text/csv",
  "size_bytes": 26,
  "created_at": "2026-10-25T16:04:12.512Z",
  "downloadable": true,
  "expires_at": "2026-10-26T16:04:12.512Z",
  "scope": null
}
```

## Upload a file

`POST /files`

Upload one file. Send it as a multipart form (`multipart/form-data`) with the file in the `file` part. Your tool sets the `Content-Type` header with the form's boundary, so leave it to the tool. The file keeps the name and content type its part names.

The same bytes are stored once per workspace, but every upload makes a file with its own id. This route ignores `Idempotency-Key`, so a retry after a timeout can make a second file. List the files and look for yours before you upload again.

### Body

- `file` (file, required): The file to upload, up to 500 MB (500,000,000 bytes). Send one file only.
- `expires_in_seconds` (integer): Seconds until the file expires, 3,600 (one hour) to 7,776,000 (90 days). A form field is text, so send `86400`. Left out, the file does not expire.

### Returns

`200` and the file.

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/files" \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19" \
  -F 'file=@price-list.csv;type=text/csv' \
  -F 'expires_in_seconds=86400'
```

```json
{
  "type": "file",
  "id": "file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35",
  "filename": "price-list.csv",
  "mime_type": "text/csv",
  "size_bytes": 26,
  "created_at": "2026-10-25T16:04:12.512Z",
  "downloadable": true,
  "expires_at": "2026-10-26T16:04:12.512Z",
  "scope": null
}
```

### Errors

- 400 `invalid_request_error`: The form is not valid (`error_code` `validation_error`): it has no `file` part (`file is required.`), more than one file (`Send one file per upload.`), an `expires_in_seconds` that is not a whole number from 3,600 to 7,776,000, or it could not be read.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 413 `request_too_large`: The file is over 500 MB, or the workspace would pass 1 TB of files (`error_code` `request_too_large`). Upload a smaller file, or delete files you no longer need.
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## List files

`GET /files`

List the files in the key's workspace, newest first: uploads, files tasks made, and files you kept. A file that expired stays in the list for 30 days.

### Query parameters

- `limit` (integer): How many to answer, 1 to 100. The default is 20.
- `page` (string): The `next_page` of the answer before, to read the next page.
- `order` (string): `desc`, newest first (the default), or `asc`, oldest first. One of `desc`, `asc`.
- `created_at[gt]` (time): Only those made after this time, an RFC 3339 time compared to the millisecond the answer shows. `created_at[gte]`, `created_at[lt]` and `created_at[lte]` work the same way.
- `scope_id` (task id): Only the files this task made. A file you kept has no `scope` any more, so this filter does not find it.

### Returns

A list: `data` holds the files, and `next_page` is the `page` to send for the next ones (`null` on the last page).

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/files?scope_id=task_01JA8Z6Q4M3T9V2K7X5R1B0C8D&limit=10" \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "data": [
    {
      "type": "file",
      "id": "file_f2a6c8e0b4d14a9e8c7b5d3f1a9e0c64",
      "filename": "weekly-report.pdf",
      "mime_type": "application/pdf",
      "size_bytes": 48213,
      "created_at": "2026-10-25T16:09:30.118Z",
      "downloadable": true,
      "expires_at": null,
      "scope": {
        "type": "task",
        "id": "task_01JA8Z6Q4M3T9V2K7X5R1B0C8D"
      }
    }
  ],
  "next_page": null
}
```

### Errors

- 400 `invalid_request_error`: A filter is not valid, such as a `limit` outside 1 to 100 or a `scope_id` that is not a task id (`error_code` `validation_error`). `page` is not a page this list gave with the same `order` and `created_at` filters (`error_code` `invalid_cursor`). Read the first page again.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Read a file

`GET /files/{file_id}`

Read one file's details. To get its bytes, download it.

### Path parameters

- `file_id` (file id, required): The file, such as `file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35`.

### Returns

The file.

### Example

```bash
curl https://core.thelittleworker.com/api/v1/files/file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35 \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "type": "file",
  "id": "file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35",
  "filename": "price-list.csv",
  "mime_type": "text/csv",
  "size_bytes": 26,
  "created_at": "2026-10-25T16:04:12.512Z",
  "downloadable": true,
  "expires_at": "2026-10-26T16:04:12.512Z",
  "scope": null
}
```

### Errors

- 400 `invalid_request_error`: `file_id` is not a file id. `error_code` is `validation_error`.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No file in the key's workspace has this id, it was deleted, or it expired more than 30 days ago (`File not found.`).
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Download a file

`GET /files/{file_id}/content`

Download a file's bytes. The answer is the file itself, not JSON. Its `content-type` header is the file's `mime_type`, and its `content-disposition` header names the file as an attachment.

### Path parameters

- `file_id` (file id, required): The file, such as `file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35`.

### Returns

`200` and the file's bytes. An expired file answers `404` at once, before its bytes are removed.

### Example

```bash
curl https://core.thelittleworker.com/api/v1/files/file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35/content \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19" \
  -o price-list.csv
```

```text
sku,price
A1,2.50
A2,4.00
```

### Errors

- 400 `invalid_request_error`: `file_id` is not a file id. `error_code` is `validation_error`.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No file in the key's workspace has this id, it was deleted, or it expired more than 30 days ago (`File not found.`). Or the file expired within the last 30 days, so its details still show but its bytes do not (`This file's content is gone.`).
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Keep a file

`POST /files/{file_id}/keep`

Keep a file a task made, so it stays when the task's history goes. The file becomes a saved file with no `scope`, like an upload, and goes only when you delete it. Keeping an upload, or a file you already kept, answers it as it is.

### Path parameters

- `file_id` (file id, required): The file, such as `file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35`.

### Returns

The file, with `scope` set to `null`.

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/files/file_f2a6c8e0b4d14a9e8c7b5d3f1a9e0c64/keep" \
  -X POST \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "type": "file",
  "id": "file_f2a6c8e0b4d14a9e8c7b5d3f1a9e0c64",
  "filename": "weekly-report.pdf",
  "mime_type": "application/pdf",
  "size_bytes": 48213,
  "created_at": "2026-10-25T16:09:30.118Z",
  "downloadable": true,
  "expires_at": null,
  "scope": null
}
```

### Errors

- 400 `invalid_request_error`: `file_id` is not a file id. `error_code` is `validation_error`.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No file in the key's workspace has this id, it was deleted, or it expired more than 30 days ago (`File not found.`).
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.

## Delete a file

`DELETE /files/{file_id}`

Delete a file. Its details are gone from every read, and its bytes are removed once nothing else in the workspace holds the same bytes, such as another file with the same content.

### Path parameters

- `file_id` (file id, required): The file, such as `file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35`.

### Returns

`type` is `file_deleted`, with the file's `id`.

### Example

```bash
curl "https://core.thelittleworker.com/api/v1/files/file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35" \
  -X DELETE \
  -H "x-api-key: $LITTLE_WORKER_API_KEY" \
  -H "Little-Worker-Version: 2026-10-19"
```

```json
{
  "type": "file_deleted",
  "id": "file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35"
}
```

### Errors

- 400 `invalid_request_error`: `file_id` is not a file id. `error_code` is `validation_error`.
- 401 `authentication_error`: The key is missing, removed, or not a key. Send it in `x-api-key`.
- 404 `not_found_error`: No file in the key's workspace has this id, or it was deleted already (`File not found.`).
- 429 `rate_limit_error`: The workspace made too many requests this minute. Wait the seconds in `Retry-After`, then try again.
