If you landed here, you probably typed something like “OpenClaw calendar integration with Google Calendar setup” into your search bar. Same. I spent last weekend wiring my personal Google Calendar into OpenClaw so I could yell “/meet with Sam next Tuesday” in Telegram and have the agent handle the messy bits. The official docs cover the basics, but gloss over a few foot-guns. This post is the blow-by-blow I wish I had.

Why bother marrying OpenClaw and Google Calendar?

Quick pitch:

  • Unified chat-first workflow. I already live in Slack & Telegram; forcing myself into Google’s UI breaks flow.
  • Cross-tool automations. OpenClaw can pull repo diffs, grep my shell, and now check whether I’m in a meeting before kicking off a deploy.
  • Morning briefings. At 07:00 the agent DM’s me today’s agenda with weather and GitHub PRs requiring review. No more tab farming.
  • “Find me a free slot” actually works. The scheduling assistant digs through free/busy blocks and proposes options in plain English.

Downsides? A bit of OAuth yak-shaving, and you’ll be storing a refresh token on disk or in ClawCloud’s Secrets Vault. If that scares you, stop here.

Prerequisites and sanity checks

  • Node.js 22+ (OpenClaw 0.31.4 currently fails hard on earlier LTS releases)
  • A Google account that can create OAuth credentials (G-Suite admin sometimes required)
  • OpenClaw gateway ≥ 0.31.0 and daemon ≥ 0.17.0. Check with npx openclaw --version
  • On-prem install or an active ClawCloud project. I’ll call out both paths.
  • git, jq, and gcloud CLI if you dislike web consoles.

Step 1 — Create Google Cloud project and enable Calendar API

1.1 Spin up a project

Head to console.cloud.google.com  → IAM & Admin / Create Project. Name it “OpenClaw Calendar Bridge”. Don’t overthink the org/folder unless company policy forces you.

1.2 Enable the API

Inside the new project: APIs & Services / Library → search “Google Calendar API” → Enable.

1.3 OAuth consent screen

Still under APIs & Services choose OAuth consent screen.

  • App type: Internal if you’re on Google Workspace; else External, testing mode is fine.
  • Add yourself as a test user if external.
  • Scopes: click “Add or remove scopes” → search calendar → check .../auth/calendar and .../auth/calendar.events.
  • Save. Google will gripe about verification; ignore for personal use.

1.4 Create OAuth client ID

  • CredentialsCreate CredentialsOAuth client ID
  • Application type: Web application
  • Authorized redirect URI: http://localhost:3000/auth/google/callback (OpenClaw default)  + any ClawCloud URL you plan to use, e.g. https://myagent.claw.run/auth/google/callback
  • Download the JSON. Google names it client_secret_XXXXXXXX.json. Keep it safe.

Step 2 — Feed the credentials to OpenClaw

You have two deployment modes. Pick one.

2.1 Local / self-hosted install

  1. Move the JSON to ~/.config/openclaw/google-oauth.json
  2. Export env vars so the gateway can read them:
export GOOGLE_CLIENT_ID=$(jq -r '.installed.client_id' ~/.config/openclaw/google-oauth.json) export GOOGLE_CLIENT_SECRET=$(jq -r '.installed.client_secret' ~/.config/openclaw/google-oauth.json) export GOOGLE_REDIRECT_URI="http://localhost:3000/auth/google/callback"
  1. openclaw gateway --port 3000
  2. Visit http://localhost:3000/integrations, click Google Calendar, follow the OAuth dance.

2.2 ClawCloud

  1. Open your project → Settings / Secrets Vault → add three keys:
GOOGLE_CLIENT_ID <value from JSON> GOOGLE_CLIENT_SECRET <value from JSON> GOOGLE_REDIRECT_URI https://YOUR_AGENT_ID.claw.run/auth/google/callback
  1. Redeploy the gateway from the dashboard. The container picks up secrets automatically.
  2. From the web UI, open Integrations and connect Google Calendar.

If the callback bombs with redirect_uri_mismatch, double-check that the URI is identical in all three places (Google console, env var, and actual browser address).

Step 3 — Validating OAuth and first read

OpenClaw stores the access + refresh token in its encrypted keychain (~/.openclaw/keychain.json for self-host, or ClawCloud’s KMS). To verify:

openclaw shell > claw integrations list ✔ google-calendar connected

Now ask the agent in chat:

/cal list --next 3

Response should include the next three events with start/end ISO timestamps. If you see 403 insufficient permissions, your scope list in Step 1.3 is incomplete.

Step 4 — Creating and modifying events via chat

The calendar plugin ships with four intents: create_event, update_event, delete_event, find_free_slot. They’re ordinary JSON tools exposed to the agent. Example prompt:

@clawbot book 1h with Alice and Ben tomorrow afternoon about "Q3 roadmap"

Under the hood the LLM will:

  1. Call find_free_slot with constraints (duration 60 min, participants busy times, window 12:00-18:00)
  2. Pick the first free block, then call create_event

You’ll see a proposed time; reply "yes" to confirm or suggest “make it 3 PM instead”. If confirmation is on (OPENCLAW_REQUIRE_CONFIRM=true), the agent waits. Power users flip it off and trust the bot.

4.1 Updating an event

/cal move "Q3 roadmap" to Friday 10am

The agent does a fuzzy match on the summary plus an events.list query, grabs the eventId, and issues events.patch with new start/end. It will re-run conflict detection (next section) before saving.

Step 5 — Conflict detection & busy-time sanity

OpenClaw treats conflicts in three tiers:

  • Hard – you’re explicitly busy. Event creation is aborted.
  • Soft – you’re tentatively busy (maybe). Bot asks for confirmation unless overridden.
  • Free – no conflict. Proceed.

Internally it calls the freebusy.query endpoint covering your primary calendar + any others listed in OPENCLAW_CAL_FREEBUSY_CALENDARS (comma-sep IDs). Example env var:

export OPENCLAW_CAL_FREEBUSY_CALENDARS="primary,team@company.com"

If you need stricter rules — say, block lunchtimes even when the calendar is blank — create a secondary calendar called Focus, insert recurring events, and add it to the list.

Step 6 — Morning briefing integration

OpenClaw’s scheduler (daemon process) can fire cron-like jobs. I use this crontab to drop a daily agenda in Telegram:

// ~/.openclaw/cron.json [ { "id": "morning-briefing", "schedule": "0 7 * * *", "tool": "morning_briefing", "config": { "channels": ["telegram://@p_stone"], "include_weather": true, "include_pending_prs": true, "days_ahead": 1 } } ]

The morning_briefing tool bundles:

  • calendar.events.list for the next days_ahead
  • Calls to Composio’s GitHub connector (pulls.list)
  • Open-weather API (optional)

Output is a nicely formatted message:

⏰ Today:
09:00-09:30 1:1 with Dana
11:00-12:00 Backend sync (Zoom)

PRs waiting on you: 3
Temp in Vienna: 16°C, light rain

Good enough that I haven’t opened Google Calendar on mobile in weeks.

Step 7 — Scheduling assistant: "find me a free slot"

The find_free_slot tool accepts:

  • durationMinutes
  • windowStart / windowEnd (RFC 3339)
  • participantEmails (optional; requires their calendars to be shared free/busy)
  • minGap (buffer between meetings)

Sample manual call via the gateway’s REST interface (good for testing):

curl -X POST http://localhost:3000/tools/find_free_slot \ -H 'Authorization: Bearer <token>' \ -H 'Content-Type: application/json' \ -d '{ "durationMinutes": 60, "windowStart": "2024-05-20T08:00:00Z", "windowEnd": "2024-05-24T18:00:00Z", "participantEmails": ["alice@example.com", "ben@example.com"], "minGap": 15 }'

The response is an array of RFC 3339 start times. Feed the first entry back into create_event or let the agent handle it automatically.

Edge cases I hit:

  • Time-zones matter. The API returns Zulu; convert with moment.tz or rely on the chat client to localise.
  • If a participant’s calendar isn’t shared, Google returns HTTP 404 rather than empty busy slots. The tool suppresses those but logs a warning.
  • Large windows (months) can breach Google’s quota. Cap at one week or paginate.

Troubleshooting cheat-sheet

  • ERR_BAD_REQUEST 400 “invalid_grant”
    Token expired or revoked. Re-link the integration.
  • Bot says it booked, but event missing
    Check if you’re using a secondary calendar. Add "calendarId": "primary" to tool config or env var OPENCLAW_CAL_DEFAULT_ID.
  • Events off by one hour
    Your server TZ != Google account TZ. Export TZ in the gateway’s environment.
  • OAuth screen stuck on “app not verified”
    Click “Advanced → Go to <app> (unsafe)”. For org-wide installs, you’ll need Google to verify scopes; not worth it for internal use.

Next steps

Add GitHub Issues or Notion tasks to the morning briefing. Or wire the scheduling assistant into your CI pipeline so deploys only run when you’re not in a meeting. The building blocks are here; the rest is YAML and a bit of perseverance.