Google Calendar ExecModule
Overview
GoogleCalendarModule connects ValkyrAI workflows to Google Calendar API v3 through the native map I/O ExecModule ABI. Workflow Studio discovers it as GoogleCalendarModule; its OAuth access token resolves only from an encrypted IntegrationAccount.
The initial production operation set covers calendar discovery, bounded event reads, availability, and governed event mutations:
- list_calendars and get_calendar
- list_events, get_event, and list_instances
- free_busy
- create_event, update_event, quick_add_event, and delete_event
List operations follow only Google-provided opaque page tokens and never accept provider URLs. Read-only requests and free/busy queries can retry transient failures. Event creates, patches, quick-adds, and deletes run once so ambiguous timeouts cannot duplicate, reschedule, notify, or remove calendar activity.
Usage
- Enable Google Calendar API in a Google Cloud project.
- Complete OAuth consent and authorize the narrowest Calendar scopes needed by the selected operations.
- Store the current OAuth access token in the encrypted apiKey field of an IntegrationAccount.
- Optionally store the default calendar ID in IntegrationAccount.accountId; otherwise the module uses primary.
- Set the account status to READY and bind it through ExecModuleConfig.authConfig.integrationAccount.
- Use list_calendars or get_calendar to verify the intended calendar before mutations.
- For updates, provide an ETag from the latest event read when concurrency protection is required.
- Reconcile Google Calendar state before repeating any write after an ambiguous connection failure.
Workflow input cannot provide OAuth credentials or an arbitrary endpoint. Production routing is fixed to www.googleapis.com over HTTPS.
Inputs
| Name | Type | Requirement | Default | Description and constraints |
|---|---|---|---|---|
| operation | string | Required | None | One of the 10 documented operations. |
| calendarId | string | Calendar/event operations | IntegrationAccount.accountId or primary | Google Calendar ID, maximum 1,024 characters. |
| eventId | string | Event get/update/delete/instances | None | Opaque Google event ID using letters, digits, underscore, or hyphen. |
| event | object or JSON string | Event create/update | None | Allowlisted event fields, 512 KiB maximum, nesting limited to eight levels. |
| quickAddText | string | quick_add_event | None | Natural-language event description, 1–4,096 characters. |
| timeMin / timeMax | RFC3339 string | free_busy; optional list bound | None | Offset-bearing timestamps; timeMax must be later than timeMin. |
| calendarIds | string array | Optional free_busy | calendarId | One through 50 calendar or group IDs. |
| timeZone | string | Optional | Provider default | Valid IANA time-zone ID. |
| query | string | Optional list_events | None | Free-text event search, maximum 1,024 characters. |
| pageToken | string | Optional list continuation | None | Opaque Google continuation token, maximum 4,096 characters. |
| limit | integer | Optional | 100 | Maximum emitted resources, 1–10,000. |
| returnAll | boolean | Optional | false | Continue provider pages until exhaustion or the result cap. |
| sendUpdates | string | Event mutations | none | none, all, or externalOnly. |
| etag | string | Optional update/delete | None | If-Match value from a current event read. |
| confirmDelete | boolean | delete_event | false | Must be true before a delete request is sent. |
Event payloads accept only id, summary, description, location, start, end, attendees, recurrence, reminders, visibility, transparency, colorId, guestsCanInviteOthers, guestsCanModify, guestsCanSeeOtherGuests, extendedProperties, source, and attachments.
Timed events require both start.dateTime and end.dateTime as offset-bearing RFC3339 timestamps. All-day events require both start.date and end.date in YYYY-MM-DD form. Attendee email addresses, recurrence lines, array sizes, text lengths, and nested fields are validated before the provider request.
Outputs
| Name | Type | When present | Description |
|---|---|---|---|
| status | string | Always | success or error. |
| operation | string | Always | Normalized operation name. |
| resourceStatus | string | Provider returns status | Google Calendar resource status, such as confirmed or cancelled. |
| attempts | integer | Always | Provider attempts across all pages. |
| httpStatus | integer | Provider responded | Last Google Calendar HTTP status. |
| data | object | Single-resource/write/free-busy success | Calendar, event, deletion receipt, or free/busy response. |
| id / htmlLink | string | Provider returns field | Safe convenience event identity and browser link. |
| items / count | array / integer | List success | Bounded resources and emitted count. |
| hasMore | boolean | List success | Whether another provider page exists. |
| nextPageToken | string | Another page exists | Opaque continuation token. |
| error | object | Failure | Safe code, message, optional HTTP status, and retryable flag. |
OAuth tokens and authorization headers never enter outputs. Provider error text passes through ValkyrAI's sensitive-data redaction policy.
IntegrationAccount Requirements
| Setting | Requirement |
|---|---|
| Provider | Google OAuth 2.0 application with Calendar API enabled |
| Read scopes | calendar.calendarlist.readonly, calendars.readonly, and events.readonly as needed |
| Availability scope | calendar.freebusy or calendar.events.freebusy |
| Write scope | calendar.events, calendar.events.owned, or calendar when broader calendar management is intentionally required |
| accountId | Optional default calendar ID; primary is used when absent |
| apiKey | Current OAuth access token in an encrypted SecureField |
| password | Legacy encrypted token fallback only; prefer apiKey |
| status | READY or legacy unset status; CLOSED and ERROR fail closed |
OAuth authorization-code exchange, refresh-token storage, access-token refresh, revocation, and rotation belong to the platform IntegrationAccount lifecycle. Workflow inputs never handle OAuth secrets.
Configuration
{
"version": "1.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:google-calendar-operations"
},
"retryPolicy": {
"maxAttempts": 3,
"backoffStrategy": "EXPONENTIAL",
"initialDelayMs": 1000,
"maxDelayMs": 60000,
"jitter": false
},
"executionConfig": {"timeoutMs": 30000},
"payloadConfig": {
"parameters": "{\"operation\":\"list_events\",\"calendarId\":\"primary\",\"limit\":100}"
}
}
The integration-account value is symbolic. Persisted workflows use the generated relationship rather than a plaintext token or calendar identifier.
Operations
| Operation | Google Calendar behavior | Side effect |
|---|---|---|
| list_calendars | Lists calendars subscribed by the authenticated user. | Read-only; safe retries and bounded pagination. |
| get_calendar | Reads one calendar's metadata. | Read-only; safe retries. |
| list_events | Lists expanded events ordered by start time with optional time/search filters. | Read-only; safe retries and bounded pagination. |
| get_event | Reads one event by ID. | Read-only; safe retries. |
| list_instances | Lists instances of one recurring event. | Read-only; safe retries and bounded pagination. |
| free_busy | Queries busy windows for one through 50 calendars or groups. | Read-only POST; safe retries. |
| create_event | Inserts one allowlisted timed or all-day event. | Creates an event and may notify guests; single attempt. |
| update_event | Applies a partial event patch and optional If-Match guard. | Mutates an event and may notify guests; single attempt. |
| quick_add_event | Creates one event from bounded natural-language text. | Creates an event and may notify guests; single attempt. |
| delete_event | Deletes one event after explicit confirmation. | Destructive and potentially notifying; single attempt. |
Errors and Failure Modes
| Code | Typical cause | Retryable | Resolution |
|---|---|---|---|
| VALIDATION_ERROR | Missing/invalid ID, time, time zone, event, attendee, recurrence, deletion guard, or bound. | No | Correct input; no unsafe request was sent. |
| UNSUPPORTED_OPERATION | Unknown operation. | No | Select a documented operation. |
| INTEGRATION_ACCOUNT_REQUIRED | No bound account. | No | Bind a Google Calendar IntegrationAccount. |
| INTEGRATION_ACCOUNT_NOT_READY | Account is closed or in error. | No | Repair or reconnect the account. |
| CREDENTIAL_MISSING | Bound account has no decryptable access token. | No | Refresh or re-authorize the IntegrationAccount. |
| GOOGLE_CALENDAR_HTTP_400 | Google rejected a field, time, recurrence, or request combination. | No | Correct the safe validation detail. |
| GOOGLE_CALENDAR_HTTP_401 / 403 | Token expired, scope missing, or calendar access denied. | No | Re-authorize with the narrow required scopes. |
| GOOGLE_CALENDAR_HTTP_404 | Calendar or event is absent or invisible. | No | Re-read/list using the same account. |
| GOOGLE_CALENDAR_HTTP_409 / 412 | Event ID conflict or stale If-Match ETag. | No | Read current state and rebuild the mutation. |
| GOOGLE_CALENDAR_HTTP_429 / 5xx | Rate limit or transient provider failure. | Yes for reads | Honor Retry-After; reconcile writes before repeating them. |
| NETWORK_ERROR | Timeout, DNS, TLS, or connectivity failure. | Yes for reads | Verify connectivity and provider state. |
| RESPONSE_TOO_LARGE | Response exceeded 5 MiB. | No | Lower the limit or narrow the time range. |
| INVALID_PROVIDER_RESPONSE | A list response returned a non-array items field. | No | Verify Google API compatibility and request context. |
Example
Create one approved customer activation review:
{
"operation": "create_event",
"calendarId": "primary",
"sendUpdates": "all",
"event": {
"summary": "Customer activation review",
"description": "Review onboarding progress and unblock production activation.",
"location": "Google Meet",
"start": {
"dateTime": "2026-08-10T10:00:00-07:00",
"timeZone": "America/Los_Angeles"
},
"end": {
"dateTime": "2026-08-10T10:30:00-07:00",
"timeZone": "America/Los_Angeles"
},
"attendees": [
{"email": "customer@example.com"}
]
}
}
Expected result:
{
"status": "success",
"operation": "create_event",
"attempts": 1,
"httpStatus": 200,
"id": "event123",
"data": {
"id": "event123",
"status": "confirmed"
}
}
Notes
- Pagination: calendar lists use pages of at most 250 and event lists use pages of at most 2,500. Total output is capped at 10,000. Only opaque nextPageToken values from Google are followed.
- Rate limits: GET requests and free/busy queries retry HTTP 408, 429, 500, 502, 503, and 504 according to RetryPolicy, including numeric or HTTP-date Retry-After values. Mutations never retry automatically.
- API limits: request bodies are capped at 512 KiB, responses at 5 MiB, free/busy calendars at 50, attendees at 200, recurrence lines at 50, and event arrays at 1,000.
- Idempotency: create, update, quick-add, and delete are single-attempt. A caller-supplied Google-compatible event id can provide create correlation; otherwise reconcile by ID, time range, and summary before retrying an ambiguous write.
- Concurrency: update_event and delete_event can send If-Match from the latest event ETag. Google partial patch requests consume more quota than update requests, so reserve patches for bounded changes.
- Destructive behavior: only delete_event is destructive and requires confirmDelete=true. Guest notifications are controlled explicitly by sendUpdates.
- API behavior: recurring events, all-day boundaries, organizer permissions, Workspace policies, attachments, reminders, guest delivery, and calendar ACLs can impose provider-side rules beyond local structural validation.
- Security: credentials remain in IntegrationAccount SecureFields. Fixed-host routing, bounded identifiers, allowlisted event fields, forbidden credential-like nested keys, capped payloads/responses, and redacted provider messages prevent arbitrary routing and secret reflection.
- External verification: request construction, pagination, validation, ETag handling, retry safety, redaction, response mapping, and metadata discovery have deterministic local tests. Live OAuth consent, refresh, calendar policies, guest email delivery, quotas, Meet conference creation, and calendar mutations require separately authorized Google credentials and are not exercised in repository tests.
- Deferred operations: calendar create/update/delete, ACL management, settings, colors, watch channels/triggers, event import/move, batch endpoints, Meet conference generation, attachments requiring Drive scopes, OAuth refresh, and arbitrary API calls.
- Functional references: n8n Google Calendar node source, Google Calendar Events API, Google Freebusy query, and Google Calendar OAuth scopes.