How to Configure Google Calendar for Voce AI

Wire a Google Calendar into Voce so it can check availability and book appointments live, on the call.

AI Voice Agents

What this enables

When configured, Voce can:

  • Check real-time availability on a Google Calendar during a call
  • Book appointments live and create the event
  • Send a calendar invite to the caller's email (if provided)
  • Respect per-tenant booking windows so only specific days/hours are offered
Per-tenant: each Voce tenant uses their own Google Cloud project, OAuth credentials, and target calendar. The procedure below is repeated for each new tenant.

Prerequisites

  • A Google account whose calendar Voce should write to (e.g. [email protected] or a dedicated scheduling mailbox)
  • Access to Google Cloud Console with permissions to create projects + OAuth credentials
  • ~15 minutes

Step 1 โ€” Create the Google Cloud Project and Enable the Calendar API

  1. Open https://console.cloud.google.com
  2. Top-left project selector โ†’ New Project (or use an existing project)
  3. Name it something descriptive, e.g. voce-google-appointments
  4. After it's created, switch to that project
  5. Left nav โ†’ APIs & Services โ†’ Library
  6. Search "Google Calendar API" โ†’ click it โ†’ click Enable
  7. Wait ~30 seconds for the API to become available

Step 2 โ€” Configure the OAuth Consent Screen (Google Auth Platform)

The new Google Cloud UI calls this the "Google Auth Platform."

  1. Left nav โ†’ Google Auth Platform (or APIs & Services โ†’ OAuth consent screen in older UI)
  2. Audience tab:
    • User type: External (works for internal use too โ€” keeps it simple)
    • Publishing status: leave as Testing (no verification needed for internal use)
  3. Scroll to Test users section โ†’ + ADD USERS
  4. Add the email of the Google account whose calendar Voce will use (e.g. [email protected])
  5. Save
Without this step, the user account whose calendar you're authorizing will see "Access blocked: app has not completed the Google verification process."

Step 3 โ€” Create an OAuth 2.0 Client (Web Application)

  1. Left nav โ†’ APIs & Services โ†’ Credentials
  2. + CREATE CREDENTIALS โ†’ OAuth client ID
  3. Application type: Web application (critical โ€” Desktop / Android won't work with the playground flow)
  4. Name: e.g. Voce Calendar OAuth
  5. Authorized redirect URIs โ†’ + ADD URI:
    https://developers.google.com/oauthplayground
    Exact match โ€” no trailing slash, no www, must be HTTPS.
  6. Create
  7. Copy the Client ID and Client Secret (download the JSON if you want a backup). You'll need both in Step 6.

Step 4 โ€” Generate the Refresh Token via OAuth Playground

  1. Open https://developers.google.com/oauthplayground in a browser signed in as the calendar owner
  2. Click the โš™ gear icon (top right)
  3. Check "Use your own OAuth credentials"
  4. Paste your Client ID and Client Secret from Step 3
  5. Check "Force prompt" โ†’ select "Consent screen" (forces Google to issue a new refresh token; without this you may get only an access_token)
  6. Close the settings panel
  7. Step 1 โ€” left panel: scroll down to find Calendar API v3, expand it, and check:
    https://www.googleapis.com/auth/calendar
    CRITICAL: must be the broader calendar scope, NOT calendar.events alone. calendar.events cannot read free/busy data and will cause "insufficient authentication scopes" errors.
  8. Click Authorize APIs
  9. Sign in as the calendar owner (must be a test user from Step 2)
  10. Click through the consent screens โ†’ Continue / Allow
  11. Step 2 โ€” top center: click Exchange authorization code for tokens
  12. The right panel shows the response with access_token AND refresh_token โ€” copy the refresh_token (long string starting with 1//)

Step 5 โ€” Identify the Target Calendar ID

Two options depending on what you want Voce to write to:

Option A: The auth'd user's primary calendar (simplest)

  • Use the literal string primary as the Calendar ID

Option B: A dedicated shared calendar (recommended for cleaner separation)

  1. Open https://calendar.google.com
  2. Left sidebar โ†’ find the calendar (e.g. "Voce Appointments") under My calendars
  3. Hover โ†’ click the three dots โ†’ Settings and sharing
  4. Scroll to Integrate calendar section
  5. Copy the Calendar ID โ€” looks like:
    [email protected]

Step 6 โ€” Configure in the Voce Dashboard

  1. Open the Voce tuning dashboard
  2. Scroll to Integrations ยท Google Calendar credentials
  3. Fill in:
    • OAuth Client ID โ€” from Step 3
    • OAuth Client Secret โ€” from Step 3
    • OAuth Refresh Token โ€” from Step 4 (the 1//-prefixed value)
    • Calendar ID โ€” from Step 5 (primary or the long ID)
  4. Scroll up to Integrations ยท Calendar backend (which one is active)
  5. Active calendar backend โ†’ Google Calendar
  6. Booking Windows (optional but recommended) โ€” restrict bookable times, e.g.:
    tue 14:00-17:00, thu 14:00-17:00
    Empty = 9-5 M-F default.
  7. Save

Step 7 โ€” Verify

Voce will use the new calendar on the next inbound call. To test the plumbing without making a call, the operator can run the smoke script on the Voce VM (in /tmp/voce_gcal_test.py) โ€” it validates OAuth refresh and a free/busy query.

A successful test outputs:

โœ… access token obtained
โœ… free/busy returned N busy ranges for today
โœ… End-to-end Google Calendar integration is working.

Common Errors and Fixes

Error messageWhat it meansFix
Error 400: redirect_uri_mismatchThe Playground URL isn't in your OAuth client's allowed redirectsStep 3.5 โ€” add https://developers.google.com/oauthplayground to Authorized redirect URIs (exact match, no trailing slash)
Error 403: access_denied โ€” app has not completed the Google verification processThe signed-in account isn't a test userStep 2 โ€” add that email under Google Auth Platform โ†’ Audience โ†’ Test users
unauthorized_client (HTTP 401 on refresh)The refresh_token was issued for a different OAuth clientStep 4 โ€” re-run with "Use your own OAuth credentials" checked AND "Force prompt โ†’ Consent screen" checked. Confirm Client ID matches dashboard.
insufficient authentication scopes (HTTP 403 on free/busy)The refresh_token has calendar.events scope only, which can't read free/busyStep 4.7 โ€” re-do the dance using the broader https://www.googleapis.com/auth/calendar scope
Google Calendar API has not been used in project ... or it is disabled (HTTP 403)Calendar API isn't enabled on the projectStep 1.5-7 โ€” enable the API; Google's error message includes a direct link
invalid_grant on refreshRefresh token was revoked, expired, or password changedRe-run Step 4 to generate a fresh refresh_token; paste into dashboard

Operational Notes

  • Refresh tokens are long-lived secrets. They're marked secret in the Voce dashboard and never logged. Treat them like a password.
  • Access tokens last ~1 hour and are cached in Voce's process memory. Refreshed automatically when expired. No operator action needed.
  • Test mode is fine forever for internal use. You don't need to "publish" the OAuth consent screen unless you want anyone with a Google account to be able to authorize (not the case for Voce).
  • Per-tenant: every tenant onboarding repeats Steps 1โ€“7 with their own Google account, project, and credentials. Each tenant's config lives in Voce's per-tenant Postgres store.
  • Switching backends: Voce can use either Google Calendar OR Microsoft Calendar (Outlook/M365) per tenant. The "Active calendar backend" dropdown picks which is in use. Both can be configured simultaneously and switched without a restart.

Related

  • Microsoft Calendar (Outlook/M365) setup โ€” uses Microsoft Graph + Azure AD app registration. Different procedure, same dashboard fields.
  • Booking windows format โ€” see dashboard field help text for the full syntax (DOW HH:MM-HH:MM, comma-separated, multiple windows per day allowed).
  • Sales demo flow โ€” when a calendar backend is active, Voce automatically offers booking as a third option for sales-interested callers ("Connect now / Book demo / Callback").