---
title: "Upload a file"
description: "Stores a file in the workspace, the way the Upload button on the Files page does. The body is the file itself, not JSON and not multipart, with its type in `Content-Type`."
url: "https://openemail.uk/docs/api/files/upload"
area: "API"
category: "Mailbox"
---

# Upload a file

Stores a file in the workspace, the way the Upload button on the Files page does. The body is the file itself, not JSON and not multipart, with its type in `Content-Type`.

`POST /files`

## POST /files

Stores a file in the workspace, the way the Upload button on the Files page does. The body is the file itself, not JSON and not multipart, with its type in `Content-Type`.

## Example

Needs `threads:write`. Name the file with the `filename` query parameter, or with an `X-Filename` header, URI-encoded. The answer is a `201` with the file, and its id can be attached to a send as `{ fileId }`.

**curl**

```
curl -X POST "$OE/files?filename=price-list.pdf" -H "$AUTH" -H 'content-type: application/pdf' --data-binary @price-list.pdf
```

**Response**

```
{
  "object": "file",
  "id": "file_0c4e7a91d2b84f63a5e19b7d",
  "filename": "price-list.pdf",
  "mimeType": "application/pdf",
  "sizeBytes": 248112,
  "direction": "uploaded",
  "threadId": null,
  "messageId": null,
  "deliveredTo": null,
  "usage": null,
  "deletable": true,
  "createdAt": "2026-09-24T09:12:40.000Z"
}
```

> A name is cleaned rather than refused: characters a file name cannot hold become `_`, and a name longer than 255 characters is cut, keeping its extension. A `Content-Type` that is not a MIME type is stored as `application/octet-stream`.

> No name is a 400 `upload_no_name`, a program or a script a 400 `upload_dangerous`, an empty body a 400 `upload_empty`, and a file over 100 MB a 413 `upload_too_large`.

> A workspace takes 500 uploads an hour, deleted ones included, and holds 10 GB of uploads at a time. Past those the answer is a 429 `upload_rate_limited` or a 507 `upload_storage_full`. A 502 `upload_failed` means storage failed, and is worth retrying.

> A key that reaches every address uploads to the whole workspace, with `deliveredTo` null. A key limited to particular addresses or domains uploads to the first address it holds, and one that holds none is a 403 `upload_no_address`.
