Lead Generation & Automation
The SMS/text follow-up sequences that turn a new lead into a booked call.
Business: AZ Palm Tree Lights Phone: (480) 631-7754 Platform: Quo (OpenPhone) CRM: Jobber Automation: n8n (your n8n instance) Last updated: 2026-05-04
Lead Source Trigger n8n Workflow Quo
─────────────────────────────────────────────────────────────────────────────────────
Meta Lead Ad ──────> Webhook (Meta Lead Ads) ──> Sequence 1 Workflow ──> SMS via Quo API
Google Ads call ──────> Jobber new contact ──> Sequence 1 Workflow ──> SMS via Quo API
Website form ──────> Webhook (Fillout/Tally) ──> Sequence 1 Workflow ──> SMS via Quo API
Fillout CX Survey ──────> Webhook (Fillout) ──> Sequence 2 Workflow ──> SMS via Quo API
Manual trigger ──────> n8n Manual/Scheduled ──> Sequence 3 Workflow ──> SMS via Quo API
Jobber job complete ──────> Jobber webhook ──> Sequence 4 Workflow ──> SMS via Quo API
Quo sends inbound message webhooks to a designated endpoint. The n8n reply-listener workflow:
replied = true in the Google Sheet tracking logquo-replied) via Jobber APIreplied = true, the step is skippedThis is the exit condition for all sequences that exit on reply.
Target: Cold leads from Meta lead ads, Google Ads calls, or website form submissions Tone: Warm, personal, no pressure Goal: Get the lead on a 5-minute call with Jack
Text 1 — Immediate (within 2 minutes of lead entry)
Hey [first_name], it's Jack from AZ Palm Tree Lights! Thanks for reaching out. I'd love to learn about your property and put together a free quote. When's a good time for a quick 5-min call? - Jack (480) 631-7754
Character count: ~192 (1 SMS segment) Send as: SMS
Text 2 — +4 hours (if no reply to Text 1)
Hey [first_name], just following up! I've got some availability [today/tomorrow] if you'd like to chat about lighting up your palms. No pressure either way.
Character count: ~155 (1 SMS segment) Send as: SMS Note: "today/tomorrow" should be dynamically replaced — use today if sent before 3pm AZ time, "tomorrow" if after 3pm
Text 3 — +24 hours from lead entry (if no reply)
Here's one we just finished in [neighborhood]. Would love to do something similar for your place. Got 5 minutes? - Jack
Send as: MMS with install photo See Photo Recommendations section for image selection guidance Note: [neighborhood] = Scottsdale or Paradise Valley based on lead's zip code if available, otherwise default to "Scottsdale"
Text 4 — +3 days from lead entry (if no reply)
Hey [first_name], last text from me! If palm tree lighting is still on your radar, I'm here whenever you're ready. You can also check out our work at azpalmtreelights.com/gallery. - Jack
Character count: ~180 (1 SMS segment) Send as: SMS
Exit Conditions:
quo-replied in Jobbern8n Workflow: Sequence 1
See n8n Workflow Architecture section for full node diagram.
Target: Existing Trimlight customers who responded "Yes, I'd love a free quote" on the customer experience survey Tone: Warm, VIP, relationship-first — these people already know and trust Jack Goal: Convert warm interest into a booked quote call
Critical note: These are Trimlight customers. AZ Palm Tree Lights and Trimlight must remain brand-separate in all systems. This sequence comes from the AZ Palm Tree Lights Quo number (480) 631-7754, not from any Trimlight-associated number. The message copy acknowledges the Trimlight relationship conversationally but does not present itself as Trimlight outreach.
Text 1 — Immediate (within 5 minutes of survey submission)
Hey [first_name], it's Jack! Thanks for filling out the survey — excited you're interested in palm tree lighting. Since you're already a Trimlight customer, I'd love to give you a VIP quote. When works for a quick call?
Character count: ~212 (1 SMS segment) Send as: SMS
Text 2 — +24 hours (if no reply)
Hey [first_name], just bumping this up. As a thank you for the survey, you've got $100 off any new lighting project. Want to lock that in?
Character count: ~141 (1 SMS segment) Send as: SMS Note: The $100 discount is a real commitment — log the offer in Jobber notes when this message sends so Jack can honor it
Text 3 — +4 days from survey submission (if no reply)
Here's what palm tree lighting looks like on a home similar to yours. I can put together a custom quote in about 10 minutes. Just text back a good time! - Jack
Send as: MMS with before/after photo See Photo Recommendations section — use upscale Scottsdale/PV home photos that match Trimlight customer demographics
Exit Conditions:
Target: Leads who were quoted 30+ days ago but never booked (pulled from Jobber "Quoted" status, age 30+ days) Tone: Low-pressure check-in, acknowledges time has passed Goal: Revive dormant interest, create urgency with booking window and discount Trigger: Manual — run this quarterly or at the start of booking season (August/September push recommended)
Text 1 — Day 1
Hey [first_name], it's Jack from AZ Palm Tree Lights. We chatted [time_reference] about lighting up your palms. Wanted to check in — is that still something you're interested in? We've got some new install photos I'd love to share.
Character count: ~220 (1 SMS segment) Send as: SMS Note: [time_reference] = "a while back" as default, or "in [month_name]" if quote date is known and within the current year
Text 2 — +3 days (if no reply)
No worries if the timing wasn't right before. We're booking installs for [next_month] now. If you want to get on the schedule before peak season, just text back and I'll send over an updated quote.
Character count: ~189 (1 SMS segment) Send as: SMS Note: [next_month] = calendar month name one month ahead of send date
Text 3 — +7 days from Text 1 (if no reply)
Last one from me, [first_name]! Just wanted to let you know we're offering $200 off for customers who book before [cutoff_date]. If you're interested, I'm here. If not, totally understand. - Jack
Character count: ~187 (1 SMS segment) Send as: SMS Note: [cutoff_date] = set manually when triggering the sequence batch, typically end of current month or 2 weeks out. Log the offer date in the tracking sheet.
Exit Conditions:
Enrollment process: Jack or ops team pulls a Jobber report of "Quoted" jobs older than 30 days, exports a CSV with name + phone, uploads to n8n manual trigger or pastes into a Google Sheet intake tab. Workflow reads the sheet and enrolls each contact.
Target: Completed Jobber jobs (status = "Completed") Tone: Friendly, grateful, assumes success Goal: Get Google reviews, plant referral seed Trigger: Jobber webhook fires on job status change to "Completed" — n8n listens and waits 3 days before sending Text 1
Text 1 — +3 days post-install
Hey [first_name], it's Jack! Hope you're loving the new palm tree lighting. How's everything looking? Any questions about the app or color settings?
Character count: ~148 (1 SMS segment) Send as: SMS Purpose: Opens the loop with a service check-in before asking for a review. Gives the customer a chance to raise any issues privately.
Text 2 — +7 days post-install
Send regardless of whether Text 1 received a reply. If they replied positively, this flows naturally. If no reply, the tone still works.
So glad to hear it! Would you mind leaving us a quick Google review? It really helps other homeowners find us. Here's the link: [google_review_url]. Thanks so much!
Character count: ~183 (1 SMS segment) Send as: SMS Note: [google_review_url] = AZ Palm Tree Lights Google Business Profile review link. Get the short link from Google Business Profile dashboard: https://business.google.com > Your Profile > Ask for reviews > copy the short URL. Hardcode this into the n8n workflow config once.
Exception: If Text 1 received a complaint or issue reply, DO NOT auto-send Text 2. The n8n workflow must check for a negative-sentiment flag set manually by Jack, or simply pause and send Jack a notification to handle manually.
Text 3 — +14 days post-install
Hey [first_name], one more thing — if you know anyone who'd love their palms lit up, we offer a $100 referral bonus for every friend who books. Just have them mention your name!
Character count: ~175 (1 SMS segment) Send as: SMS
Exit Conditions:
Reply Listener Workflow (always-on, separate workflow)
[Quo Inbound Webhook]
|
[Extract phone number + message body]
|
[Google Sheets: lookup phone in "Active Enrollments" tab]
|
[If phone found in active enrollment]
|
├── [Update enrollment row: replied = TRUE, reply_text = message, reply_time = now]
|
└── [Jobber API: add tag "quo-replied" to contact]
This webhook URL gets registered once in Quo's settings (Settings > Integrations > Webhooks > Inbound SMS).
Trigger options (pick the applicable one per source):
[Trigger: New lead webhook]
|
[Extract: first_name, phone, source, neighborhood/zip]
|
[Normalize phone to E.164 format: +1XXXXXXXXXX]
|
[Check Google Sheet "Active Enrollments": is phone already enrolled?]
|
[If already enrolled] ──> [Stop — do not double-enroll]
|
[If new]
|
[Google Sheets: Add row to Active Enrollments]
{phone, first_name, sequence=1, enrolled_at, source, replied=FALSE}
|
[Quo API: Send Text 1 — immediate]
|
[Google Sheets: Log to Sent Log tab]
|
[Wait 4 hours]
|
[Check Active Enrollments: replied = TRUE?]
|
[If replied] ──> [Exit sequence, log exit_reason="replied", done]
|
[If not replied]
|
[Resolve today/tomorrow variable based on AZ time]
|
[Quo API: Send Text 2]
|
[Google Sheets: Log to Sent Log tab]
|
[Wait until 24 hours from enrollment time]
|
[Check Active Enrollments: replied = TRUE?]
|
[If replied] ──> [Exit sequence]
|
[If not replied]
|
[Resolve neighborhood variable from zip code]
|
[Quo API: Send Text 3 — MMS with photo URL]
|
[Google Sheets: Log to Sent Log tab]
|
[Wait until 72 hours from enrollment time]
|
[Check Active Enrollments: replied = TRUE?]
|
[If replied] ──> [Exit sequence]
|
[If not replied]
|
[Quo API: Send Text 4]
|
[Google Sheets: Log to Sent Log tab]
|
[Update Active Enrollments: status = "completed"]
n8n nodes used: Webhook, Code (for phone normalization), Google Sheets (read + write), HTTP Request (Quo API), Wait, IF
[Trigger: Fillout webhook — survey submission]
|
[Check: Q2 answer = "Yes, I'd love a free quote"]
|
[If No] ──> [Stop — not a hot lead]
|
[If Yes]
|
[Extract: first_name, phone, email]
|
[Normalize phone]
|
[Google Sheets: Add row to Active Enrollments]
{phone, first_name, sequence=2, enrolled_at, source="cx-survey", replied=FALSE}
|
[Quo API: Send Text 1 — immediate]
|
[Jobber API: Add note to contact — "$100 discount offered via survey sequence, [date]"]
|
[Google Sheets: Log]
|
[Wait 24 hours]
|
[Check replied]
|
[If replied] ──> [Exit]
|
[If not replied]
|
[Quo API: Send Text 2]
|
[Google Sheets: Log]
|
[Wait until 96 hours from enrollment]
|
[Check replied]
|
[If replied] ──> [Exit]
|
[If not replied]
|
[Quo API: Send Text 3 — MMS with before/after photo]
|
[Google Sheets: Log]
|
[Update enrollment: status = "completed"]
[Trigger: Manual — Google Sheets intake tab populated by Jack/ops]
OR
[Trigger: Scheduled — weekly, reads Jobber "Quoted" jobs > 30 days old via Jobber API]
|
[For each contact in batch]
|
[Check Active Enrollments: already enrolled in Seq 3?]
|
[If yes] ──> [Skip]
|
[If no]
|
[Normalize phone]
|
[Resolve time_reference from quote_date field]
|
[Google Sheets: Add enrollment row]
{phone, first_name, sequence=3, enrolled_at, jobber_quote_id, replied=FALSE}
|
[Quo API: Send Text 1]
|
[Google Sheets: Log]
|
[Wait 3 days]
|
[Check replied]
|
[If replied] ──> [Exit]
|
[If not replied]
|
[Resolve next_month variable]
|
[Quo API: Send Text 2]
|
[Google Sheets: Log]
|
[Wait until 7 days from enrollment]
|
[Check replied]
|
[If replied] ──> [Exit]
|
[If not replied]
|
[Read cutoff_date from workflow config variable]
|
[Quo API: Send Text 3]
|
[Google Sheets: Log]
|
[Update enrollment: status = "completed"]
Workflow config variable: Set cutoff_date as a static variable in the workflow before running a batch. Update it each time you run a reactivation campaign.
[Trigger: Jobber webhook — job status changed to "Completed"]
|
[Extract: job_id, client_first_name, client_phone, completed_at]
|
[Check: is this an AZ Palm Tree Lights job? (check job tag or assigned brand field)]
|
[If Trimlight job] ──> [Stop — wrong brand]
|
[If AZPTL job]
|
[Normalize phone]
|
[Google Sheets: Add enrollment row]
{phone, first_name, sequence=4, enrolled_at, jobber_job_id, replied=FALSE, issue_flag=FALSE}
|
[Wait 3 days from completed_at]
|
[Quo API: Send Text 1 — check-in]
|
[Google Sheets: Log]
|
[Wait until 7 days from completed_at]
|
[Check issue_flag = TRUE (set manually by Jack if complaint received)]
|
[If issue_flag = TRUE]
|
└── [Send Slack/email notification to Jack: "Manual follow-up needed for [name]"]
[Stop sequence]
|
[If issue_flag = FALSE]
|
[Quo API: Send Text 2 — review request with Google URL]
|
[Google Sheets: Log]
|
[Wait until 14 days from completed_at]
|
[Check issue_flag]
|
[If issue_flag = TRUE] ──> [Stop]
|
[If FALSE]
|
[Quo API: Send Text 3 — referral offer]
|
[Google Sheets: Log]
|
[Update enrollment: status = "completed"]
Quo (OpenPhone) provides a REST API. Documentation: https://www.openphone.com/docs/api-reference
Base URL: https://api.openphone.com/v1
Authentication: Bearer token (API key from Quo workspace settings) Settings > My Account > API Keys > Create API Key
Required scopes: messages:read, messages:write, phone-numbers:read
POST https://api.openphone.com/v1/messages
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"from": "+14806317754",
"to": ["+1XXXXXXXXXX"],
"content": "Hey [first_name], it's Jack..."
}
POST https://api.openphone.com/v1/messages
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"from": "+14806317754",
"to": ["+1XXXXXXXXXX"],
"content": "Here's one we just finished in Scottsdale...",
"mediaUrls": ["https://azpalmtreelights.com/assets/install-1.jpg"]
}
Note: Media must be served via a publicly accessible HTTPS URL. Host photos on AZ Palm Tree Lights website or an S3 bucket. Do not use Google Drive links — they are not reliably served as raw media.
Register one webhook URL in Quo settings to receive all inbound messages:
Settings > Integrations > Webhooks > Add Webhook
your n8n webhookmessage.receivedQuo will POST a JSON payload for every inbound SMS/MMS. The Reply Listener workflow receives this.
If Jack's Quo plan does not include API access, the fallback is:
This is slower but still systematizes the process. Upgrade to a Quo plan with API access as soon as volume justifies it.
File name: AZ Palm Tree Lights — Quo Sequence Tracking
Location: Google Drive, in the AZ Palm Tree Lights folder
Tracks every contact currently in a sequence.
| Column | Type | Notes |
|---|---|---|
| phone | Text | E.164 format (+1XXXXXXXXXX) — primary key |
| first_name | Text | |
| sequence | Number | 1, 2, 3, or 4 |
| enrolled_at | Timestamp | ISO 8601 |
| source | Text | meta, google, website, cx-survey, manual, jobber-complete |
| replied | Boolean | TRUE/FALSE — updated by Reply Listener workflow |
| reply_text | Text | First reply message body |
| reply_time | Timestamp | |
| issue_flag | Boolean | For Seq 4 — Jack sets manually if complaint |
| status | Text | active, completed, exited-reply, exited-manual |
| jobber_contact_id | Text | For linking back to Jobber |
| jobber_job_id | Text | Seq 4 only |
Append-only log of every message sent.
| Column | Type | Notes |
|---|---|---|
| sent_at | Timestamp | |
| phone | Text | |
| first_name | Text | |
| sequence | Number | |
| step | Number | 1, 2, 3, or 4 |
| message_type | Text | sms or mms |
| message_preview | Text | First 80 chars of message |
| quo_message_id | Text | From Quo API response |
| status | Text | sent, failed |
All inbound replies captured by the Reply Listener workflow.
| Column | Type | Notes |
|---|---|---|
| received_at | Timestamp | |
| phone | Text | |
| first_name | Text | |
| sequence | Number | Active sequence at time of reply |
| step_when_replied | Number | Which step they had received |
| reply_text | Text | Full message body |
| converted | Boolean | Did this lead become a booked job? — fill in manually |
| notes | Text | Jack's notes |
Use COUNTIF/QUERY formulas to summarize:
| Metric | Formula approach |
|---|---|
| Enrollments by sequence | COUNTIF(Active Enrollments!C:C, "1") |
| Reply rate by sequence | Replies / Enrollments |
| Conversion rate | Converted / Enrollments |
| Avg messages sent before reply | AVERAGEIF on Sent Log |
| Discount offers outstanding | COUNTIF sent log for Seq 2 Text 2 + Seq 3 Text 3 |
Used to batch-enroll reactivation leads manually.
| Column | Notes |
|---|---|
| first_name | |
| phone | Raw format OK — n8n normalizes |
| quote_date | From Jobber |
| jobber_quote_id | |
| neighborhood | Scottsdale / PV / Arcadia / Other |
| processed | n8n marks TRUE after enrollment |
Jack or ops pastes Jobber CSV export here. The Sequence 3 workflow polls this tab on a schedule.
Important: This section is informational, not legal advice. Consult an attorney familiar with TCPA if you have questions.
The Telephone Consumer Protection Act (TCPA) requires prior express written consent before sending marketing texts using an autodialer or automated system. Violations carry statutory damages of $500–$1,500 per message.
| Sequence | Classification | Consent Requirement |
|---|---|---|
| Seq 1 (New Lead — inbound) | Transactional/informational — the lead contacted you first | Implied consent via inbound inquiry. Still best practice to include opt-out. |
| Seq 2 (CX Survey) | Marketing — reaching out to existing customers | Need prior consent. The survey form must include a consent checkbox. |
| Seq 3 (Reactivation) | Marketing — outbound to cold leads | Need prior written consent captured at original lead form. |
| Seq 4 (Post-Install) | Mixed — part service (app questions), part marketing (review, referral) | Text 1 is transactional. Texts 2–3 are marketing. Consent from service agreement/intake form covers this for existing customers. |
Website form / Meta Lead Ad / Google Ads landing page: Add checkbox: "I agree to receive text messages from AZ Palm Tree Lights about my inquiry and related offers. Message and data rates may apply. Reply STOP to opt out."
CX Survey (Fillout): Add consent language above the phone field: "By providing your number, you consent to receive text messages from AZ Palm Tree Lights. Reply STOP to unsubscribe."
Jobber intake / service agreement: Add a checkbox to the client intake form or quote acceptance confirming text consent.
Every sequence must honor STOP replies. The Reply Listener workflow should:
opted_out = TRUE in the Active Enrollments sheet, exit all active sequences for that number, and log to an Opt-Out tabQuo may handle STOP replies automatically at the platform level — verify this in your Quo settings. Even if Quo handles it, maintain your own opt-out list in Google Sheets as a backup.
For any message where character count allows, append: "Reply STOP to opt out."
Under TCPA, marketing texts should only be sent between 8 AM and 9 PM in the recipient's local time zone. All recipients are in Arizona (MST, no daylight saving). n8n Wait nodes should check the send time and delay until 8 AM AZ time if a scheduled message would otherwise send outside that window.
AZ time: UTC-7 year-round (Arizona does not observe DST, except the Navajo Nation).
| Variable | Source | Notes |
|---|---|---|
[first_name] |
Lead form / Jobber contact | Required — do not send if missing. Use "there" as fallback only for Seq 1 Text 1. |
phone (recipient) |
Lead form / Jobber contact | Must be a US mobile number. Landlines cannot receive SMS. Normalize to E.164. |
| Variable | Sequence | Source | Fallback |
|---|---|---|---|
[today/tomorrow] |
Seq 1 Text 2 | n8n time node (AZ time) | "soon" |
[neighborhood] |
Seq 1 Text 3 | Lead zip code lookup table | "Scottsdale" |
[time_reference] |
Seq 3 Text 1 | Jobber quote_date | "a while back" |
[next_month] |
Seq 3 Text 2 | n8n date node (+1 month) | "next month" |
[cutoff_date] |
Seq 3 Text 3 | Workflow config variable | Set before each campaign run |
[google_review_url] |
Seq 4 Text 2 | Static — hardcoded once | No fallback — must be set |
Build a small lookup table in Google Sheets or as a JSON object in n8n:
85250, 85251, 85252, 85253, 85254, 85255, 85257, 85258, 85259, 85260, 85261, 85262, 85266, 85268 → "Scottsdale"
85253 → "Paradise Valley"
85018, 85012, 85016 → "Arcadia"
default → "Scottsdale"
| Metric | Target | Notes |
|---|---|---|
| Reply rate (any step) | 30–40% | Industry average for warm inbound SMS is 25–45% |
| Reply rate within Text 1 | 15–25% | Speed of first text is the biggest driver |
| Conversion to booked call | 20–30% of enrollments | |
| Conversion to booked job | 10–15% of enrollments | Depends on quote-to-close rate |
| Best performing step | Text 1 | 60–70% of all replies will come on Text 1 |
Key lever: Text 1 must send within 2 minutes. Every minute of delay reduces reply probability significantly. If n8n can't guarantee 2-minute delivery, build in a Slack alert to Jack for immediate manual backup.
| Metric | Target | Notes |
|---|---|---|
| Reply rate | 40–55% | These are warm, existing customers — expect higher engagement |
| Conversion to booked quote | 30–40% | |
| $100 discount redemption rate | Track in Google Sheet | Log every time Jack honors this offer |
| Metric | Target | Notes |
|---|---|---|
| Reply rate | 15–25% | Cold-er — lower expectations |
| Conversion to booked job | 5–10% of batch | Even 5% reactivation is high ROI |
| $200 discount redemption rate | Track separately | Note the cost per acquisition |
Run timing recommendation: Launch Seq 3 batches in August (before peak season) and in February (post-season, catch anyone who deferred).
| Metric | Target | Notes |
|---|---|---|
| Google review conversion | 20–35% | Average review request → review rate via SMS is 15–30% |
| Referral leads generated | 1 per 10 completions | Track referral source in Jobber |
| Net Promoter Signal (positive reply to Text 1) | 70%+ | High positive rate validates install quality |
azpalmtreelights.com/assets/mms/ or a dedicated S3 bucket. Never use Google Drive, Dropbox, or iCloud links — they redirect and may not render as inline images.seq1-text3-scottsdale-palms-01.jpg, seq2-text3-pv-before-after.jpg, etc.Goal: Show aspirational output. Make the lead picture their own home.
Goal: Show transformation. These are homeowners who understand what exterior lighting does for a home.
Texts in Seq 4 are about relationship and service, not sales. Keep them SMS-only. Adding photos can make it feel like marketing rather than a genuine check-in. The only exception would be if a future iteration includes a "here's what your install looks like in our portfolio" message.
Create a shared Google Drive folder: AZ Palm Tree Lights / MMS Photo Library
Structure:
MMS Photo Library/
├── seq1-new-lead/
│ ├── scottsdale-twilight-01.jpg
│ ├── scottsdale-twilight-02.jpg
│ └── pv-twilight-01.jpg
├── seq2-survey/
│ ├── before-after-01.jpg
│ └── before-after-02.jpg
└── archive/
└── (retired photos)
In each n8n workflow, reference the photo URLs as workflow-level variables so they can be updated without touching the workflow logic.
Before activating any sequence, verify the following:
your n8n webhookPart of your Outdoor Vibes asset library — yours to keep and run.