Two-way sync with Google Calendar is two separate jobs bolted together. Outbound: when someone books you, an event is written into your Google Calendar. Inbound: when there is already an event in your Google Calendar, that time disappears from your booking page. Most sync problems come from assuming one direction implies the other.
A one-way integration that only writes bookings into your calendar will still let people book on top of your dentist appointment. A one-way integration that only reads your calendar keeps your slots honest but leaves your calendar empty of the bookings themselves. You want both, and you want to know which calendars are feeding each direction.
This article covers what happens when you click Connect, what the permission screen is really asking for, how to pick which calendars block your availability, and why the delay between a change and it showing up varies from a second to a few minutes.
What Two Way Sync Means In Practice
Break it into the two flows and test them separately.
Write: a confirmed booking becomes a real event on one nominated calendar. It should carry the client name, the service, the meeting link or address, and ideally a link back to the booking record. When the client reschedules, the same event moves rather than a second one appearing. When the client cancels, the event is deleted or marked cancelled.
Read: every event on the calendars you have marked as "check for conflicts" is treated as busy time, and any bookable slot that overlaps it is hidden. That includes events you created by hand, events other people invited you to, and bookings written by a different scheduling tool.
The read direction is the one that prevents embarrassment, so it is the one worth testing first. Put a block on your calendar for tomorrow afternoon, wait a moment, and reload your public booking page. If the slot is still offered, the read direction is not doing its job. There is a fuller checklist in double booking prevention.
Connecting Your Account: What Happens During OAuth
You never give a scheduling tool your Google password. The connection uses OAuth 2.0, and from your side it looks like this:
- You click Connect Google Calendar. The app redirects your browser to a Google sign-in page on
accounts.google.com. - You choose which Google account to use. This matters if you are signed into a personal account and a work account at the same time, because the connection is bound to whichever one you pick.
- Google shows a consent screen naming the application and listing what it wants to do. This list is the scopes, covered in the next section.
- You approve, and Google redirects your browser back to the application with a short-lived authorisation code in the URL.
- The application exchanges that code, server to server, for an access token and a refresh token. The access token is what gets used on each API call and expires in about an hour. The refresh token is the long-lived credential that quietly buys new access tokens.
Two consequences of that last step are worth remembering. First, the refresh token is the connection: if it is invalidated, sync stops and the fix is always to reconnect, never to wait. Second, you can revoke it yourself at any time from your Google Account under Security, then Third-party apps with account access. Revoking there stops sync immediately, which is exactly what you want when someone leaves the team.
The Scopes You Are Granting
Google has split calendar access into narrow scopes so an application can ask for the least it needs. These are the ones that show up on a scheduling consent screen.
| Scope | What it allows |
|---|---|
calendar.freebusy | Query busy intervals only. No titles, no guests, no locations. |
calendar.calendarlist.readonly | See the list of calendars on the account so you can choose which ones matter. No event data. |
calendar.events.readonly | Read full event details on calendars you can access. |
calendar.events | Read and write events. Needed to create, move and delete booking events. |
calendar.app.created | Full access, but only to a calendar the application created itself. Nothing else on the account. |
calendar | Full read and write access to all calendars and their settings. More than a booking tool needs. |
Google classes the broader calendar scopes as sensitive, which means an application asking for them has to pass Google verification before accounts outside the developer organisation can connect. If you are ever taken to a screen warning that an app is unverified, stop and check what you are connecting to.
A sensible pairing for scheduling is calendar.calendarlist.readonly plus calendar.freebusy for the read direction and calendar.events for the write direction. That combination never needs to read the contents of your existing meetings.
Primary, Secondary And Subscribed Calendars
A Google account is not one calendar. It has:
- A primary calendar, whose identifier is the account email address. It is created with the account and cannot be deleted.
- Secondary calendars you created yourself, with identifiers ending in
@group.calendar.google.com. Typical examples: Personal, Team Leave, Room 2. - Subscribed calendars shared with you by other people, plus things like national holidays and sports fixtures.
A good integration lets you answer two different questions separately.
Which calendars count as busy? Usually the primary calendar plus any personal or team calendar that represents real commitments. Almost never the holidays calendar, because a public holiday appearing as an all-day event should not necessarily wipe out a working day, and never a colleague subscription unless their time genuinely blocks yours.
Where do bookings get written? One calendar, chosen deliberately. Writing bookings to a dedicated secondary calendar such as Bookings is tidy: you can share that one calendar with a receptionist without exposing your personal appointments, and you can colour it distinctly. Just remember to keep that same calendar ticked as a conflict source, or the tool will not see its own bookings when it comes to check.
If you run several staff members, each person connects their own Google account and each gets their own read set and write target. Nobody should be sharing one connection.
Free/Busy Versus Full Event Access
There are two ways to read a calendar, and the difference is mostly about privacy.
Free/busy asks Google a narrow question: between these two dates, which intervals are busy? The answer is a list of start and end times. No summary, no description, no attendees, no location. It is enough to decide whether a slot can be offered, and it is all a booking page needs.
Full event access returns the whole event object. That buys you things free/busy cannot do: showing "Dentist, 2pm" in your dashboard instead of an anonymous grey block, distinguishing an invitation you declined from one you accepted, reading a meeting link, or reasoning about all-day events based on their titles.
The privacy-preserving option matters more than it sounds. If you are a therapist, a lawyer or an HR manager, your calendar titles are confidential by themselves. "Interview, J. Smith, 3pm" leaks something even if nobody reads the body. Choosing free/busy means the scheduling system holds start and end times and nothing else, which is a much easier conversation with a privacy officer and a much smaller problem if anything is ever breached.
One behaviour to know: free/busy queries deliberately exclude events marked as free rather than busy. If you keep travel time on your calendar as a free event, it will not block anything. That is a frequent surprise and is covered in why calendar sync fails.
Push Notifications Versus Polling
The last piece is how quickly a change in Google Calendar reaches the booking page.
Polling means the application asks Google for changes on a timer, for example every five or fifteen minutes. It is simple, it works everywhere, and its worst case delay is the length of the interval.
Push notifications mean the application registers a watch channel on a calendar, and Google sends an HTTPS request to the application whenever something on that calendar changes. Those requests do not contain the changed event. They carry headers identifying the channel and the resource and a state value that says, in effect, "something changed here". The application then does an incremental fetch to find out what.
Three practical details follow from that design:
- Incremental fetches use a sync token from the previous fetch. If the token has gone stale, Google returns a gone response and the application has to do a full resync of that calendar. Well built integrations handle this silently.
- Watch channels expire and must be renewed on a schedule. A sync that works for weeks then quietly stops is often an expired channel that was never renewed.
- The receiving URL has to be a verified HTTPS domain registered with Google. This is why push is a property of the product you use, not something you can enable yourself.
Even with push, treat the calendar as eventually consistent. The real protection against a double booking is not fast sync, it is a fresh availability check at the moment someone presses Confirm, immediately before the event is created. If a tool only relies on cached availability, a slow sync becomes a booked-over lunch.
What To Check When A Change Does Not Appear
In order, because the cheap checks fail most often:
- Is the calendar you edited ticked as a conflict source? Editing your Personal calendar when only your primary is monitored explains most of these.
- Is the event set to show as Busy? A free event is invisible to availability checks by design.
- Is the connection still live, or was it revoked when a password changed or an admin removed the app?
- Is the change inside the window the sync looks at? Most syncs only track a rolling range of weeks ahead, so an event fifteen months out may not be picked up until later.
- Is it just lag? Wait one polling interval before treating it as broken.
Setting It Up In appntmnts
In appntmnts, Google Calendar is connected per user from the integrations screen. You approve the Google consent screen once, choose which of your calendars count as busy, and choose the single calendar bookings are written to. Rescheduling moves the existing event rather than creating a second one, and cancelling removes it. If your calendar lives somewhere else, the same screen covers Microsoft 365 and CalDAV and iCloud.
The details of what appntmnts requests and how to disconnect are on the Google Calendar integration page. If you are evaluating, connect a throwaway calendar first, put two blocks on it, and watch what your public booking page does. That five minute test tells you more than any feature list.