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

# Files

Some website tools take a file, such as a receipt to upload, or give one back, such as a statement to download. Pomerado keeps each file only as long as the run needs it, encrypted, and never opens it.

Files need a signed-in caller: a Dashboard session, an MCP client signed in to Pomerado, or an API key with the `runs:create` permission (the default permissions include it). A file belongs to you alone; another member's file, or a file in another account, answers `not_found`.

## Send a file to a run

A tool's file field is a string. Give it one of:

* **A file ID** (`file_…`) from an upload. Use this for any file larger than a few hundred kilobytes.
* **An https URL.** Pomerado fetches it when you start the run. The URL must be https on the default port, with no username or password, and must resolve to a public address. It may redirect at most three times, must answer within 20 seconds and must send a `Content-Length`.
* **A `data:` URI** of at most 512 KB, such as `data:application/pdf;base64,JVBERi0…`.

Pomerado stores a URL's or data URI's bytes just as it stores an upload, checks them the same way, and replaces each with a file ID in the job's input.

### Upload a file

Create the upload with `POST /v1/files`, or from an MCP client with `call_pomerado_api` and the operation `files.create`:

```json theme={null}
{ "name": "receipt.pdf", "media_type": "application/pdf", "size": 48213 }
```

`size` is optional; when you give it, the upload must be exactly that long. The answer is `201`:

```json theme={null}
{
  "id": "file_3b9e0c6f1a2d4e5f8a7b6c5d4e3f2a1b",
  "name": "receipt.pdf",
  "media_type": "application/pdf",
  "size": 48213,
  "sha256": null,
  "status": "awaiting_upload",
  "direction": "input",
  "job_id": null,
  "created_at": "2026-10-08T17:00:00.000Z",
  "expires_at": "2026-10-08T18:00:00.000Z",
  "upload_url": "https://…/v1/file-uploads/…"
}
```

Then PUT the bytes to `upload_url` once, with no `Authorization` header:

```bash theme={null}
curl -T receipt.pdf -H "Content-Type: application/pdf" "<upload_url>"
```

The answer is `200` with the file, now `ready`, with its `size` and `sha256`. If an upload fails, PUT it again until the file expires. Pass the file's ID as the field's value when you start the run:

```json theme={null}
{ "input": { "receipt": "file_3b9e0c6f1a2d4e5f8a7b6c5d4e3f2a1b" } }
```

`GET /v1/files/{id}` reads a file's metadata, never its bytes; `DELETE /v1/files/{id}` erases it now.

## Get a file from a run

A run that collects a file returns it in its result as a `$file` object:

```json theme={null}
{
  "statement": {
    "$file": {
      "id": "file_9c8b7a6f5e4d3c2b1a0f9e8d7c6b5a4f",
      "name": "statement-2026-09.pdf",
      "media_type": "application/pdf",
      "size": 182044,
      "sha256": "5f2d…",
      "download_url": "https://…/v1/file-downloads/…",
      "expires_at": "2026-10-08T17:30:00.000Z"
    }
  }
}
```

`download_url` works once, with no `Authorization` header: `curl -o statement.pdf "<download_url>"`. The file is erased as soon as one download finishes, which means Pomerado handed its last byte to the network, not that your client saved it; save the body before anything else, and check it against `sha256`. A download that breaks off before its last byte may start again until `expires_at`. Compare the bytes with `sha256` if you need proof they are complete.

## Limits

| Limit | Value |
| - | - |
| One file | 25 MiB |
| Files in one run | 10, and 50 MiB together |
| A `data:` URI | 512 KB |
| An unused upload | erased after 1 hour |
| A file a run used | erased when its job ends |
| A run's output file | erased after its first complete download, or after 30 minutes |
| Every file of an account | erased when the account is deleted |

Pomerado accepts PDF, common image, audio and video types, ZIP and office documents, and plain text, CSV, Markdown, calendar, XML and JSON files; `application/octet-stream` takes any other bytes. A file's first bytes must match its media type. Programs and scripts (`.exe`, `.sh`, `.js`, `.dmg` and others) are refused by name, type and content. Files are not scanned for malware, so open a downloaded file with the care you would give any other.

## Errors

| Code | Status | Meaning |
| - | - | - |
| `file_too_large` | 413 | The file is larger than `details.max_bytes`. |
| `file_type_refused` | 415 | `details.reason`: `executable`, `unsupported_type` or `content_mismatch` (its bytes don't match its media type). |
| `file_unavailable` | 409 | `details.reason`: `expired`, `not_uploaded`, `in_use` (another run took it) or `upload_in_progress`. |
| `file_limit_exceeded` | 413 | The run's files are more than `details.max_files` or `details.max_total_bytes`. |
| `file_fetch_failed` | 422 | Pomerado couldn't fetch a URL input; `details.reason` says why. |

See [Errors](/errors) for every code.


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