URL: /code/integrations/google-calendar · Id: `page:/code/integrations/google-calendar`

# Google Calendar

> Your agents offer the times you are free, book the call the buyer picks, send the invite and move it when either side changes plans.

Your agents offer the times you are free, book the call the buyer picks, send the invite and move it when either side changes plans.

```text title="Run it · Ask"
Offer Rosa at Northgate Rentals three 30-minute slots I am free next week, between 10:00 and 16:00 Central.
```

```ts title="Run it · SDK"
import { $ } from 'startup-builder'

const result = await $.try.Meeting.book({ contact: 'contacts/rosa-northgate', at: 'offer 3, 30 minutes, weekdays 10:00 to 16:00 America/Chicago, next week' })
```

```json title="Run it · Response"
{
  "$type": "Meeting",
  "mode": "try",
  "stored": false,
  "contact": "rosa@northgate-rentals.com",
  "offered": [
    "2026-11-03T16:00:00Z",
    "2026-11-04T19:30:00Z",
    "2026-11-05T17:00:00Z"
  ],
  "calendar": "google",
  "sentence": "Would Tuesday at 10:00, Wednesday at 1:30 or Thursday at 11:00 Central work for 30 minutes?"
}
```

- **Connect:** Sign in once, 2 grants
- **Runs through:** [Your account](/code/integrations#three-ways)
- **First run:** Book one call from a positive reply, free on your account
- **Sites:** None, it holds your records

## Connect once
1. Call `Integration.connect` with `system: 'scheduling'`, then open the `connectUrl` it returns and sign in as the Google account whose calendar you keep.
2. Approve the two grants Google lists: your free and busy times, and the events your agents book.
3. Set your bookable hours once, such as weekdays 10:00 to 16:00 in your time zone; every booking stays inside them.

| Grant | What it unlocks | Scope |
| --- | --- | --- |
| Your free and busy times | Your agents offer only times you are free; they never read what your other events are | `https://www.googleapis.com/auth/calendar.freebusy` |
| The events your agents book | Create, move and cancel the Meetings your agents book, with the invite and the video link | `https://www.googleapis.com/auth/calendar.events` |

| To | Do this |
| --- | --- |
| Pause | `Integration.pause` stops every call through Google at once and keeps the token; `Integration.resume` picks up from the same cursor. |
| Reconnect | `Integration.reconnect` returns a fresh `connectUrl` that lasts one hour, and keeps the Integration's id and cursor, so nothing imports twice. |
| Disconnect | `Integration.revoke` revokes the token with Google; every record it brought in stays in your records. |

## What your agents do
| Ask | Verb | Runs through | Price |
| --- | --- | --- | --- |
| Offer three times I am free in my reply. | [`Meeting.book`](/code/apps/scheduling/book) | Your account | Free |
| Move the call to Thursday, same time. | [`Meeting.update`](https://startupbuilder.dev/business/nouns/meeting/update) | Your account | Free |
| Book the time they picked and send the invite with a video link. | [`Meeting.book`](/code/apps/scheduling/book) | Your account | Free |
| Mark yesterday’s call as a no-show and send the rebook note. | [`Meeting.update`](https://startupbuilder.dev/business/nouns/meeting/update) | Your account | Free |

Calls on your own account are free.

## What it holds
| Noun | The account’s object | When | Which side leads |
| --- | --- | --- | --- |
| [Meeting](https://startupbuilder.dev/business/nouns/meeting) | `event` | Your agent books a call | Your records |
| [Meeting](https://startupbuilder.dev/business/nouns/meeting) | `event` | The buyer moves or declines the invite in their own calendar | The account |

## Events and webhooks
| Direction | Event |
| --- | --- |
| Emits | `Meeting.booked`, `Meeting.updated` |
| Hears `events.watch` | Becomes `Meeting.updated` |

Any Event can wake a Workflow, and a Watch delivers it to your endpoint, signed ([Events and webhooks](/code/events)).

## Limits
| What | Limit | Call |
| --- | --- | --- |
| Free and busy times | Read live before every offer and never stored, so an offer is never stale by more than the minutes it takes to reply | [`Meeting.book`](/code/apps/scheduling/book) |
| Bookable hours | Only inside the hours you set and never over a busy block on any calendar you connected | [`Meeting.book`](/code/apps/scheduling/book) |
| One invite | Each Meeting sends one invite from your calendar; a move updates it, so the buyer never holds two | Every call |

## Field map
| Our field | Google Calendar’s field | Notes | Calls |
| --- | --- | --- | --- |
| `Meeting.at` | `event.start.dateTime and event.end.dateTime` | Each with its timeZone | [`Meeting.book`](/code/apps/scheduling/book) |
| `Meeting.contact` | `event.attendees[].email` | None. | [`Meeting.book`](/code/apps/scheduling/book) |
| `Meeting.agenda` | `event.description` | None. | [`Meeting.book`](/code/apps/scheduling/book) |
| `Meeting.link` | `event.conferenceData.createRequest` | Adds a video link to the invite | [`Meeting.book`](/code/apps/scheduling/book) |

## What try does here
Reads your free and busy times for real, picks the slot, and returns the invite it would send; nothing is written to your calendar.

## How long a connection lasts
A connection lasts until you revoke it in your Google Account; Google also ends a grant left unused for six months, and every call your agents make counts as use.

The platform asks you to reconnect 7 days before it runs out; until you do, a call that needs it refuses with [`needs-reauth`](/code/refusals#needs-reauth).

## Plays and strategies
| Kind | Where |
| --- | --- |
| Plays | [Waitlist interviews](/plays/signup-interviews), [Closer handoff](/plays/closer-handoff), [Paid pilot](/plays/paid-pilot), [Cold email](/plays/cold-email), [Instant response](/plays/instant-response) |
| Strategies | [Hands-on](/strategy/hands-on) |

## Used by
- **Services:** [Booked first calls](/capabilities/reach-out/booked-first-calls)

## Reference

| Call | Path | Price |
| --- | --- | --- |
| [`Meeting.book`](/code/apps/scheduling/book) | `POST /meetings/book` | Free |
| [`Meeting.update`](https://startupbuilder.dev/business/nouns/meeting/update) | `POST /meetings/update` | Free |
| [`Meeting.book`](/code/apps/scheduling/book) | `POST /meetings/book` | Free |
| [`Meeting.update`](https://startupbuilder.dev/business/nouns/meeting/update) | `POST /meetings/update` | Free |