The GBP Maintenance Nightmare

Every salon owner knows the pain: you change your hours, add a new service, or update your holiday schedule — and then you have to update it on your website, your Google Business Profile, your Yelp listing, your Instagram, and a dozen other platforms. For a single salon, it's annoying. For a platform like AiSalonHub that manages hundreds of salon listings, it's impossible to do manually.

When we started AiSalonHub, each salon partner manually maintained their own GBP. The result? Inconsistent hours, outdated service menus, and missed opportunities from seasonal promotions that never made it to Google Search.

The Architecture: CMS as Source of Truth

The core insight was simple: **the EmDash CMS should be the single source of truth for every salon's public data.** Any change made in the CMS automatically flows outward to connected platforms. Here's how the pipeline works:

```

Salon updates hours in CMS

→ EmDash webhook triggers Cloudflare Worker

→ Worker checks GBP update queue in D1

→ Calls Google My Business API v4.9

→ Updates GBP listing for that salon

→ Logs result + error to analytics table

```

Key Components

**1. Change Detection via EmDash Webhooks** — When a salon record is updated in the EmDash admin panel, the plugin's `afterSave` hook fires. We capture the changed fields (hours, services, photos, description) and push a message to a D1 queue table.

**2. The GBP Sync Worker** — A Cloudflare Worker runs on a cron schedule (every 30 minutes) or on-demand when triggered by the webhook. It:

- Reads pending updates from `gbp_update_queue` table

- Batches updates by salon GBP account (each salon has its own auth token)

- Calls Google My Business API's `locations/{locationId}/localPosts` and `locations/{locationId}/serviceItems` endpoints

- Retries 3 times with exponential backoff on 429/503 errors

**3. Token Management** — Each salon authenticates their GBP once during onboarding. We store the OAuth 2.0 refresh token encrypted in Cloudflare KV with a per-salon namespace key. The Worker auto-refreshes tokens when they expire.

Reduction in Manual Work

Before automation, we estimated each salon spent about 45 minutes per week on GBP maintenance. For 100 active salons, that's **75 person-hours per week** — essentially a full-time employee.

After the pipeline went live:

- **92% of GBP updates now happen within 5 minutes of CMS changes**

- Manual GBP edits dropped from 45 min/week to 3 min/week per salon

- Average response time for holiday hour updates: **under 2 minutes**

- Seasonal promotion posts (holiday specials, new services) went from 0/week to 8+ posts per salon per month on Google

What We Learned

**Batch by auth scope, not by salon.** We initially tried to process all pending updates in a single loop, but Google's rate limiting kicked in fast. The fix was simple: group updates by the salon's GBP account token and process one account at a time with rate-limit headers.

**Don't trust 200 OK on GBP API.** Google's API sometimes returns 200 with an error body when the update partially fails (photo upload succeeded but description was rejected). We now parse the response body and mark the specific field as failed, retrying only that field.

**Handle GBP unverified listings gracefully.** Not every salon has completed Google's verification process. We skip unverified locations silently and flag them in the admin dashboard so the onboarding team can nudge the salon.

Next Steps

We're building a weekly digest feature that summarizes GBP activity: "Your salon profile was updated 12 times this week, resulting in 340 Google Search impressions and 12 direction requests." This closes the loop — salon owners can see the direct impact of keeping their profile fresh on Google.