- Go to console.cloud.google.com
- Click the project dropdown at the top → New Project
- Name it something like
HomeHQ→ Create - Make sure the new project is selected in the dropdown
- Go to APIs & Services → Library (left sidebar)
- Search for Google Calendar API
- Click it → Enable
- Go to APIs & Services → OAuth consent screen
- Choose a user type:
- Internal (recommended if you have Google Workspace) — restricts access to users in your Workspace org. No test user setup or app review needed, and refresh tokens never expire from publishing status.
- External — use this if your Google account is a regular
@gmail.com. Requires adding yourself as a test user (see step 6) and publishing the app to production (see step 4b) — do not skip this.
- Click Create
- Fill in the required fields:
- App name:
HomeHQ - User support email: your email
- Developer contact email: your email
- App name:
- Click Save and Continue
- On the Scopes step, click Add or Remove Scopes
- Add the scope that matches your
google.calendarAccessconfig setting:- read-only (default):
https://www.googleapis.com/auth/calendar.readonly - read-write (create / edit / delete events):
https://www.googleapis.com/auth/calendar.events
- read-only (default):
calendar.eventsgrants read and write to events (display the calendar and create, edit, and delete events from the dashboard);calendar.readonlyis display-only. Neither grants calendar-list/settings management (the broader…/auth/calendarscope would).- Switching an existing deployment from read-only to read-write means re-running the
OAuth flow at
/setupso Google re-issues a token carrying the new scope. - Click Update → Save and Continue
- Add the scope that matches your
- External only: On the Test users step, click Add Users and add the Google account email that owns the family calendars. (Internal apps skip this — all Workspace users are already authorized.)
- Click Save and Continue → Back to Dashboard
- Go to APIs & Services → Credentials
- Click + Create Credentials → OAuth client ID
- Application type: Web application
- Name:
HomeHQ - Under Authorized redirect URIs, add:
http://localhost:3000/api/oauth/callback(for local dev)https://your-domain.com/api/oauth/callback(for production, if you have one — can add later)
- Click Create
- You'll see your Client ID and Client Secret — copy both
This is the trap that kills External apps. While an External app's publishing status is Testing, Google expires its refresh tokens after 7 days. The dashboard works for a week, then sync dies with
invalid_grant: the footer's sync indicator turns amber and says to reconnect at/setup, and cached events keep showing until you do. (Internal apps are immune — skip this section.)
- Go to APIs & Services → OAuth consent screen
- Under Publishing status, click Publish App → confirm
- The app will show as "unverified" — that's fine for personal use. When connecting, Google shows a warning screen; click Advanced → Go to HomeHQ (unsafe). It's your own app reading your own calendar.
Add the credentials to your .env file:
GOOGLE_CLIENT_ID=your-client-id-here.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret-here
COOKIE_SECRET=generate-with-openssl-rand-hex-32
NEXT_PUBLIC_BASE_URL=http://localhost:3000
Generate COOKIE_SECRET if you haven't already:
openssl rand -hex 32The redirect URI HomeHQ sends is NEXT_PUBLIC_BASE_URL plus /api/oauth/callback, exactly, so the one you registered in step 4 has to match that string: same scheme, same host, no trailing slash. A redirect_uri_mismatch error is nearly always one of those.
Then sign in with the household PIN and open /setup to connect. A board's own PIN can't reach /setup, by design.
- Google Cloud project created
- Google Calendar API enabled
- OAuth consent screen configured (Internal for Workspace, or External with test user added)
- External only: app published to production (avoids 7-day refresh token expiry)
-
calendar.readonlyorcalendar.eventsscope added (matchinggoogle.calendarAccess) - OAuth client ID created (Web application type)
- Redirect URI set to
http://localhost:3000/api/oauth/callback -
GOOGLE_CLIENT_IDadded to.env -
GOOGLE_CLIENT_SECRETadded to.env -
COOKIE_SECRETadded to.env