- All required variables set in
.env(local) or Cloud Run config (production) -
GOOGLE_CLIENT_IDis valid -
GOOGLE_CLIENT_SECRETis valid -
GOOGLE_REFRESH_TOKENis valid and not revoked -
GSC_PROPERTY_URLmatches Search Console property exactly -
GBP_LOCATION_NAMEuses correct format:accounts/{id}/locations/{id} -
GA4_MEASUREMENT_IDis correct (format:G-XXXXXXXXXX) -
GA4_API_SECRETis valid - (Optional) Google Ads variables if using keyword features
- OAuth consent screen configured in Google Cloud Console
- Required scopes enabled:
-
https://www.googleapis.com/auth/webmasters.readonly -
https://www.googleapis.com/auth/business.manage
-
- Refresh token obtained through OAuth flow
- Test account has access to all required Google properties
- Search Console API enabled in Google Cloud Project
- Google My Business API enabled
- PageSpeed Insights API enabled
- Google Analytics Data API enabled
- (Optional) Google Ads API enabled
Run these commands to verify all endpoints work locally:
# Start server
npm run dev
# 1. Health Check
curl http://localhost:3000/health
# Expected: {"status":"healthy","version":"1.0.0",...}
# 2. Search Console Queries
curl http://localhost:3000/get_gsc_queries
# Expected: Array of queries with localIntent flags
# 3. Page Copy Generation
curl -X POST http://localhost:3000/compose_page_copy \
-H "Content-Type: application/json" \
-d '{"query":"kids haircuts newnan ga","targetUrl":"https://zanycuts.com/test"}'
# Expected: Complete SEO copy object with all fields
# 4. Fetch Reviews
curl http://localhost:3000/fetch_reviews?limit=3
# Expected: Array of reviews with suggested replies
# 5. PageSpeed Check
curl -X POST http://localhost:3000/check_pagespeed \
-H "Content-Type: application/json" \
-d '{"url":"https://zanycuts.com","strategy":"mobile"}'
# Expected: Metrics object with LCP, CLS, INP
# 6. GA4 Event (Test Event)
curl -X POST http://localhost:3000/send_ga4_event \
-H "Content-Type: application/json" \
-d '{"eventName":"test_event","params":{"test":"true"}}'
# Expected: {"success":true,"clientId":"gen_..."}
# 7. GBP Post (DRAFT - Don't publish to production)
curl -X POST http://localhost:3000/write_gbp_post \
-H "Content-Type: application/json" \
-d '{"message":"Test post from SEO Desk - please ignore this test post."}'
# Expected: {"success":true,"postName":"...","publishTime":"..."}
# NOTE: Delete this test post from GBP manually after testing
# 8. (Optional) Keyword Ideas
curl -X POST http://localhost:3000/get_keyword_ideas \
-H "Content-Type: application/json" \
-d '{"seedKeywords":["kids haircuts"],"locations":["Newnan, GA"]}'
# Expected: Array of keyword ideas with volume and CPC- Go to GA4 > Admin > Data Streams > Measurement Protocol API secrets
- Verify API secret matches
GA4_API_SECRET - Send a test event using
/send_ga4_event - Check GA4 DebugView within 5-10 minutes
- Confirm event appears with correct parameters
- Run
npm run lint- No errors - Run
npm run format- All files formatted - Run
npm run build- TypeScript compiles successfully - No
console.logstatements in production code (onlyconsole.info,console.warn,console.error) - No hardcoded secrets in code
- All files under 300 lines (cursor rules compliance)
# Authenticate
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
# Deploy (first time)
gcloud run deploy zanycutsdesk \
--source . \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--memory 512Mi \
--cpu 1 \
--timeout 60 \
--set-env-vars NODE_ENV=production
# Get service URL
gcloud run services describe zanycutsdesk \
--region us-central1 \
--format 'value(status.url)'gcloud run services update zanycutsdesk \
--region us-central1 \
--update-env-vars \
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com,\
GOOGLE_CLIENT_SECRET=your-client-secret,\
GOOGLE_REFRESH_TOKEN=1//your-refresh-token,\
GSC_PROPERTY_URL=sc-domain:zanycuts.com,\
GBP_LOCATION_NAME=accounts/123456789/locations/987654321,\
GA4_MEASUREMENT_ID=G-XXXXXXXXXX,\
GA4_API_SECRET=your-api-secretReplace YOUR_CLOUD_RUN_URL with your actual Cloud Run service URL:
CLOUD_RUN_URL="https://zanycutsdesk-xxxxx-uc.a.run.app"
# Health check
curl $CLOUD_RUN_URL/health
# Test Search Console
curl $CLOUD_RUN_URL/get_gsc_queries
# Test copy generation
curl -X POST $CLOUD_RUN_URL/compose_page_copy \
-H "Content-Type: application/json" \
-d '{"query":"kids haircuts newnan ga","targetUrl":"https://zanycuts.com/test"}'- Go to ChatGPT Settings > Apps (or your OpenAI Apps interface)
- Click "Create new app"
- Name:
ZanyCuts SEO Desk - Base URL: Your Cloud Run URL (e.g.,
https://zanycutsdesk-xxxxx-uc.a.run.app) - Authentication: None (OAuth handled server-side)
- Import tool definitions from
src/client/app.ts(TOOLS array) - Test each tool in ChatGPT interface:
-
get_gsc_queries -
compose_page_copy -
fetch_reviews -
reply_to_review -
write_gbp_post -
check_pagespeed -
send_ga4_event - (Optional)
get_keyword_ideas - (Optional)
add_negative_keywords
-
# Stream logs
gcloud run services logs tail zanycutsdesk --region us-central1
# Check for errors
gcloud run services logs read zanycutsdesk \
--region us-central1 \
--filter 'severity>=ERROR' \
--limit 50- Show staff how to access the tool in ChatGPT
- Walk through Flow 1: Content and GBP Post
- Walk through Flow 2: Reviews and Reputation
- Walk through Flow 3: Speed and Health
- Explain when to use each flow
- Show how to edit generated copy before publishing
- Emphasize: Always review GBP posts before publishing
- Set up Cloud Logging alerts for errors
- Monitor API usage quotas (Search Console, GBP, PageSpeed)
- Check GA4 for incoming events
- Review GBP posts for quality
- Collect staff feedback after 1 week
- Review most-used features
- Optimize slow endpoints (if any)
- Add caching for frequently accessed data (if needed)
- Update content templates based on staff feedback
If issues occur in production:
# List recent revisions
gcloud run revisions list --service zanycutsdesk --region us-central1
# Rollback to previous revision
gcloud run services update-traffic zanycutsdesk \
--region us-central1 \
--to-revisions REVISION_NAME=100Symptoms: OAUTH_REFRESH_FAILED errors in logs
Solutions:
- Verify refresh token hasn't been revoked in Google Account settings
- Check Client ID and Client Secret are correct
- Re-run OAuth flow to get new refresh token
- Update
GOOGLE_REFRESH_TOKENin Cloud Run config
Symptoms: GSC_UNAUTHORIZED or 403 errors
Solutions:
- Verify OAuth account has Search Console access
- Check
GSC_PROPERTY_URLexactly matches property in Search Console - Ensure OAuth scope includes
webmasters.readonly
Symptoms: GBP_LOCATION_NOT_FOUND or 404 errors
Solutions:
- Verify
GBP_LOCATION_NAMEformat:accounts/{account_id}/locations/{location_id} - Use GBP API to list locations and confirm IDs
- Ensure OAuth account has management access
Symptoms: Events sent successfully but not in GA4
Solutions:
- Wait 5-10 minutes (events can be delayed)
- Check DebugView instead of Realtime reports
- Verify
GA4_MEASUREMENT_IDandGA4_API_SECRET - Use
/send_ga4_eventwithdebug: truefor validation
Symptoms: PAGESPEED_TIMEOUT errors
Solutions:
- Retry with same URL (may be temporary)
- Try desktop strategy instead of mobile
- Check if URL is accessible publicly
- No secrets in code or Git history
-
.envfile in.gitignore - Cloud Run environment variables encrypted at rest
- OAuth refresh token rotated periodically (every 6 months)
- API quotas set appropriately to prevent abuse
- Service only accessible via HTTPS
- No PII logged in application logs
- Review access limited to authorized staff
- Family-friendly tone in all generated content
- UTM parameters consistent across all GBP links
- "Newnan, GA" included in all required SEO fields
- Internal links present in all generated copy
- Review replies sound human and not templated
- No automated changes to Google Ads budgets (as designed)
- GA4 events include no PII
Deployment Date: _____________
Deployed By: _____________
Cloud Run URL: _____________
ChatGPT App Name: ZanyCuts SEO Desk
Status: ☐ Testing | ☐ Production | ☐ Rollback Required