Google Calendar
The io.b3os/google-calendar pack: 6 actions with call snippets, fixture outputs, connection setup and error codes, generated from version 1.0.0.
io.b3os/google-calendar 1.0.0 · calendar, scheduling, productivity · MIT
Read and schedule with Google Calendar using your own Google OAuth token: list calendars, list and search events in a time window, read one event, find free and busy time across calendars, create events with attendees, and delete events. Needs calendar.readonly for reading and calendar.events to create or delete.
Every call's result carries upstream_status and timing: validate_ms, inject_ms, render_ms, upstream_ms, map_ms and total_ms. upstream is the service's time; the others are B3OS's own (call actions). The snippets below show no timing values, because a fixture records none.
Connection
google, type oauth2: Google account (Calendar).
An OAuth access token for your Google account from your own Google Cloud OAuth client, with the scopes listed here. Store the refresh_token, client_id and client_secret with it so the vault can refresh the token (access tokens last an hour); B3OS never uses a Google client of its own.
How to get one: developers.google.com.
Scopes the pack's actions use: https://www.googleapis.com/auth/calendar.readonly, https://www.googleapis.com/auth/calendar.events. The manifest names them per connection, not per action.
The credential is sent only to www.googleapis.com.
Store the credential once as a vault connection for this pack and key, then name its id in a call's connections. Leave the key out and B3OS uses the project's newest connection for the pack and key without a subject (connections).
Hosts it reaches: www.googleapis.com.
Actions
| Action | Effect | MCP tool | Title |
|---|---|---|---|
list-calendars | read | google-calendar__list-calendars | List calendars |
list-events | read | google-calendar__list-events | List events |
get-event | read | google-calendar__get-event | Get event |
find-free-busy | read | google-calendar__find-free-busy | Find busy time |
create-event | write | google-calendar__create-event | Create event |
delete-event | irreversible | google-calendar__delete-event | Delete event |
list-calendars
List calendars. Calendars in the user's list with id, name, time zone, access role (owner, writer, reader, freeBusyReader) and whether it is the primary one.
Ref io.b3os/google-calendar/list-calendars@1.0.0 · effect read · connection google · MCP tool google-calendar__list-calendars
bashcurl -s https://beta-api.b3os.org/v1/actions/call \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "ref": "io.b3os/google-calendar/list-calendars@1", "input": {}, "connections": {"google": "conn_01m5t6cbgdjs1ketgcqwa2sne8"} }'
tsimport { B3OS } from "@b3os/sdk";const b3os = new B3OS();const result = await b3os.actions.call({ ref: "io.b3os/google-calendar/list-calendars@1", input: {}, connections: { google: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },});console.log(result.output);
pythonfrom b3os import B3OSb3os = B3OS()result = b3os.actions.call({ "ref": "io.b3os/google-calendar/list-calendars@1", "input": {}, "connections": {"google": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},})print(result["output"])
json{ "method": "tools/call", "params": {"name": "google-calendar__list-calendars", "arguments": {}}}
Expose the pack as tools with --packs google-calendar on the local server or ?packs=google-calendar on the remote URL, and the agent calls google-calendar__list-calendars with the action's input. The tool uses your newest connection for each key without a subject. Without exposing the pack, actions_call takes the same input with ref.
Output, labeled fixture: List calendars (documented response shape, example values). From the bundle's fixtures/list-calendars.json, not a live call.
json{ "calendars": [ { "id": "mona@acme.example", "summary": "mona@acme.example", "time_zone": "America/Los_Angeles", "access_role": "owner", "primary": true }, { "id": "c_1a2b3c4d5e6f@group.calendar.google.com", "summary": "On-call", "time_zone": "UTC", "access_role": "writer", "primary": false } ]}
When the service answers with an error, the call fails with upstream_error and pack_error.code is one of:
pack_error.code | Service status | Retryable | Message |
|---|---|---|---|
not_found | 404 | no | built from the service's response |
connection_unauthorized | 401 | no | Google rejected the access token (expired or revoked); refresh or reconnect it. |
rate_limited | 429, 403 | yes | built from the service's response |
forbidden | 403 | no | built from the service's response |
invalid_request | 400 | no | built from the service's response |
conflict | 409 | no | built from the service's response |
list-events
List events. Events in a time window, recurring events expanded into single occurrences and sorted by start time, optionally matching free text. Returns summary, time, location, Meet link, organizer and attendee responses. Page with page_token.
Ref io.b3os/google-calendar/list-events@1.0.0 · effect read · connection google · MCP tool google-calendar__list-events
bashcurl -s https://beta-api.b3os.org/v1/actions/call \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "ref": "io.b3os/google-calendar/list-events@1", "input": { "time_min": "2026-10-27T00:00:00-07:00", "time_max": "2026-10-28T00:00:00-07:00", "max_results": 2 }, "connections": {"google": "conn_01m5t6cbgdjs1ketgcqwa2sne8"} }'
tsimport { B3OS } from "@b3os/sdk";const b3os = new B3OS();const result = await b3os.actions.call({ ref: "io.b3os/google-calendar/list-events@1", input: { time_min: "2026-10-27T00:00:00-07:00", time_max: "2026-10-28T00:00:00-07:00", max_results: 2, }, connections: { google: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },});console.log(result.output);
pythonfrom b3os import B3OSb3os = B3OS()result = b3os.actions.call({ "ref": "io.b3os/google-calendar/list-events@1", "input": { "time_min": "2026-10-27T00:00:00-07:00", "time_max": "2026-10-28T00:00:00-07:00", "max_results": 2, }, "connections": {"google": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},})print(result["output"])
json{ "method": "tools/call", "params": { "name": "google-calendar__list-events", "arguments": { "time_min": "2026-10-27T00:00:00-07:00", "time_max": "2026-10-28T00:00:00-07:00", "max_results": 2 } }}
Expose the pack as tools with --packs google-calendar on the local server or ?packs=google-calendar on the remote URL, and the agent calls google-calendar__list-events with the action's input. The tool uses your newest connection for each key without a subject. Without exposing the pack, actions_call takes the same input with ref.
Output, labeled fixture: List events (documented response shape, example values). From the bundle's fixtures/list-events.json, not a live call.
json{ "events": [ { "id": "7f1a2b3c4d5e6f7a8b9c0d1e2f", "status": "confirmed", "summary": "Release sync", "description": null, "location": "Room 4 / Meet", "start": "2026-10-27T09:00:00-07:00", "end": "2026-10-27T09:30:00-07:00", "all_day": false, "html_link": "https://www.google.com/calendar/event?eid=7f1a2b3c4d5e6f7a8b9c0d1e2f", "meet_link": "https://meet.google.com/abc-defg-hij", "organizer": "mona@acme.example", "attendees": [ {"email": "mona@acme.example", "response": "accepted"}, {"email": "hubot@acme.example", "response": "needsAction"} ], "recurring_event_id": null }, { "id": "a1b2c3d4e5f6a7b8c9d0e1f2a3", "status": "confirmed", "summary": "Offsite", "description": null, "location": "Room 4 / Meet", "start": "2026-10-27", "end": "2026-10-28", "all_day": true, "html_link": "https://www.google.com/calendar/event?eid=a1b2c3d4e5f6a7b8c9d0e1f2a3", "meet_link": null, "organizer": "mona@acme.example", "attendees": [], "recurring_event_id": null } ], "time_zone": "America/Los_Angeles", "next_page_token": null}
When the service answers with an error, the call fails with upstream_error and pack_error.code is one of:
pack_error.code | Service status | Retryable | Message |
|---|---|---|---|
not_found | 404 | no | built from the service's response |
connection_unauthorized | 401 | no | Google rejected the access token (expired or revoked); refresh or reconnect it. |
rate_limited | 429, 403 | yes | built from the service's response |
forbidden | 403 | no | built from the service's response |
invalid_request | 400 | no | built from the service's response |
conflict | 409 | no | built from the service's response |
get-event
Get event. One event by id with its time, description, location, Meet link, organizer and attendees.
Ref io.b3os/google-calendar/get-event@1.0.0 · effect read · connection google · MCP tool google-calendar__get-event
bashcurl -s https://beta-api.b3os.org/v1/actions/call \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "ref": "io.b3os/google-calendar/get-event@1", "input": {"event_id": "7f1a2b3c4d5e6f7a8b9c0d1e2f"}, "connections": {"google": "conn_01m5t6cbgdjs1ketgcqwa2sne8"} }'
tsimport { B3OS } from "@b3os/sdk";const b3os = new B3OS();const result = await b3os.actions.call({ ref: "io.b3os/google-calendar/get-event@1", input: { event_id: "7f1a2b3c4d5e6f7a8b9c0d1e2f" }, connections: { google: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },});console.log(result.output);
pythonfrom b3os import B3OSb3os = B3OS()result = b3os.actions.call({ "ref": "io.b3os/google-calendar/get-event@1", "input": {"event_id": "7f1a2b3c4d5e6f7a8b9c0d1e2f"}, "connections": {"google": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},})print(result["output"])
json{ "method": "tools/call", "params": { "name": "google-calendar__get-event", "arguments": {"event_id": "7f1a2b3c4d5e6f7a8b9c0d1e2f"} }}
Expose the pack as tools with --packs google-calendar on the local server or ?packs=google-calendar on the remote URL, and the agent calls google-calendar__get-event with the action's input. The tool uses your newest connection for each key without a subject. Without exposing the pack, actions_call takes the same input with ref.
Output, labeled fixture: Get event (documented response shape, example values). From the bundle's fixtures/get-event.json, not a live call.
json{ "id": "7f1a2b3c4d5e6f7a8b9c0d1e2f", "status": "confirmed", "summary": "Release sync", "description": "Agenda: v2.4.1 go/no-go", "location": "Room 4 / Meet", "start": "2026-10-27T09:00:00-07:00", "end": "2026-10-27T09:30:00-07:00", "all_day": false, "html_link": "https://www.google.com/calendar/event?eid=7f1a2b3c4d5e6f7a8b9c0d1e2f", "meet_link": "https://meet.google.com/abc-defg-hij", "organizer": "mona@acme.example", "attendees": [ {"email": "mona@acme.example", "response": "accepted"}, {"email": "hubot@acme.example", "response": "needsAction"} ], "recurring_event_id": null}
When the service answers with an error, the call fails with upstream_error and pack_error.code is one of:
pack_error.code | Service status | Retryable | Message |
|---|---|---|---|
not_found | 404 | no | built from the service's response |
connection_unauthorized | 401 | no | Google rejected the access token (expired or revoked); refresh or reconnect it. |
rate_limited | 429, 403 | yes | built from the service's response |
forbidden | 403 | no | built from the service's response |
invalid_request | 400 | no | built from the service's response |
conflict | 409 | no | built from the service's response |
find-free-busy
Find busy time. Busy intervals of up to 50 calendars (or people's primary calendars, by email) in a window, to find a time everyone is free. Calendars you cannot see come back with an error instead of intervals.
Ref io.b3os/google-calendar/find-free-busy@1.0.0 · effect read · connection google · MCP tool google-calendar__find-free-busy
bashcurl -s https://beta-api.b3os.org/v1/actions/call \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "ref": "io.b3os/google-calendar/find-free-busy@1", "input": { "time_min": "2026-10-27T09:00:00-07:00", "time_max": "2026-10-27T17:00:00-07:00", "calendars": ["mona@acme.example", "ceo@acme.example"] }, "connections": {"google": "conn_01m5t6cbgdjs1ketgcqwa2sne8"} }'
tsimport { B3OS } from "@b3os/sdk";const b3os = new B3OS();const result = await b3os.actions.call({ ref: "io.b3os/google-calendar/find-free-busy@1", input: { time_min: "2026-10-27T09:00:00-07:00", time_max: "2026-10-27T17:00:00-07:00", calendars: ["mona@acme.example", "ceo@acme.example"], }, connections: { google: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },});console.log(result.output);
pythonfrom b3os import B3OSb3os = B3OS()result = b3os.actions.call({ "ref": "io.b3os/google-calendar/find-free-busy@1", "input": { "time_min": "2026-10-27T09:00:00-07:00", "time_max": "2026-10-27T17:00:00-07:00", "calendars": ["mona@acme.example", "ceo@acme.example"], }, "connections": {"google": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},})print(result["output"])
json{ "method": "tools/call", "params": { "name": "google-calendar__find-free-busy", "arguments": { "time_min": "2026-10-27T09:00:00-07:00", "time_max": "2026-10-27T17:00:00-07:00", "calendars": ["mona@acme.example", "ceo@acme.example"] } }}
Expose the pack as tools with --packs google-calendar on the local server or ?packs=google-calendar on the remote URL, and the agent calls google-calendar__find-free-busy with the action's input. The tool uses your newest connection for each key without a subject. Without exposing the pack, actions_call takes the same input with ref.
Output, labeled fixture: Find busy time (documented response shape, example values). From the bundle's fixtures/find-free-busy.json, not a live call.
json{ "calendars": [ { "id": "mona@acme.example", "busy": [ { "start": "2026-10-27T16:00:00Z", "end": "2026-10-27T16:30:00Z" } ], "error": null }, {"id": "ceo@acme.example", "busy": [], "error": "notFound"} ]}
When the service answers with an error, the call fails with upstream_error and pack_error.code is one of:
pack_error.code | Service status | Retryable | Message |
|---|---|---|---|
not_found | 404 | no | built from the service's response |
connection_unauthorized | 401 | no | Google rejected the access token (expired or revoked); refresh or reconnect it. |
rate_limited | 429, 403 | yes | built from the service's response |
forbidden | 403 | no | built from the service's response |
invalid_request | 400 | no | built from the service's response |
conflict | 409 | no | built from the service's response |
create-event
Create event. Create an event with a summary, start and end times (or all-day dates), optional description, location and attendees, optionally with a Google Meet link. send_updates controls invitation emails. Not idempotent.
Ref io.b3os/google-calendar/create-event@1.0.0 · effect write · connection google · MCP tool google-calendar__create-event
bashcurl -s https://beta-api.b3os.org/v1/actions/call \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Idempotency-Key: 7c1e2f6a-1d43-4a8e-b2a5-3f0c9d5e8b11" \ -H "Content-Type: application/json" \ -d '{ "ref": "io.b3os/google-calendar/create-event@1", "input": { "summary": "Release sync", "start": "2026-10-27T09:00:00-07:00", "end": "2026-10-27T09:30:00-07:00", "time_zone": "America/Los_Angeles", "attendees": ["hubot@acme.example"], "add_meet": true }, "connections": {"google": "conn_01m5t6cbgdjs1ketgcqwa2sne8"} }'
tsimport { B3OS } from "@b3os/sdk";const b3os = new B3OS();const result = await b3os.actions.call({ ref: "io.b3os/google-calendar/create-event@1", input: { summary: "Release sync", start: "2026-10-27T09:00:00-07:00", end: "2026-10-27T09:30:00-07:00", time_zone: "America/Los_Angeles", attendees: ["hubot@acme.example"], add_meet: true, }, connections: { google: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },}, { idempotencyKey: crypto.randomUUID() });console.log(result.output);
pythonimport uuidfrom b3os import B3OSb3os = B3OS()result = b3os.actions.call({ "ref": "io.b3os/google-calendar/create-event@1", "input": { "summary": "Release sync", "start": "2026-10-27T09:00:00-07:00", "end": "2026-10-27T09:30:00-07:00", "time_zone": "America/Los_Angeles", "attendees": ["hubot@acme.example"], "add_meet": True, }, "connections": {"google": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},}, idempotency_key=str(uuid.uuid4()))print(result["output"])
json{ "method": "tools/call", "params": { "name": "google-calendar__create-event", "arguments": { "summary": "Release sync", "start": "2026-10-27T09:00:00-07:00", "end": "2026-10-27T09:30:00-07:00", "time_zone": "America/Los_Angeles", "attendees": ["hubot@acme.example"], "add_meet": true } }}
Expose the pack as tools with --packs google-calendar on the local server or ?packs=google-calendar on the remote URL, and the agent calls google-calendar__create-event with the action's input. The tool uses your newest connection for each key without a subject. Without exposing the pack, actions_call takes the same input with ref.
Output, labeled fixture: Create event (documented response shape, example values). From the bundle's fixtures/create-event.json, not a live call.
json{ "id": "7f1a2b3c4d5e6f7a8b9c0d1e2f", "status": "confirmed", "summary": "Release sync", "description": null, "location": "Room 4 / Meet", "start": "2026-10-27T09:00:00-07:00", "end": "2026-10-27T09:30:00-07:00", "all_day": false, "html_link": "https://www.google.com/calendar/event?eid=7f1a2b3c4d5e6f7a8b9c0d1e2f", "meet_link": "https://meet.google.com/abc-defg-hij", "organizer": "mona@acme.example", "attendees": [ {"email": "mona@acme.example", "response": "accepted"}, {"email": "hubot@acme.example", "response": "needsAction"} ], "recurring_event_id": null}
When the service answers with an error, the call fails with upstream_error and pack_error.code is one of:
pack_error.code | Service status | Retryable | Message |
|---|---|---|---|
not_found | 404 | no | built from the service's response |
connection_unauthorized | 401 | no | Google rejected the access token (expired or revoked); refresh or reconnect it. |
rate_limited | 429, 403 | yes | built from the service's response |
forbidden | 403 | no | built from the service's response |
invalid_request | 400 | no | built from the service's response |
conflict | 409 | no | built from the service's response |
delete-event
Delete event. Delete an event (for a recurring series, the whole series unless the id is one occurrence). Attendees are emailed only if send_updates says so.
Ref io.b3os/google-calendar/delete-event@1.0.0 · effect irreversible · connection google · MCP tool google-calendar__delete-event
bashcurl -s https://beta-api.b3os.org/v1/actions/call \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Idempotency-Key: 7c1e2f6a-1d43-4a8e-b2a5-3f0c9d5e8b11" \ -H "Content-Type: application/json" \ -d '{ "ref": "io.b3os/google-calendar/delete-event@1", "input": {"event_id": "7f1a2b3c4d5e6f7a8b9c0d1e2f"}, "connections": {"google": "conn_01m5t6cbgdjs1ketgcqwa2sne8"} }'
tsimport { B3OS } from "@b3os/sdk";const b3os = new B3OS();const result = await b3os.actions.call({ ref: "io.b3os/google-calendar/delete-event@1", input: { event_id: "7f1a2b3c4d5e6f7a8b9c0d1e2f" }, connections: { google: "conn_01m5t6cbgdjs1ketgcqwa2sne8" },}, { idempotencyKey: crypto.randomUUID() });console.log(result.output);
pythonimport uuidfrom b3os import B3OSb3os = B3OS()result = b3os.actions.call({ "ref": "io.b3os/google-calendar/delete-event@1", "input": {"event_id": "7f1a2b3c4d5e6f7a8b9c0d1e2f"}, "connections": {"google": "conn_01m5t6cbgdjs1ketgcqwa2sne8"},}, idempotency_key=str(uuid.uuid4()))print(result["output"])
json{ "method": "tools/call", "params": { "name": "google-calendar__delete-event", "arguments": {"event_id": "7f1a2b3c4d5e6f7a8b9c0d1e2f"} }}
Expose the pack as tools with --packs google-calendar on the local server or ?packs=google-calendar on the remote URL, and the agent calls google-calendar__delete-event with the action's input. The tool uses your newest connection for each key without a subject. Without exposing the pack, actions_call takes the same input with ref.
Output, labeled fixture: Delete event (documented response shape, example values). From the bundle's fixtures/delete-event.json, not a live call.
json{"deleted": true, "event_id": "7f1a2b3c4d5e6f7a8b9c0d1e2f"}
When the service answers with an error, the call fails with upstream_error and pack_error.code is one of:
pack_error.code | Service status | Retryable | Message |
|---|---|---|---|
not_found | 404 | no | built from the service's response |
connection_unauthorized | 401 | no | Google rejected the access token (expired or revoked); refresh or reconnect it. |
rate_limited | 429, 403 | yes | built from the service's response |
forbidden | 403 | no | built from the service's response |
invalid_request | 400 | no | built from the service's response |
conflict | 409 | no | built from the service's response |