Календарь (CalDAV)

CalDAV — это стандартный протокол доступа к календарю поверх HTTP. Он есть у Nextcloud и ownCloud, у Яндекс.Календаря, у iCloud, у Fastmail, у большинства корпоративных серверов (SOGo, Zimbra, Radicale). Смысл для платформы простой: один протокол вместо десятка проприетарных API — воркфлоу, написанный на Nextcloud, переезжает на другой сервер сменой адреса в подключении, а не переписыванием графа.

Единица данных в CalDAV — не «строка в базе», а файл: одно событие это документ iCalendar (.ics), лежащий по своему адресу внутри коллекции-календаря. Отсюда всё остальное на этой странице: у события есть href (путь), есть etag (версия), а изменение — это чтение файла, правка и запись обратно.

Что хранит подключение

Подключение заводится в НастройкиИнтеграции, вид — CalDAV. Общая механика подключений (секреты, права, приоритет порта над полем) описана в обзоре интеграций; здесь — только то, что специфично для календаря.

CalDAV

Поля формы подключения

Провайдер
Nextcloud

Подставит адрес DAV-эндпоинта. Логин и пароль вводятся вручную.

Базовый URL*
https://cloud.example.com/remote.php/dav/
Имя пользователя*
ivanov
Пароль*
••••••••

У большинства провайдеров сюда идёт пароль приложения, а не пароль от аккаунта.

Режим обнаружения
Автоматически (PROPFIND)
URL календаря по умолчанию

Заполнен — ноды можно оставлять без адреса календаря.

URL исходящих

Нужен только для отправки приглашений (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
Запрос к календарю
Записать событие календаря
Выход
  • Исполнение + данные
  • Данные
Одна конфигурация раздаёт подключение двум календарным нодам. Порядок исполнения задаёт верхняя цепочка, подключение приезжает сбоку проводом данных.

Чтение: сначала запрос, потом адреса

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

  1. Список календарей — один раз, чтобы узнать href коллекций. Дальше адрес живёт в подключении или в поле ноды.

  2. Запрос к календарю — с адресом коллекции и диапазоном (с и по в формате ISO 8601). Возвращает список событий с уже разобранными полями: href, etag, uid, summary, dtstart, dtend, признак «весь день», часовой пояс, правило повторения, статус. Диапазон ограничен 366 днями, ответ — 500 объектами.

  3. Мультизапрос календаря или Получить событие календаря — если нужен полный iCalendar, а не сводка. Мультизапрос берёт до 50 адресов за вызов; одиночная нода — один. Оба возвращают поле ics с исходным документом.

Адрес события — это то, что вернул запрос: nodes.<нода запроса>.output.events[0].href. Придумывать его самому не нужно и нельзя — он назначен сервером.

Запрос к календарю
Calendar storeSuccess

Запись: href, etag и защита от чужой правки

Записывающих нод три, и различаются они не «силой», а тем, что именно они перезаписывают.

Записать событие пишет объект целиком. Пустой etag означает «создать» (или «перезаписать не глядя»), непустой — «записать, только если событие с тех пор не менялось». Тело события собирается двумя способами: из полей формы (название, начало, конец, место, описание, участники, правило повторения) либо сырым документом iCalendar, если нужного поля в форме нет. Сырой документ, если он непустой, всегда выигрывает у полей — это сделано ради уже сохранённых графов: они несут сырое тело и не знают про переключатель.

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

Удалить событие снимает объект по адресу; etag необязателен и здесь.

Код ошибкиКогда приходит
NOT_FOUNDадреса больше нет: событие удалили или href скопирован неверно
ACCESS_DENIEDлогин или пароль не приняты, либо у аккаунта нет прав на календарь
PRECONDITION_FAILEDetag устарел — событие изменили между чтением и записью
CONFLICTсервер отказался принять запись в текущем состоянии коллекции
SIZE_LIMITответ сервера больше 8 МБ
VALIDATIONне заполнено обязательное поле, диапазон больше 366 дней, больше 50 адресов
PROVIDER_ERRORсеть или сервер: единственный код, который повторяют автоматически

Различие последней строки и остальных — не косметика. Детерминированный отказ (устаревший etag, пустой адрес) повторять бессмысленно, и движок его не повторяет; сетевой сбой повторяется до трёх раз. Каждый код доезжает до ветки on_error как error_code, так что на «событие удалили» можно ответить одним способом, а на «сервер лёг» — другим. Подробнее — Ошибки.

Свободные слоты: занятость против запроса

Занятость календаря отвечает на вопрос «когда человек занят», не показывая, чем. Ответ — список интервалов начало/конец, без названий, участников и описаний. Это и есть причина, по которой нода существует отдельно от запроса: на многих серверах свободно-занятое доступно тем, кому содержимое календаря не показывают, и такой ответ безопасно отдать в чат клиенту.

Считать свободные окна нода не умеет — она отдаёт занятые. Инвертировать интервалы (взять рабочий день, вычесть занятое, оставить куски нужной длины) сегодня надо самому: нодой кода или моделью. Отдельной ноды «предложи три слота» нет.

Вход
Конфигурация CalDAV
Занятость календаря
JavaScript-код
Записать событие календаря
Выход
  • Исполнение + данные
  • Данные
Типовая запись клиента: смотрим занятость, выбираем окно, создаём событие.

Планирование: приглашения, и почему они работают не везде

Планирование в календаре отправляет участникам приглашение (REQUEST), ответ на приглашение (REPLY) или отмену (CANCEL). Технически это запись документа iCalendar в особую коллекцию — «исходящие» вашего аккаунта, а рассылку писем участникам делает уже сам сервер.

Отсюда два жёстких условия. Во-первых, в подключении должен быть заполнен URL исходящих. Во-вторых, сервер должен уметь планирование вообще: у iCloud и Google CalDAV его нет. Платформа знает это заранее и отказывает сразу, называя провайдера, вместо того чтобы отправить запрос и получить невнятный 403, который читается как «неверный пароль». Приглашения через эти сервисы отправляются их собственными средствами, а не по CalDAV.

Календарь как инструмент агента

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

Вход
LLM
Конфигурация CalDAV
Инструмент календаряT
ИИ-агент
Выход
  • Исполнение + данные
  • LLM
  • Данные
  • Расширение
  • Исполнение + данные + стрим
Календарь у агента: конфигурация подключается к инструменту проводом данных, инструмент — к агенту конфигурационной связью.

В форме ноды перечислены двенадцать операций галочками. Девять включены по умолчанию — все, кроме трёх планирующих (приглашение, ответ, отмена). Причина та же, что выше: без адреса исходящих и на неподдерживающем провайдере они падают на каждом вызове, а инструмент, который гарантированно не работает, хуже отсутствующего — модель тратит на него раунд и бюджет вызовов. Больше того: даже включённые галочкой планирующие инструменты не будут показаны модели, если подключение их не тянет. Это осознанно — пусть модель выберет путь, который может сработать.

Инструмент календаряT
Calendar store

Где это обычно рвётся

СимптомЧто произошлоЧто делать
Проверка: «Подключиться не удалось», логин точно верныйпровайдер не принимает пароль от аккаунта по 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» в порт или выбрать подключение в форме — порт обязательный, поэтому в редакторе это предупреждение, а на запуске и деплое ошибка
Агент говорит, что календарь недоступенподключение выбрано, но не отвечает или не подходитинструменты объявляются модели в любом случае, и без рабочего подключения вызов возвращает ей текстовую ошибку — смотреть в запуске текст вызова инструмента, а не молчание агента

Что дальше