WF-Q1 · Quote Request Intake, Assignment and Acknowledgement
Why: the request is the only moment where the business has the customer's full attention and none of their patience spent. Everything that happens in the next five minutes determines whether this becomes a quote or a statistic.
Triggers: quote request form submitted; inbound call tagged as a quote inquiry; chat or social DM classified by BO as a price request; an inbound email matching quote keywords; a lead-form submission from a connected ad platform.
Actions, in order:
- Create the card at Request Received; stamp quote_requested_at; write quote_source and quote_number from a sequence.
- Assign quote_owner immediately: round robin within territory or service specialism, respecting who is on shift. Every branch after this depends on a named human existing, so the workflow must never exit here with the field empty. If no owner is available, assign the fallback user and flag it.
- Branch on business hours. In hours → proceed. Out of hours → send the out-of-hours acknowledgement with a specific promised time ("first thing tomorrow, before 9:30") and queue the owner's task for the start of the next working day. A vague "we'll be in touch soon" is worth nothing.
- Send the acknowledgement within 60 seconds. SMS where consent exists, email otherwise: "Thanks {{first_name}}, got your request for {{service_type}}. I'm {{quote_owner_first_name}} and I'll be looking after this. You'll have your quote by {{quote_due_by}}. Two quick things so I can price it properly: {{question_1}} and {{question_2}}?"
- The two questions are the whole trick. An acknowledgement that asks something converts a form submission into a conversation; one that only thanks them ends it. Ask the two that most often cause a delayed or wrong quote in this business.
- Post to the internal channel and create a task for quote_owner, due in 15 minutes during business hours.
- Hand the thread to BO for scope capture, decision-maker confirmation and (where the service needs it) site-visit booking. Write decision_maker_name, decision_maker_confirmed and decision_due_date.
- Move to Qualifying and Scoping, or Site Visit Booked where a visit is required.
- Wait 2 hours. Condition: no response to the qualifying questions. Send one nudge. Wait until next morning. Still nothing → create a call task for the owner. Three unanswered attempts → move to Not Now: Future and stop. Never prepare a speculative quote for someone who will not answer two questions; it is the most reliable way to waste senior time.
Exit: scoping complete and in preparation; or unresponsive after three attempts.
WF-Q2 · Preparation SLA and Internal Escalation
Why: this is where the real delay lives, and it is invisible because it is internal. Most businesses chase customers hard and never chase themselves.
Trigger: card enters In Preparation, or the scoping checklist is completed.
Actions:
- Set quote_due_by from job_complexity: Simple → same working day; Standard → 48 working hours; Complex → 5 working days. Count working hours, not calendar hours, or every Friday request is overdue by Monday.
- Create the preparation task for quote_owner with that due date, and block time in their quote-preparation calendar.
- Wait until 50% of the SLA has elapsed. Not sent → reminder to the owner only. Quiet, internal, no drama.
- Wait until the SLA elapses. Not sent → add quote-overdue-internal, notify the owner and their manager in the internal channel, and. This is the important part: message the customer before they notice: "{{first_name}}, I said you'd have this by {{quote_due_by}} and I'm running behind. You'll have it by {{new_time}}, apologies for the delay." A proactive apology costs almost nothing and preserves the quote. Silence past a promised date is what loses it.
- Wait 24 hours past SLA. Still not sent → escalate to the owner of the business, and reassign if quote_band is Large or Major. A major quote sitting unprepared for two days past its deadline is a commercial incident, not an admin lapse.
- Blocker branch: where preparation is waiting on the customer (a measurement, a document, an access arrangement) move to Qualifying and Scoping, pause the SLA clock, and run a three-step chase on the missing item. Do not let a customer-caused delay show as an internal failure, and do not let it hide as no delay at all.
Exit: quote sent; reassigned; or closed as unresponsive.
WF-Q3 · Quote Delivery and Read Confirmation
Trigger: quote document marked ready to send.
Pre-send conditions: block the send if any fail: quote_expires_at is populated; quote_amount is populated; quote_version is set; the recipient is the confirmed decision maker or the decision maker is copied; scope and exclusions are present; payment terms are present.
Actions, in order:
- Stamp quote_sent_at; calculate time_to_quote_hours; set quote_band; add quote-sent; move to Quote Sent.
- Send the email: the document link, a three-line summary of what it covers, the expiry date written as a date, and one line on what happens if they say yes. Attach nothing that cannot be opened on a phone.
- Wait 2 minutes. Send the SMS: "{{first_name}}, your quote for {{service_type}} is in your inbox and here: {{quote_link}}. It's valid until {{quote_expires_at}}. Happy to walk you through it: I've got {{slot_1}} or {{slot_2}} if that's easier than reading it."
- Offer the walkthrough every time. A quote explained live closes better than a quote emailed, and the offer itself signals confidence.
- Wait 4 hours. Condition: quote_viewed_at still empty and the email shows no open. Treat this as a delivery problem before treating it as a decision problem: send a plain-text version from a different template, and create a task for the owner to confirm the email address by phone. A quote that never arrived is the most expensive and most common failure in this build, and it is invisible unless you look for it.
- On first view: stamp quote_viewed_at, add quote-viewed, move to Viewed, and notify the owner in the internal channel. A quote being read right now is the best moment there will ever be for a call.
- Continue to WF-Q4 and WF-Q5 in parallel.
Exit: quote viewed, or the delivery-failure branch resolved.
WF-Q4 · The Chase Ladder
Why it is built as a ladder: one follow-up is not a chase, and five of the same email is harassment. The ladder works because it rotates channel, changes the question each time, and has a published ending.
Trigger: card in Quote Sent or Viewed with no decision.
Global conditions checked before every step: no decision recorded; chase_count under 5; last_chase_at more than 36 hours ago; quote not expired; no opt-out; decision_due_date has not been given as a later date. If the customer said they would decide on the 20th, the ladder pauses until the 20th and resumes then. Chasing before a date they gave you is the fastest way to be told no.
Cadence by quote_band. Small and Mid: days 2, 5, 9, 13 and expiry. Large and Major: days 1, 3, 6, 9, 12 and expiry, with a higher proportion of calls and the owner personally on at least two steps. Bigger decisions need more contact, not less. The instinct to leave big quotes alone is exactly backwards.
Actions, in order:
- Chase 1: day 2, SMS, soft and specific. "Hi {{first_name}}, just checking the quote came through alright: anything you want me to run through? {{quote_link}}" Set chase_channel_last = SMS; increment chase_count.
- Chase 2: day 5, phone call by the owner. Not a message. The script has one job: find the objection. "I'm not chasing for a decision. I just want to make sure the quote actually answers what you need." On no answer: voicemail, then the same content by SMS. Never two voicemails.
- Chase 3: day 9, email with the decision prompt. Subject: "Quote {{quote_number}}, where would you like to leave it?" Body: three buttons: Go ahead, I have a question, Not for now. The third button is what makes this step work. Most silence is not indecision; it is reluctance to say no. Give people a courteous exit and a large share of them take it, which converts dead pipeline into a recorded loss with a reason attached.
- Chase 4: day 13, SMS with the expiry. "{{first_name}}, quote {{quote_number}} expires on {{quote_expires_at}}. If you'd like it held or updated, just say, and if the timing's wrong, no problem at all, tell me when to come back."
- Chase 5: expiry day, the honest close. "That's quote {{quote_number}} expired today, {{first_name}}. I'll stop chasing you. It's all yours if you want to pick it back up: {{quote_link}}. One tap tells me why it wasn't right, which genuinely helps us: {{lost_reason_link}}" Then stop. There is no sixth message.
- Continue to WF-Q8.
Rules that keep this from becoming nagging: never send two chases in the same day; never chase outside 09:00–18:00 local on a working day; never chase a contact who has given a decision_due_date in the future; never send the same wording twice; and cap at 5 contacts enforced by chase_count in the field, so a revision cannot silently stack a second ladder on top of the first.
Exit conditions: any decision; any question raised; a revision issued (which restarts the ladder at chase 1 with a fresh count); expiry reached; opt-out.
WF-Q5 · View-Behavior Triggers
Why: view data converts a blind chase into an informed one. The same silence means three completely different things depending on whether the quote was never opened, opened once, or opened five times.
Trigger: quote document viewed.
Actions and branches:
- Increment quote_view_count; stamp quote_last_viewed_at.
- First view within 2 hours of sending → notify the owner immediately: "{{first_name}} is reading quote {{quote_number}} now." A call inside that window is the highest-converting action available anywhere in this build.
- View count reaches 3, or two views from different devices → add quote-hot, create a priority call task due within 4 hours, and notify the internal channel. Repeated reading without a reply nearly always means an unasked question (commonly about price, phasing or timing) and the person is far more likely to voice it on a call than in writing.
- A view by a new recipient, or a forward detected → the quote has reached someone else. Set decision_maker_confirmed = false, add decision-maker-unconfirmed, and have the owner ask, plainly and without awkwardness, who else is involved. Then send a version written for that person.
- No view 48 hours after sending → add quote-unopened, verify deliverability, and resend by a different channel. Do not advance the chase ladder's tone while the quote may still be unread; escalating pressure on someone who never received the document is both futile and annoying.
- A view after the chase ladder has gone quiet, or after expiry → reopen the card, notify the owner, and reach out the same day: "Saw you were looking at that quote again, {{first_name}}, want me to refresh it?" A re-view is a buying signal regardless of how old the quote is.
Exit: decision recorded.
WF-Q6 · Objection Routing and Revision Control
Trigger: "I have a question" tapped; BO classifies an inbound reply as an objection; or the owner logs one from a call.
Actions:
- Write objection_type; add quote-question-raised; move to Question Raised; cancel every pending chase; create a task for quote_owner due in 2 working hours.
- Branch on objection_type:
- Price → owner only, by phone, never by automated message. The brief is to understand the comparison before changing anything: what are they comparing against, and is it the same scope. A reflexive discount against an unexamined comparison is the most expensive habit in any quoting business.
- Timing → confirm whether it is their timing or yours. Their timing → set decision_due_date, pause the ladder, move to Not Now: Future with a dated re-entry. Your timing → check the calendar for a genuine alternative before replying.
- Scope unclear → revise, do not explain. If the quote generated this question, the document is at fault, and the same gap is costing you quotes you never hear back about. Fix the template as well as the quote.
- Need approval → the real work is enabling the internal champion. Send a one-page summary written for the approver, offer to join a call, and ask for the approver's name and the decision date. Update decision_maker_name.
- Comparing quotes → no discount. Send the comparison aids. What is included that is commonly excluded, warranty, references, insurance, timescale, and ask directly what would make the decision easy. Record competitor_named.
- Finance or payment terms → route to whoever is authorized to discuss terms, send the approved options only, and never improvise a payment plan in an automation.
- Trust or references → send references, credentials, insurance, and recent work. This objection is cheap to answer and frequently decisive.
- Technical question → route to the technical owner with a 4-hour SLA. BO answers nothing technical it cannot quote from the document verbatim.
- On any revision: increment quote_version and revision_count; set a fresh quote_expires_at; supersede the previous version in the document module so exactly one quote is live; resend through WF-Q3; restart the chase ladder with chase_count reset.
- At revision_count of 3: stop revising and escalate to the owner of the business. Three revisions means the scope was never agreed, and the next version will not fix that either. Have the conversation instead.
- Wait 24 hours after any objection is logged. Condition: no response from the owner recorded → escalate to their manager. An unanswered objection is a lost job; this is the most urgent queue in the build.
Exit: revision issued; objection answered and the ladder resumed; or moved to a terminal stage.
WF-Q7 · Win Path: Verbal Yes to Scheduled
Why the urgency: the gap between a verbal yes and a signature is where more revenue is lost than anywhere else in this pipeline. Enthusiasm decays in hours, competitors are still in play, and an unsigned yes is not a sale. This workflow measures its own steps in minutes.
Trigger: "Go ahead" tapped; owner logs a verbal acceptance; or a signature is returned.
Actions, in order:
- Move to Verbal Yes; add verbal-yes; cancel every pending chase; notify the internal channel.
- Within 1 minute, send the signature request. Same day is not good enough. "Brilliant, {{first_name}}, here it is to sign, takes about a minute on your phone: {{signature_link}}. Once that's in I'll confirm your start date." Stamp signature_requested_at; add awaiting-signature.
- Hold a provisional start date in the job calendar immediately and name it in the message. A date makes the decision concrete and creates a reason to sign now.
- Wait 4 hours. Unsigned → one SMS nudge. Wait until next morning. Unsigned → call task for the owner. Wait 2 days. Unsigned → escalate; a verbal yes that has not signed in 48 hours has usually met an obstacle nobody has named yet, and it needs a conversation rather than another link.
- On signature: stamp signed_at; move to Won: Signed; add quote-won; file the executed document against the card; announce the win internally with quote_band, quote_source and time_to_quote_hours, so the team sees which sources and which response times are producing wins.
- Deposit branch where deposit_required is true: send the payment link by SMS and email within 2 minutes of signature, while they are still holding the phone. Stamp deposit_requested_at; add deposit-outstanding. Wait 1 day → reminder. Wait 3 days → call task. The start date stays provisional and is explicitly described as provisional until the deposit clears. Never let an automation take payment without the authorization the customer actually gave.
- On payment: stamp deposit_paid_at; confirm scheduled_start_date; move to Scheduled; release the provisional hold into a confirmed booking.
- Operational handover: create the job, notify the delivery team with the scope and the scoping-checklist attachments, and send the customer a short what-happens-next message naming their contact and the date. Set handover_complete.
- Wait 2 days. Send the onboarding sequence or the pre-start checklist. A won quote that goes quiet between signature and start date generates cancellations.
Exit: scheduled and handed over; or signature abandoned and escalated.
WF-Q8 · Loss, Expiry and Reason Capture
Why you must force an ending: an unresolved quote is worse than a lost one. It inflates the forecast, occupies attention, and teaches the business nothing. The purpose of this workflow is to convert silence into a recorded outcome.
Trigger: "Not for now" tapped; owner logs a loss; or quote_expires_at passes with no decision.
Actions:
- On an explicit no: reply within the hour, graciously and with no attempt to reopen: "Thanks for telling me, {{first_name}}, genuinely appreciated, it's better than wondering. If it helps us improve, one tap: {{lost_reason_link}}. Best of luck with it." A business that takes a no well is the business people come back to.
- Write lost_reason and competitor_named from the form or the owner's note. Move to Lost: Reason Known; add quote-lost; remove from the forecast.
- On expiry with no answer: move to Expired: No Decision, add quote-expired, set lost_reason = No decision made, and expire the quote in the document module so the price genuinely stops being live. A quote that is still honourable six months later is an open-ended commitment nobody agreed to.
- Branch on lost_reason:
- Price → no counter-offer by automation. Flag for the monthly pricing review; a pattern across many losses is useful, a reflexive discount on one is not.
- Timing or Project canceled → move to Not Now: Future, set a dated re-entry from what the customer said, and continue to WF-Q9.
- Went elsewhere → record competitor_named and send one short, unbitter message asking what tipped it. The answers to this question are the cheapest competitive intelligence available, and people answer it surprisingly often.
- Scope changed → offer to requote once, with a clear deadline.
- Never a fit → feed back to whoever owns lead qualification. This is an acquisition problem being reported through the quoting process, and the fix is upstream.
- Lost contact → check for a bad phone or email address first. A meaningful share of these are data failures rather than decisions.
- At 7 days after the loss: create a task for the owner to make one personal follow-up call on Large and Major losses only. Nothing else; this is relationship work, not a second chase.
- Add to the long-cycle nurture audience, and continue to WF-Q9.
Exit: terminal, with a reason recorded. A card leaving this build without a lost_reason is a reporting defect and should appear on the weekly hygiene flag.
WF-Q9 · Long-Cycle Nurture and Dated Re-Entry
Why: a large share of lost quotes were never a no, only a not-yet. The difference between a business that captures those and one that does not is whether a date was written down.
Trigger: card enters Not Now: Future, Lost: Reason Known or Expired: No Decision.
Actions:
- Set a dated re-entry from what the customer actually said ("after the summer", "next budget year", "once we've moved") stored in decision_due_date. Where they gave nothing, default to 90 days.
- Enroll in a light nurture: one useful message a month, maximum. Completed work similar to theirs, a seasonal note, a genuine change in service or availability. No quotes, no discounts, no "just checking in".
- On the re-entry date: create a task for the original quote_owner (the same person, who remembers them) to make one personal approach: "{{first_name}}, you mentioned {{timing_reason}} when we spoke in {{month}}, is now a better time? Happy to refresh the quote." One attempt, by a human, referencing what they said. This single task converts better than any sequence in this workflow.
- If they re-engage: create a new quote card with a new quote_number, linked to the old one. Never resurrect an expired quote at its old price; prices move and reviving a stale figure is how a win becomes a loss-making job.
- Seasonal and trigger-based re-entry: where the service is seasonal, re-enter the audience ahead of the season rather than on a fixed interval.
- Hard stop: two unanswered annual cycles → remove from nurture and archive. Set a retention rule for quote documents and personal data, and apply it automatically.
Exit: re-engaged into a new quote; or archived.
WF-Q10 · Daily Quote Board and Weekly Reporting
Why: the build's value compounds only if somebody looks at it every morning. This workflow makes the pipeline unignorable.
Trigger: scheduled, 08:00 each working day, and 08:00 Monday.
Daily actions:
- Post the board to the internal channel, in four sections: overdue internally (past quote_due_by, not sent). Always first, because it is the only section the business fully controls; hot (quote-hot or viewed in the last 24 hours); objections unanswered past 24 hours; verbal yes unsigned past 24 hours.
- Create or re-raise one task per item, assigned to the named owner, with the stage dwell time shown so age is visible rather than inferred.
- Flag every card with no quote_owner, no quote_expires_at, or no decision_maker_confirmed. These three gaps account for most of the quotes that quietly vanish.
Weekly actions:
- Email the owner the digest: quotes requested, sent, won, lost and expired; median and 90th-percentile time_to_quote_hours; win rate by quote_band, by quote_source and by quote_owner; objection and loss-reason distribution; average chase_count on wins versus losses; and the count of cards sitting past their stage dwell limits.
- Report win rate against time_to_quote_hours bands. This is usually the single most persuasive chart the build produces, and it tends to settle the internal argument about whether speed matters.
- Flag any owner whose median preparation time is materially worse than the team's, and any service type whose revision_count runs high. The latter is a template or scoping problem, not a sales one.
- Forecast hygiene: list every quote still counted as open past its expiry date. Force each one to a stage. A forecast containing dead quotes is not a forecast.
Exit: runs indefinitely.