Calendar
Knobs Calendar supports multiple calendars per workspace, single and recurring events, attendees with RSVPs, free/busy lookups, external ICS subscriptions, and a token-gated published ICS feed.
All routes are workspace-scoped and accept a session JWT or API key, except the public feed at the end. Calendars use the per-object ACL model (owner + grants + workspaceAccess) with one extra rung: freeBusy, which reveals busy intervals only. Event attendees can always read and RSVP to their own events regardless of calendar access.
Calendars
Section titled “Calendars”| Endpoint | Description |
|---|---|
GET /v1/workspaces/{wid}/calendar/calendars |
List calendars you can see (ensures your default personal calendar exists) |
POST /v1/workspaces/{wid}/calendar/calendars |
Create {name, color?, timeZone?} |
GET /v1/workspaces/{wid}/calendar/calendars/{cid} |
Calendar metadata |
PATCH /v1/workspaces/{wid}/calendar/calendars/{cid} |
Rename / recolor / retimezone |
DELETE /v1/workspaces/{wid}/calendar/calendars/{cid} |
Delete (the default calendar can’t be deleted) |
GET /v1/workspaces/{wid}/calendar/calendars/{cid}/access |
Sharing panel (incl. the freeBusy level) |
PUT /v1/workspaces/{wid}/calendar/calendars/{cid}/access |
Replace {workspaceAccess, grants} |
Events & instances
Section titled “Events & instances”Render views from instances — the server expands recurrences and applies ACL/privacy filtering:
| Endpoint | Description |
|---|---|
GET /v1/workspaces/{wid}/calendar/instances |
Expanded instances; ?from=&to= (RFC 3339, required) and ?calendarIds= (comma-separated) |
POST /v1/workspaces/{wid}/calendar/events |
Create an event (single or recurring) |
GET /v1/workspaces/{wid}/calendar/events/{eid} |
The event document (series master, single, or override) |
PATCH /v1/workspaces/{wid}/calendar/events/{eid} |
Edit; on a series add ?mode=single|following|all&originalStart= |
DELETE /v1/workspaces/{wid}/calendar/events/{eid} |
Delete/cancel with the same ?mode=&originalStart= scoping |
POST /v1/workspaces/{wid}/calendar/events/{eid}/rsvp |
{response, originalStart?} — attendee self-service (accepted/declined/tentative; per-occurrence when originalStart is set) |
Create an event
Section titled “Create an event”curl -X POST https://api.knobs.io/v1/workspaces/{wid}/calendar/events \ -H "Authorization: Bearer knak_..." \ -H "Content-Type: application/json" \ -d '{ "calendarId": "c_11ff…", "title": "Sprint planning", "startLocal": "2026-07-21T10:00:00", "endLocal": "2026-07-21T11:00:00", "timeZone": "America/New_York", "recurrence": ["RRULE:FREQ=WEEKLY;BYDAY=TU"], "attendeeEmails": ["sam@acme.knobs.io"], "reminders": [{"method": "notification", "minutesBefore": 10}] }'Times are wall-clock local (startLocal/endLocal) plus an IANA timeZone — recurring events stay at the same local time across DST. allDay: true with date-only values creates all-day events. Recurrence is RFC 5545 RRULE strings.
Edit one occurrence vs. the series
Section titled “Edit one occurrence vs. the series”curl -X PATCH "https://api.knobs.io/v1/workspaces/{wid}/calendar/events/{eid}?mode=single&originalStart=2026-07-28T14:00:00Z" \ -H "Authorization: Bearer knak_..." \ -H "Content-Type: application/json" \ -d '{"title": "Sprint planning (moved)", "startLocal": "2026-07-28T11:00:00", "endLocal": "2026-07-28T12:00:00"}'mode=single overrides one occurrence, mode=following splits the series, mode=all edits the master.
Free/busy & settings
Section titled “Free/busy & settings”| Endpoint | Description |
|---|---|
POST /v1/workspaces/{wid}/calendar/freebusy |
{uids?[], emails?[], from, to} → merged busy blocks + working hours per target (intervals only, never titles) |
GET /v1/workspaces/{wid}/calendar/settings |
Your display timezone, working hours, default reminders |
PUT /v1/workspaces/{wid}/calendar/settings |
Replace those settings |
Quick-add, extraction & source links
Section titled “Quick-add, extraction & source links”| Endpoint | Description |
|---|---|
POST /v1/workspaces/{wid}/calendar/events:quick-add |
NLP quick-add {text, tz, confirm} — confirm:false returns {draft} for review; confirm:true creates → {draft, event}. Rate-limited |
POST /v1/workspaces/{wid}/calendar/events:extract-from-mail |
LLM event draft from a thread {threadId, tz?} → {draft, sourceRef} |
POST /v1/workspaces/{wid}/calendar/events:create-from-mail |
Non-AI source-link create {objectId, title?} from a mail thread |
POST /v1/workspaces/{wid}/calendar/events:create-from-chat |
Same from a chat message |
POST /v1/workspaces/{wid}/calendar/invites:import |
Import an inbound iCalendar invite {messageId, respond?} — adds it to your default calendar and optionally sends the iTIP reply |
Subscriptions (external ICS)
Section titled “Subscriptions (external ICS)”| Endpoint | Description |
|---|---|
GET /v1/workspaces/{wid}/calendar/subscriptions |
List ICS subscriptions |
POST /v1/workspaces/{wid}/calendar/subscriptions |
Add {url, name} — creates a read-only overlay calendar |
DELETE /v1/workspaces/{wid}/calendar/subscriptions/{sid} |
Remove (and its overlay calendar) |
POST /v1/workspaces/{wid}/calendar/subscriptions/{sid}/poll |
Poll the feed now |
Subscribed calendars are read-only (403 read_only_calendar on event writes).
Publishing a calendar (ICS feed)
Section titled “Publishing a calendar (ICS feed)”Owners can publish a calendar behind a capability token:
| Endpoint | Description |
|---|---|
POST /v1/workspaces/{wid}/calendar/calendars/{cid}/feed/rotate |
Mint/rotate the publish token → {token, path} — shown once; rotating invalidates the old URL |
The published feed itself is the platform’s one anonymous data-bearing GET (note: not under /v1):
GET /calendar-feeds/{cid}/{token}/calendar.icscurl https://api.knobs.io/calendar-feeds/{cid}/{token}/calendar.icsIt requires no bearer, is rate-limited per IP, answers a uniform 404 to a bad calendar or token, and exports private events as busy placeholders. Paste the URL into any calendar app that supports ICS subscriptions.