Calendar Relay publishes one subscription calendar from two sources:
- upstream
webcal://orhttps://iCalendar feeds, managed through the HTTP API - appointments created by producers through the small HTTP API
Apple Calendar can subscribe to the combined calendar:
webcal://calendar-relay.misei.dev/calendar.ics
Or only the events created through this API:
webcal://calendar-relay.misei.dev/created.ics
This compose file follows the iac-cloud service template and expects the platform Valkey secret resolver:
docker platform compose.yml up --build -dCreate the API key manually in the Valkey UI before deployment. The compose file reads it with:
API_KEY: "{&calendar_relay_api_key!}"Persistent data lives at:
/vol/calendar-relay/data
Set these in compose.yml:
PUBLIC_BASE_URL: base URL shown by/WEBCAL_URLS: optional comma-separated upstream calendars used only to seed an empty database on first startupCACHE_TTL_SECONDS: upstream fetch cache TTLCALENDAR_NAME: display name for the generated calendar
Send the API key with X-API-Key or Authorization: Bearer.
A small browser UI is available at:
https://calendar-relay.misei.dev/admin
It asks for the API key, then shows subscription links, upstream webcal URL editing, and copyable implementation instructions for another agent/service.
Create an appointment:
curl -X POST https://calendar-relay.misei.dev/api/events \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Dentist",
"start": "2026-07-06T09:00:00",
"end": "2026-07-06T09:30:00",
"timezone": "Europe/Berlin",
"location": "Berlin",
"recurrence": "FREQ=WEEKLY;COUNT=6"
}'recurrence is optional. It accepts an iCalendar RRULE string, with or without
the RRULE: prefix, or an object such as:
{
"frequency": "weekly",
"interval": 1,
"count": 6
}List upstream calendar URLs:
curl https://calendar-relay.misei.dev/api/webcal-urls \
-H "X-API-Key: $API_KEY"Replace upstream calendar URLs online:
curl -X PUT https://calendar-relay.misei.dev/api/webcal-urls \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"urls": [
"webcal://example.com/team.ics",
"https://example.com/holidays.ics"
]
}'Cancel an appointment while keeping a STATUS:CANCELLED entry in the feed:
curl -X POST https://calendar-relay.misei.dev/api/events/<uid>/cancel \
-H "X-API-Key: $API_KEY"Remove an appointment from the feed:
curl -X DELETE https://calendar-relay.misei.dev/api/events/<uid> \
-H "X-API-Key: $API_KEY"