Skip to main content
Required permission: Calendar → Read to list, Calendar → Write to create, change or delete. One permission covers calendars and their events: a calendar you may list but whose events you may not read describes no integration anybody wants.
These are organization calendars. A key never sees anybody’s personal one.Calendars a person makes in the web app belong to that person and are invisible to every API key, as are the events on them. Calendars a key creates are visible to your team in the app.

The one thing to read first

A repeating event does not happen at an instant. “Every weekday at 09:00” means nine o’clock on the clock on the wall — and the instant that names moves by an hour when the clocks change. A series stored as a UTC instant plus a rule is therefore wrong for half the year. So Ruber stores a local time, an IANA zone and a duration, and this API is explicit about which of the two you are looking at:

What is on

Returns one entry per time something happens. A weekly stand-up is one row in the database and four entries in a month.

One day at a time

A local day is not 24 hours, and date is not from plus a day.The 25th of October 2026 is 25 hours long in London; the 29th of March is 23. ?date= converts both edges from wall clock in time_zone, so the window is the day people actually lived — a fixed 86,400,000 milliseconds would close an hour early in October and drop everything after 23:00.Consecutive days tile exactly: one day’s end is the next day’s start, so nothing at midnight is counted twice or missed.
Passing date together with from or to is refused rather than merged — they would have to agree, and there is no useful answer when they do not.
Look at those two instants. 08:00Z and then 09:00Z — Britain left summer time on the 25th. The meeting did not move; 09:00 stayed 09:00, which is the whole point of storing a local time.
Overlap, not containment. An event that starts before your window and ends inside it is returned. Asking only for events starting in a range is how a “what is on today” call loses the overnight shift.
Occurrences are expanded per request rather than stored, so an event edited in the app is correct here on the next call — there is no index to fall behind.

One series

The event in its own terms: starts_at_local and time_zone (the rule’s definition), starts_at (the instant the first occurrence begins), duration_minutes, rrule, exdates and series_ends_atnull for a rule with no end.

Create an event

The three forms of starts_at

Both timed forms are unambiguous and both are honoured exactly as written. The middle one is what a repeating event wants.
Prefer duration_minutes over ends_at for a repeating event. An end instant describes the first occurrence only, so a series expressed that way changes length halfway through the year, when the clocks go back.
A date that does not exist is refused, not rounded. 2026-02-30 is a 400 — not the 2nd of March, which is what naive date parsing produces and what would otherwise silently anchor a whole series two days out.

Change or delete a series

PATCH changes only the fields you name — but timing is one field. Naming time_zone, starts_at, duration_minutes, ends_at, all_day or rrule recomputes the whole timing together, because writing a new zone against an unchanged local time does not translate an event, it reinterprets it.
starts_at_local stays 09:00; starts_at moves from 08:00Z to 07:00Z.
DELETE removes the whole series, not one occurrence. The answer says which it was:

When does this one repeat

GET /v1/events answers “what is on” across the whole calendar. This answers “when does this happen”, which is the other question — and getting it from the list endpoint means asking for every event in a window and discarding all but one, over and over, to walk a series forward. Takes from/to (default: now to 90 days out), or date and time_zone, and limit.
There is the clock change again: 08:00Z, then 09:00Z. The meeting stayed at 09:00 local throughout. has_more is true when the limit was reached, so a caller walking a long series knows to ask again from the last occurrence rather than concluding it ended.

When is anybody free

The same data as the event list, and a different question. “What is on Thursday” wants titles; “when could we meet on Thursday” wants blocks of time — and turning one into the other means expanding recurrence, sorting, and merging overlaps at the joins. Takes from/to (default: now to 7 days out, at most 92 days), or date and time_zone, and calendar.
Blocks are merged. Three overlapping meetings are one busy block, and a meeting ending at 10:00 followed by one starting at 10:00 is one unbroken hour — not two with a gap nobody can use. free is the gaps between them, clipped to the window.
No working hours are applied here. This endpoint does not know whose day it is describing or when they work, and inventing 09:00–17:00 would make “free” mean something you did not ask for.Working hours live on a booking link — see its slots, which applies opening hours, a buffer, a lead time and a horizon on top of exactly this.
Cancelled events make nobody busy, and encrypted events are not counted — see below.

Calendars

name is required and unique to nothing — two calendars may share one. time_zone is the default every new event on the calendar inherits; changing it does not move events that already exist.
Deleting a calendar deletes its events. The answer says how many: {"deleted": true, "events_deleted": 12}. Read event_count first if that matters.A calendar with a booking link pointing at it cannot be deleted at all — a live booking page whose calendar vanished would take reservations into nothing. Remove the booking link first.

Not available yet

Editing one occurrence of a series. “Move just next Tuesday’s” splits the rule and writes a separate override row. The app does it; the API does not yet, because a half-right implementation leaves a series with a hole or a duplicate. Overrides made in the app are readable here, flagged is_override. Encrypted events are never returned. A Private event’s title, location and description are ciphertext, and the key that reads them is in one person’s browser. Such events are omitted rather than returned nameless — see Private mailboxes for the same rule applied to mail. Attendees. Invitations and RSVPs are in the app and are not exposed yet. Booking links are — see Booking.

Errors

See Errors for the full response shape.