Календарь (CalDAV)
CalDAV — это стандартный протокол доступа к календарю поверх HTTP. Он есть у Nextcloud и ownCloud, у Яндекс.Календаря, у iCloud, у Fastmail, у большинства корпоративных серверов (SOGo, Zimbra, Radicale). Смысл для платформы простой: один протокол вместо десятка проприетарных API — воркфлоу, написанный на Nextcloud, переезжает на другой сервер сменой адреса в подключении, а не переписыванием графа.
Единица данных в CalDAV — не «строка в базе», а файл: одно событие это документ
iCalendar (.ics), лежащий по своему адресу внутри коллекции-календаря. Отсюда всё
остальное на этой странице: у события есть href (путь), есть etag (версия), а
изменение — это чтение файла, правка и запись обратно.
Что хранит подключение
Подключение заводится в Настройки → Интеграции, вид — CalDAV. Общая механика подключений (секреты, права, приоритет порта над полем) описана в обзоре интеграций; здесь — только то, что специфично для календаря.
CalDAV
Поля формы подключения
Подставит адрес DAV-эндпоинта. Логин и пароль вводятся вручную.
У большинства провайдеров сюда идёт пароль приложения, а не пароль от аккаунта.
Заполнен — ноды можно оставлять без адреса календаря.
Нужен только для отправки приглашений (scheduling).
Список Провайдер заполняет базовый URL за вас: Nextcloud,
ownCloud, Яндекс.Календарь, iCloud, Fastmail и «Другой (вручную)».
Для Nextcloud и ownCloud спрашивается только хост — путь /remote.php/dav/ дописывается
сам. Логин и пароль пресет не трогает никогда: это единственное, что знаете только вы.
Режим обнаружения отвечает на вопрос «как найти ваши календари по этому адресу». «Автоматически (PROPFIND)» — стандартный путь: спросить у сервера текущего пользователя, у пользователя — его домашнюю коллекцию календарей, у коллекции — список календарей внутри. Если сервер отвечает не по канону, есть два ручных обхода: указать домашнюю коллекцию или сразу конкретный календарь. Ручной режим не «строже» — он просто пропускает шаги обнаружения, которые у вашего сервера не работают.
URL календаря по умолчанию стоит заполнить почти всегда. Ноды, которым нужен адрес коллекции, при пустом поле берут его отсюда — иначе один и тот же длинный URL приходится вставлять в каждую ноду графа.
Отдельного выбора календаря из списка в форме сегодня нет: адреса коллекций узнаются
нодой Список календарей — она возвращает href и отображаемое имя каждого календаря.
Один раз запустили, скопировали нужный href в подключение, дальше про адреса можно забыть.
Одиннадцать нод
Девять из них — обычные шаги графа с портом calendar_store. Десятая, Инструмент
календаря, отдаёт те же операции агенту — у неё тот же порт, но входа запуска нет.
Одиннадцатая, Конфигурация CalDAV, шагом графа не является вовсе: у неё нет ни входа
запуска, ни порта calendar_store — она сама этот порт и питает.
| Нода | Что делает | Читает или пишет |
|---|---|---|
| Конфигурация CalDAV | выбирает сохранённое подключение и раздаёт его остальным нодам | — |
| Список календарей | перечисляет коллекции аккаунта: href, имя, цвет | чтение |
| Запрос к календарю | события (VEVENT) или задачи (VTODO) в диапазоне дат | чтение |
| Мультизапрос календаря | пачкой забирает объекты по списку href — до 50 за вызов | чтение |
| Получить событие календаря | один объект целиком: сам iCalendar плюс разобранные поля | чтение |
| Записать событие календаря | создаёт или заменяет объект целиком | запись |
| Изменить событие календаря | правит отдельные поля существующего события | запись |
| Удалить событие календаря | удаляет объект | запись |
| Занятость календаря | интервалы «занято» без содержимого событий | чтение |
| Планирование в календаре | отправляет приглашение, ответ или отмену участникам | запись |
| Инструмент календаря | те же операции как инструменты ИИ-агента | оба |
Все они берут подключение одинаково: либо нода Конфигурация CalDAV проводом данных в
порт calendar_store, либо выбор подключения прямо в форме ноды. Порт сильнее поля.
Порт при этом обязательный: граф, где не подключён ни порт, ни поле, редактор пометит
предупреждением, а запуск и деплой отклонит — чтобы отказ пришёл до того, как оплаченный
запуск дойдёт до половины.
- Исполнение + данные
- Данные
Чтение: сначала запрос, потом адреса
Порядок работы с CalDAV всегда один и тот же, и он неочевиден: сначала спрашиваем диапазон дат, из ответа получаем адреса, по адресам берём подробности.
-
Список календарей — один раз, чтобы узнать
hrefколлекций. Дальше адрес живёт в подключении или в поле ноды. -
Запрос к календарю — с адресом коллекции и диапазоном (
сипов формате ISO 8601). Возвращает список событий с уже разобранными полями:href,etag,uid,summary,dtstart,dtend, признак «весь день», часовой пояс, правило повторения, статус. Диапазон ограничен 366 днями, ответ — 500 объектами. -
Мультизапрос календаря или Получить событие календаря — если нужен полный iCalendar, а не сводка. Мультизапрос берёт до 50 адресов за вызов; одиночная нода — один. Оба возвращают поле
icsс исходным документом.
Адрес события — это то, что вернул запрос: nodes.<нода запроса>.output.events[0].href.
Придумывать его самому не нужно и нельзя — он назначен сервером.
Запись: href, etag и защита от чужой правки
Записывающих нод три, и различаются они не «силой», а тем, что именно они перезаписывают.
Записать событие пишет объект целиком. Пустой etag означает «создать» (или
«перезаписать не глядя»), непустой — «записать, только если событие с тех пор не менялось».
Тело события собирается двумя способами: из полей формы (название, начало, конец, место,
описание, участники, правило повторения) либо сырым документом iCalendar, если нужного
поля в форме нет. Сырой документ, если он непустой, всегда выигрывает у полей — это
сделано ради уже сохранённых графов: они несут сырое тело и не знают про переключатель.
Изменить событие — это чтение-правка-запись: нода сама забирает текущий .ics,
подменяет заполненные поля и пишет обратно. Пустое поле означает «не трогать»,
а не «стереть». Так меняют время встречи, не потеряв список участников и вложенные
данные, которых в форме нет.
Удалить событие снимает объект по адресу; etag необязателен и здесь.
| Код ошибки | Когда приходит |
|---|---|
| NOT_FOUND | адреса больше нет: событие удалили или href скопирован неверно |
| ACCESS_DENIED | логин или пароль не приняты, либо у аккаунта нет прав на календарь |
| PRECONDITION_FAILED | etag устарел — событие изменили между чтением и записью |
| CONFLICT | сервер отказался принять запись в текущем состоянии коллекции |
| SIZE_LIMIT | ответ сервера больше 8 МБ |
| VALIDATION | не заполнено обязательное поле, диапазон больше 366 дней, больше 50 адресов |
| PROVIDER_ERROR | сеть или сервер: единственный код, который повторяют автоматически |
Различие последней строки и остальных — не косметика. Детерминированный отказ (устаревший
etag, пустой адрес) повторять бессмысленно, и движок его не повторяет; сетевой сбой
повторяется до трёх раз. Каждый код доезжает до ветки on_error как error_code, так что
на «событие удалили» можно ответить одним способом, а на «сервер лёг» — другим. Подробнее —
Ошибки.
Свободные слоты: занятость против запроса
Занятость календаря отвечает на вопрос «когда человек занят», не показывая, чем.
Ответ — список интервалов начало/конец, без названий, участников и описаний. Это и есть
причина, по которой нода существует отдельно от запроса: на многих серверах свободно-занятое
доступно тем, кому содержимое календаря не показывают, и такой ответ безопасно отдать в
чат клиенту.
Считать свободные окна нода не умеет — она отдаёт занятые. Инвертировать интервалы (взять рабочий день, вычесть занятое, оставить куски нужной длины) сегодня надо самому: нодой кода или моделью. Отдельной ноды «предложи три слота» нет.
- Исполнение + данные
- Данные
Планирование: приглашения, и почему они работают не везде
Планирование в календаре отправляет участникам приглашение (REQUEST), ответ на приглашение (REPLY) или отмену (CANCEL). Технически это запись документа iCalendar в особую коллекцию — «исходящие» вашего аккаунта, а рассылку писем участникам делает уже сам сервер.
Отсюда два жёстких условия. Во-первых, в подключении должен быть заполнен URL исходящих. Во-вторых, сервер должен уметь планирование вообще: у iCloud и Google CalDAV его нет. Платформа знает это заранее и отказывает сразу, называя провайдера, вместо того чтобы отправить запрос и получить невнятный 403, который читается как «неверный пароль». Приглашения через эти сервисы отправляются их собственными средствами, а не по CalDAV.
Календарь как инструмент агента
Инструмент календаря — та же дюжина операций, но вызывает их модель. Это разница между графом «на каждый вопрос свой маршрут» и диалогом «запиши меня на вторник после обеда», где модель сама решает: посмотреть занятость → выбрать окно → создать событие → сказать человеку, что получилось.
- Исполнение + данные
- LLM
- Данные
- Расширение
- Исполнение + данные + стрим
В форме ноды перечислены двенадцать операций галочками. Девять включены по умолчанию — все, кроме трёх планирующих (приглашение, ответ, отмена). Причина та же, что выше: без адреса исходящих и на неподдерживающем провайдере они падают на каждом вызове, а инструмент, который гарантированно не работает, хуже отсутствующего — модель тратит на него раунд и бюджет вызовов. Больше того: даже включённые галочкой планирующие инструменты не будут показаны модели, если подключение их не тянет. Это осознанно — пусть модель выберет путь, который может сработать.
Где это обычно рвётся
| Симптом | Что произошло | Что делать |
|---|---|---|
| Проверка: «Подключиться не удалось», логин точно верный | провайдер не принимает пароль от аккаунта по CalDAV | завести пароль приложения в настройках аккаунта провайдера; для Яндекса, iCloud и большинства почтовых сервисов это обязательно |
| Проверка прошла, календарей найдено 0 | базовый URL указывает не на DAV-эндпоинт (обычно вставлен адрес веб-интерфейса) | взять адрес из подсказки провайдера в форме или из документации сервера: у Nextcloud это /remote.php/dav/ |
| Нода падает: «calendar_url is required» | адрес коллекции не задан ни в ноде, ни в подключении | заполнить URL календаря по умолчанию в подключении — один раз на все ноды |
| Событие создалось на три часа раньше | время передано без часового пояса | писать время с Z или со смещением (2026-07-01T10:00:00+03:00), либо заполнить поле часового пояса в ноде |
| PRECONDITION_FAILED на второй записи подряд | во вторую запись передан etag из первой — он устарел в момент её успеха | брать etag из вывода предыдущей записи, а не из чтения перед ней |
| VALIDATION: диапазон превышает 366 дней | запрос «за всё время» | разбить на годовые интервалы или сузить вопрос |
| Запуск отклонён: у ноды не подключён обязательный порт | порт calendar_store у инструмента пуст, и подключение в форме не выбрано | подключить ноду «Конфигурация CalDAV» в порт или выбрать подключение в форме — порт обязательный, поэтому в редакторе это предупреждение, а на запуске и деплое ошибка |
| Агент говорит, что календарь недоступен | подключение выбрано, но не отвечает или не подходит | инструменты объявляются модели в любом случае, и без рабочего подключения вызов возвращает ей текстовую ошибку — смотреть в запуске текст вызова инструмента, а не молчание агента |
Что дальше
Интеграции: обзор
Как подключения хранятся и кто может их менять.
Агент и инструменты
Цикл модели, права инструментов, бюджеты.
Запуск по расписанию
Чтобы календарь опрашивался сам по утрам.
Ошибки и ветка on_error
Как разводить «событие удалили» и «сервер лёг».
Шаблоны и переменные
Откуда в поля нод приезжают даты и адреса.
Справочник нод
Полные схемы всех одиннадцати календарных нод.