CalDAV Integration
Davis CalDAV server integration, tsdav/ical.js libraries, event lifecycle, and recurrence rule handling.
CalDAV Integration
All calendar and scheduling data lives exclusively on the Davis CalDAV server. Sharetribe privateData stores only references (calendarUrl, davisUserId).
Event Creation Flow
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Browser
participant API as Express API
participant ST as Sharetribe
participant Davis
Browser->>API: POST /api/calendar/events
API->>ST: Verify user session<br/>(get childIds, credentials)
API->>API: Verify calendar ownership
API->>API: Decrypt child credentials<br/>(AES-256-GCM)
API->>Davis: PUT VEVENT<br/>(CalDAV with rrule)
Davis-->>API: 201 Created
API-->>Browser: Event dataDavis Server
Davis is a CalDAV/CardDAV server. Run locally with:
docker compose up -dThis starts Davis + Postgres. Configuration is in docker-compose.yml.
Environment Variables
| Variable | Description |
|---|---|
DAVIS_CALDAV_URL | CalDAV endpoint (default: http://localhost:9000/dav) |
DAVIS_API_URL | REST API endpoint (default: http://localhost:9000/api) |
DAVIS_API_TOKEN | Admin API token |
Libraries
- tsdav — WebDAV/CalDAV client for Node.js
- ical.js — Parse and build iCalendar (ICS) data
CalDAV URL Encoding
CalDAV URLs contain slashes, so they can't be used directly in REST route params. The calendarId used in API routes is the CalDAV URL encoded as base64url:
encodeCalendarId(calendarUrl) // Buffer.from(url).toString('base64url')
decodeCalendarId(calendarId) // Buffer.from(id, 'base64url').toString('utf-8')Calendar Lifecycle
- On child creation (
/api/children/create): if the parent has Davis credentials, a CalDAV calendar is automatically created for the child - Manual creation (
/api/calendar/create): creates a calendar for a child that doesn't have one yet - On session publish (
/api/sessions/publish): a CalDAV calendar is created on the vendor's Davis account with WEEKLY recurring events matching the session schedule
The calendar URL is stored in the child's Sharetribe privateData as calendarUrl.
Event CRUD
All endpoints verify calendar ownership before proceeding (the calendar must belong to one of the authenticated user's children).
| Endpoint | Method | Description |
|---|---|---|
/api/calendar/events/:calendarId | GET | Fetch all events from a calendar |
/api/calendar/events | POST | Create event (supports rrule) |
/api/calendar/events/update | POST | Update existing event |
/api/calendar/events/delete | POST | Delete event |
/api/calendar/list | GET | List all calendars |
/api/calendar/create | POST | Create calendar for a child |
Recurrence (rrule)
Events support iCalendar RRULE for recurrence:
{
"summary": "Swimming",
"dtstart": "2025-01-06T15:00:00.000Z",
"dtend": "2025-01-06T16:00:00.000Z",
"rrule": {
"freq": "WEEKLY",
"byday": ["MO"]
}
}Supported frequencies: DAILY, WEEKLY, WEEKLY with byday. The frontend expands rrules into individual occurrences for display (see calendarUtils.ts).
davisUserId vs davisUsername
davisUsername: the user's login credential (typically their email)davisUserId: Davis's internal user ID used in CalDAV URL paths (/dav/<davisUserId>/calendars/...)
Both are stored in the parent's Sharetribe privateData. Calendar URL construction uses davisUserId, not davisUsername.