Functional description
Beheer > Integraties > Google Calendar

The Google Calendar integration synchronises appointments between a Google Calendar (Google Workspace or a regular Google account) and i-Reserve. It talks directly to the Google Calendar API v3 over OAuth 2.0. This article describes the functional background: the connection method, how synchronisation works and the limitations.

Connection method: delegated OAuth

The integration uses delegated authentication (OAuth 2.0 authorization-code flow): one Google user signs in once and consents, after which i-Reserve stores an access_token + refresh_token and refreshes them automatically. There is no service-account or domain-wide-delegation mode (unlike Outlook, which also has an app-only mode for room mailboxes). The integration is therefore user-centric: it works from the calendars that single user has access to.

Requested scopes:

  • https://www.googleapis.com/auth/calendar.readonly — list calendars and read events.
  • https://www.googleapis.com/auth/calendar.events — create, update and delete events.

Consent is requested with access_type=offline (yields the refresh token) and prompt=consent.

Multiple calendars: object → calendar mapping

One integration can serve multiple Google calendars via an object → calendar mapping: per i-Reserve object you choose which Google calendar (calendar id) events go to. A wildcard (*) acts as the default calendar for objects without their own row. For outbound the calendar of the booking's object is used; for inbound the calendar belonging to the inbound object is followed (or the first mapped calendar).

Unlike the Outlook integration there is no separate room / resource mailbox concept; you connect resources by mapping the relevant Google calendar to an i-Reserve object.

How synchronisation works

The integration is bidirectional and uses both push and polling.

  • Inbound (Google → i-Reserve) reads via events.list on calendars/{calendar-id}/events with a sync token for incremental (delta) sync; on the first run a −90 to +365 day window. Pages through nextPageToken, the nextSyncToken is stored for the next round.
  • Push: i-Reserve registers a Google watch channel (events/watch) that pushes changes to a public endpoint. The channel expires and is auto-renewed by cron once it is within 2 days of expiry. Notifications are routed by channel id.
  • Polling fallback: if a calendar had no sync for more than 30 minutes, cron queues an inbound sync so missed notifications are recovered.
  • Echo prevention: while processing inbound events, outbound triggers are deliberately suppressed so an incoming change is not immediately pushed back to Google (no infinite loop).

Inbound: from Google appointment to booking

  1. Match an existing booking — first on the stored external event id (field ires_field, default external_id), falling back to a [booking_id] suffix in the title.
  2. Cancelled (event status cancelled) → the linked booking gets the cancel status and is unlinked (if allowed).
  3. Existing booking → only date/time are updated; the booking status stays. Allowed if the event was created through this integration (channel external) or if “allow booking update” is on. With “ignore validation errors on update” changes are applied even on a conflict (otherwise logged as a remark).
  4. New → a booking is created on the mapped object (channel external/google), with the event title/description as a remark, in the configured “inbound status”.

Outbound: from booking to Google appointment

Based on booking status i-Reserve creates events (POST events), updates them (PATCH events/{id}) or deletes them (DELETE events/{id}), driven by “create on status” and “delete on status” (the cancel status always deletes). Extra options:

  • Google Meet link creation (optional) and storing the join URL in a booking field. On update an existing Meet link is preserved (not regenerated).
  • Reminders: a popup and/or e-mail reminder, in minutes before the start.
  • Customer as attendee (optional) based on the customer details.

Limitations

  • Delegated only (one OAuth user). No service account / domain-wide delegation; connect resources via the object→calendar mapping.
  • The integration works within that single user's access and within the project's Google Calendar API quota.
  • When the external id is missing, inbound matching falls back to a [booking_id] title suffix; a reused title could theoretically hit the wrong booking — the ires_field is the reliable link.