Google Business Profile review-response automation
Revvy
A multi-tenant SaaS that ingests Google Business Profile reviews at scale and generates on-brand reply drafts through a tunable draft-to-approve-to-publish pipeline, engineered so throughput and per-call cost stay controlled once real review volume arrives.
The problem
Responding to Google reviews is a manual, per-location chore that does not scale: a business with many locations accumulates reviews faster than a person can read, draft an on-brand reply, and post it. Doing this naively in software is worse — a sequential sync of a few thousand reviews takes over an hour, and generating a reply for every review spends model budget on reviews that are already answered or do not warrant one.
Constraints
- Real end-customer data under Google's platform terms — the integration handles live review and business data, so nothing customer-identifying can leak into logs, media, or the portfolio.
- Google Business Profile access tokens expire and refresh tokens can be permanently revoked (invalid_grant); the system has to survive token expiry mid-sync and detect dead connections rather than silently failing.
- Model calls cost money per review and rate limits are real — throughput can't come at the price of runaway spend or hitting provider limits.
- Multi-tenant from the start: every workspace has its own locations, prompts, automation settings, and cached views that must stay isolated.
- Kept infrastructure minimal — no separate queue broker to operate alongside the database.
Approach & key decisions
Made pre-filtering the first stage of the pipeline — skip reviews already replied to, below the star threshold, or already drafted before spending a single model call.
The cheapest generation is the one you avoid. Filtering ineligible reviews up front is where most of the cost discipline comes from; it targets a documented 30-50% cut in generation spend without changing output quality.
Built three generation strategies matched to a decision matrix rather than one code path: parallel (p-limit at 20 concurrent) for small and real-time work, a throttled mode (10 concurrent plus delays) for rate-limit safety, and an OpenAI Batch API path for non-urgent bulk jobs.
Throughput, cost, and latency trade off against each other. Small syncs want speed, bulk backfills tolerate a 24-hour turnaround for roughly half the cost, and rate limits need a safety valve — one strategy can't serve all three honestly.
Wrote a real OAuth token lifecycle instead of a happy-path integration: refresh access tokens with a 5-minute expiry buffer, cache the token within a request, and detect invalid_grant to flag a connection as needing reauthorization.
Long-running syncs outlive a token's lifetime, and revoked grants are a permanent failure, not a retry. Distinguishing a transient refresh from a dead connection is what keeps the product from either crashing or looping forever on a workspace that has to reconnect.
Chose pg-boss so the job queue lives entirely in PostgreSQL — no Redis or separate broker.
For a single-operator build, one fewer piece of infrastructure to run and reason about is worth more than a marginally faster queue. Sync, generate, and post all became typed jobs on the database already in the stack.
Batched database writes inside transactions and cached read-heavy pages with per-workspace tagged revalidation.
A full sync that issues ~2000 individual queries is the hidden bottleneck behind the model calls; upserting in batches (design target ~20-40 queries) and invalidating cache by workspace tag on sync keeps both writes and page loads fast without stale cross-tenant data.
What I built
- Built the Google Business Profile integration on NextAuth v5 with offline access: a token layer that refreshes with a 5-minute expiry buffer, caches the access token in-request, and marks connections reauthRequired on invalid_grant.
- Implemented review ingestion that paginates the GBP reviews API, maps star enums, and batch-upserts into PostgreSQL via Prisma inside transactions.
- Built the reply pipeline with configurable per-location prompts, business/SEO context injection, and first-name-only personalization, across multiple OpenAI models (gpt-4o-mini default, with gpt-5 code paths).
- Implemented the three-mode automation model — manual, draft-then-approve, and auto-post at or above a per-location star threshold.
- Engineered throughput three ways: parallel generation via p-limit at 20 concurrent, a throttled 10-concurrent mode for rate-limit safety, and a Batch API path for cheap bulk work.
- Built background processing on pg-boss with typed SYNC_REVIEWS, GENERATE_REPLY, and POST_REPLY handlers, plus an optional GBP Pub/Sub endpoint to trigger syncs.
- Built the operator dashboard: connect, locations, and an inbox to generate, edit, approve, and publish drafts with per-location settings.
Outcomes
- 6-10x Large-review sync (2000 reviews) improved from ~60-120 min sequential to ~10-20 min parallel — roughly 6-10xDocumented · PERFORMANCE.md; not independently audited
- ~6x 1000-review sync improved from ~30-60 min to ~5-10 minDocumented · PERFORMANCE.md
- 30-50% Pre-filtering targets a 30-50% reduction in generation cost by not drafting ineligible reviewsTarget · design target
- ~50% Batch API path offers ~50% cost savings on bulk jobs where a 24-hour turnaround is acceptableTarget · design target / provider pricing
- 50-200ms Cached page loads reduced from ~300-1000ms to ~50-200msDocumented · PERFORMANCE.md
Limitations & what's next
- Performance numbers come from an internal PERFORMANCE.md — documented benchmarks and design targets, not independently audited production metrics. They should be read as documented, never guaranteed.
- The product is deployed but not generally available; there are no verified users or revenue, and the README still describes it honestly as an early, functional build.
- Pinecone is scaffolded as hooks for per-location voice memory but is not part of the shipped generation loop — reply voice is driven by per-location prompts and injected context today.
- The AI layer is currently OpenAI-specific (with gpt-5 code paths staged); it is structured to swap models but has not been exercised against another provider.
- Next: close the loop on per-location voice memory, add reply-quality evaluation before auto-post is trusted more broadly, and measure the cost and throughput targets against real production volume rather than benchmark runs.