Why a retried job makes a second event
When your app asks Google to create an event and the connection fails after Google has created it, the app cannot tell whether the event exists. If it simply tries again, there are two. Google's guide to creating events describes the remedy: supply your own event ID when you insert, so that the retry refers to the same event. The ID can be set only at creation, which is why the rule must be designed before the first event is written.
- Derive the ID from the booking's own stable identifier.
- Do not derive it from the booking's time, which changes when it is rescheduled.
- Store the ID with the booking so a move or cancel can find it.
What an acceptable ID looks like
The event resource documentation says a client-supplied ID may use only lowercase letters a to v and digits 0 to 9, must be 5 to 1024 characters and must be unique within the calendar. Hyphens are not in that character set, so an identifier such as a UUID has to be written without them. Google adds that collisions may not be caught at creation because the system is globally distributed, and recommends UUID-style values to make a clash unlikely.
- A hash or encoding of the booking ID, mapped into the allowed alphabet, also fits.
- Test an ID with a character outside the set, to see the error before production does.
- Do not reuse one ID for two bookings, even after a cancellation, without testing what the calendar does.
Duplicates, moves and cancellations
Google's error guide says a 409 duplicate response means an item with that ID already exists, and tells you to call update to change an existing item. So the sync's create step can treat a 409 as: the event exists, update it to match the booking. A move updates the same event; a cancellation deletes it, and a deleted event appears with a cancelled status in sync results. What happens if a cancelled booking is later reinstated with the same ID is not stated on the pages read, so the safe course is to test it on a test calendar and write down the result.
- Create, update and delete must all be keyed by the same ID.
- Rate-limit errors come back as 403 or 429 and are retried with exponential backoff.
- Guests are emailed when sendUpdates is all or externalOnly, so set it deliberately.
Time zones
An event has a start and end, each a date-time with an optional IANA zone name such as Europe/London. If the date-time has no UTC offset, the zone must be given; for recurring events it is always required. A booking taken in one zone and viewed in another needs the zone stored, not guessed from the server. Test cases should include a date either side of a clock change and a booking made in a zone different from the server's, with the expected local times written down before the code is run.
- Store the local date, local time and zone with the booking.
- A local time that the clock change skips, or repeats, needs a rule agreed in writing.
- An opaque event blocks time on the calendar; a transparent one does not.
How the paid outcome is accepted
The Google Calendar outcome is accepted when three agreed bookings, including a clock-change case and another time zone, show the expected local times, repeating a booking leaves one event, a move changes the same event and a cancel removes it. The fixed £445 test price is untested and payment follows your sign-off. Reading the calendar back for availability is a separate scope.
Sources and limits
- Google Calendar: event resource Checked 2026-10-11.
- A client-supplied event ID uses lowercase letters a-v and digits 0-9, 5 to 1024 characters, unique within the calendar.
- start.timeZone is an IANA name; it is required for recurring events and needed when dateTime has no offset.
- A cancelled status means the event was deleted or cancelled; transparency opaque means busy.
- Google Calendar: create events Checked 2026-10-11.
- A client-supplied ID keeps a local database in sync and prevents duplicates if a call fails partway; it can be set only at insert.
- sendUpdates set to all or externalOnly notifies guests by email.
- Google Calendar: error handling Checked 2026-10-11.
- A 409 duplicate error means an item with that ID already exists; use update to change it.
- Exponential backoff is recommended for rate-limit errors, which return 403 or 429.