# Справочник API ToDowl

Все инструменты MCP, все HTTP-эндпоинты и все команды терминала, с теми параметрами, которые сервер действительно принимает. Написано по коду и сверено с живым сервером.

41 инструмент MCP · 7 HTTP-эндпоинтов · 37 команд CLI. Сверено с живым сервером 2026-08-28.

https://mcp.todowl.com/
https://help.todowl.com/ru/api/

## С чего начать

### Один адрес, один ключ

Всё, что происходит за пределами браузера, идёт через `https://mcp.todowl.com/`: AI-клиенты, утилита `todowl` и всё, что вы напишете сами. Второго API и отдельного эндпоинта к базе не существует.

Вызов доказывает, кто он, личным API-токеном в заголовке `Authorization`. Сессию между вызовами сервер не хранит, поэтому заголовок нужен в каждом запросе.

Каждый инструмент работает с тем аккаунтом, которому принадлежит токен. Ни один инструмент не принимает идентификатор пользователя, и обратиться к чужим данным невозможно.

```bash
curl https://mcp.todowl.com/ \
  -H "Authorization: Bearer todowl_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": { "name": "list_tasks", "arguments": { "status": "open", "limit": 5 } }
  }'
```

### Даты это даты, а не слова

Любая дата это обычная календарная дата в виде `YYYY-MM-DD`, любое время это 24-часовое `HH:MM`. В какой зоне находится вызывающий, сервер не знает, поэтому слова вроде «завтра» превращаются в дату до вызова.

Названия это не идентификаторы. Запрос, где проект или привычка названы словами, начинается с `list_projects` или `list_habits`, и дальше передаётся id из этого ответа.

### Что приходит в ответ

Инструмент отвечает JSON внутри текстовой части ответа MCP, записанным настолько плотно, насколько это возможно. Все примеры на этой странице разложены для чтения.

Пустые поля не отправляются как null, их просто нет. У задачи без списка в ответе не будет `list_id`. А вот `false` сохраняется, потому что «не задано» и «не сделано» это разные вещи.

Отказ это не HTTP-ошибка. Вызов отвечает 200, ставит `isError` и кладёт в `{ "error": "…" }` одну фразу, ровно ту, которую показало бы приложение.

Длинный текст урезается на выходе: 300 символов на строку в списке и 8000, когда запрошена одна запись. Вставленная в описание картинка превращается в `[image]`, а обрезка помечается, чтобы никто не принял кусок описания за всё описание.

### Оставить, снять или заменить

Инструмент обновления читает поле тремя разными способами, в зависимости от того, что вы отправили. Не передали поле: оно не тронуто. Передали значение: оно заменяет прежнее. Передали `null`: поле снимается, как если бы вы очистили его в приложении.

Не любое поле можно снять таким способом: у заголовка всегда должно быть какое-то значение, а у булева поля уже есть своё выключенное состояние. Там, где `null` разрешён, об этом написано в таблице параметров на этой странице.

Единственное, что нельзя снять, это проект. Задача, запись календаря и заметка всегда лежат в проекте, а строка без него не показывается в приложении ни на одном экране, поэтому уйти из проекта можно только в другой. Список отвязать по-прежнему можно: либо через `null`, либо флагом `clear_list` у `move_task`.

### Удаление это не стирание

Инструмент удаления убирает запись в корзину, ровно как приложение. Продолжение истории это `list_trash`, `restore_from_trash` и `delete_from_trash_forever`.

Исключений два, и других нет: `delete_from_trash_forever` стирает, а `delete_habit` уносит привычку вместе со всей историей отметок, потому что корзины у привычек не существует.

## Ограничения, которые держит сервер

| Что | Ограничение |
| --- | --- |
| Строк за один вызов | 50 by default, 200 maximum |
| Задач за один batch_create_tasks | 20 |
| Заголовок задачи и записи | 500 |
| Описание задачи и записи | 20000 |
| Заголовок заметки | 300 |
| Текст заметки | 100000 |
| Название проекта и списка | 200 |
| Напоминание, минут до начала | 0-10080 |
| Тело запроса | 4 MB |
| Сообщений ассистенту в час | 60 |

## Инструменты MCP

Что AI-клиент может делать в вашем аккаунте. Сгруппировано так же, как код сервера, и названо ровно так, как это назовёт клиент.

### Задачи

Девять инструментов, на которых держатся все дела: список, поиск, создание одной задачи или сразу двадцати, правка, отметка о выполнении, перенос и удаление в корзину.

Написано по: `mcp/src/tools/tasks.ts`

#### `list_tasks` (читает)

Ваши задачи, ближайшие сверху. Именно отсюда берутся ответы на «что сегодня», «что осталось в проекте», «что вообще без даты». Все переданные фильтры действуют одновременно.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `status` | string, одно из open, completed, all, по умолчанию open | необязательный | Какие задачи показывать. По умолчанию open, то есть невыполненные; all возвращает и те и другие. |
| `date` | string, YYYY-MM-DD | необязательный | Только задачи ровно на эту дату. |
| `date_from` | string, YYYY-MM-DD | необязательный | Только задачи с этой даты и позже. |
| `date_to` | string, YYYY-MM-DD | необязательный | Только задачи по эту дату включительно. |
| `no_date` | boolean | необязательный | true вернёт только задачи без даты, то есть входящие. Фильтры по датам при этом не действуют. |
| `project_id` | string, UUID | необязательный | Только задачи в этом проекте. |
| `list_id` | string, UUID | необязательный | Только задачи в этом списке. |
| `priority` | string, одно из high, medium, low, none | необязательный | Только задачи с этим приоритетом. |
| `include_archived` | boolean, по умолчанию false | необязательный | Показывать и архивные задачи. По умолчанию нет. |
| `limit` | integer, 1-200, по умолчанию 50 | необязательный | Сколько строк вернуть. По умолчанию 50, больше 200 сервер не отдаст. |
| `offset` | integer | необязательный | Сколько строк пропустить, чтобы листать длинный результат. |

Пример вызова:

```json
{
  "status": "open",
  "date_from": "2026-09-01",
  "date_to": "2026-09-07",
  "limit": 20
}
```

Ответ:

```json
{
  "count": 2,
  "tasks": [
    {
      "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
      "title": "Send the invoice",
      "date": "2026-09-01",
      "start_time": "10:00:00",
      "all_day": false,
      "completed": false,
      "priority": "high",
      "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5"
    },
    {
      "id": "b7419e60-8c25-4d13-a0f6-52d7c1b93e48",
      "title": "Water the plants",
      "date": "2026-09-03",
      "all_day": true,
      "completed": false,
      "priority": "none"
    }
  ]
}
```

Полезно знать:

- Даты только календарные. Сервер не знает, что такое «завтра», поэтому дату считаете вы и передаёте в виде YYYY-MM-DD.
- В списке приходит короткий набор полей, он виден в примере. Всё остальное, включая описание, отдаёт get_task.
- no_date сильнее, чем date, date_from и date_to: с ним фильтры по датам не работают.
- Архивные задачи скрыты, пока их не попросишь. То, что лежит в корзине, отсюда не возвращается никогда.
- В списке описание обрезается до 300 символов, а картинка внутри описания заменяется на [image]. Так одна страница задач не съедает весь контекст модели.

#### `search_tasks` (читает)

Найти задачи по словам, а не по id или дате. Ищется подстрока в заголовке и описании, регистр не важен.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `query` | string, 1-200 | обязательный | Текст, который ищем в заголовке или описании. |
| `status` | string, одно из open, completed, all, по умолчанию open | необязательный | Какие задачи показывать. По умолчанию open, то есть невыполненные; all возвращает и те и другие. |
| `include_archived` | boolean, по умолчанию false | необязательный | Показывать и архивные задачи. По умолчанию нет. |
| `limit` | integer, 1-200, по умолчанию 50 | необязательный | Сколько строк вернуть. По умолчанию 50, больше 200 сервер не отдаст. |

Пример вызова:

```json
{
  "query": "invoice",
  "status": "all",
  "limit": 10
}
```

Ответ:

```json
{
  "count": 1,
  "tasks": [
    {
      "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
      "title": "Send the invoice",
      "date": "2026-09-01",
      "all_day": false,
      "completed": false,
      "priority": "high"
    }
  ]
}
```

Полезно знать:

- Это подстрока, а не поисковая машина: ни масок, ни словоформ. Символы *, % и _ вырезаются из запроса до того, как он дойдёт до базы.
- Результаты приходят в порядке недавних изменений, свежие сверху.

#### `get_task` (читает)

Одна задача по id, со всем, что на ней есть: описание, расписание, цвет, правило повтора, отметки времени.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `task_id` | string, UUID | обязательный | UUID задачи. |

Пример вызова:

```json
{
  "task_id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33"
}
```

Ответ:

```json
{
  "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
  "type": "task",
  "title": "Send the invoice",
  "description": "August hours, the usual template.",
  "date": "2026-09-01",
  "start_time": "10:00:00",
  "all_day": false,
  "completed": false,
  "priority": "high",
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "reminder": 30,
  "is_archived": false,
  "is_deleted": false,
  "created_at": "2026-08-24T09:12:44.318Z",
  "updated_at": "2026-08-28T07:01:02.905Z"
}
```

Полезно знать:

- Здесь описание обрезается по 8000 символов, а не по 300, как в списке: этот вызов сделан именно про эту задачу.
- Если задачи с таким id в аккаунте нет, придёт ошибка, а не пустой ответ.

#### `create_task` (меняет)

Создать одну задачу в проекте. Задача без даты попадает во входящие, но задачи без проекта не бывает.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `title` | string, 1-500 | обязательный | Заголовок задачи, та самая строка, которую видно в списке. |
| `description` | string, ≤ 20000 | необязательный | Развёрнутые заметки к задаче, произвольный текст. Чтобы снять поле, передайте null. |
| `date` | string, YYYY-MM-DD | необязательный | Дата, YYYY-MM-DD. Без неё задача попадёт во входящие. Чтобы снять поле, передайте null. |
| `start_time` | string, HH:MM | необязательный | Время начала, HH:MM, 24-часовой формат, в местном времени пользователя. Чтобы снять поле, передайте null. |
| `end_time` | string, HH:MM | необязательный | Время окончания, HH:MM, 24-часовой формат. Чтобы снять поле, передайте null. |
| `all_day` | boolean | необязательный | true, если задача занимает весь день, а не отрезок времени. |
| `priority` | string, одно из high, medium, low, none | необязательный | Приоритет: high, medium, low или none. Чтобы снять поле, передайте null. |
| `project_id` | string, UUID | необязательный | UUID проекта, его отдаёт list_projects. Не передавать его можно, только если проект в аккаунте один: тогда задача уйдёт в него. Если проектов несколько, а project_id нет, вызов вернёт ошибку со списком проектов на выбор. |
| `list_id` | string, UUID | необязательный | UUID списка, его отдаёт list_lists. Чтобы снять поле, передайте null. |
| `reminder` | integer, 0-10080 | необязательный | Напоминание, за сколько минут до начала. Чтобы снять поле, передайте null. |
| `color` | string, ≤ 32 | необязательный | Свой цвет в hex, например #6366f1. Чтобы снять поле, передайте null. |

Пример вызова:

```json
{
  "title": "Send the invoice",
  "date": "2026-09-01",
  "start_time": "10:00",
  "priority": "high",
  "reminder": 30,
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5"
}
```

Ответ:

```json
{
  "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
  "type": "task",
  "title": "Send the invoice",
  "date": "2026-09-01",
  "start_time": "10:00:00",
  "all_day": false,
  "completed": false,
  "priority": "high",
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "reminder": 30,
  "is_archived": false,
  "is_deleted": false,
  "created_at": "2026-08-28T07:01:02.905Z",
  "updated_at": "2026-08-28T07:01:02.905Z",
  "project": { "id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5", "name": "Work" }
}
```

Полезно знать:

- Каждая задача лежит в проекте. Отдельной кучи «без проекта» тут нет: экраны задач запрашиваются по проекту, поэтому задача, записанная без него, останется в базе, но не появится нигде. Поэтому вызов отклоняется, а не подбирает проект на своё усмотрение.
- В ответе приходит новый id и название проекта, чтобы можно было сказать человеку, куда именно попала задача, а не просто «добавлено».
- Записи календаря создаются не здесь. Встреча или событие это create_event, и именно тип решает, на каком экране запись окажется.

#### `batch_create_tasks` (меняет)

До 20 задач за один вызов. Это то, чем стоит пользоваться, когда список надиктовали целиком, вместо того чтобы звать create_task снова и снова.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `tasks` | object[], 1-20 | обязательный | Задачи, которые нужно создать. Поля те же, что у create_task. |

Поля каждого элемента `tasks`:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `title` | string, 1-500 | обязательный | Заголовок задачи, та самая строка, которую видно в списке. |
| `description` | string, ≤ 20000 | необязательный | Развёрнутые заметки к задаче, произвольный текст. Чтобы снять поле, передайте null. |
| `date` | string, YYYY-MM-DD | необязательный | Дата, YYYY-MM-DD. Без неё задача попадёт во входящие. Чтобы снять поле, передайте null. |
| `start_time` | string, HH:MM | необязательный | Время начала, HH:MM, 24-часовой формат, в местном времени пользователя. Чтобы снять поле, передайте null. |
| `end_time` | string, HH:MM | необязательный | Время окончания, HH:MM, 24-часовой формат. Чтобы снять поле, передайте null. |
| `all_day` | boolean | необязательный | true, если задача занимает весь день, а не отрезок времени. |
| `priority` | string, одно из high, medium, low, none | необязательный | Приоритет: high, medium, low или none. Чтобы снять поле, передайте null. |
| `project_id` | string, UUID | необязательный | UUID проекта, его отдаёт list_projects. Правило то же, что у create_task, и действует на всю пачку: одной строки без проекта хватит, чтобы отклонить весь вызов, поэтому половина надиктованного списка не потеряется. |
| `list_id` | string, UUID | необязательный | UUID списка, его отдаёт list_lists. Чтобы снять поле, передайте null. |
| `reminder` | integer, 0-10080 | необязательный | Напоминание, за сколько минут до начала. Чтобы снять поле, передайте null. |
| `color` | string, ≤ 32 | необязательный | Свой цвет в hex, например #6366f1. Чтобы снять поле, передайте null. |

Пример вызова:

```json
{
  "tasks": [
    { "title": "Buy milk" },
    { "title": "Book the dentist", "date": "2026-09-04", "priority": "medium" }
  ]
}
```

Ответ:

```json
{
  "created": 2,
  "tasks": [
    {
      "id": "1c8d43a7-6b02-4e59-9f31-7a4c0e2b6d95",
      "title": "Buy milk",
      "all_day": false,
      "completed": false,
      "priority": "none"
    },
    {
      "id": "4e70b915-2f38-4c6a-b8d0-19c53e7a4f26",
      "title": "Book the dentist",
      "date": "2026-09-04",
      "all_day": false,
      "completed": false,
      "priority": "medium"
    }
  ]
}
```

Полезно знать:

- У каждого элемента те же поля, что у create_task.
- Двадцать это жёсткий потолок. Список длиннее придётся разбить на несколько вызовов.
- Они записываются одним запросом, поэтому появятся либо все, либо ни одной.

#### `update_task` (меняет)

Поменять поля у существующей задачи. Трогаются только те поля, которые вы передали.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `task_id` | string, UUID | обязательный | UUID задачи. |
| `title` | string, 1-500 | необязательный | Заголовок задачи, та самая строка, которую видно в списке. |
| `description` | string, ≤ 20000 | необязательный | Развёрнутые заметки к задаче, произвольный текст. Чтобы снять поле, передайте null. |
| `date` | string, YYYY-MM-DD | необязательный | Дата, YYYY-MM-DD. Без неё задача попадёт во входящие. Чтобы снять поле, передайте null. |
| `start_time` | string, HH:MM | необязательный | Время начала, HH:MM, 24-часовой формат, в местном времени пользователя. Чтобы снять поле, передайте null. |
| `end_time` | string, HH:MM | необязательный | Время окончания, HH:MM, 24-часовой формат. Чтобы снять поле, передайте null. |
| `all_day` | boolean | необязательный | true, если задача занимает весь день, а не отрезок времени. |
| `priority` | string, одно из high, medium, low, none | необязательный | Приоритет: high, medium, low или none. Чтобы снять поле, передайте null. |
| `project_id` | string, UUID | необязательный | Переносит задачу в этот проект. null сюда передать нельзя: задача всегда принадлежит проекту. |
| `list_id` | string, UUID | необязательный | UUID списка, его отдаёт list_lists. Чтобы снять поле, передайте null. |
| `reminder` | integer, 0-10080 | необязательный | Напоминание, за сколько минут до начала. Чтобы снять поле, передайте null. |
| `color` | string, ≤ 32 | необязательный | Свой цвет в hex, например #6366f1. Чтобы снять поле, передайте null. |
| `is_archived` | boolean | необязательный | true убирает задачу в архив: из списков она пропадает, поиском находится. false возвращает обратно. |

Пример вызова:

```json
{
  "task_id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
  "date": "2026-09-02",
  "priority": "medium"
}
```

Ответ:

```json
{
  "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
  "type": "task",
  "title": "Send the invoice",
  "date": "2026-09-02",
  "start_time": "10:00:00",
  "all_day": false,
  "completed": false,
  "priority": "medium",
  "is_archived": false,
  "is_deleted": false,
  "updated_at": "2026-08-28T07:04:19.442Z"
}
```

Полезно знать:

- Кроме task_id нужно передать хотя бы одно поле, иначе вызов будет отклонён, а не тихо ничего не сделает.
- Чтобы снять поле, отправьте null, а не пустую строку; если поле не передано, оно просто останется прежним. Исключение это проект: его можно только сменить на другой. Отвязать список помогает move_task с clear_list.
- Отметить задачу выполненной это complete_task, переложить в другое место это move_task.

#### `complete_task` (меняет)

Отметить задачу выполненной или открыть её заново, передав completed=false.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `task_id` | string, UUID | обязательный | UUID задачи. |
| `completed` | boolean, по умолчанию true | необязательный | true отмечает задачу выполненной, это поведение по умолчанию. false открывает её заново. |

Пример вызова:

```json
{
  "task_id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33"
}
```

Ответ:

```json
{
  "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
  "type": "task",
  "title": "Send the invoice",
  "date": "2026-09-02",
  "completed": true,
  "priority": "medium",
  "is_archived": false,
  "is_deleted": false,
  "updated_at": "2026-08-28T07:06:00.117Z"
}
```

Полезно знать:

- Второй такой же вызов на той же задаче ничего не меняет.

#### `move_task` (меняет)

Переложить задачу в другой проект или список либо вынуть её обратно во входящие.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `task_id` | string, UUID | обязательный | UUID задачи. |
| `project_id` | string, UUID | необязательный | UUID проекта, куда переносим. |
| `list_id` | string, UUID | необязательный | UUID списка, куда переносим. |
| `clear_list` | boolean | необязательный | true отвязывает задачу от списка, в проекте она остаётся. |

Пример вызова:

```json
{
  "task_id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "clear_list": true
}
```

Ответ:

```json
{
  "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
  "type": "task",
  "title": "Send the invoice",
  "date": "2026-09-02",
  "completed": true,
  "priority": "medium",
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "is_archived": false,
  "is_deleted": false,
  "updated_at": "2026-08-28T07:08:41.220Z"
}
```

Полезно знать:

- Нужно назвать хотя бы одно направление: project_id, list_id или clear_list. Иначе будет ошибка.
- Задача всегда лежит в каком-то проекте, поэтому уйти из проекта можно только в другой. Оставить задачу совсем без проекта нельзя: такая строка не показывается в приложении ни на одном экране.
- В ответе приходит название проекта, куда задача переехала, чтобы его можно было назвать человеку.
- Направление сначала проверяется в вашем аккаунте, поэтому задача не может уехать в чужой проект.

#### `delete_task` (удаляет)

Убрать задачу в корзину. Это то же мягкое удаление, что и в приложении, поэтому её ещё можно вернуть.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `task_id` | string, UUID | обязательный | UUID задачи. |

Пример вызова:

```json
{
  "task_id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33"
}
```

Ответ:

```json
{
  "trashed": true,
  "task": {
    "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
    "title": "Send the invoice",
    "is_deleted": true,
    "deleted_at": "2026-08-28T07:10:55.003Z"
  }
}
```

Полезно знать:

- Ничего не стирается. Вернуть можно через restore_from_trash с kind=task, а стирает уже delete_from_trash_forever.
- Только задачи. Встреча или событие удаляются через delete_event.

### Календарь

Встречи и события, а ещё тот единственный инструмент, который читает сразу целый период, вместе с датированными задачами.

Написано по: `mcp/src/tools/calendar.ts`

#### `list_events` (читает)

Всё, что стоит между двумя датами, включительно: встречи, события и датированные задачи вперемешку, по порядку. Именно этим отвечают на вопрос «как выглядит моя неделя».

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `date_from` | string, YYYY-MM-DD | обязательный | Первый день периода, YYYY-MM-DD. |
| `date_to` | string, YYYY-MM-DD | обязательный | Последний день периода, YYYY-MM-DD, включительно. |
| `types` | string[], одно из task, meeting, event | необязательный | Какие типы записей показывать. По умолчанию все три. |
| `include_archived` | boolean, по умолчанию false | необязательный | Показывать и архивные записи. По умолчанию нет. |
| `include_completed` | boolean, по умолчанию true | необязательный | Показывать и уже выполненные записи. По умолчанию да. |
| `limit` | integer, 1-200, по умолчанию 50 | необязательный | Сколько строк вернуть. По умолчанию 50, больше 200 сервер не отдаст. |

Пример вызова:

```json
{
  "date_from": "2026-09-01",
  "date_to": "2026-09-07",
  "types": ["meeting", "event"]
}
```

Ответ:

```json
{
  "count": 1,
  "from": "2026-09-01",
  "to": "2026-09-07",
  "entries": [
    {
      "id": "e2f70a95-4d1c-4b83-97a6-0b5e8c3d21f7",
      "type": "meeting",
      "title": "Weekly sync",
      "date": "2026-09-02",
      "start_time": "11:00:00",
      "end_time": "11:30:00",
      "all_day": false,
      "completed": false,
      "priority": "none"
    }
  ]
}
```

Полезно знать:

- Задачи включены по умолчанию, потому что день это день. Чтобы оставить только встречи и события, передайте types.
- Архивные записи отсюда не приходят никогда, флага для них нет, как и для того, что лежит в корзине.
- Порядок такой: дата, потом записи на весь день, потом время начала.
- Для работы с делами есть list_tasks: там фильтры, нужные списку задач, а здесь период, нужный календарю.

#### `create_event` (меняет)

Поставить в календарь встречу или событие.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `title` | string, 1-500 | обязательный | Как называется запись. |
| `date` | string, YYYY-MM-DD | обязательный | День, на который она поставлена, YYYY-MM-DD. |
| `start_time` | string, HH:MM | необязательный | Время начала, HH:MM, 24-часовой формат, местное время пользователя. |
| `end_time` | string, HH:MM | необязательный | Время окончания, HH:MM, 24-часовой формат. |
| `all_day` | boolean | необязательный | true для записи на весь день. |
| `type` | string, одно из meeting, event, по умолчанию event | необязательный | Тип записи. По умолчанию event. |
| `description` | string, ≤ 20000 | необязательный | Развёрнутые заметки, произвольный текст. |
| `project_id` | string, UUID | необязательный | UUID проекта: это календарь, на котором стоит запись, и её цвет. Не передавать его можно, только если проект в аккаунте один. Если проектов несколько, а project_id нет, вызов вернёт ошибку со списком проектов на выбор. |
| `color` | string, ≤ 32 | необязательный | Свой цвет в hex, например #6366f1. |
| `reminder` | integer, 0-10080 | необязательный | Напоминание, за сколько минут до начала. |

Пример вызова:

```json
{
  "title": "Weekly sync",
  "date": "2026-09-02",
  "start_time": "11:00",
  "end_time": "11:30",
  "type": "meeting",
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5"
}
```

Ответ:

```json
{
  "id": "e2f70a95-4d1c-4b83-97a6-0b5e8c3d21f7",
  "type": "meeting",
  "title": "Weekly sync",
  "date": "2026-09-02",
  "start_time": "11:00:00",
  "end_time": "11:30:00",
  "all_day": false,
  "completed": false,
  "priority": "none",
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "is_archived": false,
  "is_deleted": false,
  "created_at": "2026-08-28T07:12:30.771Z",
  "updated_at": "2026-08-28T07:12:30.771Z"
}
```

Полезно знать:

- Дело создаётся через create_task. Живут они в одном месте, и именно тип решает, на каком экране окажется запись.
- Чужой project_id будет отклонён до того, как что-то запишется.

#### `update_event` (меняет)

Перенести или отредактировать встречу либо событие: день, время, заголовок, заметки, цвет, проект. Трогаются только переданные поля.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `event_id` | string, UUID | обязательный | UUID записи, его отдаёт list_events. |
| `title` | string, 1-500 | необязательный | Новый заголовок. |
| `date` | string, YYYY-MM-DD | необязательный | Новый день, YYYY-MM-DD. |
| `start_time` | string, HH:MM | необязательный | Новое время начала, HH:MM. Чтобы снять поле, передайте null. |
| `end_time` | string, HH:MM | необязательный | Новое время окончания, HH:MM. Чтобы снять поле, передайте null. |
| `all_day` | boolean | необязательный | true превращает запись в событие на весь день. |
| `description` | string, ≤ 20000 | необязательный | Новые заметки. Чтобы снять поле, передайте null. |
| `project_id` | string, UUID | необязательный | Перенести запись в этот проект. null сюда передать нельзя: запись всегда стоит на каком-то календаре. |
| `color` | string, ≤ 32 | необязательный | Свой цвет в hex. Чтобы снять поле, передайте null. |
| `reminder` | integer, 0-10080 | необязательный | Напоминание, за сколько минут до начала. Чтобы снять поле, передайте null. |

Пример вызова:

```json
{
  "event_id": "e2f70a95-4d1c-4b83-97a6-0b5e8c3d21f7",
  "date": "2026-09-03",
  "start_time": "12:00",
  "end_time": "12:30"
}
```

Ответ:

```json
{
  "id": "e2f70a95-4d1c-4b83-97a6-0b5e8c3d21f7",
  "type": "meeting",
  "title": "Weekly sync",
  "date": "2026-09-03",
  "start_time": "12:00:00",
  "end_time": "12:30:00",
  "all_day": false,
  "completed": false,
  "priority": "none",
  "is_archived": false,
  "is_deleted": false,
  "updated_at": "2026-08-28T07:15:02.610Z"
}
```

Полезно знать:

- Задачи отсюда недоступны. Если направить вызов на id задачи, придёт ошибка со ссылкой на update_task.
- Кроме event_id нужно передать хотя бы одно поле.
- У date null не работает: у записи всегда есть день, поэтому её можно перенести, но не снять.

#### `delete_event` (удаляет)

Убрать встречу или событие в корзину, то же мягкое удаление, что и в приложении.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `event_id` | string, UUID | обязательный | UUID записи, его отдаёт list_events. |

Пример вызова:

```json
{
  "event_id": "e2f70a95-4d1c-4b83-97a6-0b5e8c3d21f7"
}
```

Ответ:

```json
{
  "trashed": true,
  "event": {
    "id": "e2f70a95-4d1c-4b83-97a6-0b5e8c3d21f7",
    "title": "Weekly sync",
    "is_deleted": true,
    "deleted_at": "2026-08-28T07:16:44.118Z"
  }
}
```

Полезно знать:

- Вернуть можно через restore_from_trash с kind=event.
- Задачи удаляются через delete_task.

### Проекты и списки

Структура, в которую складывают задачи. В проектах лежат списки, в списках задачи, и то и другое можно не удалять, а убрать в архив.

Написано по: `mcp/src/tools/structure.ts`

#### `list_projects` (читает)

Все проекты аккаунта с их id и цветом. Вызывайте его первым, когда проект назван словами: так дальше уйдёт правильный project_id.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `include_archived` | boolean, по умолчанию false | необязательный | Показывать и архивные проекты. По умолчанию нет. |
| `limit` | integer, 1-200, по умолчанию 50 | необязательный | Сколько строк вернуть. По умолчанию 50, больше 200 сервер не отдаст. |

Пример вызова:

```json
{
  "include_archived": false
}
```

Ответ:

```json
{
  "count": 2,
  "projects": [
    {
      "id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
      "name": "Work",
      "color": "#6366f1",
      "icon": "briefcase",
      "is_active": true,
      "is_default": false,
      "order": 1,
      "is_archived": false,
      "created_at": "2026-05-02T10:14:07.882Z"
    },
    {
      "id": "6b0a2e54-71d3-4f89-93c1-40b7e5a2c6d9",
      "name": "Home",
      "color": "#22c55e",
      "is_active": true,
      "is_default": true,
      "order": 2,
      "is_archived": false,
      "created_at": "2026-05-02T10:14:07.882Z"
    }
  ]
}
```

Полезно знать:

- Архивные проекты скрыты, пока их не попросишь, а проекты из корзины отсюда не приходят.
- Порядок тот же, что в боковом меню, а при равном месте первым идёт более старый.

#### `create_project` (меняет)

Создать проект. В ответе приходит id, в который дальше складывают задачи.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `name` | string, 1-200 | обязательный | Название проекта. |
| `description` | string, ≤ 2000 | необязательный | Короткая строка о проекте, необязательная. |
| `color` | string, ≤ 32 | необязательный | Цвет в hex, например #6366f1. По умолчанию индиго. |
| `icon` | string, ≤ 64 | необязательный | Название иконки, необязательно. |

Пример вызова:

```json
{
  "name": "Work",
  "color": "#6366f1",
  "icon": "briefcase"
}
```

Ответ:

```json
{
  "id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "name": "Work",
  "color": "#6366f1",
  "icon": "briefcase",
  "is_active": true,
  "is_default": false,
  "order": 1,
  "is_archived": false,
  "created_at": "2026-08-28T07:18:11.409Z"
}
```

Полезно знать:

- Без цвета проект получится индиговым, это тот же цвет по умолчанию, что и в приложении.

#### `update_project` (меняет)

Переименовать проект или поменять цвет, иконку, описание, архивность. Трогаются только переданные поля.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `project_id` | string, UUID | обязательный | UUID проекта, его отдаёт list_projects. |
| `name` | string, 1-200 | необязательный | Новое название проекта. |
| `description` | string, ≤ 2000 | необязательный | Короткая строка о проекте. |
| `color` | string, ≤ 32 | необязательный | Цвет в hex, например #6366f1. |
| `icon` | string, ≤ 64 | необязательный | Название иконки. |
| `is_archived` | boolean | необязательный | true убирает проект в архив, false возвращает обратно. |

Пример вызова:

```json
{
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "name": "Work 2026",
  "is_archived": false
}
```

Ответ:

```json
{
  "id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "name": "Work 2026",
  "color": "#6366f1",
  "icon": "briefcase",
  "is_active": true,
  "is_default": false,
  "order": 1,
  "is_archived": false,
  "created_at": "2026-05-02T10:14:07.882Z"
}
```

Полезно знать:

- Архивирование это способ убрать проект с глаз, не удаляя: он уходит из бокового меню, а его задачи остаются на месте.
- Кроме project_id нужно передать хотя бы одно поле.

#### `delete_project` (удаляет)

Убрать проект в корзину. Его задачи и списки продолжают на него ссылаться и возвращаются вместе с ним.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `project_id` | string, UUID | обязательный | UUID проекта, его отдаёт list_projects. |

Пример вызова:

```json
{
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5"
}
```

Ответ:

```json
{
  "trashed": true,
  "project": {
    "id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
    "name": "Work 2026",
    "is_deleted": true,
    "deleted_at": "2026-08-28T07:20:52.336Z"
  }
}
```

Полезно знать:

- Проект по умолчанию удалить нельзя ни отсюда, ни в приложении. Именно туда попадает всё неразобранное.
- Чтобы убрать проект с глаз, не удаляя, заархивируйте его через update_project.

#### `list_lists` (читает)

Списки, то есть подгруппы внутри проектов. Передайте project_id, чтобы увидеть только его списки.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `project_id` | string, UUID | необязательный | Только списки этого проекта. |
| `include_archived` | boolean, по умолчанию false | необязательный | Показывать и архивные списки. По умолчанию нет. |
| `limit` | integer, 1-200, по умолчанию 50 | необязательный | Сколько строк вернуть. По умолчанию 50, больше 200 сервер не отдаст. |

Пример вызова:

```json
{
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5"
}
```

Ответ:

```json
{
  "count": 1,
  "lists": [
    {
      "id": "7a4e9c31-2b60-4d8f-a1c5-3e97b6042f18",
      "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
      "name": "Backlog",
      "icon": "inbox",
      "order": 1,
      "is_archived": false,
      "created_at": "2026-05-02T10:16:33.501Z"
    }
  ]
}
```

Полезно знать:

- Список может жить и вне проекта, тогда в строке просто не будет project_id.
- Архивные списки скрыты, пока их не попросишь.

#### `create_list` (меняет)

Создать список, внутри проекта или сам по себе.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `name` | string, 1-200 | обязательный | Название списка. |
| `project_id` | string, UUID | необязательный | Проект, в котором создать список. |
| `icon` | string, ≤ 64 | необязательный | Название иконки, необязательно. |
| `color` | string, ≤ 32 | необязательный | Цвет в hex, например #6366f1. |

Пример вызова:

```json
{
  "name": "Backlog",
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "icon": "inbox"
}
```

Ответ:

```json
{
  "id": "7a4e9c31-2b60-4d8f-a1c5-3e97b6042f18",
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "name": "Backlog",
  "icon": "inbox",
  "order": 1,
  "is_archived": false,
  "created_at": "2026-08-28T07:22:04.918Z"
}
```

Полезно знать:

- Чужой project_id будет отклонён до того, как что-то запишется.

#### `update_list` (меняет)

Переименовать список, поменять иконку или цвет, перенести в другой проект, убрать в архив.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `list_id` | string, UUID | обязательный | UUID списка, его отдаёт list_lists. |
| `name` | string, 1-200 | необязательный | Новое название списка. |
| `icon` | string, ≤ 64 | необязательный | Название иконки. |
| `color` | string, ≤ 32 | необязательный | Цвет в hex, например #6366f1. |
| `project_id` | string, UUID | необязательный | Перенести список в этот проект. |
| `is_archived` | boolean | необязательный | true убирает список в архив, false возвращает обратно. |

Пример вызова:

```json
{
  "list_id": "7a4e9c31-2b60-4d8f-a1c5-3e97b6042f18",
  "name": "Later"
}
```

Ответ:

```json
{
  "id": "7a4e9c31-2b60-4d8f-a1c5-3e97b6042f18",
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "name": "Later",
  "icon": "inbox",
  "order": 1,
  "is_archived": false,
  "created_at": "2026-05-02T10:16:33.501Z"
}
```

Полезно знать:

- Кроме list_id нужно передать хотя бы одно поле.
- Перенос в чужой проект будет отклонён.

#### `delete_list` (удаляет)

Убрать список в корзину. Задачи из него остаются в своём проекте и просто теряют список, ровно как в приложении.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `list_id` | string, UUID | обязательный | UUID списка, его отдаёт list_lists. |

Пример вызова:

```json
{
  "list_id": "7a4e9c31-2b60-4d8f-a1c5-3e97b6042f18"
}
```

Ответ:

```json
{
  "trashed": true,
  "list": {
    "id": "7a4e9c31-2b60-4d8f-a1c5-3e97b6042f18",
    "name": "Later",
    "is_deleted": true,
    "deleted_at": "2026-08-28T07:24:10.220Z"
  }
}
```

Полезно знать:

- Вернуть можно через restore_from_trash с kind=list.

### Заметки

Заметки обычным текстом: список и поиск, чтение одной, создание, замена текста целиком, удаление в корзину.

Написано по: `mcp/src/tools/notes.ts`

#### `list_notes` (читает)

Заголовки заметок с их id: сначала закреплённые, дальше по времени последней правки. Передайте query, чтобы искать по заголовку и тексту.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `query` | string, 1-200 | необязательный | Подстрока для поиска в заголовке или тексте, регистр не важен. |
| `include_archived` | boolean, по умолчанию false | необязательный | Показывать и архивные заметки. По умолчанию нет. |
| `limit` | integer, 1-200, по умолчанию 50 | необязательный | Сколько строк вернуть. По умолчанию 50, больше 200 сервер не отдаст. |

Пример вызова:

```json
{
  "query": "invoice",
  "limit": 20
}
```

Ответ:

```json
{
  "count": 1,
  "notes": [
    {
      "id": "c81b5d47-0e39-4b2a-9f64-5a7d31e8c026",
      "title": "Invoice template",
      "project_id": null,
      "is_pinned": true,
      "is_archived": false,
      "sort_order": 0,
      "created_at": "2026-06-11T08:20:15.004Z",
      "updated_at": "2026-08-20T19:41:03.772Z"
    }
  ]
}
```

Полезно знать:

- Текста заметок здесь нет. Он приходит из get_note.
- Запрос ищет сразу и по заголовку, и по тексту.

#### `get_note` (читает)

Одна заметка по id, с текстом в виде plain text.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `note_id` | string, UUID | обязательный | UUID заметки. |

Пример вызова:

```json
{
  "note_id": "c81b5d47-0e39-4b2a-9f64-5a7d31e8c026"
}
```

Ответ:

```json
{
  "id": "c81b5d47-0e39-4b2a-9f64-5a7d31e8c026",
  "title": "Invoice template",
  "project_id": null,
  "is_pinned": true,
  "is_archived": false,
  "sort_order": 0,
  "created_at": "2026-06-11T08:20:15.004Z",
  "updated_at": "2026-08-20T19:41:03.772Z",
  "content_text": "Hours, rate, bank details.

Send on the first working day."
}
```

Полезно знать:

- Текст обрезается по 8000 символов, а вставленная в него картинка заменяется на [image].

#### `create_note` (меняет)

Создать заметку из обычного текста. Пустые строки станут в редакторе разрывами абзацев.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `title` | string, ≤ 300 | необязательный | Заголовок заметки. Оставьте пустым, если заметка без названия. |
| `content` | string, ≤ 100000 | необязательный | Текст заметки, обычный plain text. |
| `project_id` | string, UUID | необязательный | Проект, к которому относится заметка: это папка, в которой она появится. Не передавать его можно, только если проект в аккаунте один. Если проектов несколько, а project_id нет, вызов вернёт ошибку со списком проектов на выбор. |
| `is_pinned` | boolean | необязательный | Закрепить заметку наверху списка. |

Пример вызова:

```json
{
  "title": "Invoice template",
  "content": "Hours, rate, bank details.

Send on the first working day.",
  "is_pinned": true
}
```

Ответ:

```json
{
  "id": "c81b5d47-0e39-4b2a-9f64-5a7d31e8c026",
  "title": "Invoice template",
  "is_pinned": true,
  "is_archived": false,
  "sort_order": 0,
  "created_at": "2026-08-28T07:26:47.512Z",
  "updated_at": "2026-08-28T07:26:47.512Z",
  "content_text": "Hours, rate, bank details.

Send on the first working day."
}
```

Полезно знать:

- Только обычный текст. Форматирование, ссылки и картинки этот инструмент не пишет, но созданная им заметка нормально открывается и правится в приложении.
- Заметку можно создать без заголовка и без текста, приложение покажет её как безымянную.

#### `update_note` (меняет)

Поменять у заметки заголовок, текст, проект, закрепление или архивность.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `note_id` | string, UUID | обязательный | UUID заметки, его отдаёт list_notes. |
| `title` | string, ≤ 300 | необязательный | Новый заголовок. |
| `content` | string, ≤ 100000 | необязательный | Новый текст заметки, plain text. Он заменяет прежний целиком. |
| `project_id` | string, UUID | необязательный | Перенести заметку в этот проект. null сюда передать нельзя: заметка всегда лежит в проекте. |
| `is_pinned` | boolean | необязательный | Закрепить заметку или снять закрепление. |
| `is_archived` | boolean | необязательный | Убрать заметку в архив или вернуть обратно. |

Пример вызова:

```json
{
  "note_id": "c81b5d47-0e39-4b2a-9f64-5a7d31e8c026",
  "content": "Hours, rate, bank details.

Send on the first working day.

New: VAT line."
}
```

Ответ:

```json
{
  "id": "c81b5d47-0e39-4b2a-9f64-5a7d31e8c026",
  "title": "Invoice template",
  "is_pinned": true,
  "is_archived": false,
  "sort_order": 0,
  "created_at": "2026-06-11T08:20:15.004Z",
  "updated_at": "2026-08-28T07:28:19.660Z",
  "content_text": "Hours, rate, bank details.

Send on the first working day.

New: VAT line."
}
```

Полезно знать:

- content заменяет текст целиком. Чтобы дописать к заметке, сначала прочитайте её через get_note и отправьте старый текст вместе с новым.
- Кроме note_id нужно передать хотя бы одно поле.

#### `delete_note` (удаляет)

Убрать заметку в корзину, то же мягкое удаление, что и в приложении.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `note_id` | string, UUID | обязательный | UUID заметки, его отдаёт list_notes. |

Пример вызова:

```json
{
  "note_id": "c81b5d47-0e39-4b2a-9f64-5a7d31e8c026"
}
```

Ответ:

```json
{
  "trashed": true,
  "note": {
    "id": "c81b5d47-0e39-4b2a-9f64-5a7d31e8c026",
    "title": "Invoice template",
    "is_deleted": true
  }
}
```

Полезно знать:

- Вернуть можно через restore_from_trash с kind=note.

### Привычки

Привычки и ежедневные отметки, вместе с историей, по которой считают серии.

Написано по: `mcp/src/tools/habits.ts`

#### `list_habits` (читает)

Привычки, которые вы отмечаете, вместе с их id. Вызывайте его перед любой отметкой.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `include_inactive` | boolean, по умолчанию false | необязательный | Показывать и приостановленные привычки. По умолчанию нет. |
| `limit` | integer, 1-200, по умолчанию 50 | необязательный | Сколько строк вернуть. По умолчанию 50, больше 200 сервер не отдаст. |

Пример вызова:

```json
{
  "include_inactive": false
}
```

Ответ:

```json
{
  "count": 1,
  "habits": [
    {
      "id": "5c9a11de-7f42-4e06-8b3d-1c60a95d7f84",
      "name": "Read 20 pages",
      "icon": "book",
      "color": "#f59e0b",
      "time_of_day": "evening",
      "is_active": true,
      "sort_order": 0,
      "created_at": "2026-07-01T06:30:11.204Z",
      "updated_at": "2026-08-27T20:02:48.911Z"
    }
  ]
}
```

Полезно знать:

- Приостановленные привычки скрыты, пока их не попросишь.

#### `get_habit_checkins` (читает)

История отметок одной привычки за период, свежие сверху. Именно отсюда берутся ответы про серии и регулярность.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `habit_id` | string, UUID | обязательный | UUID привычки, его отдаёт list_habits. |
| `date_from` | string, YYYY-MM-DD | обязательный | Первый день периода, YYYY-MM-DD. |
| `date_to` | string, YYYY-MM-DD | обязательный | Последний день периода, YYYY-MM-DD, включительно. |
| `limit` | integer, 1-200, по умолчанию 50 | необязательный | Сколько строк вернуть. По умолчанию 50, больше 200 сервер не отдаст. |

Пример вызова:

```json
{
  "habit_id": "5c9a11de-7f42-4e06-8b3d-1c60a95d7f84",
  "date_from": "2026-08-01",
  "date_to": "2026-08-31"
}
```

Ответ:

```json
{
  "habit": "Read 20 pages",
  "habit_id": "5c9a11de-7f42-4e06-8b3d-1c60a95d7f84",
  "count": 2,
  "completed_count": 1,
  "checkins": [
    {
      "id": "8f2b6c05-14ad-4e73-9c81-2b407fd6a319",
      "habit_id": "5c9a11de-7f42-4e06-8b3d-1c60a95d7f84",
      "date": "2026-08-27",
      "completed": true,
      "created_at": "2026-08-27T20:02:48.911Z"
    },
    {
      "id": "3d5e9017-6b24-4c8f-91a0-7e13c5b8d024",
      "habit_id": "5c9a11de-7f42-4e06-8b3d-1c60a95d7f84",
      "date": "2026-08-26",
      "completed": false,
      "created_at": "2026-08-26T21:44:02.180Z"
    }
  ]
}
```

Полезно знать:

- completed_count это дни, которые засчитаны, а count это все строки за период, вместе с пропусками.
- День, для которого строки нет вообще, просто не трогали, и это не то же самое, что записанный пропуск.

#### `checkin_habit` (меняет)

Отметить привычку сделанной или прямо записать пропуск за конкретный день.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `habit_id` | string, UUID | обязательный | UUID привычки, его отдаёт list_habits. |
| `date` | string, YYYY-MM-DD | обязательный | День, за который ставим отметку, YYYY-MM-DD. |
| `completed` | boolean, по умолчанию true | необязательный | true, если день засчитан, это поведение по умолчанию. false записывает пропуск. |

Пример вызова:

```json
{
  "habit_id": "5c9a11de-7f42-4e06-8b3d-1c60a95d7f84",
  "date": "2026-08-28",
  "completed": true
}
```

Ответ:

```json
{
  "habit": "Read 20 pages",
  "checkin": {
    "id": "b04c7e18-32f5-4a69-8d70-1e6b95c3f402",
    "habit_id": "5c9a11de-7f42-4e06-8b3d-1c60a95d7f84",
    "date": "2026-08-28",
    "completed": true,
    "created_at": "2026-08-28T07:30:55.744Z"
  }
}
```

Полезно знать:

- Повторный вызов за тот же день перезаписывает этот день, а не добавляет вторую отметку.
- Дата обычная календарная, поэтому «сегодня» вычисляет тот, кто вызывает.

#### `create_habit` (меняет)

Начать отмечать новую привычку. В ответе приходит id, по которому дальше ставят отметки.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `name` | string, 1-200 | обязательный | Название привычки, например «Читать 20 страниц». |
| `icon` | string, ≤ 64 | необязательный | Название иконки, необязательно. |
| `color` | string, ≤ 32 | необязательный | Цвет в hex, например #6366f1. |
| `time_of_day` | string, одно из morning, afternoon, evening, anytime, по умолчанию anytime | необязательный | В какое время дня привычка. По умолчанию anytime. |

Пример вызова:

```json
{
  "name": "Read 20 pages",
  "time_of_day": "evening",
  "color": "#f59e0b"
}
```

Ответ:

```json
{
  "id": "5c9a11de-7f42-4e06-8b3d-1c60a95d7f84",
  "name": "Read 20 pages",
  "color": "#f59e0b",
  "time_of_day": "evening",
  "is_active": true,
  "sort_order": 0,
  "created_at": "2026-08-28T07:32:12.008Z",
  "updated_at": "2026-08-28T07:32:12.008Z"
}
```

Полезно знать:

- Без time_of_day привычка будет anytime, то есть в любое время дня.

#### `update_habit` (меняет)

Переименовать привычку или поменять иконку, цвет, время дня, а также приостановить её и вернуть в работу.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `habit_id` | string, UUID | обязательный | UUID привычки, его отдаёт list_habits. |
| `name` | string, 1-200 | необязательный | Новое название. |
| `icon` | string, ≤ 64 | необязательный | Название иконки. |
| `color` | string, ≤ 32 | необязательный | Цвет в hex. |
| `time_of_day` | string, одно из morning, afternoon, evening, anytime | необязательный | В какое время дня привычка. |
| `is_active` | boolean | необязательный | false ставит привычку на паузу, true возвращает в работу. |

Пример вызова:

```json
{
  "habit_id": "5c9a11de-7f42-4e06-8b3d-1c60a95d7f84",
  "is_active": false
}
```

Ответ:

```json
{
  "id": "5c9a11de-7f42-4e06-8b3d-1c60a95d7f84",
  "name": "Read 20 pages",
  "color": "#f59e0b",
  "time_of_day": "evening",
  "is_active": false,
  "sort_order": 0,
  "created_at": "2026-07-01T06:30:11.204Z",
  "updated_at": "2026-08-28T07:33:40.126Z"
}
```

Полезно знать:

- Отметки не трогаются, в том числе когда привычку ставят на паузу.
- Пауза через is_active=false это способ перестать отмечать, не теряя историю.

#### `delete_habit` (удаляет)

Удалить привычку вместе со всей историей отметок, навсегда.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `habit_id` | string, UUID | обязательный | UUID привычки, его отдаёт list_habits. |

Пример вызова:

```json
{
  "habit_id": "5c9a11de-7f42-4e06-8b3d-1c60a95d7f84"
}
```

Ответ:

```json
{
  "deleted": true,
  "habit_id": "5c9a11de-7f42-4e06-8b3d-1c60a95d7f84",
  "name": "Read 20 pages"
}
```

Полезно знать:

- У привычек нет корзины. Это действие не отменить.
- Если нужно просто перестать отмечать, но сохранить историю, поставьте привычку на паузу через update_habit.

### Корзина

Один набор инструментов на всё, что можно удалить: задачи, записи календаря, заметки, проекты и списки.

Написано по: `mcp/src/tools/trash.ts`

#### `list_trash` (читает)

Всё удалённое, что ещё можно вернуть: задачи, записи календаря, заметки, проекты и списки. Передайте kinds, чтобы смотреть что-то одно.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `kinds` | string[], одно из task, event, note, project, list | необязательный | Какие виды показывать. По умолчанию все. |
| `limit` | integer, 1-200, по умолчанию 50 | необязательный | Сколько строк вернуть во всём ответе, по всем видам сразу. По умолчанию 50, больше 200 сервер не отдаст. |

Пример вызова:

```json
{
  "kinds": ["task", "note"],
  "limit": 20
}
```

Ответ:

```json
{
  "count": 2,
  "items": [
    {
      "kind": "task",
      "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
      "title": "Send the invoice",
      "date": "2026-09-02",
      "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
      "deleted_at": "2026-08-28T07:10:55.003Z"
    },
    {
      "kind": "note",
      "id": "c81b5d47-0e39-4b2a-9f64-5a7d31e8c026",
      "title": "Invoice template",
      "project_id": null,
      "updated_at": "2026-08-28T07:29:44.512Z"
    }
  ]
}
```

Полезно знать:

- limit действует на каждый вид отдельно, поэтому запрос всех пяти видов с limit 50 может вернуть до 250 строк.
- В каждой строке есть её вид, и именно это значение ждут restore_from_trash и delete_from_trash_forever.
- Привычек здесь нет. У них корзины не существует вовсе.

#### `restore_from_trash` (меняет)

Вернуть удалённую задачу, запись календаря, заметку, проект или список туда, где они были.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `kind` | string, одно из task, event, note, project, list | обязательный | Что восстанавливаем, в том виде, как это назвал list_trash. |
| `id` | string, UUID | обязательный | UUID записи, в том виде, как его вернул list_trash. |

Пример вызова:

```json
{
  "kind": "task",
  "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33"
}
```

Ответ:

```json
{
  "restored": true,
  "kind": "task",
  "item": {
    "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
    "title": "Send the invoice",
    "date": "2026-09-02",
    "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
    "deleted_at": null
  }
}
```

Полезно знать:

- kind и id должны быть той парой, которую вернул list_trash. Неверный вид при верном id ничего не найдёт.
- Возвращённый проект приходит вместе со своими задачами и списками.

#### `delete_from_trash_forever` (удаляет)

Стереть то, что уже лежит в корзине.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `kind` | string, одно из task, event, note, project, list | обязательный | Что стираем, в том виде, как это назвал list_trash. |
| `id` | string, UUID | обязательный | UUID записи, в том виде, как его вернул list_trash. |

Пример вызова:

```json
{
  "kind": "task",
  "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33"
}
```

Ответ:

```json
{
  "deleted": true,
  "kind": "task",
  "item": {
    "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
    "title": "Send the invoice",
    "date": "2026-09-02",
    "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
    "deleted_at": "2026-08-28T07:10:55.003Z"
  }
}
```

Полезно знать:

- Это действие не отменить.
- Оно трогает только то, что уже удалили один раз. Живую запись сначала придётся удалить обычным способом, и именно это не даёт одному вызову стереть работающие данные.

### Совместная работа

Общий доступ к проекту и назначение задач на людей. Всё выполняется от вашего имени и по тем же правилам, что и панель «Поделиться»: приглашать можно на Pro, менять что-либо может владелец.

Написано по: `mcp/src/tools/sharing.ts`

#### `list_project_members` (читает)

Все, у кого есть доступ к проекту, с ролью и с user_id, который нужен, чтобы поменять роль, убрать человека или назначить на него задачу.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `project_id` | string, UUID | обязательный | UUID проекта, его отдаёт list_projects. |

Пример вызова:

```json
{
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5"
}
```

Ответ:

```json
{
  "count": 2,
  "members": [
    {
      "user_id": "a3d5f712-96b4-4c08-bd21-7e60f4c9a583",
      "role": "owner",
      "name": "Nick",
      "email": "nick@example.com",
      "added_at": "2026-05-02T10:14:07.882Z"
    },
    {
      "user_id": "0d8c41b6-73e5-4f92-a107-64b2e9d35c8a",
      "role": "editor",
      "name": null,
      "email": "sam@example.com",
      "added_at": "2026-08-14T12:03:51.117Z"
    }
  ]
}
```

Полезно знать:

- Читать можно только те проекты, в которых вы состоите.
- Проект, которым ни с кем не делились, отвечает пустым списком и пояснением, а не ошибкой.
- Имена и адреса берутся из профилей и могут отсутствовать у того, кто не заполнял имя. Ответом в любом случае остаются роли.

#### `invite_to_project` (меняет)

Отправить приглашение на адрес почты. Человек получит его письмом, а если у этого адреса уже есть аккаунт ToDowl, то ещё и в приложении.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `project_id` | string, UUID | обязательный | UUID проекта, его отдаёт list_projects. |
| `email` | string, email | обязательный | Кого приглашаем. |
| `role` | string, одно из editor, viewer, по умолчанию viewer | необязательный | viewer по умолчанию или editor. |

Пример вызова:

```json
{
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "email": "sam@example.com",
  "role": "editor"
}
```

Ответ:

```json
{
  "invited": "sam@example.com",
  "role": "editor",
  "status": "pending",
  "has_account": true,
  "invite_id": "f13b7a29-0c64-4e85-9d31-58a70e6c4b12"
}
```

Полезно знать:

- Совместная работа доступна на Pro, и приглашать может только владелец проекта. Оба отказа приходят обычной фразой, а не ошибкой базы.
- viewer может читать, editor может менять.
- Поле has_account в ответе говорит, был ли у адреса аккаунт ToDowl. От этого зависит, как сформулировать следующую фразу человеку.

#### `get_project_share_link` (меняет)

Ссылка, которую можно дать человеку, чтобы он вошёл в проект сам. Для случаев, когда почты нет или человек просит именно ссылку.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `project_id` | string, UUID | обязательный | UUID проекта, его отдаёт list_projects. |
| `role` | string, одно из editor, viewer, по умолчанию viewer | необязательный | Что даёт ссылка: viewer по умолчанию или editor. |

Пример вызова:

```json
{
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "role": "viewer"
}
```

Ответ:

```json
{
  "url": "https://app.todowl.com/join/hq7Ktpm3zRxv",
  "role": "viewer",
  "reused": true
}
```

Полезно знать:

- Войти по ссылке может любой, кто её получил, с той ролью, которую она даёт. Стоит сказать об этом, когда её передаёте.
- Повторный запрос по тому же проекту и той же роли возвращает ту же ссылку с пометкой reused: true, а не делает вторую. Ссылка здесь бессрочная и многоразовая, поэтому каждая лишняя это ещё один вечный ключ к проекту, о создании которого владелец не знал.
- Ссылку со сроком или ограничением по числу входов, сделанную в приложении, инструмент не трогает: возвращается только бессрочная, того вида, что он создаёт сам.
- Правила те же, что у приглашения: Pro и только владелец.
- Токен из двенадцати символов, в алфавите нет 0, O, 1, l и I: такую ссылку часто диктуют вслух и набирают руками.
- Инструмента, который отзывает ссылку, нет. Отзыв делается в панели «Поделиться» в приложении.

#### `set_project_member_role` (меняет)

Перевести участника между viewer и editor.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `project_id` | string, UUID | обязательный | UUID проекта. |
| `user_id` | string, UUID | обязательный | user_id участника, его отдаёт list_project_members. |
| `role` | string, одно из editor, viewer | обязательный | editor или viewer. |

Пример вызова:

```json
{
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "user_id": "0d8c41b6-73e5-4f92-a107-64b2e9d35c8a",
  "role": "viewer"
}
```

Ответ:

```json
{
  "updated": true,
  "user_id": "0d8c41b6-73e5-4f92-a107-64b2e9d35c8a",
  "role": "viewer"
}
```

Полезно знать:

- Может только владелец проекта.
- user_id берётся из list_project_members.

#### `remove_project_member` (удаляет)

Забрать у человека доступ. Проект пропадает из его бокового меню, и у него не остаётся ничего.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `project_id` | string, UUID | обязательный | UUID проекта. |
| `user_id` | string, UUID | обязательный | user_id участника, его отдаёт list_project_members. |

Пример вызова:

```json
{
  "project_id": "1f6d0b72-5c84-4a19-b3f7-8e2c9d10a4b5",
  "user_id": "0d8c41b6-73e5-4f92-a107-64b2e9d35c8a"
}
```

Ответ:

```json
{
  "removed": true,
  "user_id": "0d8c41b6-73e5-4f92-a107-64b2e9d35c8a"
}
```

Полезно знать:

- Может только владелец проекта, и самого владельца убрать нельзя.
- Передачи владения в API нет намеренно: это единственное действие в совместной работе, которое тот, кто его сделал, отменить уже не может.

#### `assign_task` (меняет)

Назначить задачу в общем проекте на одного из участников или снять её с него, передав assigned=false.

Параметры:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `task_id` | string, UUID | обязательный | UUID задачи. |
| `user_id` | string, UUID | обязательный | user_id участника, его отдаёт list_project_members. |
| `assigned` | boolean, по умолчанию true | необязательный | true назначает задачу, это поведение по умолчанию. false снимает назначение. |

Пример вызова:

```json
{
  "task_id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
  "user_id": "0d8c41b6-73e5-4f92-a107-64b2e9d35c8a"
}
```

Ответ:

```json
{
  "assigned": true,
  "task_id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
  "user_id": "0d8c41b6-73e5-4f92-a107-64b2e9d35c8a"
}
```

Полезно знать:

- Человек должен уже быть в проекте. Назначением его туда не добавить.
- user_id берётся из list_project_members.

## HTTP-эндпоинты

Обычная HTTP-поверхность mcp.todowl.com: сам транспорт MCP, эндпоинты выпуска и отзыва ключей и два, с которыми говорит ассистент внутри приложения.

### Эндпоинт MCP

Один адрес, Streamable HTTP, никакой сессии между вызовами. Токен передаётся в каждом запросе, и каждый ответ приходит от имени того аккаунта, которому этот токен принадлежит.

Написано по: `mcp/src/index.ts`, `mcp/src/server.ts`, `mcp/src/auth.ts`

#### POST / (API-токен)

Транспорт MCP. Говорит на JSON-RPC 2.0: initialize, tools/list, tools/call.

Запрос:

```http
POST https://mcp.todowl.com/
Authorization: Bearer todowl_YOUR_KEY
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2025-06-18

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "list_tasks",
    "arguments": { "status": "open", "limit": 5 }
  }
}
```

Ответ:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      { "type": "text", "text": "{\"count\":1,\"tasks\":[…]}" }
    ]
  }
}
```

Полезно знать:

- `/mcp` это тот же самый эндпоинт под вторым именем, для клиентов, которые требуют путь.
- Ответ приходит потоком событий, поэтому клиенту, который читает его вручную, нужно достать JSON из последнего кадра `data:`.
- Ответ инструмента это JSON внутри поля `text`, записанный максимально плотно. Примеры на этой странице разложены для чтения, в проводе так не бывает.
- Инструмент, который отказал, отвечает `isError: true` и `{ "error": "…" }` в тексте, а не HTTP-ошибкой.
- GET и DELETE по тому же пути принимаются и ничего не делают: сессии, в которую можно стримить или которую можно закрыть, здесь нет.
- Тело запроса ограничено 4 МБ.

#### GET /health (без ключа)

Жив ли сервер. Единственный эндпоинт, которому не нужен ключ.

Запрос:

```http
curl https://mcp.todowl.com/health
```

Ответ:

```json
{
  "ok": true,
  "service": "todowl",
  "version": "0.1.0"
}
```

### Личные токены

Выпуск и отзыв ключей. Здесь нужен вход в аккаунт, а не API-токен: чтобы выдать ключ от аккаунта, надо уже быть в нём. Именно сюда ходят страница настроек и `todowl auth login`.

Написано по: `mcp/src/index.ts`, `mcp/src/tokens.ts`

#### POST /auth/token (вход в аккаунт)

Выпустить личный API-токен. Отвечает 201 и показывает токен один раз.

Тело запроса:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `name` | string, ≤ 120, по умолчанию API token | необязательный | Как назвать ключ, чтобы потом отличить его от других. Длинные имена обрезаются до 120 символов. |

Запрос:

```http
POST https://mcp.todowl.com/auth/token
Authorization: Bearer <session access token>
Content-Type: application/json

{ "name": "Claude on the laptop" }
```

Ответ:

```json
{
  "token": "todowl_QY2fJ8sK…",
  "warning": "This is the only time the token is shown. Store it now; it cannot be retrieved again.",
  "id": "d4a91b76-30c8-4f52-9e17-6b03a5c2d84f",
  "name": "Claude on the laptop",
  "prefix": "todowl_QY2fJ8",
  "scopes": [],
  "created_at": "2026-08-28T07:40:11.284Z",
  "last_used_at": null,
  "revoked_at": null
}
```

Полезно знать:

- Токен показывается здесь и больше нигде. У нас хранится только его SHA-256, поэтому прочитать его обратно не может никто, включая нас.
- Токен это `todowl_` и ещё 43 буквы и цифры. Всё остальное отклоняется ещё до обращения к базе.
- Ограниченных прав пока нет. У ключа тот же доступ к аккаунту, что и у вас в браузере.

#### GET /auth/tokens (вход в аккаунт)

Все ключи аккаунта, свежие сверху, максимум сто.

Ответ:

```json
{
  "tokens": [
    {
      "id": "d4a91b76-30c8-4f52-9e17-6b03a5c2d84f",
      "name": "Claude on the laptop",
      "prefix": "todowl_QY2fJ8",
      "scopes": [],
      "created_at": "2026-08-28T07:40:11.284Z",
      "last_used_at": "2026-08-28T09:12:03.660Z",
      "revoked_at": null
    }
  ]
}
```

Полезно знать:

- Отозванные ключи остаются в списке с заполненным `revoked_at`, чтобы история того, какие ключи были, не терялась.
- `last_used_at` обновляется не чаще раза в минуту на ключ, чтобы болтливый клиент не превращал каждый вызов инструмента в лишнюю запись.

#### DELETE /auth/tokens/:id (вход в аккаунт)

Отозвать ключ. Он перестаёт работать сразу.

В пути:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `id` | string, UUID | обязательный | Id ключа, его отдаёт GET /auth/tokens. |

Ответ:

```json
{
  "revoked": true,
  "id": "d4a91b76-30c8-4f52-9e17-6b03a5c2d84f",
  "name": "Claude on the laptop",
  "prefix": "todowl_QY2fJ8",
  "scopes": [],
  "created_at": "2026-08-28T07:40:11.284Z",
  "last_used_at": "2026-08-28T09:12:03.660Z",
  "revoked_at": "2026-08-28T10:02:47.915Z"
}
```

Полезно знать:

- Уже отозванный ключ, как и ключ чужого аккаунта, отвечает 404. Ни в том, ни в другом случае о чужих ключах ничего не сообщается.
- Отзыв не трогает данные. Клиенты с этим ключом начинают получать 401 прямо посреди разговора.

### Ассистент внутри приложения

То, с чем разговаривает ассистент внутри app.todowl.com. Здесь он для полноты картины: ему нужна сессия браузера, а не API-токен, поэтому свой клиент на нём не построить.

Написано по: `mcp/src/index.ts`, `mcp/src/assistant.ts`

#### POST /assistant/chat (вход в аккаунт)

Один ход разговора. Модель выбирает инструменты, они выполняются в вашем аккаунте, и ответ приходит вместе с отчётом о сделанном.

Тело запроса:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `messages` | object[] | обязательный | Разговор целиком, в формате сообщений OpenAI. Хотя бы одно сообщение. |
| `timezone` | string | необязательный | Зона IANA из браузера, чтобы «завтра» означало завтра читателя. |
| `locale` | string | необязательный | Язык, на котором отвечать. |
| `approve` | boolean | необязательный | true, когда человек только что согласился на удаление, которого ждала модель. |
| `resume` | boolean | необязательный | true, когда приложение продолжает незаконченную работу, а не человек говорит что-то новое. |

Ответ:

```json
{
  "reply": "Added it for Tuesday.",
  "messages": [],
  "actions": [{ "tool": "create_task", "ok": true }],
  "cards": [
    {
      "kind": "task",
      "action": "created",
      "id": "9b2c1f04-3a7e-4c51-9d18-6f0b2a7d4e33",
      "title": "Send the invoice",
      "date": "2026-09-02"
    }
  ],
  "usage": { "promptTokens": 1840, "completionTokens": 96, "costUsd": 0.000412 },
  "model": "deepseek/deepseek-v4-flash"
}
```

Полезно знать:

- Ход, который хочет что-то удалить, останавливается и возвращает `pending` вместо действия. Приложение спрашивает и повторяет тот же запрос с `approve`.
- `continues: true` означает, что ход упёрся в лимит шагов, а не закончил дело. Расшифровка отправляется обратно с `resume`, и работа продолжается.
- 60 сообщений на аккаунт в час, счёт ведётся в памяти, поэтому перезапуск его обнуляет. Сверх лимита приходит 429.
- Если ключ ассистента не настроен, эндпоинт отвечает 503, а остальной сервер продолжает работать как обычно.

#### POST /assistant/title (вход в аккаунт)

Короткое название разговора, чтобы список истории читался осмысленно.

Тело запроса:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `messages` | object[] | обязательный | Разговор, который нужно назвать. Хотя бы одно сообщение. |
| `locale` | string | необязательный | Язык названия. |

Ответ:

```json
{
  "title": "Invoice for August",
  "subtitle": "Task added for Tuesday"
}
```

Полезно знать:

- Ограничения те же, что у обычного хода: сессия, включённый ассистент и часовой лимит. Это те же деньги.

## Командная строка

Утилита todowl, команда за командой. Каждая из них это вызов инструментов выше, поэтому в каждом блоке написано, какого именно.

### Сама утилита

Где лежит токен, как сокращаются id и какие опции работают везде.

Написано по: `cli/src/index.ts`, `cli/src/config.ts`, `cli/README.md`

#### `todowl --help`

Полный список команд, тот самый, по которому написана эта страница.

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |
| `--version` | boolean | необязательный | Напечатать версию и выйти. |
| `--help` | boolean | необязательный | То же самое, что запустить утилиту без аргументов. |

Пример вызова:

```bash
todowl --help
```

Полезно знать:

- Короткие формы: `-h` и `-v`.
- Любая опция должна быть известна утилите. Незнакомая опция останавливает команду, а не проглатывается молча.
- Id можно сократить до первых символов, которые печатает `todowl … list`, но не короче шести. Если под сокращение подходит больше одной записи, будет ошибка, а не догадка.

### Вход

Вход выполняется один раз на машину. Токен лежит в ~/.config/todowl/config.json с правами только для вас, и это намеренно файл, а не переменная окружения.

Написано по: `cli/src/commands/auth.ts`, `cli/src/config.ts`

#### `todowl auth login`

Войти и сохранить на этой машине свежий личный токен.

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--provider <google|apple>` | string, одно из google, apple, по умолчанию google | необязательный | Какой кнопкой входить в браузере. |
| `--email <address>` | string | необязательный | Спросить пароль прямо в терминале, вместо того чтобы открывать браузер. Удобно по SSH. |
| `--no-browser` | boolean | необязательный | Напечатать ссылку для входа вместо того, чтобы открывать её. |
| `--name <name>` | string | необязательный | Как назвать созданный токен. По умолчанию берётся имя этой машины. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl auth login --provider apple
```

Полезно знать:

- Браузер отдаёт сессию на локальный порт, который CLI открывает на время входа и сразу после закрывает.
- Сессия обменивается на долгоживущий токен через `POST /auth/token`. Хранится именно токен, а не сессия.
- Если браузер не отвечает пять минут, команда сдаётся.

#### `todowl auth token <TOKEN>`

Использовать уже готовый токен вместо входа. Это вариант для скриптов и CI.

Пример вызова:

```bash
todowl auth token todowl_QY2fJ8sK…
```

Полезно знать:

- Без аргумента утилита спросит токен прямо в терминале.
- Токен сначала проверяется на сервере и только потом объявляется рабочим. Отклонённый токен не сохраняется.

#### `todowl auth status`

Под кем эта машина вошла и работает ли ещё сохранённый токен.

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl auth status --json
```

Полезно знать:

- Показывает начало токена, но не сам токен. Если токен перестал работать, команда завершается ненулевым кодом.
- `todowl auth` без продолжения делает то же самое.

#### `todowl auth logout`

Забыть токен, сохранённый на этой машине.

Пример вызова:

```bash
todowl auth logout
```

Полезно знать:

- Удаляется только локальный файл. Сам токен на сервере остаётся рабочим, поэтому, если он мог утечь, отзовите его в настройках.

### Проекты и списки

Структура, в которую складывают задачи.

Написано по: `cli/src/commands/structure.ts`, `cli/src/commands/tasks.ts`

#### `todowl project list`

Проекты, с их id и цветами.

Другие написания: `todowl projects`, `todowl project`

Вызывает: `list_projects`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--all` | boolean | необязательный | Показать и то, что обычно скрыто: архивные записи, а для привычек ещё и приостановленные. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl project list --all
```

#### `todowl project create --name "Work"`

Создать проект.

Другие написания: `todowl project add`

Вызывает: `create_project`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--name <name>` | string | обязательный | Название проекта. |
| `--description <text>` | string | необязательный | Строка о том, что это за проект. |
| `--color <hex>` | string | необязательный | Цвет, например #6366f1. |
| `--icon <name>` | string | необязательный | Название иконки. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl project create --name "Work" --color "#6366f1" --icon briefcase
```

#### `todowl project update <id>`

Переименовать проект или поменять цвет, иконку, описание, архивность.

Другие написания: `todowl project edit`

Вызывает: `update_project`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--name <name>` | string | необязательный | Новое название. |
| `--description <text>` | string | необязательный | Новое описание. |
| `--color <hex>` | string | необязательный | Новый цвет. |
| `--icon <name>` | string | необязательный | Новая иконка. |
| `--archive` | boolean | необязательный | Убрать в архив. |
| `--unarchive` | boolean | необязательный | Вернуть из архива. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl project update 1f6d0b72 --name "Work 2026"
```

Полезно знать:

- Нужно указать хотя бы одно изменение, иначе команда об этом скажет и остановится.
- Id можно сократить до первых символов, которые печатает `todowl … list`, но не короче шести. Если под сокращение подходит больше одной записи, будет ошибка, а не догадка.

#### `todowl project delete <id>`

Убрать проект в корзину.

Другие написания: `todowl project rm`

Вызывает: `delete_project`

Пример вызова:

```bash
todowl project delete 1f6d0b72
```

Полезно знать:

- Вернуть можно так: `todowl trash restore --kind project <id>`.

#### `todowl list list`

Списки, при желании только одного проекта.

Другие написания: `todowl lists`, `todowl list`

Вызывает: `list_lists`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--project <id>` | string | необязательный | Проект, по id или по первым символам id. |
| `--all` | boolean | необязательный | Показать и то, что обычно скрыто: архивные записи, а для привычек ещё и приостановленные. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl list list --project 1f6d0b72
```

#### `todowl list create --name "Backlog"`

Создать список, внутри проекта или сам по себе.

Другие написания: `todowl list add`

Вызывает: `create_list`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--name <name>` | string | обязательный | Название списка. |
| `--project <id>` | string | необязательный | Проект, по id или по первым символам id. |
| `--icon <name>` | string | необязательный | Название иконки. |
| `--color <hex>` | string | необязательный | Цвет в hex, например #6366f1. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl list create --name "Backlog" --project 1f6d0b72
```

#### `todowl list update <id>`

Переименовать список, поменять иконку, перенести в другой проект или убрать в архив.

Другие написания: `todowl list edit`

Вызывает: `update_list`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--name <name>` | string | необязательный | Новое название. |
| `--icon <name>` | string | необязательный | Новая иконка. |
| `--color <hex>` | string | необязательный | Цвет в hex, например #6366f1. |
| `--project <id>` | string | необязательный | Проект, по id или по первым символам id. |
| `--archive` | boolean | необязательный | Убрать в архив. |
| `--unarchive` | boolean | необязательный | Вернуть из архива. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl list update 7a4e9c31 --name "Later"
```

#### `todowl list delete <id>`

Убрать список в корзину. Его задачи остаются в проекте.

Другие написания: `todowl list rm`

Вызывает: `delete_list`

Пример вызова:

```bash
todowl list delete 7a4e9c31
```

### Задачи

Та половина утилиты, которой пользуются каждый день.

Написано по: `cli/src/commands/tasks.ts`

#### `todowl task list`

Задачи, с теми же фильтрами, что и в приложении.

Другие написания: `todowl tasks`, `todowl task`

Вызывает: `list_tasks`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--status <open|completed|all>` | string, одно из open, completed, all, по умолчанию open | необязательный | Какие задачи показывать. |
| `--project <id>` | string | необязательный | Проект, по id или по первым символам id. |
| `--list <id>` | string | необязательный | Список, по id или по первым символам id. |
| `--date <YYYY-MM-DD>` | string, YYYY-MM-DD | необязательный | Только этот день. |
| `--from <YYYY-MM-DD>` | string, YYYY-MM-DD | необязательный | С этого дня и позже. |
| `--to <YYYY-MM-DD>` | string, YYYY-MM-DD | необязательный | По этот день включительно. |
| `--priority <high|medium|low|none>` | string, одно из high, medium, low, none | необязательный | Только этот приоритет. |
| `--no-date` | boolean | необязательный | Только задачи без даты, то есть входящие. |
| `--limit <n>` | integer, 1-200, по умолчанию 50 | необязательный | Сколько строк. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl task list --status open --from 2026-09-01 --to 2026-09-07
```

Полезно знать:

- В таблице печатаются первые восемь символов id. Полностью их печатает `--json`.

#### `todowl task create --title "Call the bank"`

Создать задачу.

Другие написания: `todowl task add`

Вызывает: `create_task`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--title <text>` | string | обязательный | Как называется задача. |
| `--description <text>` | string | необязательный | Развёрнутые заметки. |
| `--project <id>` | string | необязательный | Проект, по id или по первым символам id. |
| `--list <id>` | string | необязательный | Список, по id или по первым символам id. |
| `--date <YYYY-MM-DD>` | string, YYYY-MM-DD | необязательный | Дата. Без неё задача попадёт во входящие. |
| `--time <HH:MM>` | string, HH:MM | необязательный | Время начала. |
| `--priority <high|medium|low|none>` | string, одно из high, medium, low, none | необязательный | Приоритет. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl task create --title "Call the bank" --date 2026-09-01 --priority high
```

#### `todowl task update <id>`

Изменить существующую задачу.

Другие написания: `todowl task edit`

Вызывает: `update_task`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--title <text>` | string | необязательный | Новый заголовок. |
| `--description <text>` | string | необязательный | Новые заметки. |
| `--date <YYYY-MM-DD>` | string, YYYY-MM-DD | необязательный | Новая дата. |
| `--time <HH:MM>` | string, HH:MM | необязательный | Новое время начала. |
| `--priority <high|medium|low|none>` | string, одно из high, medium, low, none | необязательный | Новый приоритет. |
| `--archive` | boolean | необязательный | Убрать в архив. |
| `--unarchive` | boolean | необязательный | Вернуть из архива. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl task update a1b2c3d4 --date 2026-09-02 --priority medium
```

#### `todowl task move <id>`

Переложить задачу в другой проект или список.

Вызывает: `move_task`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--project <id>` | string | необязательный | Проект, по id или по первым символам id. |
| `--list <id>` | string | необязательный | Список, по id или по первым символам id. |
| `--no-list` | boolean | необязательный | Отвязать от списка, в проекте задача останется. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl task move a1b2c3d4 --project 7f3e1c02
```

Полезно знать:

- Нужно назвать хотя бы одно направление, иначе команда спросит, куда переносить.
- Оставить задачу без проекта нельзя: такая задача не показывается в приложении ни на одном экране. Вместо этого переложите её в другой проект.

#### `todowl task complete <id>`

Отметить задачу выполненной или открыть заново.

Другие написания: `todowl task done`

Вызывает: `complete_task`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--reopen` | boolean | необязательный | Наоборот, открыть задачу заново. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl task complete a1b2c3d4
```

#### `todowl task delete <id>`

Убрать задачу в корзину.

Другие написания: `todowl task rm`

Вызывает: `delete_task`

Пример вызова:

```bash
todowl task delete a1b2c3d4
```

### Календарь

Встречи и события из терминала.

Написано по: `cli/src/commands/content.ts`

#### `todowl event list`

Что стоит в календаре. Сегодня, если не сказать иначе.

Другие написания: `todowl events`, `todowl event`

Вызывает: `list_events`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--date <YYYY-MM-DD>` | string, YYYY-MM-DD | необязательный | Один день. |
| `--from <YYYY-MM-DD>` | string, YYYY-MM-DD | необязательный | Первый день периода. |
| `--to <YYYY-MM-DD>` | string, YYYY-MM-DD | необязательный | Последний день периода. |
| `--tasks` | boolean | необязательный | Показать и датированные задачи, а не только встречи и события. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl event list --from 2026-09-01 --to 2026-09-07
```

Полезно знать:

- Без `--tasks` команда запрашивает только встречи и события, а сам инструмент по умолчанию отдаёт ещё и задачи.

#### `todowl event create --title "Standup" --date 2026-09-01`

Поставить в календарь встречу или событие.

Другие написания: `todowl event add`

Вызывает: `create_event`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--title <text>` | string | обязательный | Как называется запись. |
| `--date <YYYY-MM-DD>` | string, YYYY-MM-DD | обязательный | День, на который она стоит. |
| `--time <HH:MM>` | string, HH:MM | необязательный | Время начала. |
| `--end <HH:MM>` | string, HH:MM | необязательный | Время окончания. |
| `--all-day` | boolean | необязательный | Запись на весь день. |
| `--type <meeting|event>` | string, одно из meeting, event, по умолчанию event | необязательный | Тип записи. |
| `--description <text>` | string | необязательный | Развёрнутые заметки. |
| `--project <id>` | string | необязательный | Проект, по id или по первым символам id. |
| `--color <hex>` | string | необязательный | Свой цвет. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl event create --title "Standup" --date 2026-09-01 --time 11:00 --end 11:30 --type meeting
```

#### `todowl event update <id>`

Перенести или отредактировать запись календаря.

Другие написания: `todowl event edit`

Вызывает: `update_event`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--title <text>` | string | необязательный | Новый заголовок. |
| `--date <YYYY-MM-DD>` | string, YYYY-MM-DD | необязательный | Новый день. |
| `--time <HH:MM>` | string, HH:MM | необязательный | Новое время начала. |
| `--end <HH:MM>` | string, HH:MM | необязательный | Новое время окончания. |
| `--all-day` | boolean | необязательный | Сделать записью на весь день. |
| `--description <text>` | string | необязательный | Новые заметки. |
| `--project <id>` | string | необязательный | Проект, по id или по первым символам id. |
| `--color <hex>` | string | необязательный | Свой цвет. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl event update e2f70a95 --date 2026-09-03 --time 12:00
```

Полезно знать:

- Запись ищется в промежутке год назад и год вперёд, поэтому неделю указывать не нужно. Расширить или сузить это окно можно через `--from` и `--to`.

#### `todowl event delete <id>`

Убрать запись календаря в корзину.

Другие написания: `todowl event rm`

Вызывает: `delete_event`

Пример вызова:

```bash
todowl event delete e2f70a95
```

### Заметки

Обычный текст на входе, обычный текст на выходе.

Написано по: `cli/src/commands/content.ts`

#### `todowl note list`

Заметки, закреплённые сверху.

Другие написания: `todowl notes`, `todowl note`

Вызывает: `list_notes`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--query <text>` | string | необязательный | Искать по заголовкам и тексту. |
| `--all` | boolean | необязательный | Показать и то, что обычно скрыто: архивные записи, а для привычек ещё и приостановленные. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl note list --query invoice
```

#### `todowl note get <id>`

Напечатать одну заметку целиком, вместе с текстом.

Другие написания: `todowl note show`

Вызывает: `get_note`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl note get c81b5d47
```

#### `todowl note create`

Создать заметку из обычного текста.

Другие написания: `todowl note add`

Вызывает: `create_note`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--title <text>` | string | необязательный | Заголовок. Заметка без него будет безымянной, это разрешено. |
| `--content <text>` | string | необязательный | Текст. |
| `--project <id>` | string | необязательный | Проект, по id или по первым символам id. |
| `--pin` | boolean | необязательный | Закрепить наверху списка. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl note create --title "Invoice template" --content "Hours, rate, bank details."
```

#### `todowl note update <id>`

Изменить заметку.

Другие написания: `todowl note edit`

Вызывает: `update_note`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--title <text>` | string | необязательный | Новый заголовок. |
| `--content <text>` | string | необязательный | Новый текст. Он заменяет прежний целиком. |
| `--project <id>` | string | необязательный | Проект, по id или по первым символам id. |
| `--pin` | boolean | необязательный | Закрепить. |
| `--unpin` | boolean | необязательный | Снять закрепление. |
| `--archive` | boolean | необязательный | Убрать в архив. |
| `--unarchive` | boolean | необязательный | Вернуть из архива. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl note update c81b5d47 --pin
```

#### `todowl note delete <id>`

Убрать заметку в корзину.

Другие написания: `todowl note rm`

Вызывает: `delete_note`

Пример вызова:

```bash
todowl note delete c81b5d47
```

### Привычки

Поставить отметку, посмотреть месяц, поставить привычку на паузу.

Написано по: `cli/src/commands/content.ts`

#### `todowl habit list`

Привычки, с указанием времени дня для каждой.

Другие написания: `todowl habits`, `todowl habit`

Вызывает: `list_habits`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--all` | boolean | необязательный | Показать и то, что обычно скрыто: архивные записи, а для привычек ещё и приостановленные. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl habit list --all
```

#### `todowl habit create --name "Read 20 pages"`

Начать отмечать привычку.

Другие написания: `todowl habit add`

Вызывает: `create_habit`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--name <text>` | string | обязательный | Как называется привычка. |
| `--when <morning|afternoon|evening|anytime>` | string, одно из morning, afternoon, evening, anytime, по умолчанию anytime | необязательный | В какое время дня она стоит. |
| `--icon <name>` | string | необязательный | Название иконки. |
| `--color <hex>` | string | необязательный | Цвет. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl habit create --name "Read 20 pages" --when evening
```

#### `todowl habit update <id>`

Переименовать привычку, поменять её вид или поставить на паузу.

Другие написания: `todowl habit edit`

Вызывает: `update_habit`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--name <text>` | string | необязательный | Новое название. |
| `--icon <name>` | string | необязательный | Новая иконка. |
| `--color <hex>` | string | необязательный | Новый цвет. |
| `--when <morning|afternoon|evening|anytime>` | string, одно из morning, afternoon, evening, anytime | необязательный | В какое время дня она стоит. |
| `--pause` | boolean | необязательный | Перестать отмечать, история сохраняется. |
| `--resume` | boolean | необязательный | Снова начать отмечать. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl habit update 5c9a11de --pause
```

#### `todowl habit checkin <id>`

Отметить привычку за день или записать пропуск.

Другие написания: `todowl habit check`

Вызывает: `checkin_habit`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--date <YYYY-MM-DD>` | string, YYYY-MM-DD | необязательный | За какой день. Без флага это сегодня. |
| `--miss` | boolean | необязательный | Записать день как пропущенный. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl habit checkin 5c9a11de
```

#### `todowl habit log <id>`

История отметок одной привычки, день за днём.

Вызывает: `get_habit_checkins`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--from <YYYY-MM-DD>` | string, YYYY-MM-DD | необязательный | Первый день. Без флага это первое число текущего месяца. |
| `--to <YYYY-MM-DD>` | string, YYYY-MM-DD | необязательный | Последний день. Без флага это сегодня. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl habit log 5c9a11de --from 2026-08-01 --to 2026-08-31
```

#### `todowl habit delete <id>`

Удалить привычку вместе с историей, навсегда.

Другие написания: `todowl habit rm`

Вызывает: `delete_habit`

Пример вызова:

```bash
todowl habit delete 5c9a11de
```

Полезно знать:

- У привычек нет корзины. Обратимый вариант это пауза через `--pause`.

### Корзина

Всё удалённое и два способа с этим поступить.

Написано по: `cli/src/commands/content.ts`

#### `todowl trash list`

Что лежит в корзине и что ещё можно вернуть.

Другие написания: `todowl trash`

Вызывает: `list_trash`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--kind <task|event|note|project|list>` | string, одно из task, event, note, project, list | необязательный | Только один вид записей. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl trash list --kind task
```

#### `todowl trash restore --kind <kind> <id>`

Вернуть запись туда, где она была.

Вызывает: `restore_from_trash`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--kind <task|event|note|project|list>` | string, одно из task, event, note, project, list | обязательный | Что именно возвращаем. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl trash restore --kind task 9b2c1f04
```

#### `todowl trash delete --kind <kind> <id>`

Стереть одну запись навсегда.

Другие написания: `todowl trash rm`

Вызывает: `delete_from_trash_forever`

Опции:

| Имя | Тип | Обязательный | Что делает |
| --- | --- | --- | --- |
| `--kind <task|event|note|project|list>` | string, одно из task, event, note, project, list | обязательный | Что именно стираем. |
| `--json` | boolean | необязательный | Напечатать один машиночитаемый объект и больше ничего. Именно это делает утилиту пригодной для скриптов. |

Пример вызова:

```bash
todowl trash delete --kind task 9b2c1f04
```

Полезно знать:

- Это действие не отменить.

## Правило, по которому живёт эта страница

Новый инструмент, эндпоинт или команда без блока на этой странице считается незаконченной работой. В репозитории есть проверка, которая падает, когда справочник и сервер расходятся, поэтому страница не может тихо устареть.
