Workflow 1: Request Capture & Instant Response
Trigger: any of. Estimate request form submitted, web chat started, inbound SMS from an unknown number, inbound call answered, or a form on a partner or marketplace listing.
- Match or create the contact on phone number first, then email. Match on service address as a secondary check so a repeat customer at the same property links to their history instead of creating a second record.
- Create the card in New Request. Write first_touch_timestamp, lead_source, source_campaign, service_type, service_address, urgency, job_scope_summary.
- Geocode the address into zip_zone and drive_time_bucket. Branch immediately: if the zone is outside the service area, jump to Workflow 3.
- Branch on urgency. If emergency: ring the on-call phone, send the internal alert, and send the homeowner "Calling you right now. If you don't hear from us in 5 minutes, call {{number}} directly." Skip every nurture step below; an emergency job is won by a ringing phone, not a sequence.
- Branch on consent. If sms_consent is true, send the SMS. If not, send the email version and create a call task.
- Send SMS immediately, no wait: "Hi {{first_name}}, this is {{company_name}}, got your request for {{service_type}} at {{street}}. I can get an estimator out to take a look. Are mornings or afternoons better for you? Reply STOP to opt out."
- Write first_response_timestamp. Move card to Contacted: No Reply.
- Branch on hours. Inside business hours: assign assigned_estimator by zone, notify the team channel, and create a call task due in 5 minutes. Outside hours: BO holds the thread and the call task is created for the next business morning at open.
- If photos were uploaded, notify the estimator with the photos attached and ask one question on the card: can this be scoped virtually. A yes routes the booking to the Virtual Estimate calendar and saves the drive.
Exit conditions: homeowner replies (hand to Workflow 2), site visit booked, sms-stop, or card closed.
Workflow 2: Qualify and Book the Site Visit
Trigger: homeowner replies on any channel, or answers the outbound call.
- Move card to Qualifying. Capture, in this order: confirm the service address, confirm service_type and rough scope, confirm urgency, then ask the decision-maker question.
- The decision-maker question is scripted because it is the highest-value question in the build: "When we come out, will everyone who's part of the decision be there? It saves you a second appointment." Write decision_makers_count and all_decision_makers_present.
- Ask about competing bids plainly: "Are you getting other quotes? Totally normal. It just helps me know what to include." Write competing_bids_count. Apply competing-bids when it is one or more, which changes the follow-up cadence later.
- Branch on scope and photos. Simple scope with usable photos: offer Virtual Estimate slots and apply virtual-ok. Otherwise offer Estimate Appointment slots from the estimator whose zone-restricted availability matches zip_zone, so the booking lands inside an existing route.
- On booking: move card to Site Visit Scheduled, write assigned_estimator, and send confirmation SMS: "You're set, {{first_name}}, {{estimator_first_name}} will be out {{day}} between {{window_start}} and {{window_end}}. He'll text when he's on the way." Offer a window, not a to-the-minute time, and then beat it.
- If no slot is accepted after two offers: create a call task rather than sending a third list of times. A homeowner who will not pick a slot by text usually has a question they have not asked.
- Immediately send the Pre-Visit Scope Questionnaire by SMS and email with a photo prompt.
Exit conditions: site visit booked, out of area, homeowner declines, or hand to Workflow 4 on no reply.
Workflow 3: Out of Area / Not a Fit
Trigger: zip_zone resolves outside the service area, or the service type is one the company does not take.
- Apply out-of-area. Send immediately: "Thanks for reaching out, {{first_name}}, {{street}} is just outside the area we cover. I don't want to waste your time, so here's who I'd call instead: {{referral_name}}, {{referral_number}}."
- Move card to Out of Area / Not a Fit with the reason and the zip recorded. Do not nurture; do not add to any list.
- Log the zip against the service type. The monthly report of out-of-area requests by zip is a genuine expansion signal and costs nothing to collect.
- If a referral partner relationship exists, notify the partner with the homeowner's permission only.
Exit conditions: card closed. This workflow runs once.
Workflow 4: No-Reply Ladder
Trigger: card in Contacted: No Reply for 2 hours with no inbound response.
Global conditions: quiet hours 8:00am–8:00pm local; exits instantly on any reply or booking; every step gated on sms-stop.
- +2 hours. SMS: "{{first_name}}, still happy to get someone out to look at {{service_type}}. Here's our schedule if you'd rather just pick a time: {{booking_link}}"
- +4 hours. Call attempt 1 from assigned_estimator or the office, whichever the company decides owns first contact.
- Day 1, 9:00am. Email: subject "Your {{service_type}} estimate". Body names the estimator, says what the visit involves and how long it takes, says the estimate is free and written, and links the self-scheduling page.
- Day 1, 3:00pm. Call attempt 2, different time of day on purpose.
- Day 3, 11:00am. SMS: "Hi {{first_name}}, did you still want that estimate, or have you handled it already? Either answer is fine, just let me know." Giving permission to say no raises the reply rate and produces a clean loss reason instead of silence.
- Day 5. Final email with the self-scheduling link and nothing else to do.
- Day 6. Move card to Lost: No Response, keeping lead_source and source_campaign attached. Apply reheat-90.
Exit conditions: any reply, booking, sms-stop, or day 6 reached.
Workflow 5: Pre-Visit Preparation and No-Show Prevention
Trigger: estimate appointment booked.
- Immediately: confirmation SMS plus an email carrying the window, the estimator's name and photo, roughly how long the visit takes, what the homeowner should have ready, and the questionnaire link.
- Wait 24 hours. If the questionnaire is incomplete, SMS: "{{first_name}}, two quick questions before {{estimator_first_name}} comes out. Takes a minute and makes the estimate more accurate: {{questionnaire_link}}"
- T-2 days. Branch on all_decision_makers_present. If false or empty: "{{first_name}}, is there anyone else involved in deciding on this? If so it's worth having them there {{day}}, saves doing it twice." Update the field from the reply.
- T-1 day, 5:00pm. SMS: "See you tomorrow between {{window_start}} and {{window_end}}, {{first_name}}. {{estimator_first_name}} will text when he's on the way. Reply R if you need to move it."
- Morning of, 7:30am. SMS with the route position: "Morning {{first_name}}, you're {{position}} on {{estimator_first_name}}'s route today, still looking like {{refined_window}}."
- On the estimator marking en route. SMS: "{{estimator_first_name}} is on his way: about {{eta_minutes}} minutes out." This one message prevents most wasted trips.
- No answer on arrival: estimator taps a button that sends "{{first_name}}, {{estimator_first_name}} is at {{street}} now: give him a call at {{estimator_number}}?" and creates an urgent office call task. Waiting fifteen minutes in a truck in silence is how a route gets destroyed.
- If no-show confirmed: apply a no-show flag, send "Sorry we missed you today: want to pick another time? {{booking_link}}", and route to a single rebooking attempt. A second no-show requires a phone confirmation before any estimator drives again, which is a policy the company sets, not the automation.
Exit conditions: site visit marked complete, canceled, or no-show handled.
Workflow 6: Estimate Delivery Clock
Trigger: site visit marked complete.
The company's close rate is tied more tightly to this clock than to its pricing. Pick a delivery commitment, state it to the homeowner on site, and enforce it here.
- Move card to Estimate Being Built. Start the clock from the completion timestamp.
- Send the homeowner the expectation immediately: "Thanks for your time today, {{first_name}}. {{estimator_first_name}} is putting your options together: you'll have everything by {{commitment_time}}." A stated deadline the company then meets is itself a selling point, and it is free.
- Create the build task for assigned_estimator with a hard due time.
- At 50% of the committed window: remind the estimator.
- At 100% with the estimate not sent: notify the sales manager or owner. No exceptions and no silent extensions. This escalation is the mechanism, and removing it removes the workflow.
- At 150%: send the homeowner a short, honest holding message: "{{first_name}}, {{estimator_first_name}} is still finalizing a couple of numbers on yours. You'll have it by {{new_time}}, sorry for the wait.": and escalate again. A homeowner told the truth about a delay stays in the game; a homeowner left in silence calls the next company.
- On send: write estimate_sent_datetime, estimate_number, options_presented_count, estimate_expiry_date, and compute estimate_delivery_hours. Move card to Estimate Sent, apply estimate-sent, hand to Workflow 7.
Exit conditions: estimate sent, or job disqualified on site. In which case the Loss Reason form is required before the card closes.
Workflow 7: Estimate Follow-Up Ladder
Trigger: card enters Estimate Sent.
This is the workflow most companies do not have, and the one that produces the largest measurable gain. Its central feature is that it branches on whether the estimate has actually been opened, because an unopened estimate is a delivery problem and an opened one is a decision problem.
- Immediately on send. SMS: "{{first_name}}, your estimate's ready. Three options so you can see the trade-offs: {{estimate_link}}. Call or text me with any questions.: {{estimator_first_name}}" Plus the email version with the same link and a plain-language summary of what each option includes and excludes.
- On first view event. Write estimate_viewed_datetime, increment estimate_view_count, move card to Estimate Viewed, apply estimate-viewed, and notify assigned_estimator in real time: "{{first_name}} is reading the estimate now." Create a call task due in 60 minutes. This single notification is the highest-conversion moment in the entire pipeline.
- Unviewed branch, +4 hours. SMS: "Just checking it came through okay, {{first_name}}, here it is again: {{estimate_link}}"
- Unviewed branch, day 1. Call task: confirm receipt by voice. Do not pitch; confirm delivery. If the email bounced or the number is wrong, this is where you find out instead of recording a false loss.
- Day 2, viewed or not. SMS: "{{first_name}}, any questions on the options? Happy to walk through the differences in two minutes on the phone if that's easier."
- Day 3. Email with the one thing an estimate PDF cannot carry: proof. Photos of comparable completed work, the warranty terms in plain English, what happens on install day, and the financing link. No price change.
- Day 4. Call attempt 2, placed in the Follow-Up Call Block.
- Day 6. Branch on competing_bids_count. If one or more: "{{first_name}}, if you've got other quotes in hand I'm happy to look at them with you, not to beat a number, just so you can compare what's actually included. The cheap ones usually leave something out." If zero: "{{first_name}}, where did you land on this one?"
- Day 8. Branch on estimate_view_count. Three or more views: apply viewed-3-plus, notify the owner, and have the owner or senior estimator make the call personally. Repeat views mean active deliberation, and that call is worth making at a higher level. Zero or one view: treat as low intent and continue the standard ladder.
- Day 10. SMS referencing the expiry honestly: "{{first_name}}, your pricing holds through {{estimate_expiry_date}}, after that I'd have to re-check material costs. Want me to lock it in?" Only use this if the expiry is real; a fake deadline is noticed and remembered.
- Day 13. The permission-to-close message: "{{first_name}}, I don't want to keep bugging you. Should I close this out, or is it still live?" This message produces more clean answers than any other in the ladder.
- Day 15. Move card to the matching Lost stage. The Loss Reason form is mandatory. Apply reheat-90. Hand to Workflow 11.
Exit conditions: acceptance form submitted, card moved to In Discussion or Verbal Yes by a human, homeowner declines, sms-stop, or day 15 reached. Cadence cap: never more than one SMS plus one call in a day, and never two messages in the same hour across workflows.
Workflow 8: Objection and Revision Branch
Trigger: card moved to In Discussion, or a reply matching a price, timing or scope objection.
- Price objection. Apply price-objection. Do not discount automatically. An automated discount trains every future homeowner to object. Create a call task, send the financing link, and offer a reduced-scope option as a tracked revision: "{{first_name}}, I can't move the number on that scope, but I can show you a version that fits a different budget. Want me to put it together?"
- Timing objection. Capture the real date. Write reheat_date, move the card to a scheduled nurture, and set a task for two weeks before that date. Then actually honor it. A timing objection remembered six months later closes at a rate that surprises most owners.
- Scope change. Send the Scope Change Request form, increment revision_count, and restart Workflow 6's delivery clock at a shortened window. A revision is a buying signal and it deserves the same urgency as the original.
- Competitor comparison. Create a call task and send the inclusions comparison, never a rebuttal by text. Write lost_to_competitor if the job is lost, because the aggregate of that field is the company's only honest competitive picture.
- Decision-maker objection ("I need to talk to my wife"). Offer a scheduled three-way call at a specific time rather than leaving it open: "Want me to hop on a quick call with both of you? I've got {{slot_1}} or {{slot_2}}." An open-ended "let me know" at this point is where the job dies.
- At revision_count of 3: escalate to the owner. Three revisions means the scope, the budget or the fit is not what anyone thinks it is, and more revisions will not fix it.
Exit conditions: acceptance, card moved to a Lost stage with a reason, or scheduled reheat.
Workflow 9: Signature and Deposit
Trigger: Estimate Acceptance form submitted, or a human moves the card to Verbal Yes.
- On verbal yes. Send the acceptance link within 5 minutes: "Great news {{first_name}}, here's the agreement to sign, takes about a minute: {{acceptance_link}}" Then a reminder at 2 hours, a call task at 1 day, and an owner escalation at 3 days. A verbal yes that stays verbal for a week is not a yes.
- On signature. Write option_selected, contract_signed_datetime, contract_status. Move card to Signed: Deposit Pending, apply signed, notify the owner and the scheduler.
- Deposit request in the same session. The payment step appears immediately after signature on the same page, not as a separate email later. Write deposit_requested_date, set deposit_status = requested.
- If not paid in-session, +2 hours. SMS: "{{first_name}}, just the deposit left and we can get you on the schedule: {{payment_link}}"
- Day 1. Email with the invoice attached and the payment link. Day 2. Call task.
- Day 4. SMS: "{{first_name}}, I'm holding {{install_window}} for you but I can only hold it a couple more days without the deposit." Only send this if the hold is genuine.
- Day 7. Escalate to the owner and release the install slot back to the calendar. A card in this stage with no money is blocking real capacity.
- On payment. Write deposit_paid_date, set deposit_status = paid, apply deposit-paid, and hand to Workflow 10.
Exit conditions: deposit paid, homeowner cancels (Loss Reason form required even post-signature. These are the most instructive losses of all), or day 7 escalation resolved by the owner.
Workflow 10: Won: Scheduling and Pre-Install
Trigger: deposit_status = paid.
- Offer install windows from the Install calendar filtered by service_type and crew capacity. Write install_date, install_window, assigned_crew. Move card to Won: Scheduled.
- Send confirmation: date, window, crew lead's name, what the homeowner needs to do beforehand, how long it will take, and what to expect on the day.
- Branch on permit_required. If true, create the permit task with its own due date and tell the homeowner the schedule depends on it. Branch on hoa_approval_required: same pattern, with the submission task owned by a named person.
- T-7 days. Email with the prep checklist. Clear access, move vehicles, secure pets, what will be noisy, whether water or power goes off and when.
- T-1 day, 4:00pm. SMS confirmation with the arrival window and the crew lead's name.
- Morning of. SMS with the refined window, then an on-the-way message when the crew departs.
- On completion. Generate the balance invoice per balance_terms, send the payment link, and hand to Workflow 12. Register the warranty and store the executed documents on the card so the next job at this address starts with the full history.
Exit conditions: job complete and balance settled.
Workflow 11: Loss Capture and Reheat
Trigger: card moved to any Lost stage.
- Gate the close. The card cannot sit in a Lost stage without the Loss Reason form submitted. If it is missing after 24 hours, task the estimator; after 48, escalate to the owner. Unreasoned losses are how a company convinces itself it has a price problem.
- Write lost_reason, lost_to_competitor, and the option that was in play when it died.
- Branch on reason. Price → reheat-90. Timing → reheat at reheat_date minus 14 days. Went another direction → reheat-90 plus a seasonal touch. No response → reheat-90, lowest-touch nurture only.
- At 30 days (price and competitor losses only). One email, no pitch: "How did the {{service_type}} work turn out?" A surprising share of these jobs were never done, or were done badly.
- At 90 days. SMS if consent holds: "{{first_name}}, checking in: did you get the {{service_type}} sorted? If not, I can re-quote at current pricing."
- Seasonally thereafter. Two emails a year, tied to the service's season, with real content rather than a re-sent estimate. Any reply reopens a fresh card with the full history attached and lead_source = reheat, so the reheat channel earns its own line in the reporting.
Exit conditions: card reopened, homeowner opts out, or two years elapsed.
Workflow 12: Review and Referral
Trigger: job marked complete.
- Wait 1 day: long enough for the homeowner to have lived with the work, short enough to still feel current.
- Send the Post-Install Survey as a single question: "How did we do, {{first_name}}, 1 to 5?"
- Branch. 4 or 5 → send the review link immediately, while the goodwill is live, and name the crew lead so the review is specific. 1 to 3 → send "Thank you for telling us: {{owner_name}} will call you today." and create an urgent owner task. No review link on a low score, ever.
- Wait 7 days after a 4 or 5. Send the referral ask separately, because bundling it with the review request halves both: "{{first_name}}, glad you're happy with it. If a neighbor ever needs {{service_type}}, send them my way: I'll take good care of them."
- Wait 11 months. Seasonal maintenance or check-in message, tagged past-customer. The cheapest job a home services company will ever sell is the second one at an address it already knows.
- Write review_requested and referral_asked so no homeowner gets asked twice and the rates are reportable by crew and by service type.
Exit conditions: survey answered and routed, or 7 days with no response after one email reminder.
Workflow 13: Pipeline Hygiene and Daily Board
Trigger: nightly, plus a morning digest before the first Follow-Up Call Block.
- Flag any card with no activity in 7 days with stalled-7-days and notify assigned_estimator. Flag at 14 days to the owner.
- Flag any card in Estimate Being Built past its committed window. The single most damaging stall in the pipeline.
- Flag any card in Verbal Yes for more than 3 days, and any card in Signed: Deposit Pending for more than 5.
- Flag estimates within 5 days of estimate_expiry_date with expiry-approaching and surface them at the top of the morning list.
- Publish the morning digest: new requests overnight, site visits today, estimates due to be sent today, estimates viewed in the last 24 hours and not yet called, and deposits outstanding. Five lines, in that order of priority.
- Send the owner a weekly summary: requests by source, median speed to first response, median estimate_delivery_hours, estimate-to-signed rate by estimator, and the loss-reason mix.
Exit conditions: none: runs every day.
Workflow 14: Consent, Opt-Out and Quiet Hours Guard
Trigger: runs ahead of every outbound message above.
- STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT: apply sms-stop, set sms_consent = false, remove from every active SMS step, send the single required confirmation. Keep email and call paths alive if those consents stand.
- START or UNSTOP: restore consent with a fresh timestamp and source.
- HELP: reply with company name, phone number and the opt-out instruction.
- Before any send: suppress if the channel consent is not true and fall back to the next consented channel or a call task.
- Before any send: enforce quiet hours 8:00am–8:00pm in the homeowner's local time. The one carve-out is a dispatch-critical message on the day of a scheduled visit, an on-the-way text, and even that stays inside the window.
- Before any send: enforce the global cadence cap. One SMS and one call per card per day, maximum, counted across every workflow. A homeowner in Workflow 7 and Workflow 11 simultaneously must not receive both.
- Suppress all marketing and nurture messaging for any card in an active job stage. A homeowner with a crew in their driveway should not receive a message asking whether they are still interested.
Exit conditions: none. This guard never turns off.