Google Calendar Integration
KaiVox syncs appointments to Google Calendar automatically — one-way (KaiVox → Google). The integration uses OAuth2 and pure Guzzle REST calls (no google/apiclient package). It cannot be tested on localhost due to Google's domain restrictions.
Cannot test locally. Google Cloud Console rejects
http://kaivox-ai.test as an OAuth redirect URI. It requires a valid public top-level domain (.com, .org, etc.). Testing must happen on kaivox-test.omajestic.com or kaivoxai.com.Current State
| Item | Status |
|---|---|
| OAuth2 connect/disconnect UI | ✅ Built — at Settings → Integrations tab |
| Auto-sync on appointment create | ✅ Built — wired in AppointmentsController |
| Auto-sync on appointment update | ✅ Built |
| Event delete on cancellation | ✅ Built |
| Token refresh logic | ✅ Built — expiry detection + refresh call |
| Testing on localhost | ❌ Blocked — Google rejects .test domain |
| Two-way sync (Google → KaiVox) | ❌ Deferred — needs push notifications/webhooks |
| Outlook/Microsoft Calendar | ❌ Deferred |
When
GOOGLE_CLIENT_ID is not set, the integration tab shows: "Google Calendar sync is being set up. It will be available soon." — tenants see no broken state.Environment Variables
| Key | Value | Required |
|---|---|---|
GOOGLE_CLIENT_ID | From Google Cloud Console → OAuth 2.0 Clients | ✅ Yes (leave blank until staging) |
GOOGLE_CLIENT_SECRET | From Google Cloud Console → OAuth 2.0 Clients | ✅ Yes (leave blank until staging) |
GOOGLE_REDIRECT_URI | Full callback URL for your environment | ✅ Yes |
.env — staging
GOOGLE_CLIENT_ID=123456789-xxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxx
GOOGLE_REDIRECT_URI=https://kaivox-test.omajestic.com/settings/google-calendar/callback
.env — production
GOOGLE_CLIENT_ID=123456789-xxxx.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxxxx
GOOGLE_REDIRECT_URI=https://kaivoxai.com/settings/google-calendar/callback
Setup Checklist (Do Once on Staging)
-
Create a Google Cloud project Go to console.cloud.google.com → New Project → name it "KaiVox AI".
-
Enable Google Calendar API APIs & Services → Library → search "Google Calendar API" → Enable.
-
Create OAuth 2.0 credentials APIs & Services → Credentials → Create Credentials → OAuth client ID.
- Application type: Web application
- Name: KaiVox AI
- Authorized redirect URIs: Add both staging and production URIs (see above)
-
Configure OAuth consent screen APIs & Services → OAuth consent screen.
- User type: External (for multi-tenant use)
- App name: KaiVox AI
- Scopes:
https://www.googleapis.com/auth/calendar - Add test users: your own Google account for testing
-
Add credentials to staging .env Copy the Client ID and Client Secret into
.envon the staging server. SetGOOGLE_REDIRECT_URIto the staging callback URL. -
Test the OAuth flow Log in as a tenant on staging → Settings → Integrations → Connect Google Calendar → complete Google consent screen → should return to Settings with a success message.
-
Verify sync works Create an appointment → check that an event appears in the connected Google Calendar. Cancel the appointment → event should be deleted from Google Calendar.
How the OAuth Flow Works (Code)
Routes
GET /settings/google-calendar/connect → redirect to Google consent screen
GET /settings/google-calendar/callback → exchange code for tokens, store in tenant
GET /settings/google-calendar/disconnect → revoke tokens, clear from tenant
Token Storage
OAuth tokens are stored in the tenants table:
tenants.google_access_token — short-lived access token
tenants.google_refresh_token — long-lived refresh token
tenants.google_token_expires_at — expiry timestamp
Sync Logic (GoogleCalendarService)
// Called from AppointmentsController on create/update:
GoogleCalendarService::syncAppointment($appointment);
// Called on cancel:
GoogleCalendarService::deleteEvent($appointment);
// Token refresh (automatic, checked before every API call):
if (now() >= $tenant->google_token_expires_at) {
GoogleCalendarService::refreshToken($tenant);
}
What Gets Synced
| KaiVox Data | Google Calendar Field |
|---|---|
| Appointment date + time | Event start/end |
| Customer name + phone | Event title: "Appointment — [Customer Name]" |
| Service name | Event description |
| Staff member | Organiser / attendee |
| Appointment notes | Event description (appended) |
| Status = cancelled | Event deleted from Google Calendar |
Two-Way Sync (Deferred)
Currently, sync is one-way only: KaiVox → Google. Changes made directly in Google Calendar are not reflected in KaiVox. Two-way sync requires Google Calendar push notifications (webhooks from Google to KaiVox) — this is deferred to a future phase.