# Configure Calendar Sync (/how-to/calendar-integrations/configure-calendar-sync)



After completing the Google Calendar OAuth flow, you need to select which specific calendar within your Google account should receive booking events. This guide covers calendar selection, what data syncs, and how multi-calendar support works.

Choosing a calendar [#choosing-a-calendar]

Immediately after the OAuth flow completes, Resytech retrieves a list of all calendars in your Google account. Each calendar shows:

| Property     | Description                                                                                                    |
| ------------ | -------------------------------------------------------------------------------------------------------------- |
| **Name**     | The calendar's display name (e.g., "Work", "Bookings", "Personal").                                            |
| **Primary**  | Whether this is your account's default calendar.                                                               |
| **Writable** | Whether your access role is `writer` or `owner`. Calendars where you only have read access cannot be selected. |

Select the calendar you want and click **Save**. If you skip this step, the calendar link remains in a `pending` state and no events will sync until you complete the selection.

What data appears on calendar events [#what-data-appears-on-calendar-events]

Each synced booking creates a Google Calendar event with the following fields:

| Event field           | Value                                                                                                                                 |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| **Title**             | `{Activity Name} - {Customer Name}`                                                                                                   |
| **Start / End**       | The booking's start and end time in UTC. Google Calendar converts this to your local timezone for display.                            |
| **Location**          | The location name associated with the booking.                                                                                        |
| **Description**       | A multi-line summary containing the confirmation code, customer name, booking status, duration in minutes, date, and start/end times. |
| **Status**            | Always set to `confirmed` on the Google Calendar event.                                                                               |
| **Extended property** | A private `managed_by: resytech` flag so you can identify Resytech-managed events programmatically.                                   |

Example event description [#example-event-description]

```
Confirmation Code: BK-12345
Customer: Jane Smith
Status: Confirmed
Duration: 120 minutes
Date: 2026-04-15
Time: 09:00 - 11:00
```

How sync operations map to booking states [#how-sync-operations-map-to-booking-states]

| Booking action                                    | Calendar result                           |
| ------------------------------------------------- | ----------------------------------------- |
| Booking created (status is not Draft or Canceled) | New event created on all linked calendars |
| Booking updated (rescheduled, details changed)    | Existing event updated in place           |
| Booking canceled                                  | Event deleted from all linked calendars   |
| Booking set to Draft                              | Event deleted (drafts are not synced)     |
| Booking created via iCal sync                     | No event created (prevents sync loops)    |

Multi-calendar support [#multi-calendar-support]

A single activity can be linked to multiple calendars. Each link is independent:

* **Different Google accounts.** You can connect Calendar A from `owner@example.com` and Calendar B from `manager@example.com`. Both will receive events for the same activity.
* **Different calendars on the same account.** If you run the OAuth flow twice with the same Google account but select a different calendar each time, both calendars receive events.
* **Independent failure handling.** If one link's token expires or its calendar is deleted, the other links continue to work. Errors are logged per-link and never block sync for other calendars.

Stale event recovery [#stale-event-recovery]

If a calendar event is manually deleted from Google Calendar but the booking still exists in Resytech, the system handles this gracefully:

1. On the next update to that booking, Resytech attempts to update the event.
2. Google returns a `404 Not Found` or `410 Gone` response.
3. Resytech removes the stale local record and creates a fresh event.

This means you do not need to manually re-sync if someone accidentally deletes an event from Google Calendar.

Token refresh [#token-refresh]

OAuth access tokens expire after approximately one hour. Resytech checks the token expiry before every sync operation:

* If the token has more than five minutes remaining, it is used as-is.
* If the token is within five minutes of expiry (or already expired), Resytech uses the stored refresh token to obtain a new access token from Google.
* The new access token and its expiry time are saved back to the database.
* If Google does not return a new refresh token during the refresh flow, the original refresh token is retained.

If the refresh fails entirely (for example, the user revoked access in their Google Account security settings), the sync operation is skipped for that link and a warning is logged. You will need to [disconnect](/how-to/calendar-integrations/disconnect-calendar) and reconnect the calendar to restore sync.
