Loading...
Loading...
Read and manage Google Calendar events / agenda / free-busy / invitations via the Calendar v3 REST API. Use when the user mentions Google Calendar events, today's agenda, this week's meetings, finding conflicts, listing invitations, checking free time, or scheduling / rescheduling / cancelling a meeting.
npx skill4agent add acedatacloud/skills google-calendarcurl + jq$GOOGLE_CALENDAR_TOKENAuthorization: Bearer $GOOGLE_CALENDAR_TOKENcalendar.readonlyopenid email profilecalendar{"error": {"code": 401|403|..., "message": "..."}}401403 insufficientPermissionscalendar.readonlyusers/me/calendarListusers/me/settings/timezonesendUpdatesgwsgwsgoogleworkspace+gws+agendaSettings.timezone+insertnpm install -g @googleworkspace/cli # or: brew install googleworkspace-cli
# Pre-built binaries also at https://github.com/googleworkspace/cli/releases
gws --versiongwsGOOGLE_WORKSPACE_CLI_TOKEN$GOOGLE_CALENDAR_TOKENgwsexport GOOGLE_WORKSPACE_CLI_TOKEN="$GOOGLE_CALENDAR_TOKEN"# Today on the primary calendar, in the account's own timezone
gws calendar +agenda
# Today / week, with explicit overrides
gws calendar +agenda --today --tz America/New_York
gws calendar +agenda --range week
# Create an event (auto-shapes attendees + sendUpdates JSON)
gws calendar +insert --calendar primary \
--json '{
"summary":"Standup",
"start":{"dateTime":"2026-05-06T10:00:00-04:00"},
"end": {"dateTime":"2026-05-06T10:30:00-04:00"},
"attendees":[{"email":"alice@example.com"}]
}' \
--params '{"sendUpdates":"all"}'+insertcalendar403 insufficientPermissions# Account confirmation + calendars the user can read
curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
"https://www.googleapis.com/calendar/v3/users/me/calendarList" \
| jq '.items[] | {id, summary, primary, accessRole, timeZone}'
# User's preferred display zone (use this when formatting times)
curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
"https://www.googleapis.com/calendar/v3/users/me/settings/timezone" \
| jq -r .valueidprimaryteam-monday@group.calendar.google.comcalendars/{id}/eventsTZ=$(curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
"https://www.googleapis.com/calendar/v3/users/me/settings/timezone" | jq -r .value)
TODAY=$(TZ=$TZ date +%Y-%m-%d)
START="${TODAY}T00:00:00Z"
END="${TODAY}T23:59:59Z"
curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
--get "https://www.googleapis.com/calendar/v3/calendars/primary/events" \
--data-urlencode "timeMin=$START" \
--data-urlencode "timeMax=$END" \
--data-urlencode 'singleEvents=true' \
--data-urlencode 'orderBy=startTime' \
--data-urlencode "timeZone=$TZ" \
| jq '.items[] | {summary, start: (.start.dateTime // .start.date), end: (.end.dateTime // .end.date), location, attendees: [.attendees[]?.email], hangout: .hangoutLink, status, htmlLink}'singleEvents=trueTZ=$(curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
"https://www.googleapis.com/calendar/v3/users/me/settings/timezone" | jq -r .value)
# Bash date math: Monday-of-this-week
MON=$(TZ=$TZ date -d "$(TZ=$TZ date +%Y-%m-%d) -$(($(TZ=$TZ date +%u) - 1)) days" +%Y-%m-%d 2>/dev/null \
|| TZ=$TZ date -v-mondayw +%Y-%m-%d) # macOS fallback
SUN=$(TZ=$TZ date -d "$MON +6 days" +%Y-%m-%d 2>/dev/null \
|| TZ=$TZ date -v+6d -j -f %Y-%m-%d "$MON" +%Y-%m-%d)
curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
--get "https://www.googleapis.com/calendar/v3/calendars/primary/events" \
--data-urlencode "timeMin=${MON}T00:00:00Z" \
--data-urlencode "timeMax=${SUN}T23:59:59Z" \
--data-urlencode 'singleEvents=true' \
--data-urlencode 'orderBy=startTime' \
| jq -r '.items[] | "\(.start.dateTime // .start.date)\t\(.summary)\t\((.attendees // []) | length) attendees"'Q='quarterly review'
curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
--get "https://www.googleapis.com/calendar/v3/calendars/primary/events" \
--data-urlencode "q=$Q" \
--data-urlencode 'singleEvents=true' \
--data-urlencode 'maxResults=20' \
| jq '.items[] | {start: .start.dateTime, summary, htmlLink}'qEVENT_ID='abc123def4567890ghijklmnop'
curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
"https://www.googleapis.com/calendar/v3/calendars/primary/events/$EVENT_ID" \
| jq '{summary, start, end, location, description, attendees, organizer, hangoutLink, conferenceData}'TZ=$(curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
"https://www.googleapis.com/calendar/v3/users/me/settings/timezone" | jq -r .value)
NOW=$(TZ=$TZ date -u +%Y-%m-%dT%H:%M:%SZ)
NEXT_WEEK=$(TZ=$TZ date -u -d "+7 days" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
|| TZ=$TZ date -u -v+7d +%Y-%m-%dT%H:%M:%SZ)
cat > /tmp/freebusy.json <<JSON
{
"timeMin": "$NOW",
"timeMax": "$NEXT_WEEK",
"timeZone": "$TZ",
"items": [
{"id": "primary"},
{"id": "team-monday@group.calendar.google.com"}
]
}
JSON
curl -sS -X POST -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
-H 'Content-Type: application/json' \
--data @/tmp/freebusy.json \
"https://www.googleapis.com/calendar/v3/freeBusy" \
| jq '.calendars'{"busy": [{"start": "...", "end": "..."}]}CAL_ID='team-monday@group.calendar.google.com'
# URL-encode the @ in the path
CAL_ENCODED=$(printf %s "$CAL_ID" | jq -sRr @uri)
curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
--get "https://www.googleapis.com/calendar/v3/calendars/$CAL_ENCODED/events" \
--data-urlencode 'singleEvents=true' \
--data-urlencode 'orderBy=startTime' \
--data-urlencode 'maxResults=20' \
| jq '.items[] | {start: .start.dateTime, summary}'PAGE_TOKEN=''
while : ; do
RESP=$(curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
--get "https://www.googleapis.com/calendar/v3/calendars/primary/events" \
--data-urlencode 'singleEvents=true' \
--data-urlencode 'orderBy=startTime' \
--data-urlencode 'maxResults=250' \
${PAGE_TOKEN:+--data-urlencode "pageToken=$PAGE_TOKEN"})
echo "$RESP" | jq -c '.items[]?'
PAGE_TOKEN=$(echo "$RESP" | jq -r '.nextPageToken // empty')
[ -z "$PAGE_TOKEN" ] && break
donecalendarcalendar.readonly403 insufficientPermissionsTZ=$(curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
"https://www.googleapis.com/calendar/v3/users/me/settings/timezone" | jq -r .value)
cat > /tmp/_cal_event.json <<JSON
{
"summary": "Sync — Q2 OKR review",
"location": "Online",
"description": "Drafted by AceDataCloud.",
"start": {"dateTime": "2026-05-12T10:00:00", "timeZone": "$TZ"},
"end": {"dateTime": "2026-05-12T10:30:00", "timeZone": "$TZ"},
"attendees": [
{"email": "alice@example.com"},
{"email": "bob@example.com"}
],
"reminders": {"useDefault": true},
"conferenceData": {
"createRequest": {
"requestId": "meet-$(date +%s)",
"conferenceSolutionKey": {"type": "hangoutsMeet"}
}
}
}
JSON
# sendUpdates: 'all' = email all attendees; 'externalOnly' = only non-org; 'none' = silent
curl -sS -X POST -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
-H 'Content-Type: application/json' \
--data @/tmp/_cal_event.json \
"https://www.googleapis.com/calendar/v3/calendars/primary/events?conferenceDataVersion=1&sendUpdates=all" \
| jq '{id, htmlLink, hangoutLink, summary, start, end, attendees}'conferenceDataTZ=$(curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
"https://www.googleapis.com/calendar/v3/users/me/settings/timezone" | jq -r .value)
cat > /tmp/_cal_recur.json <<JSON
{
"summary": "Weekly 1:1",
"start": {"dateTime": "2026-05-12T15:00:00", "timeZone": "$TZ"},
"end": {"dateTime": "2026-05-12T15:30:00", "timeZone": "$TZ"},
"recurrence": ["RRULE:FREQ=WEEKLY;BYDAY=TU;COUNT=12"]
}
JSON
curl -sS -X POST -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
-H 'Content-Type: application/json' \
--data @/tmp/_cal_recur.json \
"https://www.googleapis.com/calendar/v3/calendars/primary/events" \
| jq '{id, recurrence, summary}'FREQ=DAILYFREQ=WEEKLY;BYDAY=MO,WE,FRFREQ=MONTHLY;BYMONTHDAY=15UNTIL=20261231T235959ZCOUNT=12EVENT_ID='abc123def4567890ghijklmnop'
curl -sS -X PATCH -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"location":"Conference Room 4","description":"Now in-person."}' \
"https://www.googleapis.com/calendar/v3/calendars/primary/events/$EVENT_ID?sendUpdates=all" \
| jq '{id, summary, location, description}'PATCHPUTPATCHEVENT_ID='abc123def4567890ghijklmnop'
TZ=$(curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
"https://www.googleapis.com/calendar/v3/users/me/settings/timezone" | jq -r .value)
cat > /tmp/_cal_resched.json <<JSON
{
"start": {"dateTime": "2026-05-12T14:00:00", "timeZone": "$TZ"},
"end": {"dateTime": "2026-05-12T14:30:00", "timeZone": "$TZ"}
}
JSON
curl -sS -X PATCH -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
-H 'Content-Type: application/json' \
--data @/tmp/_cal_resched.json \
"https://www.googleapis.com/calendar/v3/calendars/primary/events/$EVENT_ID?sendUpdates=all" \
| jq '{id, summary, start, end}'EVENT_ID='abc123def4567890ghijklmnop'
CURRENT=$(curl -sS -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
"https://www.googleapis.com/calendar/v3/calendars/primary/events/$EVENT_ID?fields=attendees" \
| jq '.attendees // []')
NEW=$(echo "$CURRENT" | jq '. + [{"email":"carol@example.com"}]')
curl -sS -X PATCH -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
-H 'Content-Type: application/json' \
--data "{\"attendees\": $NEW}" \
"https://www.googleapis.com/calendar/v3/calendars/primary/events/$EVENT_ID?sendUpdates=all" \
| jq '{id, attendees}'EVENT_ID='abc123def4567890ghijklmnop'
curl -sS -X DELETE -H "Authorization: Bearer $GOOGLE_CALENDAR_TOKEN" \
"https://www.googleapis.com/calendar/v3/calendars/primary/events/$EVENT_ID?sendUpdates=all" \
-o /dev/null -w 'HTTP %{http_code}\n'204events.instancesDELETEEVENT_ID_YYYYMMDDTHHMMSSZ| HTTP | meaning | what to tell the user |
|---|---|---|
| token expired / revoked | "Reconnect the Google Calendar connector on the Connections page." |
| write scope missing | "This action needs the Calendar read+write scope, but only |
| calendar id not visible to this account | check |
| wrong event / calendar id | double-check the id and try |
| recurring event id collision | append a UUID to your |
| quota / throttling | back off ~5s, then retry once. |
$GOOGLE_CALENDAR_TOKEN