Google Business Profile — review import setup
One-time setup to pull all Michiu's Google reviews + existing replies into the review system using Google's official APIs (no scraping).
Total operator time: ~15 min hands-on + however long Google takes to approve the API access form (typically hours, sometimes days).
What this does
pnpm import:google connects to Google Business Profile as the verified
Michiu owner, fetches every review via the official Reviews API, and writes
them into the review system. Existing merchant replies are preserved as
status='posted'. Reviews without a reply land as status='new' and surface
in the admin inbox for AI variant generation on demand.
It's idempotent — re-running picks up only new reviews and updates existing ones in place.
Prerequisites
- A Google account that is a verified owner or manager of "Restaurant
Michiu" on Google Business Profile (at the time of writing:
csprkrn@gmail.com) - Access to Google Cloud Console with that same account
- Node 22+, pnpm 9+ (already required by the repo)
Step 1 — Create or pick a Google Cloud project
- Sign in at https://console.cloud.google.com as the verified owner account
- Create a new project (e.g. "Michiu Marketing") or pick an existing one
- Note the project number (top of the dashboard) — you'll need it for step 3
Step 2 — Enable the required APIs
In the project, enable all three of these:
- My Business Account Management API —
mybusinessaccountmanagement.googleapis.com - My Business Business Information API —
mybusinessbusinessinformation.googleapis.com - My Business Q&A API —
mybusinessqanda.googleapis.com
(The Q&A API isn't used by this importer, but Google's access form requires several My Business APIs to be enabled before approval.)
Enable via APIs & Services → Library, search each name, click Enable.
Step 3 — Apply for Business Profile API access
This is the slow part. Google requires a separate approval beyond just enabling the APIs — they want to verify the requester is a legitimate business owner.
- Open https://developers.google.com/my-business/content/prereqs
- Follow the "Request access to the Google Business Profile API" link
- Provide:
- The project number from step 1
- A 1-line use-case description, e.g. "Pulling our restaurant's review history into our internal admin tool."
- The contact email
- Submit and wait for the approval email
Until approved: pnpm google:auth works (OAuth itself doesn't need
approval), but pnpm import:google will return HTTP 403 with a
PERMISSION_DENIED body. The error handler surfaces a hint explaining this.
Step 4 — Create OAuth 2.0 credentials
In the same project, APIs & Services → Credentials:
- Click Create Credentials → OAuth client ID
- If prompted, configure the consent screen first:
- User Type: External
- App name: "Michiu Reviews Importer" (or anything)
- User support email: the verified owner email
- Developer contact: same
- Scopes: leave default (you'll request
business.manageat runtime) - Test users: add the verified owner email
- Application type: Desktop app
- Name: "Michiu importer"
- Save the Client ID and Client Secret
Step 5 — Wire credentials and run the OAuth flow
In ~/Code/michiu-marketing/.env.local:
GOOGLE_OAUTH_CLIENT_ID=<paste-from-step-4>
GOOGLE_OAUTH_CLIENT_SECRET=<paste-from-step-4>
Then run:
pnpm google:auth
The script:
- Prints a Google sign-in URL — open it in a browser
- Sign in as the verified owner account (
csprkrn@gmail.com) - Click Allow to grant
business.managescope - Google redirects to
http://localhost:8788?code=…; the script captures it - Exchanges for a refresh token and writes it to
.env.localunderGOOGLE_OAUTH_REFRESH_TOKEN(idempotent — re-runs replace the line)
Step 6 — Run the import
pnpm import:google
On a single-business owner account, the importer auto-discovers the account and location. If the owner has multiple businesses, the script prints the candidates and asks you to pin one via env:
GOOGLE_ACCOUNT_ID=123456789012345678901
GOOGLE_LOCATION_ID=987654321098765432109
Sample output:
[import:google] Imported from accounts/123… / Restaurant Michiu (account 123…, location 987…)
Reviews fetched: 142
Reviews created: 142
Reviews updated: 0
Replies imported: 97 (existing merchant replies)
Awaiting reply: 45
[import:google] Open http://localhost:3000/admin/reviews to review.
Re-running the import
Safe and idempotent. Each call:
- Updates rating/text/raw_payload of any review whose Google
updateTimechanged - Adds new reviews that appeared since last run
- Preserves locally-approved replies and previously-imported merchant replies (deduped by reply text)
You can run this on a cron once Phase 2 (Supabase + auth) lands.
Troubleshooting
| Symptom | Likely cause + fix |
|---|---|
pnpm google:auth says "Google did not return a refresh_token" | You already have a grant. Revoke at https://myaccount.google.com/permissions and re-run. |
pnpm import:google returns HTTP 401 | Refresh token rejected. Re-run pnpm google:auth. |
HTTP 403 with "API has not been used" or PERMISSION_DENIED | Business Profile API access not yet approved (step 3) OR the relevant My Business API isn't enabled in the project. |
| HTTP 403 with "User is not authorized" | The signed-in account isn't a verified owner/manager of the Michiu listing. |
| HTTP 404 on locations or reviews | GOOGLE_ACCOUNT_ID / GOOGLE_LOCATION_ID is wrong. Unset them and let auto-discovery run. |
| HTTP 429 | Rate limit. Default Business Profile quota is ~10 req/min. Wait and re-run. |
| Multiple accounts/locations error | Pin the right one via GOOGLE_ACCOUNT_ID / GOOGLE_LOCATION_ID env vars. |
Security
.env.localis gitignored. The refresh token never lands in a commit.- The OAuth scope is read-only as far as this importer cares (we never call
reviewReply.create). Phase 2 may add reply-posting; document that change when it happens. - To revoke access entirely: https://myaccount.google.com/permissions → remove the OAuth client.