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

# Files

> POST /v1/files, GET /v1/files, DELETE /v1/files/{file_key} — upload reports, retrieve extracted text, list, and delete.

Upload a health document to the Subject's store. Mirobody saves the original first, then extracts readable text in the background: PDFs and images use **OCR**, while spreadsheets and text files are converted to text. The agent can read that extracted text when it answers.

<Note>
  Uploads are capped at **100 MB** per file — larger requests return `413`. For big exports or realtime streams, use the [WebSocket upload](/en/api-reference/files#websocket-upload) path.
</Note>

## Upload a file

```http theme={null}
POST /v1/files
Authorization: Bearer mb_live_*
Content-Type:  multipart/form-data
```

| Field        | Description                                                                                                                                               |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `file`       | The document (PDF / image / Excel / CSV).                                                                                                                 |
| `user`       | Subject the file belongs to. **Optional** — omitting it falls back to your account's **`default`** Subject, so always pass it for per-end-user isolation. |
| `retention`  | **Optional here** (unlike `POST /v1/data`, where it's required). Same values as [Data retention](/en/api-reference/overview#data-retention).              |
| `session_id` | Optional; scopes `retention=session` uploads to a [session](/en/api-reference/lifecycle#sessions).                                                        |

```bash theme={null}
curl https://api.mirobody.ai/v1/files \
  -H "Authorization: Bearer $MIROBODY_API_KEY" \
  -F "user=alice" \
  -F "file=@checkup_2026.pdf"
```

Response:

```json theme={null}
{
  "object": "file",
  "id": "AUmIn_R-Ddg.../b/bM--fG29X....pdf",
  "filename": "checkup_2026.pdf",
  "bytes": 20544,
  "status": "processed",
  "created_at": 1782924296,
  "subject": "alice"
}
```

The file's identifier is **`id`** (a slash-containing key) — use it as `{file_key}` in the fetch and delete calls below. `created_at` is **epoch seconds**.

<Note>
  `status: "processed"` confirms the original was accepted and stored — its text may still be processing. A `GET /v1/files/{file_key}` immediately after upload can return an empty `extracted_text`; retry after a short delay.
</Note>

Uploading a file makes its text available to the agent, and Mirobody also reads the health indicators out of it — [standardized](/en/api-reference/standardization) the same way as a `POST /v1/data` write — and saves them to the Subject's store, so they're queryable from [`GET /v1/data`](/en/api-reference/data) without re-entering anything. Repeated uploads of the same file won't create duplicates. Reach for [`POST /v1/standardize`](/en/api-reference/extract) instead when you'd rather run that extraction yourself and inspect the readings first. Already have structured records? Send them to [`POST /v1/data`](/en/api-reference/data).

## List files

```http theme={null}
GET /v1/files?user=alice
Authorization: Bearer mb_live_*
```

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "object": "file",
      "id": "AUmIn_R-Ddg.../b/bM--fG29X....pdf",
      "file_key": "AUmIn_R-Ddg.../b/bM--fG29X....pdf",
      "filename": "checkup_2026.pdf",
      "file_type": "application/pdf",
      "bytes": 20544,
      "created_at": 1782924296
    }
  ],
  "subject": "alice"
}
```

`data` holds the files; `subject` echoes the Subject. Each item exposes both `id` and `file_key` (identical values). `created_at` is **epoch seconds**, matching the upload response.

## Fetch parsed text

```http theme={null}
GET /v1/files/{file_key}?user=alice
Authorization: Bearer mb_live_*
```

Returns the extracted text for one file — the same content the agent reads:

```json theme={null}
{
  "object": "file",
  "id": "AUmIn_R-Ddg.../b/bM--fG29X....pdf",
  "filename": "checkup_2026.pdf",
  "extracted_text": "Annual checkup 2026-06-16\nFasting glucose  97 mg/dL  (ref 70-110)\n...",
  "abstract": "",
  "subject": "alice"
}
```

`extracted_text` can be empty while the file's text is still being processed. `abstract` may also be empty; it isn't generated for every file type.

## WebSocket upload

For large exports or realtime capture, `wss://…/v1/files/stream` uploads over one socket with chunking and progress. Auth rides the query string (browsers can't set WebSocket headers):

```text theme={null}
wss://api.mirobody.ai/v1/files/stream?key=mb_live_...&user=alice&retention=permanent
```

Frame sequence (all JSON text frames):

1. **Server → you**: `{"type": "connection_established"}` — send nothing before this.
2. **You → server**: `{"type": "upload_start", "messageId": "<batch-id>", "files": [{"filename", "contentType", "size"}]}` — one `messageId` for the whole batch.
3. **You → server**, per 256 KB chunk: `{"type": "upload_chunk", "messageId", "filename", "chunk": "<base64>", "chunkIndex", "totalChunks"}`.
4. **You → server**: `{"type": "upload_end", "messageId"}` → server replies `upload_end_response` when every original file has been stored. Its text may still be processing.

Same 100 MB per-file cap, same Subject isolation, and the stored files are identical to a `POST /v1/files` upload — list them with `GET /v1/files`.

## Delete a file

```http theme={null}
DELETE /v1/files/{file_key}?user=alice
Authorization: Bearer mb_live_*
```

Removes the file from the API surface immediately (`404` if it isn't the Subject's file or is already deleted):

```json theme={null}
{ "object": "file", "id": "AUmIn_R-Ddg.../b/bM--fG29X....pdf", "deleted": true, "subject": "alice" }
```

<Note>
  Deleting a file removes the **file** but **not** the standardized indicators that were auto-extracted from it. To remove those readings, delete them from the data plane with [`DELETE /v1/data`](/en/api-reference/data#erase-records) (by `indicator` or `id`), or erase the whole Subject with [`DELETE /v1/subjects/{user}`](/en/api-reference/lifecycle#offboarding-a-subject). (Session-scoped uploads **are** erased together with their session-scoped readings by [`DELETE /v1/sessions/{id}`](/en/api-reference/lifecycle#sessions).)
</Note>

Time-bounded uploads are also removed automatically at **[retention](/en/api-reference/overview#data-retention) expiry** — an expired file disappears from every `GET /v1/files*` read immediately and is then permanently deleted. To erase everything about a Subject at once, use `DELETE /v1/subjects/{user}` — see [Compliance](/en/api-reference/compliance).
