> For complete lifecycle-event payload schemas and examples, use the canonical overview: Session /api/sessions; Room lifecycle, members, and room playback /api/rooms; Session playback /api/playback; Recording /api/recording; DTMF /api/keypad-input; Messages /api/messages; WebSocket Session /api/web-socket. Use /api/event-delivery for transport behavior. Endpoint pages name relevant events, but these overviews are the canonical references.

# Files

> Temporary PDF uploads, signed download URLs, expiry, and reuse in fax Messages.

Upload a PDF to `POST /v1/files`, then pass the returned `url` in a
[fax Message](/api/messages#send-a-fax). Uploading a file does not send it.

## Upload a PDF

Use [API authentication](/api/authentication) and a `multipart/form-data` body
with exactly one field named `file`. The PDF can be at most 20 MiB. Let your
HTTP client set the multipart boundary.

Replace `API_BASE_URL`, `APP_UUID`, and `API_KEY` with your API base URL and
credentials, and replace `document.pdf` with your file path:

```bash
curl -X POST "$API_BASE_URL/v1/files" \
  -H "Authorization: Bearer $APP_UUID:$API_KEY" \
  -F 'file=@document.pdf;type=application/pdf'
```

A successful upload returns `201 Created`:

```json
{
  "url": "https://api.example.com/v1/files/content/opaque-signed-token",
  "expires_at": "2026-09-16T10:00:00.000Z"
}
```

| Field        | Type   | Description                             |
| ------------ | ------ | --------------------------------------- |
| `url`        | string | Signed HTTPS URL for the stored PDF.    |
| `expires_at` | string | RFC 3339 UTC time when the URL expires. |

The response confirms that the file was stored. Document preparation happens
after you create a fax Message and can still fail if the PDF cannot be read or
converted.

Uploads do not support idempotency. Repeating an upload can create another
temporary copy. Use the required `Idempotency-Key` when
[creating the Message](/api/messages#send-a-fax).

## Use the URL in a Message

Set the fax Message's `content` to:

```json
{
  "type": "document",
  "url": "https://api.example.com/v1/files/content/opaque-signed-token"
}
```

Pass the complete URL as returned. No separate file ID is required. The URL
refers to the uploaded bytes and can be reused for several Messages before it
expires. Create those Messages with credentials for the application that
uploaded the file.

You can also send a fax from another publicly accessible HTTPS PDF URL without
using the upload endpoint. See [Send a fax](/api/messages#send-a-fax).

## Download and expiry

A signed URL lets anyone who holds it download the PDF with `GET` until
`expires_at`. No authorization header is required for that download. Treat the
complete URL as a temporary credential and keep it out of public logs and
messages.

A successful download returns `200 OK` with `Content-Type: application/pdf`.
Invalid, expired, and missing download URLs return `404 not_found`.

URLs expire after one hour by default; use the response's `expires_at` value.
After a fax Message receives `202 Accepted`, URL expiry does not interrupt that
Message's document preparation or permitted retries. Creating a new Message
from the expired URL fails validation. Unused uploads are removed after expiry.
An upload used by pending Messages remains stored until those Messages no
longer need it.

## Upload errors

| Status and code         | Meaning                                                                              |
| ----------------------- | ------------------------------------------------------------------------------------ |
| `400 invalid_request`   | The request does not contain exactly one PDF in the `file` field.                    |
| `401 unauthorized`      | The credentials are missing or invalid.                                              |
| `413 payload_too_large` | The PDF exceeds the upload limit.                                                    |
| `415 invalid_request`   | The request does not use valid `multipart/form-data`.                                |
| `429 rate_limited`      | The temporary storage quota or request rate limit was reached. Follow `Retry-After`. |
| `503 unavailable`       | File upload is unavailable.                                                          |

For the response envelope and retry guidance, see [Errors](/errors).