Google Calendar
Let hires read your calendar, find times when everyone is free, book and move meetings, and answer invitations in Google Calendar.
Explore 9 actions and their permissionsExact inputs, sign-in scopes, access limits and approval rules.
With Google Calendar, a hire can:
- list your calendars and what's on them, and read an event with who's coming and how they answered,
- see when people are busy, and suggest times when everyone is free inside working hours,
- put events on a calendar, with a Google Meet link and invitations if you like,
- move, rename or cancel events it's allowed to change, and add or remove guests,
- accept, decline or say maybe to invitations, with a note to the organiser.
It's how a personal-assistant hire keeps someone's diary: "find 45 minutes with Ali next week and send an invite" works end to end.
1. Make an app in Google Cloud
Roster connects as an app you own. If you already made one for Gmail, use the same app: turn on the Calendar API, add the two scopes below, and use its client ID and secret here too.
- Open console.cloud.google.com, and create a project (or pick one).
- APIs & Services → Library: search for Google Calendar API and click Enable.
- APIs & Services → OAuth consent screen: Internal with Google Workspace; with a personal account, External, add yourself as a test user, then Publish app (in testing, Google stops the access after 7 days).
- Add the scopes
https://www.googleapis.com/auth/calendar.eventsandhttps://www.googleapis.com/auth/calendar.readonly. - Credentials → Create credentials → OAuth client ID, type Web application. Under
Authorized redirect URIs, add the address Roster shows on the Google Calendar connection. It
ends in
/api/integrations/google-calendar/oauth/callback. - Copy the Client ID and Client secret.
2. Connect it in Roster
- Open Integrations → Tools → Add a tool, search for Google Calendar and choose it.
- Paste the OAuth app client ID and OAuth app client secret.
- Time zone: the zone hires read and write times in, like
Asia/Dubai. Leave it empty to use the calendar's own. - Click Connect, then Connect with Google Calendar on the new connection, sign in with the calendar's account and click Allow.
What Roster asks Google for
| Permission | Why |
|---|---|
View and edit events (calendar.events) | Read events; book, change, cancel and answer (only hires allowed to) |
See your calendars (calendar.readonly) | List calendars and see when people are free |
3. Give hires access
On the hire's Tools tab, click Give access on Google Calendar:
- Read only: it can list calendars and events, see who's free and suggest times. It can't book, change, cancel or answer anything.
- Read and write: it can also book, change and cancel events and answer invitations.
- Calendars: the calendars it may use, by name or ID;
primaryis the account's own. Leave it empty for every calendar. - Who it may invite: domains of people it may invite, like
yourbusiness.com. Leave it empty to let it invite anyone.
Inviting someone outside your team is a promise made in your business's name, so it waits for a person's approval: when a hire books or changes a meeting that invites anyone outside your team's domains, or moves a meeting someone outside is on, nothing is sent. The hire asks in the thread, and when the approver says yes, Roster books exactly that meeting. Meetings among your team go ahead without asking. To change this for a hire, use When it asks first on its Tools tab. To keep a hire to your own team altogether, put your domain in Who it may invite.
Hires write times like 2026-11-10T14:00, in the connection's time zone, and show every time in
it too.
Waking a hire when the calendar changes
A hire's responsibility can watch Google Calendar: it's woken when events on its calendars (or the primary one) change, are cancelled, or when a new invitation arrives. Checking costs nothing when nothing changed.
If something goes wrong
- "Isn't connected yet": press Connect with Google Calendar on the connection.
- "Can only be read, not changed": the account can see that calendar but not edit it. Share it with Make changes to events in Google Calendar.
- "Can't see their calendar" when finding a time: that person's calendar doesn't share free/busy with the account. The hire says so and suggests checking with them.
- It worked for a week, then stopped: the app is External and still in testing. Publish it, then connect again.
Tools and permissions reference
This reference is built from the same definitions Roster uses to connect Google Calendar and give hires access. It updates with each release.
Authentication and requested permissions
The account or app you connect sets the outer limit. Each hire's access can narrow it further. Connecting a tool gives it to no hire until you give access on that hire's Tools tab.
OAuth 2.0
From the OAuth client (Web application) you created in Google Cloud for Roster.
Requested scopes:
https://www.googleapis.com/auth/calendar.eventshttps://www.googleapis.com/auth/calendar.readonly
Connection fields
| Field | What to provide |
|---|---|
OAuth app client IDclientIdOptional | From the OAuth client (Web application) you created in Google Cloud for Roster. |
OAuth app client secretclientSecretOptional · sealed secret | Use the value for your account or connection. |
Time zonetimeZoneOptional | The time zone hires read and write times in. Leave empty to use the calendar's own. |
Per-hire limits
Read-only hires receive only actions that read. Read-and-write access also allows actions that change things, where the connection itself permits them. An empty resource allowlist usually means everything the connected account can reach; each field below explains its meaning.
| Field | What to provide |
|---|---|
CalendarscalendarsOptional | Calendars the hire may use, by name or ID; primary is the account's own. Leave empty for every calendar. A watch checks these (or primary). |
Who it may inviteinviteDomainsOptional | Domains of people the hire may invite to events, like example.com. Leave empty to let it invite anyone. |
Actions
9 actions: 5 only read and 4 change something. A read action may still need approval when your workspace or hire is set to ask. A locked approval requirement always applies.
9 of 9 actions. Open an action for its arguments and full input schema.
list_calendarsRead only
The calendars this hire can use: name, ID, whether it can change them, and their time zone.
Approval: Uses your workspace and hire approval settings.
This action takes no arguments.
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {}
}list_eventsRead only
Events on a calendar between two times (default: the next 7 days), soonest first, recurring ones expanded. Optionally only those matching words.
Approval: Uses your workspace and hire approval settings.
| Argument | Type | Details |
|---|---|---|
calendarOptional | string | calendar name or ID; primary is the account's ownDefault: "primary" |
fromOptional | string | from (default now): 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day |
toOptional | string | until (default a week after from): 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day |
queryOptional | string | words in the title, description, place or attendees |
limitOptional | integer | See input schema below.Default: 25 |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"calendar": {
"default": "primary",
"description": "calendar name or ID; primary is the account's own",
"type": "string",
"minLength": 1,
"maxLength": 300
},
"from": {
"type": "string",
"minLength": 10,
"maxLength": 40,
"description": "from (default now): 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day"
},
"to": {
"type": "string",
"minLength": 10,
"maxLength": 40,
"description": "until (default a week after from): 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day"
},
"query": {
"description": "words in the title, description, place or attendees",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"limit": {
"default": 25,
"type": "integer",
"minimum": 1,
"maximum": 100
}
}
}read_eventRead only
Read one event: when, where, the video call, who's invited and how they answered, and its notes.
Approval: Uses your workspace and hire approval settings.
| Argument | Type | Details |
|---|---|---|
calendarOptional | string | calendar name or ID; primary is the account's ownDefault: "primary" |
idRequired | string | the event's id, from list_events |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"calendar": {
"default": "primary",
"description": "calendar name or ID; primary is the account's own",
"type": "string",
"minLength": 1,
"maxLength": 300
},
"id": {
"type": "string",
"minLength": 1,
"maxLength": 1100,
"description": "the event's id, from list_events"
}
},
"required": [
"id"
]
}free_busyRead only
When people are busy between two times (default: the next 7 days), from their calendars. Works for colleagues whose calendars share free/busy.
Approval: Uses your workspace and hire approval settings.
| Argument | Type | Details |
|---|---|---|
peopleOptional | string[] | email addresses (or calendar IDs); primary for the account's own calendarDefault: ["primary"] |
fromOptional | string | from (default now): 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day |
toOptional | string | until (default a week after from): 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"people": {
"default": [
"primary"
],
"maxItems": 50,
"type": "array",
"items": {
"type": "string",
"minLength": 3,
"maxLength": 320
},
"description": "email addresses (or calendar IDs); primary for the account's own calendar"
},
"from": {
"type": "string",
"minLength": 10,
"maxLength": 40,
"description": "from (default now): 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day"
},
"to": {
"type": "string",
"minLength": 10,
"maxLength": 40,
"description": "until (default a week after from): 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day"
}
}
}find_timeRead only
Suggest times when everyone is free for a meeting of a given length, inside working hours on working days, in the calendar's time zone.
Approval: Uses your workspace and hire approval settings.
| Argument | Type | Details |
|---|---|---|
peopleOptional | string[] | email addresses (or calendar IDs); primary for the account's own calendarDefault: ["primary"] |
duration_minutesOptional | integer | See input schema below.Default: 30 |
fromOptional | string | earliest (default now): 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day |
toOptional | string | latest (default a week after from): 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day |
day_startOptional | string | working hours start, HH:MMDefault: "09:00" |
day_endOptional | string | working hours end, HH:MMDefault: "18:00" |
daysOptional | "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun"[] | working daysDefault: ["mon","tue","wed","thu","fri"] |
limitOptional | integer | See input schema below.Default: 5 |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"people": {
"default": [
"primary"
],
"maxItems": 50,
"type": "array",
"items": {
"type": "string",
"minLength": 3,
"maxLength": 320
},
"description": "email addresses (or calendar IDs); primary for the account's own calendar"
},
"duration_minutes": {
"default": 30,
"type": "integer",
"minimum": 10,
"maximum": 480
},
"from": {
"type": "string",
"minLength": 10,
"maxLength": 40,
"description": "earliest (default now): 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day"
},
"to": {
"type": "string",
"minLength": 10,
"maxLength": 40,
"description": "latest (default a week after from): 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day"
},
"day_start": {
"default": "09:00",
"description": "working hours start, HH:MM",
"type": "string",
"pattern": "^\\d{2}:\\d{2}$"
},
"day_end": {
"default": "18:00",
"description": "working hours end, HH:MM",
"type": "string",
"pattern": "^\\d{2}:\\d{2}$"
},
"days": {
"default": [
"mon",
"tue",
"wed",
"thu",
"fri"
],
"description": "working days",
"minItems": 1,
"type": "array",
"items": {
"type": "string",
"enum": [
"mon",
"tue",
"wed",
"thu",
"fri",
"sat",
"sun"
]
}
},
"limit": {
"default": 5,
"type": "integer",
"minimum": 1,
"maximum": 10
}
}
}create_eventRead and write
Put an event on a calendar, optionally inviting people (they get an invitation) and adding a video call. Inviting people outside the team commits the business to them, so such a call waits for a person's approval: Roster asks for you and makes it, exactly as you called it, once they approve.
Approval: when it invites someone outside your team, or moves an event they're on.
| Argument | Type | Details |
|---|---|---|
calendarOptional | string | calendar name or ID; primary is the account's ownDefault: "primary" |
titleRequired | string | See input schema below. |
startRequired | string | start: 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day |
endOptional | string | end; or give duration_minutes |
duration_minutesOptional | integer | See input schema below. |
locationOptional | string | See input schema below. |
descriptionOptional | string | See input schema below. |
attendeesOptional | string[] | email addresses to inviteDefault: [] |
video_callOptional | boolean | add a video call linkDefault: false |
notifyOptional | boolean | send invitations by emailDefault: true |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"calendar": {
"default": "primary",
"description": "calendar name or ID; primary is the account's own",
"type": "string",
"minLength": 1,
"maxLength": 300
},
"title": {
"type": "string",
"minLength": 1,
"maxLength": 300
},
"start": {
"type": "string",
"minLength": 10,
"maxLength": 40,
"description": "start: 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day"
},
"end": {
"description": "end; or give duration_minutes",
"type": "string",
"minLength": 10,
"maxLength": 40
},
"duration_minutes": {
"type": "integer",
"minimum": 5,
"maximum": 1440
},
"location": {
"type": "string",
"minLength": 1,
"maxLength": 500
},
"description": {
"type": "string",
"minLength": 1,
"maxLength": 8000
},
"attendees": {
"default": [],
"description": "email addresses to invite",
"maxItems": 100,
"type": "array",
"items": {
"type": "string",
"minLength": 3,
"maxLength": 320
}
},
"video_call": {
"default": false,
"description": "add a video call link",
"type": "boolean"
},
"notify": {
"default": true,
"description": "send invitations by email",
"type": "boolean"
}
},
"required": [
"title",
"start"
]
}update_eventRead and write
Change an event: its title, time, place, notes, or who's invited (attendees are told when notify is on). Inviting people outside the team commits the business to them, so such a call waits for a person's approval: Roster asks for you and makes it, exactly as you called it, once they approve.
Approval: when it invites someone outside your team, or moves an event they're on.
| Argument | Type | Details |
|---|---|---|
calendarOptional | string | calendar name or ID; primary is the account's ownDefault: "primary" |
idRequired | string | the event's id, from list_events |
titleOptional | string | See input schema below. |
startOptional | string | new start: 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day |
endOptional | string | new end: 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day |
locationOptional | string | See input schema below. |
descriptionOptional | string | See input schema below. |
add_attendeesOptional | string[] | See input schema below.Default: [] |
remove_attendeesOptional | string[] | See input schema below.Default: [] |
notifyOptional | boolean | tell attendees by emailDefault: true |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"calendar": {
"default": "primary",
"description": "calendar name or ID; primary is the account's own",
"type": "string",
"minLength": 1,
"maxLength": 300
},
"id": {
"type": "string",
"minLength": 1,
"maxLength": 1100,
"description": "the event's id, from list_events"
},
"title": {
"type": "string",
"minLength": 1,
"maxLength": 300
},
"start": {
"type": "string",
"minLength": 10,
"maxLength": 40,
"description": "new start: 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day"
},
"end": {
"type": "string",
"minLength": 10,
"maxLength": 40,
"description": "new end: 2026-11-10T14:00 in the calendar's time zone, or 2026-11-10 for a day"
},
"location": {
"type": "string",
"maxLength": 500
},
"description": {
"type": "string",
"maxLength": 8000
},
"add_attendees": {
"default": [],
"maxItems": 100,
"type": "array",
"items": {
"type": "string",
"minLength": 3,
"maxLength": 320
}
},
"remove_attendees": {
"default": [],
"maxItems": 100,
"type": "array",
"items": {
"type": "string",
"minLength": 3,
"maxLength": 320
}
},
"notify": {
"default": true,
"description": "tell attendees by email",
"type": "boolean"
}
},
"required": [
"id"
]
}cancel_eventRead and write
Cancel an event this calendar organised, telling attendees (with an optional note) when notify is on. For someone else's event, decline it with respond_to_invitation instead.
Approval: Uses your workspace and hire approval settings.
| Argument | Type | Details |
|---|---|---|
calendarOptional | string | calendar name or ID; primary is the account's ownDefault: "primary" |
idRequired | string | the event's id, from list_events |
noteOptional | string | a short message to attendees, where the calendar supports it |
notifyOptional | boolean | See input schema below.Default: true |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"calendar": {
"default": "primary",
"description": "calendar name or ID; primary is the account's own",
"type": "string",
"minLength": 1,
"maxLength": 300
},
"id": {
"type": "string",
"minLength": 1,
"maxLength": 1100,
"description": "the event's id, from list_events"
},
"note": {
"description": "a short message to attendees, where the calendar supports it",
"type": "string",
"minLength": 1,
"maxLength": 2000
},
"notify": {
"default": true,
"type": "boolean"
}
},
"required": [
"id"
]
}respond_to_invitationRead and write
Accept, decline or say maybe to an event someone else invited this calendar to, with an optional note to the organiser.
Approval: Uses your workspace and hire approval settings.
| Argument | Type | Details |
|---|---|---|
calendarOptional | string | calendar name or ID; primary is the account's ownDefault: "primary" |
idRequired | string | the event's id, from list_events |
responseRequired | "accept" | "decline" | "maybe" | See input schema below. |
noteOptional | string | See input schema below. |
Full input schema (JSON)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"calendar": {
"default": "primary",
"description": "calendar name or ID; primary is the account's own",
"type": "string",
"minLength": 1,
"maxLength": 300
},
"id": {
"type": "string",
"minLength": 1,
"maxLength": 1100,
"description": "the event's id, from list_events"
},
"response": {
"type": "string",
"enum": [
"accept",
"decline",
"maybe"
]
},
"note": {
"type": "string",
"minLength": 1,
"maxLength": 2000
}
},
"required": [
"id",
"response"
]
}Watches and incoming events
- This integration supports a responsibility that watches for changes. The guide above explains what it checks and how to limit it.
- No incoming webhook handler is declared for this integration.
Limits and troubleshooting
- Permission errors: check both the connected account's rights and the hire's resource and read/write limits.
- Expired or revoked access: reconnect or replace the key on the connection.
- Rate limits and unavailable services can interrupt work. The hire's timecard records the outcome.
- The action schema describes accepted inputs. It does not grant additional access or bypass approvals.