# Milanote API

> Every Milanote endpoint on Cotlet: inputs, JSON Schema, cURL, Python and Node.js examples and an example response for each call.

Source: https://cotlet.dev/docs/api/milanote

Docs sections: [Get started](https://cotlet.dev/docs.md) · [REST API](https://cotlet.dev/docs/api.md) · [MCP](https://cotlet.dev/docs/mcp.md) · [SDKs & CLI](https://cotlet.dev/docs/sdks.md) · [Integrations](https://cotlet.dev/docs/integrations.md)

11 endpoints for Milanote. Each one is a POST with a JSON body, called with your API key.

- Base URL: `https://cotlet.dev/api/v1/milanote`
- Connects with: Your own Milanote account (email and password, Google or Apple)
- Integration page: https://cotlet.dev/integrations/milanote-api-mcp-server

## Endpoints

| Endpoint | Access | Description |
|---|---|---|
| `list_boards` | read | Your boards and nested sub-boards, with title, parent board and ID. |
| `get_board` | read | Every card on a board: notes, links, images, files, to-do lists and sub-boards. |
| `search_boards` | read | Find notes and boards by text across all your boards, like the in-app search. |
| `export_board` | read | Export a board in one of the formats Milanote offers. |
| `create_board` | write | New board, at the top level or nested inside another board. |
| `add_note` | write | Note card with text, placed on a board. |
| `add_link` | write | Link card for a web address, placed on a board. |
| `upload_image` | write | Image card from a file, placed on a board. |
| `add_todo_list` | write | To-do list card with tasks, each with an optional due date. |
| `add_comment` | write | Comment card on a board, or a reply in a comment thread, as you. |
| `invite_editor` | write | Invite someone by email to edit a board and its sub-boards. |

## List boards

`POST https://cotlet.dev/api/v1/milanote/list_boards`

Access: read.

Returns boards in the order they appear in Milanote, each with its parent so nesting is clear. Boards can sit inside boards, like folders, so use parent_board_id to walk down. Your Home board is not a regular board and is not listed.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `parent_board_id` | string | no | Only boards inside this board. Default: your boards at the top level. |
| `limit` | integer | no | Number of boards to return. Default: 50. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/milanote/list_boards \
  -H "Authorization: Bearer $COTLET_API_KEY"
```

**Python**

```python
result = client.call("milanote", "list_boards")
```

**Node.js**

```js
const result = await cotlet.call('milanote', 'list_boards');
```

**CLI**

```bash
cotlet call milanote list_boards
```

### Example response

```json
{
  "boards": [
    {
      "id": "b_7Kq2Lm",
      "title": "Brand refresh",
      "parent_board_id": null
    },
    {
      "id": "b_2Xn9Dc",
      "title": "Novel outline",
      "parent_board_id": null
    },
    {
      "id": "b_5Hc3Wp",
      "title": "Q1 campaign",
      "parent_board_id": null
    }
  ]
}
```

## Get board

`POST https://cotlet.dev/api/v1/milanote/get_board`

Access: read.

Returns one entry per card with its type, text or URL, and ID, so other tools can point at it. To-do list cards include each task with its done state and due date. Sub-boards appear as cards with their own board_id; call get_board again to open one. Reads only, so it works on boards you can view as well as edit.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `board_id` | string | yes | Board ID from list_boards or the board URL. |
| `include_comments` | boolean | no | Also return comment cards and their replies. Default: false. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/milanote/get_board \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"board_id":"board_8c41d2"}'
```

**Python**

```python
result = client.call("milanote", "get_board", {"board_id":"board_8c41d2"})
```

**Node.js**

```js
const result = await cotlet.call('milanote', 'get_board', {"board_id":"board_8c41d2"});
```

**CLI**

```bash
cotlet call milanote get_board --data '{"board_id":"board_8c41d2"}'
```

### Example response

```json
{
  "board_id": "b_5Hc3Wp",
  "title": "Q1 campaign",
  "cards": [
    {
      "id": "c_41aF",
      "type": "note",
      "text": "Palette: warm greys with one orange accent."
    },
    {
      "id": "c_88bQ",
      "type": "link",
      "url": "https://example.com/type-specimen",
      "title": "Type specimen"
    },
    {
      "id": "c_12dK",
      "type": "board",
      "board_id": "b_5Hc3Xt",
      "title": "Social assets"
    }
  ]
}
```

## Search boards

`POST https://cotlet.dev/api/v1/milanote/search_boards`

Access: read.

Searches the board you have open and the contents of every other board you have created, as the Search icon in Milanote does. Results are sorted by last viewed unless you ask for last modified. Use get_board on a hit to read its cards.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `query` | string | yes | Text to look for. |
| `sort` | enum | no | last_viewed or last_modified. Default: last_viewed. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/milanote/search_boards \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"query"}'
```

**Python**

```python
result = client.call("milanote", "search_boards", {"query":"query"})
```

**Node.js**

```js
const result = await cotlet.call('milanote', 'search_boards', {"query":"query"});
```

**CLI**

```bash
cotlet call milanote search_boards --data '{"query":"query"}'
```

### Example response

```json
{
  "results": [
    {
      "board_id": "b_5Hc3Wp",
      "board_title": "Q1 campaign",
      "card_id": "c_41aF",
      "snippet": "Palette: warm greys with one orange accent."
    },
    {
      "board_id": "b_7Kq2Lm",
      "board_title": "Brand refresh",
      "card_id": "c_30pM",
      "snippet": "Color palette v2 from the workshop"
    }
  ]
}
```

## Export board

`POST https://cotlet.dev/api/v1/milanote/export_board`

Access: read.

Runs the same export as the Export button on a board and returns the text, or a download link for word, pdf and png. Home boards cannot be exported. PDF and PNG are limited to a single page; Word spreads a large board over several pages.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `board_id` | string | yes | Board ID from list_boards or the board URL. |
| `format` | enum | yes | markdown, plain_text, word, pdf or png. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/milanote/export_board \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"board_id":"b_2Xn9Dc","format":"markdown"}'
```

**Python**

```python
result = client.call("milanote", "export_board", {"board_id":"b_2Xn9Dc","format":"markdown"})
```

**Node.js**

```js
const result = await cotlet.call('milanote', 'export_board', {"board_id":"b_2Xn9Dc","format":"markdown"});
```

**CLI**

```bash
cotlet call milanote export_board --data '{"board_id":"b_2Xn9Dc","format":"markdown"}'
```

### Example response

```json
{
  "board_id": "b_2Xn9Dc",
  "format": "markdown",
  "content": "# Novel outline\n\n## Act one\n\n- Maya finds the letter\n- The ferry leaves without her\n"
}
```

## Create board

`POST https://cotlet.dev/api/v1/milanote/create_board`

Access: write.

Returns the new board ID for the tools that add cards. The board is private to you until you share it. It reaches no one, so it runs without asking; move it to the trash in Milanote to undo it.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `title` | string | yes | Board title. |
| `parent_board_id` | string | no | Board to nest it in. Default: top level. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/milanote/create_board \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"title"}'
```

**Python**

```python
result = client.call("milanote", "create_board", {"title":"title"})
```

**Node.js**

```js
const result = await cotlet.call('milanote', 'create_board', {"title":"title"});
```

**CLI**

```bash
cotlet call milanote create_board --data '{"title":"title"}'
```

### Example response

```json
{
  "board_id": "b_9Rt4Zy",
  "title": "Location scout",
  "parent_board_id": null,
  "board_url": "https://app.milanote.com/b_9Rt4Zy"
}
```

## Add note

`POST https://cotlet.dev/api/v1/milanote/add_note`

Access: write.

Adds one note card and returns its ID. On the free plan a note counts toward the 100 notes, images or links total. Other editors of the board see the card, but nobody is notified, so it runs without asking; drag it to the trash to undo it.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `board_id` | string | yes | Board ID from list_boards or the board URL. |
| `text` | string | yes | Note text. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/milanote/add_note \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"board_id":"board_8c41d2","text":"text"}'
```

**Python**

```python
result = client.call("milanote", "add_note", {"board_id":"board_8c41d2","text":"text"})
```

**Node.js**

```js
const result = await cotlet.call('milanote', 'add_note', {"board_id":"board_8c41d2","text":"text"});
```

**CLI**

```bash
cotlet call milanote add_note --data '{"board_id":"board_8c41d2","text":"text"}'
```

### Example response

```json
{
  "card_id": "c_62tN",
  "board_id": "b_5Hc3Wp",
  "type": "note",
  "text": "Kickoff moved to Tuesday."
}
```

## Add link

`POST https://cotlet.dev/api/v1/milanote/add_link`

Access: write.

Adds one link card and returns its ID and title. On the free plan a link counts toward the 100 notes, images or links total. Nobody is notified, so it runs without asking; drag the card to the trash to undo it.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `board_id` | string | yes | Board ID from list_boards or the board URL. |
| `url` | string | yes | Web address to save. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/milanote/add_link \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"board_id":"b_7Kq2Lm","url":"https://example.com/type-specimen"}'
```

**Python**

```python
result = client.call("milanote", "add_link", {"board_id":"b_7Kq2Lm","url":"https://example.com/type-specimen"})
```

**Node.js**

```js
const result = await cotlet.call('milanote', 'add_link', {"board_id":"b_7Kq2Lm","url":"https://example.com/type-specimen"});
```

**CLI**

```bash
cotlet call milanote add_link --data '{"board_id":"b_7Kq2Lm","url":"https://example.com/type-specimen"}'
```

### Example response

```json
{
  "card_id": "c_77sW",
  "board_id": "b_7Kq2Lm",
  "type": "link",
  "title": "Type specimen",
  "url": "https://example.com/type-specimen",
  "board_url": "https://app.milanote.com/b_7Kq2Lm"
}
```

## Upload image

`POST https://cotlet.dev/api/v1/milanote/upload_image`

Access: write.

Adds one image card per call and returns its ID. On the free plan an image counts toward the 100 notes, images or links total, and file uploads are capped at 10. Nobody is notified, so it runs without asking; drag the card to the trash to undo it.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `board_id` | string | yes | Board ID from list_boards or the board URL. |
| `file_base64` | string | yes | Image file contents, base64. |
| `file_name` | string | no | File name shown on the card, e.g. location-01.jpg. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/milanote/upload_image \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"board_id":"board_8c41d2","file_base64":"file base64"}'
```

**Python**

```python
result = client.call("milanote", "upload_image", {"board_id":"board_8c41d2","file_base64":"file base64"})
```

**Node.js**

```js
const result = await cotlet.call('milanote', 'upload_image', {"board_id":"board_8c41d2","file_base64":"file base64"});
```

**CLI**

```bash
cotlet call milanote upload_image --data '{"board_id":"board_8c41d2","file_base64":"file base64"}'
```

### Example response

```json
{
  "card_id": "c_90uE",
  "board_id": "b_9Rt4Zy",
  "type": "image",
  "file_name": "location-01.jpg"
}
```

## Add to-do list

`POST https://cotlet.dev/api/v1/milanote/add_todo_list`

Access: write.

Creates one to-do list with all its tasks and returns each task ID. A due date makes Milanote remind whoever set it, by email, phone push or browser notification, as their settings say. Nobody is notified when the list is created, so it runs without asking; drag it to the trash to undo it.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `board_id` | string | yes | Board ID from list_boards or the board URL. |
| `title` | string | no | List title. |
| `tasks` | object | yes | Array of { text, due_date?, due_time? }; due_date is YYYY-MM-DD, due_time HH:MM for the reminder. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/milanote/add_todo_list \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"board_id":"b_5Hc3Wp","title":"Client call, 12 Oct","tasks":[{"text":"Shorten the intro","due_date":"2026-10-16"},{"text":"Swap the hero image (Sam)","due_date":"2026-10-16","due_time":"10:00"}]}'
```

**Python**

```python
result = client.call("milanote", "add_todo_list", {"board_id":"b_5Hc3Wp","title":"Client call, 12 Oct","tasks":[{"text":"Shorten the intro","due_date":"2026-10-16"},{"text":"Swap the hero image (Sam)","due_date":"2026-10-16","due_time":"10:00"}]})
```

**Node.js**

```js
const result = await cotlet.call('milanote', 'add_todo_list', {"board_id":"b_5Hc3Wp","title":"Client call, 12 Oct","tasks":[{"text":"Shorten the intro","due_date":"2026-10-16"},{"text":"Swap the hero image (Sam)","due_date":"2026-10-16","due_time":"10:00"}]});
```

**CLI**

```bash
cotlet call milanote add_todo_list --data '{"board_id":"b_5Hc3Wp","title":"Client call, 12 Oct","tasks":[{"text":"Shorten the intro","due_date":"2026-10-16"},{"text":"Swap the hero image (Sam)","due_date":"2026-10-16","due_time":"10:00"}]}'
```

### Example response

```json
{
  "card_id": "c_55vG",
  "board_id": "b_5Hc3Wp",
  "title": "Call edits",
  "task_count": 2,
  "tasks": [
    {
      "id": "t_01",
      "text": "Shorten the intro",
      "due_date": "2026-10-16"
    },
    {
      "id": "t_02",
      "text": "Swap the hero image",
      "due_date": "2026-10-16"
    }
  ]
}
```

## Add comment

`POST https://cotlet.dev/api/v1/milanote/add_comment`

Access: write. Confirm: waits for the user's OK before it runs.

Posts under your name; people in the thread and anyone you @mention are notified. A comment cannot be deleted from a thread afterwards, and only you can edit it. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `board_id` | string | yes | Board ID from list_boards or the board URL. |
| `text` | string | yes | Comment text. @name tags a collaborator. |
| `reply_to_id` | string | no | Comment ID to reply to. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/milanote/add_comment \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"board_id":"board_8c41d2","text":"text"}'
```

**Python**

```python
result = client.call("milanote", "add_comment", {"board_id":"board_8c41d2","text":"text"})
```

**Node.js**

```js
const result = await cotlet.call('milanote', 'add_comment', {"board_id":"board_8c41d2","text":"text"});
```

**CLI**

```bash
cotlet call milanote add_comment --data '{"board_id":"board_8c41d2","text":"text"}'
```

### Example response

```json
{
  "comment_id": "m_31jB",
  "board_id": "b_5Hc3Wp",
  "text": "@Sam the new palette is on this board.",
  "notified": [
    "Sam"
  ]
}
```

## Invite editor

`POST https://cotlet.dev/api/v1/milanote/invite_editor`

Access: write. Confirm: waits for the user's OK before it runs.

Sends the invitee an email with a link; they need a free Milanote account to edit. Sharing also applies to sub-boards, and your Home board cannot be shared. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `board_id` | string | yes | Board ID from list_boards or the board URL. |
| `email` | string | yes | Their email address. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/milanote/invite_editor \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"board_id":"board_8c41d2","email":"jane@example.com"}'
```

**Python**

```python
result = client.call("milanote", "invite_editor", {"board_id":"board_8c41d2","email":"jane@example.com"})
```

**Node.js**

```js
const result = await cotlet.call('milanote', 'invite_editor', {"board_id":"board_8c41d2","email":"jane@example.com"});
```

**CLI**

```bash
cotlet call milanote invite_editor --data '{"board_id":"board_8c41d2","email":"jane@example.com"}'
```

### Example response

```json
{
  "board_id": "b_7Kq2Lm",
  "invited": "sam@example.com",
  "role": "editor",
  "status": "invited"
}
```
