Calendar (CalDAV)

CalDAV is the standard protocol for calendar access over HTTP. Nextcloud and ownCloud speak it, so do Yandex Calendar, iCloud, Fastmail and most corporate servers (SOGo, Zimbra, Radicale). The point for the platform is simple: one protocol instead of a dozen proprietary APIs — a workflow written against Nextcloud moves to another server by changing the address in the connection, not by rewriting the graph.

The unit of data in CalDAV is not “a row in a table” but a file: one event is an iCalendar document (.ics) sitting at its own address inside a calendar collection. Everything else on this page follows from that: an event has an href (its path) and an etag (its version), and editing one means reading the file, changing it and writing it back.

What the connection stores

The connection is created in SettingsIntegrations, kind CalDAV. The general mechanics of connections (secrets, permissions, port-beats-field) are covered in the integrations overview; this page covers only what is specific to calendars.

CalDAV

Connection form fields

Provider
Nextcloud

Fills in the DAV endpoint address. Login and password stay your job.

Base URL*
https://cloud.example.com/remote.php/dav/
Username*
ivanov
Password*
••••••••

At most providers this is an app password, not your account password.

Discovery mode
Automatic (PROPFIND)
Default calendar URL

Filled in — nodes can be left without a calendar address.

Outbox URL

Only needed for sending invitations (scheduling).

The Provider picker fills in the base URL for you: Nextcloud, ownCloud, Yandex Calendar, iCloud, Fastmail and “Other (manual)”. For Nextcloud and ownCloud it only asks for the host — the /remote.php/dav/ path is appended for you. A preset never touches the login or the password: those are the one thing only you know.

Discovery mode answers “how do I find your calendars at this address”. “Automatic (PROPFIND)” is the standard route: ask the server for the current user, ask the user for their calendar home, ask the home for the calendars inside it. If your server answers off-spec there are two manual bypasses: name the calendar home, or name one specific calendar. The manual modes are not “stricter” — they simply skip the discovery steps your server does not implement.

Default calendar URL is worth filling in almost always. Nodes that need a collection address fall back to it when their own field is empty — otherwise the same long URL has to be pasted into every node of the graph.

There is currently no calendar picker in the form: collection addresses are obtained with the List Calendars node, which returns the href and display name of every calendar. Run it once, copy the href you want into the connection, and you can forget about addresses afterwards.

Eleven nodes

Nine of them are ordinary graph steps carrying a calendar_store port. The tenth, Calendar Tool, hands the same operations to an agent — same port, but no run input. The eleventh, CalDAV Config, is not a graph step at all: it has neither a run input nor a calendar_store port — it is what feeds that port.

NodeWhat it doesReads or writes
CalDAV Configpicks a saved connection and hands it to the other nodes
List Calendarsenumerates the account's collections: href, name, colourread
Calendar Queryevents (VEVENT) or tasks (VTODO) within a date rangeread
Calendar Multigetbatch-fetches objects by a list of hrefs — up to 50 per callread
Get Calendar Eventone object in full: the raw iCalendar plus parsed fieldsread
Put Calendar Eventcreates or replaces a whole objectwrite
Patch Calendar Eventchanges individual fields of an existing eventwrite
Delete Calendar Eventremoves the objectwrite
Calendar Free/Busybusy intervals without any event contentread
Calendar Schedulesends an invitation, a reply or a cancellation to attendeeswrite
Calendar Toolthe same operations, as tools for an AI agentboth

They all take the connection the same way: either a CalDAV Config node wired by a data edge into the calendar_store port, or a connection picked in the node’s own form. The port beats the field. The port is also required: a graph with neither wired port nor filled field gets a warning in the editor and is rejected at run and deploy — so the refusal arrives before a paid run has already got halfway.

Entry
CalDAV Config
Calendar Query
Put Calendar Event
Exit
  • Execute + Data
  • Data
One config node hands the connection to two calendar nodes. Execution order comes from the top chain; the connection arrives sideways on a data edge.

Reading: query first, addresses second

Working with CalDAV always follows the same order, and it is not obvious: ask for a date range first, take the addresses out of the answer, then fetch details by address.

  1. List Calendars — once, to learn the href of your collections. After that the address lives in the connection or in the node’s field.

  2. Calendar Query — with a collection address and a range (from and to in ISO 8601). Returns a list of events with fields already parsed out: href, etag, uid, summary, dtstart, dtend, an all-day flag, time zone, recurrence rule, status. The range is capped at 366 days, the answer at 500 objects.

  3. Calendar Multiget or Get Calendar Event — when you need the full iCalendar rather than a summary. Multiget takes up to 50 addresses per call; the single-object node takes one. Both return an ics field with the source document.

An event’s address is whatever the query returned: nodes.<query node>.output.events[0].href. You cannot and must not invent it — the server assigns it.

Calendar Query
Calendar storeSuccess

Writing: href, etag and protection against someone else’s edit

There are three writing nodes, and they differ not in “power” but in what exactly they overwrite.

Put Calendar Event writes the whole object. An empty etag means “create” (or “overwrite blindly”), a non-empty one means “write only if the event has not changed since”. The event body is composed in one of two ways: from form fields (title, start, end, location, description, attendees, recurrence rule) or from a raw iCalendar document when the field you need is not in the form. A non-empty raw document always wins over the fields — that is deliberate, for graphs saved earlier: they carry a raw body and know nothing about the switch.

Patch Calendar Event is read-modify-write: the node fetches the current .ics itself, substitutes the fields you filled in and writes it back. An empty field means “leave alone”, not “erase”. That is how you move a meeting without losing the attendee list and the extra data the form does not show.

Delete Calendar Event removes the object at an address; the etag is optional here too.

Error codeWhen it arrives
NOT_FOUNDthe address is gone: the event was deleted, or the href was copied wrong
ACCESS_DENIEDlogin or password rejected, or the account has no rights on the calendar
PRECONDITION_FAILEDstale etag — the event changed between the read and the write
CONFLICTthe server refused the write in the collection's current state
SIZE_LIMITthe server's answer exceeds 8 MB
VALIDATIONa required field is empty, the range exceeds 366 days, more than 50 addresses
PROVIDER_ERRORnetwork or server: the only code that is retried automatically

The difference between the last row and the rest is not cosmetic. A deterministic refusal (a stale etag, an empty address) is pointless to repeat, and the engine does not repeat it; a network failure is retried up to three times. Every code reaches the on_error branch as error_code, so “the event was deleted” can be answered one way and “the server is down” another. See Errors.

Free slots: busy time versus a query

Calendar Free/Busy answers “when is this person busy” without revealing with what. The answer is a list of start/end intervals — no titles, attendees or descriptions. That is precisely why the node exists separately from the query: on many servers free/busy is readable by people who are not allowed to see calendar contents, so such an answer is safe to send to a customer in a chat.

The node does not compute free windows — it returns busy ones. Inverting the intervals (take the working day, subtract the busy time, keep the chunks of the length you need) is currently your job: with a code node or with a model. There is no “suggest three slots” node.

Entry
CalDAV Config
Calendar Free/Busy
JavaScript Code
Put Calendar Event
Exit
  • Execute + Data
  • Data
A typical booking: look at busy time, choose a window, create the event.

Scheduling: invitations, and why they do not work everywhere

Calendar Schedule sends attendees an invitation (REQUEST), a reply to an invitation (REPLY) or a cancellation (CANCEL). Technically it writes an iCalendar document into a special collection — your account’s outbox — and the server itself mails the attendees.

Two hard conditions follow. First, the connection must have an Outbox URL. Second, the server must implement scheduling at all: iCloud and Google CalDAV do not. The platform knows this in advance and refuses up front, naming the provider, rather than firing the request and collecting an opaque 403 that reads like “wrong password”. Invitations through those services are sent with their own tooling, not over CalDAV.

The calendar as an agent tool

Calendar Tool is the same dozen operations, invoked by the model. This is the difference between a graph with a route per question and a conversation like “book me Tuesday afternoon”, where the model decides for itself: check busy time → pick a window → create the event → tell the human what happened.

Entry
LLM
CalDAV Config
Calendar ToolT
AI Agent
Exit
  • Execute + Data
  • LLM
  • Data
  • Extension
  • Execute + Data + Streaming
A calendar for an agent: the config node feeds the tool over a data edge, the tool feeds the agent over a configuration link.

The node’s form lists twelve operations as checkboxes. Nine are on by default — everything except the three scheduling ones (invite, reply, cancel). The reason is the one above: without an outbox address, and on a provider that does not support scheduling, they fail on every call, and a tool that is guaranteed not to work is worse than an absent one — the model spends a round and a call budget slot learning that. Beyond that, scheduling tools that are ticked are still not shown to the model when the connection cannot support them. That is deliberate: let the model pick a path that can succeed.

Calendar ToolT
Calendar store

Where this usually breaks

SymptomWhat happenedWhat to do
Check says "could not connect", the login is definitely rightthe provider does not accept an account password over CalDAVissue an app password in the provider's account settings; for Yandex, iCloud and most mail services this is mandatory
Check passes, 0 calendars foundthe base URL does not point at a DAV endpoint (usually the web UI address was pasted)take the address from the provider hint in the form or from the server's docs: for Nextcloud it is /remote.php/dav/
Node fails with "calendar_url is required"no collection address on the node and none on the connectionfill in the default calendar URL on the connection — once, for every node
The event landed three hours earlythe time was passed without a time zonewrite times with a Z or an offset (2026-07-01T10:00:00+03:00), or fill in the node's time-zone field
PRECONDITION_FAILED on the second write in a rowthe second write got the etag from the first — which went stale the moment it succeededtake the etag from the previous write's output, not from the read before it
VALIDATION: range exceeds 366 daysan "all time" querysplit it into yearly ranges, or narrow the question
The run is rejected: a required port on the node is not wiredthe tool's calendar_store port is empty and no connection is picked in its formwire a CalDAV Config into the port or pick a connection in the form — the port is required, so this is a warning in the editor and an error at the run and deploy gate
The agent says the calendar is unavailablea connection is picked but does not answer or does not fitthe tools are advertised to the model either way, and without a working connection the call returns it a textual error — read the tool-call text in the run rather than the agent's silence

What next