# Notes: File Uploads

**Page:** api/notes/files

[Download Raw Markdown](./api/notes/files.md)

---

{/* AUTO-GENERATED — Do not edit manually. Regenerate with: npm run docs:api:generate */}



## Overview

These endpoints let you upload, download, list, and delete files and avatars associated with notebooks in Hoody. File uploads use the [TUS protocol](https://tus.io/) for resumable, chunked transfers, making them suitable for large attachments. Avatar uploads accept raw image bytes and are automatically resized to 500&times;500 JPEG.

Use these endpoints when you need to:
- Upload files (images, documents, media) into a notebook for inclusion in pages.
- Upload or retrieve user avatar images.
- Manage resumable uploads for large files via the TUS protocol.

---

## File Management

### `GET /api/v1/notes/notebooks/{notebookId}/files`

List all files uploaded to a notebook with `limit`/`offset` pagination.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `notebookId` | path | string | Yes | ID of the notebook to list files from |
| `limit` | query | integer | No | Maximum number of files to return. Default: `50` |
| `offset` | query | integer | No | Number of files to skip for pagination. Default: `0` |

#### Response




```json
{
  "files": [
    {
      "id": "f1a2b3c4d5e6f7a8b9c0d1e2",
      "name": "architecture-diagram.png",
      "mimeType": "image/png",
      "size": 482104,
      "createdAt": "2024-09-12T14:22:07.000Z",
      "createdBy": "u9k8j7h6g5f4d3s2a1",
      "documentId": "d1c2b3a4e5f6d7c8b9a0",
      "documentName": "Project Architecture"
    },
    {
      "id": "a9b8c7d6e5f4d3c2b1a0",
      "name": "meeting-notes.pdf",
      "mimeType": "application/pdf",
      "size": 102400,
      "createdAt": "2024-09-11T10:05:33.000Z",
      "createdBy": "z1y2x3w4v5u6t7s8r9",
      "documentId": "e5f6d7c8b9a0d1c2b3a4",
      "documentName": null
    }
  ],
  "total": 27
}
```




```json
{
  "message": "Notebook not found.",
  "code": "notebook_not_found",
  "details": [
    {
      "path": "notebookId",
      "message": "No notebook with ID n1b2c3d4e5f6 exists"
    }
  ]
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `notebook_not_found` | Notebook not found | No notebook exists with the provided ID | Verify notebook ID using listNotebooks |
| `notebook_no_access` | Access denied | User does not have permission to access files in this notebook | Check collaborator list or request access from the notebook owner |




#### SDK Usage

```ts
const page = await client.notes.files.listIterator({
  notebookId: "n1b2c3d4e5f6",
  limit: 50,
  offset: 0,
});
```

---

### `GET /api/v1/notes/notebooks/{notebookId}/files/{fileId}`

Download the binary content of an uploaded file. The response is returned with the file's original MIME type.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `notebookId` | path | string | Yes | ID of the notebook that owns the file |
| `fileId` | path | string | Yes | ID of the file to download |

#### Response




```json
{
  "description": "Binary file content with the original content type"
}
```




#### SDK Usage

```ts
const blob = await client.notes.files.download({
  notebookId: "n1b2c3d4e5f6",
  fileId: "f1a2b3c4d5e6f7a8b9c0d1e2",
});
```

---

### TUS Resumable Uploads


The TUS endpoints implement the [TUS resumable upload protocol](https://tus.io/). They are typically invoked transparently by the Hoody SDK or any TUS-compatible client. You normally do not need to call these directly unless you are building a custom TUS client.


The TUS protocol uses four HTTP methods on the same URL:

| Method | Purpose |
|--------|---------|
| `POST` | Create a new upload (initializes upload metadata) |
| `HEAD` | Check current upload offset and status |
| `PATCH` | Upload a chunk of file data |
| `DELETE` | Abort an in-progress upload |

All four operations share the same path and parameters.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `notebookId` | path | string | Yes | ID of the notebook that will own the file |
| `fileId` | path | string | Yes | Pre-allocated ID of the file being uploaded |

#### `POST /api/v1/notes/notebooks/{notebookId}/files/{fileId}/tus`

Create a new resumable upload.




```json
{
  "description": "Upload resource created; TUS headers returned in response"
}
```




```ts
await client.notes.files.tusCreateUpload({
  notebookId: "n1b2c3d4e5f6",
  fileId: "f1a2b3c4d5e6f7a8b9c0d1e2",
});
```

#### `HEAD /api/v1/notes/notebooks/{notebookId}/files/{fileId}/tus`

Check the current offset of an in-progress upload.




```json
{
  "description": "Response headers include Upload-Offset indicating the next byte to send"
}
```




```ts
await client.notes.files.tusCheckUpload({
  notebookId: "n1b2c3d4e5f6",
  fileId: "f1a2b3c4d5e6f7a8b9c0d1e2",
});
```

#### `PATCH /api/v1/notes/notebooks/{notebookId}/files/{fileId}/tus`

Upload a chunk of file data. Each request appends bytes at the server's current `Upload-Offset`.




```json
{
  "description": "Chunk accepted; response headers include the new Upload-Offset"
}
```




```ts
await client.notes.files.tusUploadChunk({
  notebookId: "n1b2c3d4e5f6",
  fileId: "f1a2b3c4d5e6f7a8b9c0d1e2",
});
```

#### `DELETE /api/v1/notes/notebooks/{notebookId}/files/{fileId}/tus`

Abort an in-progress upload and discard all received bytes.




```json
{
  "description": "Upload aborted; partial data discarded"
}
```




```ts
await client.notes.files.tusAbortUpload({
  notebookId: "n1b2c3d4e5f6",
  fileId: "f1a2b3c4d5e6f7a8b9c0d1e2",
});
```

---

## Avatar Management

### `POST /api/v1/notes/avatars`

Upload a raw image (JPEG, PNG, or WebP) as a user avatar. The image is automatically resized to 500&times;500 pixels and converted to JPEG.


The avatar is sent as raw binary bytes in the request body. The `Content-Type` header must match the image type (`image/jpeg`, `image/png`, or `image/webp`).


This endpoint takes no parameters.

#### Response




```json
{
  "success": true,
  "id": "a1b2c3d4e5f6a7b8c9d0e1f2"
}
```




```json
{
  "message": "No avatar file was uploaded.",
  "code": "avatar_file_not_uploaded",
  "details": [
    {
      "path": "body",
      "message": "Request body must contain a valid JPEG, PNG, or WebP image"
    }
  ]
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `avatar_file_not_uploaded` | Invalid upload | No file was uploaded or the content type is not a valid image | Send a multipart form with a JPEG or PNG image file |




```json
{
  "message": "Failed to upload avatar.",
  "code": "avatar_upload_failed",
  "details": [
    {
      "path": "server",
      "message": "Image processing pipeline returned an error"
    }
  ]
}
```

| Error Code | Title | Description | Resolution |
|------------|-------|-------------|------------|
| `avatar_upload_failed` | Upload failed | Avatar upload failed due to a server error | Retry the upload |




#### SDK Usage

```ts
const result = await client.notes.avatars.upload();
// result.id can be used with downloadAvatar to retrieve the image
```

---

### `GET /api/v1/notes/avatars/{avatarId}`

Download an avatar image. Returns the image as JPEG binary data.

#### Parameters

| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| `avatarId` | path | string | Yes | ID of the avatar returned from a prior `upload` call |

#### Response




```json
{
  "description": "JPEG binary data"
}
```




#### SDK Usage

```ts
const blob = await client.notes.avatars.download({
  avatarId: "a1b2c3d4e5f6a7b8c9d0e1f2",
});
```