Skip to content

Onboarding feedback system

Overview

An automated email campaign that collects customer feedback after onboarding and encourages satisfied clients to leave Google Reviews. The system sends up to 3 emails based on customer response, with different paths for positive responders, negative responders, and non-responders.

Key Features:

  • Automated star-rating feedback collection
  • Google Review solicitation for satisfied clients
  • Automated Google Review detection and matching
  • Support ticket creation for negative feedback
  • Fillout form with auto-reply for detailed negative feedback

Summary of system

After a client's cleaning service goes live, they automatically receive an email asking them to rate their onboarding experience (1-5 stars).

  • Happy clients (4-5 stars) are redirected to leave a Google Review, with follow-up reminders if they don't
  • Unhappy clients (1-3 stars) are redirected to a feedback form and a support ticket is automatically created
  • Non-responders get up to two reminder emails

The system automatically detects when someone posts a Google Review and stops sending them reminders. This prevents the spammy experience of asking for a review from someone who already left one.


Email Flow

                              ┌──────────────┐
                              │   Email 1    │
                              │ Initial ask  │
                              └──────┬───────┘
              ┌──────────────────────┼──────────────────────┐
              │                      │                      │
              ▼                      ▼                      ▼
      ┌──────────────┐      ┌──────────────┐       ┌──────────────┐
      │   POSITIVE   │      │  NO RESPONSE │       │   NEGATIVE   │
      │  (4-5 stars) │      │              │       │  (1-3 stars) │
      └──────┬───────┘      └──────┬───────┘       └──────┬───────┘
             │                     │                      │
             │ 1 hour              │ 3 days               │
             ▼                     ▼                      ▼
      ┌──────────────┐      ┌──────────────┐       ┌──────────────┐
      │   Email 2a   │      │   Email 2b   │       │   Campaign   │
      │ Google nudge │      │   Reminder   │       │    Ends      │
      └──────┬───────┘      └──────┬───────┘       │              │
             │                     │               │ → Fillout    │
             │ 3 days              │               │ → Auto-reply │
             │              ┌──────┴──────┐        │ → Support    │
             │              │             │        │   ticket     │
             │              ▼             ▼        └──────────────┘
             │       ┌──────────┐  ┌────────────┐
             │       │ POSITIVE │  │ NO RESPONSE│
             │       │(4-5 star)│  │            │
             │       └────┬─────┘  └─────┬──────┘
             │            │              │
             │            │ 1 hour       │ 3 days
             │            ▼              ▼
             │     ┌──────────────┐ ┌──────────────┐
             │     │   Email 3b   │ │   Email 3c   │
             │     │ Google nudge │ │ Last chance  │
             │     └──────────────┘ └──────────────┘
      ┌──────────────┐
      │   Email 3a   │
      │ Final nudge  │
      └──────────────┘

EXIT CONDITIONS:
• Negative response → Campaign ends (Fillout auto-reply + support ticket created)
• Google review posted → Campaign ends (detected automatically)
• Client no longer has active services → Removed from views

System Components

Component Purpose Location
Airtable Script Processes views, sends emails, updates statuses 15-min automation 3
Cloudflare Worker Star clicks, Google review link tracking, email proxy feedback-redirect.jordan-845.workers.dev
Google Apps Script (Email) Sends emails via Gmail API feedback-email-sender
Google Apps Script (Reviews) Detects Google reviews, matches to contacts Review Detection
Fillout Form Captures negative feedback with auto-reply Onboarding Feedback
Support Ticket Script Creates tickets from negative feedback 15-min automation 3

How It Works

Email Sending Flow

  1. Every 15 minutes, the Airtable automation runs the feedback campaign processor script
  2. The script queries each view in sequence and processes eligible contacts
  3. Emails are sent via Cloudflare Worker → Google Apps Script → Gmail API

Star Rating Click Flow

  1. Client clicks a star rating in email
  2. Request hits Cloudflare Worker
  3. Worker records rating, response type, and timestamp in Airtable
  4. Client is redirected to:
  5. 4-5 stars: Google Reviews page
  6. 1-3 stars: Fillout feedback form
  1. Client clicks "Leave a Google Review" link
  2. Request hits Cloudflare Worker
  3. Worker records click timestamp in Airtable ({Clicked Google review link})
  4. Client is redirected to Google Reviews

Google Review Detection Flow

  1. Every 15 minutes, the Review Detection script runs
  2. Fetches recent reviews from Google Business Profile API
  3. Fetches contacts who clicked the review link in the last 14 days
  4. Matches reviews to contacts by name (exact or fuzzy)
  5. Updates {Google review posted} and {Google review match confidence}
  6. Sends email alert for fuzzy matches requiring manual review

Negative Feedback Flow

  1. Client clicks 1-3 stars → redirected to Fillout form
  2. Fillout sends automatic thank-you email
  3. Every 15 minutes, Airtable script creates support ticket from negative feedback
  4. Ticket includes feedback comments, contact details, and link to contact record

Email Content

Email 1 — Initial Ask

Subject: How was your onboarding, {First name}?

Asks the client to rate their experience by clicking 1-5 stars.

Email 2a — Positive Follow-up

Subject: Glad you're happy, {First name}!

Thanks them for positive feedback and asks for a Google review. Sent 1 hour after clicking 4-5 stars.

Email 2b — Reminder

Subject: Quick reminder, {First name}

Reminds non-responders to rate their experience. Sent 3 days after Email 1.

Email 3a — Final Google Nudge

Subject: One last thing, {First name}

Final request for a Google review. Sent 3 days after Email 2a.

Email 3b — Positive Follow-up (from 2b path)

Subject: Thanks for the feedback, {First name}!

Thanks them for positive feedback and asks for a Google review. Sent 1 hour after clicking 4-5 stars in Email 2b.

Email 3c — Last Chance

Subject: Last chance to share feedback, {First name}

Final request for any feedback. Sent 3 days after Email 2b to non-responders.


Airtable Configuration

Views

View Purpose Key Filters
Ready for feedback campaign Contacts ready for Email 1 Eligibility criteria, no campaign started
Ready for Email 2a Positive responders ready for Google nudge Status = "Email 1 sent", Response = Positive, 1 hour passed
Ready for Email 2b Non-responders ready for reminder Status = "Email 1 sent", Response = empty, 3 days passed
Ready for Email 3a Ready for final Google nudge Status = "Email 2a sent", 3 days passed
Ready for Email 3b Positive responders from 2b path Status = "Email 2b sent", Response = Positive, 1 hour passed
Ready for Email 3c Non-responders ready for last chance Status = "Email 2b sent", Response = empty, 3 days passed
Ready for Complete Ready to mark complete Response = Positive, Google review posted
Google review flagged for manual review Fuzzy matches needing verification Match confidence = "Flagged for review"
Create support ticket from negative feedback Negative responders needing ticket Response = Negative, Support ticket created = unchecked

Common filters on all campaign views:

  • {Google review posted} is empty (except Complete view)
  • {Feedback response} is not "Negative"
  • Active services filter (Active, Renegotiating, or Done one-off)

Fields

Field Type Purpose
Feedback campaign status Single select Tracks current stage
Feedback campaign started Date When campaign began
Last feedback email sent Date When last email was sent
Feedback rating Number Star rating clicked (1-5)
Feedback response Single select Positive, Negative, or None
Feedback response date Date When they clicked a star
Clicked Google review link Date When they clicked the Google review link
Google review posted Date When review was detected
Google review match confidence Single select Auto-confirmed or Flagged for review
Rejected reviewer names Long text Comma-separated names to skip when matching
Review matching was correct Checkbox Confirms fuzzy match was correct
Support ticket created Checkbox Prevents duplicate ticket creation
Ready for Email 2a Formula Eligibility formula for 1-hour delay
Ready for Email 3a Formula Eligibility formula for 3-day delay
Ready for Email 3b Formula Eligibility formula for 1-hour delay

Status Values

Status Meaning
(empty) Not yet in campaign
Email 1 sent Initial email sent, awaiting response
Email 2a sent Positive follow-up sent
Email 2b sent Reminder sent to non-responder
Email 3a sent Final Google nudge sent
Email 3b sent Positive follow-up (from 2b path) sent
Email 3c sent Last chance email sent
Complete Campaign finished

Google Review Detection

How Matching Works

  1. Script fetches contacts who clicked the Google review link in the last 14 days
  2. Script fetches recent reviews from Google Business Profile API
  3. For each review, attempts to match by name:
  4. Exact match → Auto-confirmed
  5. Fuzzy match (Levenshtein distance ≤2 on last name) → Flagged for review
  6. Single name match (e.g., "Adam" matches "Adam Smith") → Flagged for review
  7. Review must be posted AFTER the contact clicked the review link

Handling Flagged Matches

When a fuzzy match occurs, you receive an email with:

  • Reviewer name (from Google)
  • Contact name (from Airtable)
  • Review posted timestamp
  • Link clicked timestamp
  • Link to the flagged matches view

If the match is CORRECT: Tick the {Review matching was correct} checkbox

If the match is INCORRECT:

  1. Clear {Google review posted} and {Google review match confidence}
  2. Add the reviewer name to {Rejected reviewer names}

The email

This is how the email looks:

Script Properties (Google Apps Script)

Property Purpose
CLIENT_ID Google OAuth client ID
CLIENT_SECRET Google OAuth client secret
AIRTABLE_PAT Airtable Personal Access Token
AIRTABLE_BASE_ID Base ID (app50ZU9jqsaWnu07)
AIRTABLE_CONTACTS_TABLE_ID Contacts table ID
FIELD_GOOGLE_REVIEW_POSTED Field ID for review posted date
FIELD_CLICKED_GOOGLE_REVIEW_LINK Field ID for click timestamp
FIELD_CONTACT_NAME Field ID for contact name
FIELD_GOOGLE_REVIEW_MATCH_CONFIDENCE Field ID for match confidence
FIELD_REJECTED_REVIEWER_NAMES Field ID for rejected names
MATCH_WINDOW_DAYS Days to look back for clicks (default: 14)

Support Ticket Creation

When a client submits negative feedback through Fillout, the system automatically creates a support ticket.

Ticket Contains

  • Comments prefixed with: "Client sent negative feedback when asked to review their onboarding experience."
  • Client's feedback in quotes
  • Whether client wants a callback
  • Link to the site
  • Contact details (name, email, phone)
  • Link back to contact record

Script Properties (Airtable)

Key input variables:

  • tableContactsViewId → 'Create support ticket from negative feedback' view
  • commentsPrefix → "Client sent negative feedback when asked to review their onboarding experience."
  • roleValue → "Client"

Cloudflare Worker Configuration

Environment Variables

Variable Purpose
AIRTABLE_PAT Personal Access Token for Airtable API
APPS_SCRIPT_URL Google Apps Script deployment URL

Troubleshooting

Emails not sending

  • Check the Automation errors table for logged errors
  • Verify the automation is enabled and running
  • Check Cloudflare Worker logs (Observability tab)

Star clicks not recording

  • Check Cloudflare Worker AIRTABLE_PAT is valid
  • Verify field IDs are correct in worker code
  • Check Worker logs for Airtable API errors

Contact not appearing in expected view

  • Verify all filter conditions are met
  • Check formula fields are calculating correctly
  • Ensure contact has active services

Google reviews not being detected

  • Check Apps Script execution logs for errors
  • Verify OAuth is still valid (run getAuthorizationUrl() if needed)
  • Confirm the review appears in Google Business Profile
  • Note: Owner/manager reviews are filtered out by Google's API

OAuth token expired

If you see auth errors in the Review Detection script logs:

  1. Open the Apps Script project
  2. Run clearAuth()
  3. Run getAuthorizationUrl()
  4. Open the URL and re-authorize

Fuzzy match incorrectly matching

  1. Clear {Google review posted} and {Google review match confidence}
  2. Add the incorrect reviewer name to {Rejected reviewer names}
  3. The script will skip that name for this contact on future runs

Files & Versions

File Version Location
feedback-campaign-processor v2.9 Airtable automation script
feedback-worker v3.2 Cloudflare Workers
feedback-email-sender v1.0 Google Apps Script
review-detection v2.9 Google Apps Script
create-support-tickets-from-negative-feedback v1.2 Airtable automation script

Last updated: December 2025