# Upload a file

Source: https://api-docs.magicdoor.com/api-reference/files/upload-file

Uploads one file as multipart form data and returns its id and paths. The file is stored on its own, not yet attached to any record — pass the returned id to whatever you want to attach it to.

> **Deprecated.** This endpoint is deprecated and may be removed in a future release.

`POST https://files.magicdoor.com/files`

**Authentication:** bearer token in the `Authorization` header, as `Bearer <token>`.

## Body

Content type: `multipart/form-data`

Send this as `multipart/form-data`, not JSON. The nested `allowedSize` values are separate form fields named `allowedSize.start` and `allowedSize.end`.

- `file` (file, required) — The file itself. Must not be empty, and must not exceed 100 MB — that ceiling is fixed and `allowedSize` can only lower it, never raise it.
- `allowedTypes` (string[]) — Refuse the upload unless its detected MIME type matches one of these. Each entry is an exact type such as `application/pdf`, a subtype wildcard such as `image/*`, or `*` for anything. Repeat the field once per pattern, and send at least one when you use it. Leave it out to accept any type. Example: `application/pdf`.
- `allowedSize` (object) — Refuse the upload unless its size in bytes falls in this range. Sent as the form fields `allowedSize.start` and `allowedSize.end`.
  - `start` (integer) — Smallest size in bytes you will accept.
  - `end` (integer) — Largest size in bytes you will accept. It also caps what this endpoint accepts overall, so it is the field to set when you want a smaller limit than 100 MB.
  - `startInclusive` (boolean) — Whether a file exactly at `start` is accepted. Default: `true`.
  - `endInclusive` (boolean) — Whether a file exactly at `end` is accepted. Default: `true`.

## Example request

```bash
curl --request POST \
  --url 'https://files.magicdoor.com/files' \
  --header 'Authorization: Bearer ACCESS_TOKEN' \
  --form 'file=@lease-agreement.pdf' \
  --form 'allowedTypes=application/pdf' \
  --form 'allowedSize.end=10485760'
```

## Responses

### 200 — The stored file

- `id` (string) — The new file's id, a Snowflake sent as a decimal string. This is what you hand to whatever should own the file.
- `name` (string) — The file name taken from the upload, with any path stripped off.
- `size` (integer) — How large the stored file is, in bytes.
- `contentType` (string) — The MIME type MagicDoor worked out by inspecting the content, not the one your form claimed. It can differ from what you sent.
- `secureUrl` (object) — Where to fetch the file back. Both paths need an access token.
  - `base` (string) — The origin the two paths hang off.
  - `raw` (string) — Path to the file as uploaded.
  - `variantTemplate` (string) — Path to a resized or re-encoded copy, with `{variant}` still in it.

```json
{
  "id": "1831208734196891648",
  "name": "lease-agreement.pdf",
  "size": 284197,
  "contentType": "application/pdf",
  "secureUrl": {
    "base": "https://files.example",
    "raw": "/files/1831208734196891648/lease-agreement.pdf",
    "variantTemplate": "/files/1831208734196891648/v/{variant}/lease-agreement.pdf"
  }
}
```

### 400 — No file, an empty file, a file over the size ceiling, a size outside `allowedSize`, or a detected type not in `allowedTypes`

### 401 — The access token is missing, expired, or invalid

> **Warning: This endpoint is on its way out**
>
> It still works and is still supported, but it is marked obsolete in MagicDoor's source and will be removed. Nothing replaces it in this reference yet — if you are building on it, tell support so the change can be planned around you.

## The file starts out unattached

An upload here is stored with no category and no owning record, and is not claimed by anything. It is a staging step: upload first, get an id back, then send that id to whichever endpoint creates or updates the record the file belongs to.

Both `allowedTypes` and `allowedSize` are guardrails you set for yourself. They are optional, and if you leave them out the only limit is the fixed 100 MB ceiling.

> **Note: The type is detected, not declared**
>
> MagicDoor reads the content to decide the MIME type and ignores the one in your multipart part. `allowedTypes` is checked against the detected type, so a `.pdf` that is really a JPEG is rejected by `allowedTypes=application/pdf`, and the `contentType` in the response can differ from what you sent.
