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
- Open https://console.cloud.google.com
- Top-left project selector โ New Project (or use an existing project)
- Name it something descriptive, e.g.
voce-google-appointments - After it's created, switch to that project
- Left nav โ APIs & Services โ Library
- Search "Google Calendar API" โ click it โ click Enable
- 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."
- Left nav โ Google Auth Platform (or APIs & Services โ OAuth consent screen in older UI)
- Audience tab:
- User type: External (works for internal use too โ keeps it simple)
- Publishing status: leave as Testing (no verification needed for internal use)
- Scroll to Test users section โ + ADD USERS
- Add the email of the Google account whose calendar Voce will use (e.g.
[email protected]) - 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)
- Left nav โ APIs & Services โ Credentials
- + CREATE CREDENTIALS โ OAuth client ID
- Application type: Web application (critical โ Desktop / Android won't work with the playground flow)
- Name: e.g.
Voce Calendar OAuth - Authorized redirect URIs โ + ADD URI:
Exact match โ no trailing slash, nohttps://developers.google.com/oauthplaygroundwww, must be HTTPS. - Create
- 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
- Open https://developers.google.com/oauthplayground in a browser signed in as the calendar owner
- Click the โ gear icon (top right)
- Check "Use your own OAuth credentials"
- Paste your Client ID and Client Secret from Step 3
- Check "Force prompt" โ select "Consent screen" (forces Google to issue a new refresh token; without this you may get only an
access_token) - Close the settings panel
- Step 1 โ left panel: scroll down to find Calendar API v3, expand it, and check:
https://www.googleapis.com/auth/calendarCRITICAL: must be the broadercalendarscope, NOTcalendar.eventsalone.calendar.eventscannot read free/busy data and will cause "insufficient authentication scopes" errors. - Click Authorize APIs
- Sign in as the calendar owner (must be a test user from Step 2)
- Click through the consent screens โ Continue / Allow
- Step 2 โ top center: click Exchange authorization code for tokens
- The right panel shows the response with
access_tokenANDrefresh_tokenโ copy the refresh_token (long string starting with1//)
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
primaryas the Calendar ID
Option B: A dedicated shared calendar (recommended for cleaner separation)
- Open https://calendar.google.com
- Left sidebar โ find the calendar (e.g. "Voce Appointments") under My calendars
- Hover โ click the three dots โ Settings and sharing
- Scroll to Integrate calendar section
- Copy the Calendar ID โ looks like:
[email protected]
Step 6 โ Configure in the Voce Dashboard
- Open the Voce tuning dashboard
- Scroll to Integrations ยท Google Calendar credentials
- 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 (
primaryor the long ID)
- Scroll up to Integrations ยท Calendar backend (which one is active)
- Active calendar backend โ Google Calendar
- Booking Windows (optional but recommended) โ restrict bookable times, e.g.:
Empty = 9-5 M-F default.tue 14:00-17:00, thu 14:00-17:00 - 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 message | What it means | Fix |
|---|---|---|
Error 400: redirect_uri_mismatch | The Playground URL isn't in your OAuth client's allowed redirects | Step 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 process | The signed-in account isn't a test user | Step 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 client | Step 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/busy | Step 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 project | Step 1.5-7 โ enable the API; Google's error message includes a direct link |
invalid_grant on refresh | Refresh token was revoked, expired, or password changed | Re-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").