---
title: "Upload a file for a Research Agent session"
method: POST
path: "/files"
tags: ["Research Agent"]
---

# Upload a file for a Research Agent session

`POST /files`

Stage a file so it can be attached to a Research Agent turn. This is a two-step, presigned upload:

1. **POST /api/v2/files** with the filename, MIME type, and exact byte size. The response returns a `file_id` and a short-lived presigned `upload_url`.
2. **PUT** the raw file bytes to `upload_url` with the same `Content-Type` and a `Content-Length` matching `size_bytes`. Do not send an `Authorization` header on the PUT — the URL is already signed.

Then pass `{ "file_id": "..." }` in the `attachments` array of a create-session or send-message request. Attached files are made available to the agent exactly as uploads made in the web interface are.

The request/response fields for this endpoint are deliberately `snake_case` (`content_type`, `size_bytes`, `file_id`, `upload_url`, `expires_at`), unlike the camelCase used elsewhere in the v2 surface. Files are capped at 30 MB, and a session accepts a bounded number of attachments; the `upload_url` and `file_id` expire at `expires_at`.

This endpoint is in early access. It returns `404 not_found` unless the Research Agent API has been enabled for the authenticated account or organization.

### Example

```bash
# 1. Register the upload
curl -X POST https://elicit.com/api/v2/files \
  -H "Authorization: Bearer elk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"filename":"trial-results.pdf","content_type":"application/pdf","size_bytes":245678}'

# 2. Upload the bytes to the returned upload_url
curl -X PUT "{upload_url}" \
  -H "Content-Type: application/pdf" \
  --data-binary @trial-results.pdf

# 3. Attach {"file_id": "..."} to a create-session or /messages request
```

## Request body

- object
  - `filename` string, required — Original filename of the upload. Used as the display name in the session.
  - `content_type` string, required — MIME type of the file.
  - `size_bytes` integer, required — Exact size of the file in bytes. Must match the uploaded object exactly. Maximum 31457280 bytes (30 MB).

## Response `200`

Upload registered. PUT the file bytes to `upload_url` before it expires.

- CreateFileResponse
  - `file_id` string, required — Opaque identifier for the staged upload. Pass it in the `attachments` array of a create-session or send-message request to attach the file to that turn.
  - `upload_url` string, required — Short-lived presigned S3 PUT URL. Upload the file bytes directly to it with the same Content-Type and Content-Length declared here.
  - `expires_at` string, required — ISO 8601 timestamp after which the upload URL and the staged file_id are no longer valid.

## Other responses

- `400` — Invalid request. The filename, content type, or size is missing or invalid, or the size exceeds the 30 MB limit.
- `401` — Authentication failed. The API key is missing, invalid, revoked, or expired.
- `402` — Insufficient quota.
- `403` — API access is not available on your current plan. Upgrade to Pro or above to use the API.
- `404` — Research Agent API early access is not enabled for this account.
- `429` — Rate limit exceeded. More than 100 requests per minute were received from your IP address; further requests are blocked for 5 minutes.
- `500` — An unexpected error occurred. Retry after a short delay.

---

[API](https://skmtc.dev/elicit/apis/elicit-api.md) · [All operations](https://skmtc.dev/elicit/apis/elicit-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/elicit/elicit-api/revisions/2b42624536e5/schema)
