Internal · delivery operations · v1 · Sept 2026

Delivery.
Every SOP.

Everything that happens after the deposit clears. Michael owns this manual. Every step is tagged by who does it. Edge cases are the red arrows — read them before you touch a client account.

IndexSixteen SOPs

DEL-00Delivery rules

Non-negotiable. Breaking one of these is how you lose a client, get a number blocked, or get sued.

  1. We manage. The client owns. Domain, GBP, Google account, Meta, phone number, contact list — always in the client's name with us added as manager. The website code and the GHL sub-account are ours. Never register anything of theirs under our email.
  2. No SMS before A2P approval. Not one. Unregistered traffic gets the number filtered and can flag the whole agency account. Email-only until the campaign shows Approved.
  3. Never touch the client's main phone number. We buy a new tracking number and forward it to their cell. We do not port their existing number. Ever.
  4. Every automation has a stop condition. Reply, STOP, booked, tag removed, or 3 attempts. If you can't say what stops it, don't turn it on.
  5. Test lead before you say "live." Form → pipeline → SMS → client notification, on the production domain, on a real phone. Screenshot in the client's Drive folder.
  6. One owner per task, written down. If a task in this manual doesn't have a chip on it, it doesn't get done. Say so and fix the manual.
  7. Client deadlines are stated with a consequence. "Photos by Friday or the launch slides a day for every day late." In writing, every time.
  8. Never break email. Any DNS change is preceded by an export of the current records and a check for MX/SPF/DKIM. Breaking a client's email is the one mistake they never forgive.
  9. Snapshot every improvement. Fix a workflow for one client → update the master snapshot the same day. The 50th client should inherit everything the 1st one taught us.
  10. Josh does not log into GHL after handoff. Michael escalates; Josh doesn't browse.
01Access + security

Vault every login, own nothing

One shared vault per client, 2FA on everything, client grants manager access instead of handing us passwords.

OwnerMKWhenDay 11 kickoffDone whenVault folder has every login and the access matrix is filled
MKCL

MKSetup

  • Create a folder in the agency password manager (Bitwarden/1Password) named exactly like the GHL sub-account: ClientName — City
  • Access matrix in the client's Drive folder: domain registrar, DNS, GBP, Google Analytics/Search Console, Meta Business, GHL sub-account, any existing CRM/booking tool. For each: who owns it, our role, 2FA method, recovery email
  • Prefer role-based access over passwords: GBP manager, Meta Business partner, registrar delegate/collaborator, Search Console user. Ask for password only when the platform has no role system
  • Any 2FA we hold is a TOTP seed stored in the vault, never SMS to a personal phone
  • Agency-level GHL admin: Josh and Michael only. VAs get sub-account user roles, never agency admin. (Agency admin and account admin are different tiers — treat them that way)
  • support@ is a shared inbox in the agency Workspace. Every client-facing email comes from it. Nobody uses personal Gmail for client work

CLClient provides on kickoff call

  • Grants GBP manager access live on the call (we watch them do it)
  • Registrar login or collaborator invite
  • EIN, legal business name exactly as on the IRS letter, business address
  • Cell number that will receive calls/texts and the email that gets lead notifications

Edge cases

  • Client doesn't know who owns the domain. WHOIS lookup → usually a former web guy or GoDaddy under an old email. Start recovery on day 11, not launch week. If unrecoverable by day 20, buy a new domain and 301 later if the old one ever comes back
  • GBP owned by a previous agency. Client requests ownership through Google's "request access" flow. Google gives the current owner 3 days to respond. Start day 11
  • Client wants to share one password for everything. Refuse politely, explain the "you own it" rule, and set up role access instead. This protects them and us
  • Client leaves / partner dispute. Because everything is in the client's name, we only ever remove our manager role. Never get in the middle
02GHL sub-account + snapshot

Clone, don't build

Every client is a clone of the master snapshot. The snapshot is the product. Build once, improve forever.

OwnerMKWhenDay 11, within 24h of depositDone whenSub-account live, snapshot loaded, custom values filled, agency billing confirmed
MK

MKMaster snapshot contents (build once)

  • Pipeline: New Lead → Contacted → Quoted → Booked → Won → Lost. Nothing else. Clients don't get to add stages until month 3
  • Custom values: business name, owner first name, owner cell, review link, booking link, service area, hours, tracking number. Every workflow message references these — never hardcode a client's name in a workflow
  • Workflows (all OFF by default in the snapshot): Speed-to-lead, Missed-call textback, Review request, Appointment reminders, No-show follow-up, Dead-lead reactivation (rung 3 only), Monthly report
  • Tags: src:website, src:gbp, src:referral, src:ads, job-complete, review-sent, review-left, dnc, vip, reactivation
  • Calendar template with 15-min buffers, reminders at 24h and 1h
  • Website form (if using GHL native form) with SMS consent checkbox, unchecked by default
  • Saved reply templates for support and for the client's inbox (quote sent, on my way, running late)

MKPer-client provisioning

  • SaaS mode: create sub-account from the Growth Plan SaaS plan so rebilling (usage, phone, email) is on. Confirm the client's card from the contract is attached at the sub-account level — if it isn't, usage bills the agency
  • Name: ClientName — City. Timezone: America/New_York (verify — FL panhandle is Central). Business address = the legal address from DEL-01
  • Load snapshot. Fill every custom value. Search the sub-account for any leftover "{{" or "[client]" placeholder before going further
  • Add the client as a user with the User role (not Admin): they can see conversations, pipeline, calendar. They cannot edit workflows
  • Install the mobile app on the client's phone on the kickoff call. Turn on push notifications for new conversations. This is where they'll live
  • Disable every default GHL notification email to the client except New Lead. Agency Dashboard emails go to support@ only
  • Turn workflows ON only when their dependency is met (see DEL-03 → 05 → 07 → 09)

Edge cases

  • Snapshot changed after a client was cloned. Re-pushing a snapshot overwrites their customizations. Push individual workflows, not the whole snapshot, to existing clients
  • Client already has a GHL account from another agency. Don't inherit it. Build ours clean, export their contacts as CSV, import with tag imported. Their old agency's A2P campaign does not transfer
  • Client wants admin. No. Admin can break workflows and delete the pipeline. Offer a screen-share walkthrough instead
  • Timezone wrong. Every reminder and quiet-hour rule fires wrong. Check it before turning on a single workflow
03A2P 10DLC

Register on day 11 or nothing texts

Brand + campaign registration through GHL. Takes days to weeks. Every SMS feature waits on it. One rejection costs a week.

OwnerMKWhenDay 11, first task after the kickoff callDone whenCampaign status = Approved and a test SMS delivers
MKJMCL

MKBefore you submit — the pre-check (30 min, prevents 90% of rejections)

  • Legal name + EIN + address match the IRS CP-575 letter and Sunbiz exactly. "Redline Epoxy LLC" ≠ "Redline Epoxy, LLC" ≠ "Redline Epoxy". Sunbiz is the source of truth for FL entities
  • Website is live (staging counts if publicly reachable) and shows the same business name + address in the footer
  • Website has a privacy policy page with an SMS clause: what we text, frequency, how to opt out, no sharing of consent data with third parties
  • Website has terms page mentioning SMS program: message/data rates, frequency, STOP/HELP
  • Every form that captures a phone number has an unchecked SMS consent checkbox with the full disclosure text next to it
  • Screenshots of the consent checkbox and the privacy clause saved to Drive — they go in the submission

MKSubmit (in GHL → sub-account → Phone → Trust Center)

  • Brand: Standard brand with EIN. Use the legal name, the registered address, the client's own email and phone (not ours). Vertical: pick the true industry
  • Campaign use case: Mixed (customer care + marketing) unless the client will only ever do transactional — then Customer Care. Mixed covers reviews, reminders, and reactivation
  • Campaign description: who they are, who opts in, what messages say. Paste-ready base below, fill the brackets
  • Opt-in description: "Customers opt in via the website contact form checkbox at [URL], by texting the business number, or verbally when booking a service, which is logged in the CRM." Attach the screenshots
  • Sample messages: 2 minimum, must include business name and opt-out language. Templates below
  • Check every box: embedded link yes, embedded phone yes, age-gated no, direct lending no
  • Log submission date on the contact record + Drive. Set a 5-business-day follow-up
Campaign description: [Business name] is a [service] company in [city], FL. Customers who request a quote on our website, call us, or book a service opt in to receive text messages about their appointment, quotes, follow-ups, review requests, and occasional offers. Frequency varies, typically 2–6 messages per job. Customers can reply STOP at any time to opt out. Sample 1: Hi [first name], it's [owner] with [Business name]. Thanks for reaching out — I'll call you in a few minutes. If now's bad, reply with a better time. Reply STOP to opt out. Sample 2: [Business name]: reminder, we're scheduled for [date] at [time] at [address]. Reply C to confirm or R to reschedule. Reply STOP to opt out.

JMWebsite side (during build)

  • Privacy policy + terms pages in the scaffold with the SMS clauses already written. Every site ships with them
  • Consent checkbox on every form: unchecked, full disclosure text visible, stored as a form field that maps to GHL's DND/consent field
  • Footer NAP identical to GBP and Sunbiz

Edge cases

  • Sole proprietor, no EIN. Register as Sole Proprietor brand (uses SSN, 1 number, low daily cap). Works for reviews and reminders. Tell Josh — the client should get an EIN (free, 10 min) before rung 3 reactivation campaigns, which will blow the cap
  • Rejected: name/EIN mismatch. Most common. Get the CP-575 and resubmit with the exact name. Resubmission costs a fee — that's why the pre-check exists
  • Rejected: consent not verifiable. Reviewer couldn't find the checkbox or privacy clause. Send direct URLs and screenshots. Make sure the site isn't behind a password on staging
  • Rejected: sample messages missing business name or STOP. Use the templates above verbatim
  • Brand approved, campaign stuck >10 business days. Open a GHL support ticket with the campaign ID. Meanwhile run email-first fallbacks (DEL-07) — do not send SMS on an unregistered number
  • Client insists on texting now. Explain that unregistered traffic gets filtered and can get the number blocked. They can text from their own cell in the meantime — we just don't automate it
  • Client changes legal entity later (new LLC, new EIN). Brand must be re-registered. Budget a week of no SMS
04Phone numbers + call routing

New tracking number, forwarded to their cell

Buy a local number in GHL, forward to the owner, record and log every call. Their existing number is untouched.

OwnerMKWhenDay 11 purchase; routing configured day 15Done whenTest call rings the owner's cell, logs in GHL, and a missed test call logs as missed
MKCL

MKPurchase + configure

  • Buy one local number matching the client's area code (Miami 305/786, WPB 561, etc.). Local presence matters for answer rates. Avoid vanity or toll-free unless the client already markets one
  • Forward to the owner's cell. Ring timeout 20 seconds — shorter than their cell voicemail so an unanswered call is logged as missed, not answered
  • Turn on call connect / whisper ("Press 1 to accept a call from your website"). This is what stops the cell's voicemail from "answering" the call and killing the textback
  • Recording ON with a recording notice. Florida is two-party consent — the notice is mandatory, not optional
  • Voicemail: record a 15-sec greeting in the owner's voice on the kickoff call. Transcription on, transcript to the conversation + owner's email
  • Business hours set on the number. After hours: straight to voicemail + textback (DEL-05). Never ring a firefighter's cell at 2am for a landscaping lead unless they ask for it
  • Caller ID: show the tracking number to the owner (so they know it's a business call), not the lead's number. Toggle in number settings
  • Spam: enable STIR/SHAKEN on the number. Reject calls from numbers with no caller ID if the client asks

MKWhere the number goes

  • Website: every phone link is the tracking number (Josh puts it in the scaffold's custom value)
  • GBP: tracking number as primary, the client's real number as an additional number. Never remove the real one — it protects NAP history and rankings
  • Ads (rung 4): a second tracking number per channel later. Not now
  • Do NOT put the tracking number on trucks, business cards, or Yelp. Those keep the client's real number — if we ever part ways, their printed number still works

Edge cases

  • Owner has two people answering. Ring group: owner first 15s, then office 15s. Or simultaneous ring. Ask which on kickoff
  • Owner's cell voicemail keeps "answering." Whisper isn't on or timeout is too long. Fix those two settings first
  • Calls show as missed even when answered. Owner is declining the whisper prompt or has a call-screening app. Walk them through it live
  • Client wants to port their real number in. No. Read rule 3. If they truly want GHL to own their main line, that's a separate quoted project with a 4-week port timeline and Josh signs off
  • Number gets flagged as spam ("Scam Likely"). Usually from outbound volume without A2P. Register a caller-ID name (CNAM) for the number, register on the free carrier reputation portals, and stop outbound until reputation recovers. Buy a new number only as a last resort
  • Client on Tampa/Panhandle Central time. Business hours on the number follow the sub-account timezone. Verify DEL-02
05Missed-call textback

Every missed call gets a text in 60 seconds

The single highest-ROI automation we sell. Fires on missed inbound calls only, once, with a stop condition.

OwnerMKWhenON only after A2P approved and DEL-04 verifiedDone whenA missed test call produces one text within 60s and the owner is notified
MK

MKWorkflow spec

  • Trigger: Call status = Missed or Voicemail, direction = inbound, on the tracking number
  • Filter out: contact has tag dnc, contact is the owner's own cell, contact is a known vendor tag, number type = landline (GHL number validation)
  • Filter out: contact already received a textback in the last 24h (custom field last_textback) — prevents double-texting a lead who calls twice
  • Wait 30 seconds (gives the owner a chance to call back first)
  • SMS from the tracking number, template below. First name only if known
  • Notify owner: push + SMS to their cell: "Missed call from [number]. We texted them. Call back when you can."
  • Create/move opportunity to New Lead, tag src:phone
  • Stop conditions: reply received, owner calls back (outbound call logged), STOP
  • Quiet hours: if missed call lands between 9pm–8am, still send the textback (they just called — that's consent in the moment), but suppress any follow-up until 8am
Textback: Hey, it's [owner] with [Business name] — sorry I missed you. I'm probably on a job. What can I help you with? I'll text back or call in a few. Reply STOP to opt out.

Edge cases

  • Spam/robocalls trigger textbacks. Add the "no caller ID" reject in DEL-04 and a filter for numbers with 3+ missed calls and zero replies in 7 days → tag suspected-spam, no more textbacks
  • Owner answered but it still texted. Whisper prompt not accepted in time. Fix in DEL-04, and apologize to the lead in the thread — it's fine, it reads as attentive
  • Lead replies and nobody answers. The owner is the failure point. Notification goes to the owner's cell AND the GHL app. If replies sit >1h unanswered for 3 leads in a week, Michael calls the owner. That's a churn signal
  • Existing customer calls (not a lead). Textback still fires — that's fine and normal. Don't over-engineer this
  • A2P not approved yet. Workflow stays OFF. Voicemail transcription + email notification is the fallback. Do not "just test it once"
06Website forms → GHL

Custom form, webhook into GHL, never silent

Our sites are custom code. The form posts to a GHL inbound webhook and emails a copy so a broken webhook never means a lost lead.

OwnerJM builds · MK verifiesWhenBuild week, QA day 23, re-test at launchDone whenTest submission appears as a contact, in the pipeline, with source tag and consent field set, and the backup email arrived
JMMK

JMForm spec (in the scaffold)

  • Fields: first name, phone (required), email, service (dropdown from intake), message, SMS consent checkbox (unchecked). Nothing else — every extra field costs conversions
  • Hidden fields: page URL, utm_source/medium/campaign, referrer, gclid. Populated by a 10-line script. This is how we prove where leads came from later
  • Honeypot field + submit-timing check (reject <3s). Skip reCAPTCHA v2 — it kills mobile conversions. Cloudflare Turnstile if spam gets bad
  • Phone input formats to E.164 before submit. GHL dedupes on phone — a mis-formatted number creates a duplicate contact
  • On submit: POST to GHL inbound webhook URL (from the sub-account workflow trigger) AND send a copy to support@ + owner email via the form handler. Two paths, always
  • Success state: "Got it — [owner] will text you within a few minutes." Sets the expectation the speed-to-lead SMS fulfills
  • Failure state: shows the tracking number as a fallback: "Something broke — call or text us at [number]"
  • Webhook URL and tracking number live in the scaffold's config, not hardcoded in components

MKGHL side + verification

  • Workflow trigger: Inbound Webhook → map fields to contact: name, phone, email, service → custom field, message → note, consent → DND off if checked / SMS DND on if unchecked
  • Tag src:website, create opportunity in New Lead, then hand to Speed-to-lead (DEL-07)
  • Test on staging (day 23) and production (day 29) with a real phone. Check: contact created, no duplicate, tag present, consent field correct, backup email received, owner notified
  • Weekly test lead every Monday from the VA — logged in the monitoring sheet (DEL-15). A webhook that silently dies is the #1 way to lose a client's trust

Edge cases

  • Lead unchecked SMS consent. No automated SMS. Speed-to-lead goes email + owner calls manually. Owner texting them from their own cell is fine — it's not our automation
  • Email-only lead (no phone). Speed-to-lead runs email branch only. Tag no-phone. Owner replies from GHL email
  • Same person submits twice. GHL merges on phone. Workflow re-fires — add a filter: skip speed-to-lead if contact created >5 min ago and already tagged src:website
  • Webhook URL regenerated (someone recreated the trigger). Site posts into the void. The backup email is the safety net; the Monday test catches it. Fix = update scaffold config, redeploy
  • Spam wave. Turnstile on, and a workflow filter for messages containing URLs or "SEO" → tag spam, no notification
  • Client wants a 12-field form. Show them the conversion math. If they still want it, second form on a separate "detailed quote" page — the hero form stays 4 fields
07Speed-to-lead

Text in 60 seconds, call in 5 minutes

Lead submits → instant SMS from the business → owner gets a click-to-call notification → follow-up sequence until reply or 3 attempts.

OwnerMKWhenON after DEL-06 verified; SMS branch after A2PDone whenTest lead receives SMS in <60s, owner receives notification, sequence stops on reply
MKCL

MKWorkflow spec

  • Trigger: opportunity created in New Lead with tag src:website (or src:gbp)
  • Branch on consent: SMS branch if consent = yes AND A2P approved; else email branch
  • T+0: SMS #1 (template). Email #1 in parallel (short, same content, from the client's sending domain per DEL-12)
  • T+0: owner notification — push + SMS with the lead's name, service, and a tap-to-call link
  • T+5 min: if no outbound call logged, second owner nudge: "[name] is still waiting"
  • T+2h (inside 8am–8pm only): SMS #2 "Still want that quote?"
  • T+1 day: SMS #3, last attempt, offers the booking link. Then stop, move to Contacted, tag no-response
  • Stop conditions: any reply, inbound call, appointment booked, opportunity stage changed manually, STOP
  • Quiet hours: all lead-facing sends held 8pm–8am and delivered at 8am (Florida rule — see DEL-13). Owner notifications are not held
SMS #1: Hi [first name], [owner] here with [Business name]. Got your request for [service] — I'll call you in a few minutes. If now's bad, reply with a better time. Reply STOP to opt out. SMS #2 (2h): Hey [first name], still want that [service] quote? I can call now or you can grab a time here: [booking link] SMS #3 (next day): Last one from me, [first name] — if you still need [service] done, I'm here. [booking link]. Otherwise no worries at all.

CLThe client's job (say this on kickoff, put it in the handoff email)

  • Call every new lead within 5 minutes during business hours. The system buys them 60 seconds; it doesn't close the job
  • Reply inside GHL (app), not from their personal texts, so the thread and the stop condition work
  • Move the opportunity to Quoted / Booked / Won. If they don't, reviews (DEL-09) never fire and the report (DEL-15) is empty

Edge cases

  • Lead replies "wrong number" or "who is this." Stop sequence, tag not-a-lead, no follow-up. Owner reviews weekly
  • Owner never calls. Sequence still nurtures, but a lead who gets 3 texts and no call is worse than nothing. If the "no outbound call" nudge fires on 50%+ of leads in a month, Michael has the conversation. Churn risk, not a tech problem
  • Lead came from GBP call button, not form. That's a phone lead — DEL-04/05 handle it. Don't double up sequences
  • Email branch bouncing. Sending domain not verified (DEL-12). Fix before launch, not after
  • Duplicate sequence on re-submission. DEL-06 filter handles it. Verify with a double test
08Pipeline + CRM hygiene

Six stages, the client moves the cards

The pipeline is the client's dashboard and our data source. If it's not maintained, reviews don't fire and reports lie.

OwnerMK sets up · CL maintains · VA auditsWhenKickoff training, then weekly auditDone whenNo opportunity sits in New Lead >48h without a logged touch
MKCLVA

MKStage definitions (train the client on these exact words)

  • New Lead — automation put it here. Owner hasn't spoken to them yet
  • Contacted — a real conversation happened (call answered or reply received). Automation moves it here on reply; owner moves it after a call
  • Quoted — a number was given. Enter the quote amount in the opportunity value. This is what makes the report worth reading
  • Booked — date on the calendar. Triggers reminders (DEL-11)
  • Won — job done and paid. Triggers review request (DEL-09) after 24h. Enter final value
  • Lost — pick a lost reason (price / timing / went elsewhere / no response). Lost + no-response leads feed rung 3 reactivation later

VAWeekly audit (Mondays, 10 min per client)

  • Anything in New Lead >48h with no outbound call: text the owner a list. Three weeks in a row → flag to Michael
  • Anything in Booked with a past appointment date and no Won/Lost: text the owner "did the [name] job happen?"
  • Duplicates: merge (keep the older contact, GHL merge tool)
  • Unknown-source contacts: assign a src: tag by looking at the first conversation
  • Log counts in the monitoring sheet (DEL-15)

Edge cases

  • Client wants 12 stages. No, not until month 3. More stages = fewer moves = no data
  • Client uses a separate job-management app (Jobber, Housecall Pro). GHL stays the front door for leads only; Won/Lost still gets moved for reviews. If they refuse, review trigger switches to a manual tag the VA applies from their app's job list weekly
  • Client never opens GHL. The app + push notifications are the only fix. If after 30 days they're still not in it, Michael does a 15-min screen-share, and the pipeline moves get done by the VA from the owner's weekly text summary. Cost of doing business at $500/mo
09Review automation

Ask every customer, never gate, never pay

Won → wait → SMS with the direct Google review link → one email → one reminder → stop. Google's rules are strict; we follow them exactly.

OwnerMKWhenON at launch (after A2P). First send = the 2 reviewers from onboardingDone whenTest Won produces the SMS with a working review link, and the workflow stops on a review
MKCL

MKWorkflow spec

  • Get the direct review link from GBP (Ask for reviews → the g.page/r/…/review short link). Store as custom value review_link. Test it in an incognito window — it must open the review box, not the profile
  • Trigger: opportunity → Won, OR tag job-complete added (manual fallback for DEL-08 edge case)
  • Filter: contact has consent, not tagged dnc or review-sent in the last 180 days
  • Wait: 24h for multi-day jobs; 4h for same-day services (custom value review_delay per client)
  • SMS #1 (template). Tag review-sent
  • +2 days: email with the same ask + the link as a button, from the client's sending domain
  • +5 days: SMS #2, last reminder. Then stop
  • Stop: any reply, or GBP integration detects a new review from a matching name → tag review-left, owner notified with the review text
  • Every review (good or bad) gets a reply on GBP within 48h — VA drafts, owner approves in month 1, VA posts directly from month 2 using the approved voice
SMS #1: Hi [first name], thanks for choosing [Business name]! If you've got 60 seconds, a quick Google review helps us more than you'd think: [review_link]. Either way, thanks again — [owner]. Reply STOP to opt out. SMS #2 (5 days): Hey [first name], last nudge — if the [service] turned out great, we'd love a quick review: [review_link]. Thanks! — [owner]

Edge cases

  • Do not gate. No "how was it? 1–5" with the link only going to 4–5s. Google prohibits selectively soliciting. Everyone gets the link. Unhappy customers get a call from the owner before the request fires — that's the owner's job at job completion, not our automation
  • No incentives. No discount for a review, no raffle. Violates Google policy and FTC rules. Client asks → no, and explain why
  • Client has 0–3 reviews. First 10 come from the onboarding list + past customers via a one-time manual send (owner texts from their own phone, our template). Automation handles everyone after
  • Bad review lands. Owner notified immediately. Reply within 24h: acknowledge, don't argue, take it offline. Template in saved replies. Flag reviews that violate policy (competitor, wrong business, profanity) through GBP
  • Reviews filtered/disappearing. Usually velocity (20 reviews in 2 days from a manual blast). Cap manual sends at 5/day. Never send from one device to many customers on the same Wi-Fi
  • Customer has no Google account. Some won't. Don't chase. Facebook page link as secondary only if the client actually uses Facebook
10Google Business Profile

The map pack is the real homepage

Verify, clean, optimize, post weekly, answer everything. GBP drives more calls for local service businesses than the website does.

OwnerMK setup · VA ongoingWhenDay 11 access → day 29 launch updates → weekly foreverDone whenVerified, NAP matches everywhere, categories set, 20+ photos, website + tracking number live, first post published
MKVACL

MKSetup (days 11–29)

  • Client owns it (their Google account), we're Manager. Never Primary Owner
  • Not verified → start now. Google usually requires video verification for service businesses: owner films storefront/truck/tools/signage in one continuous take. Coach them before they record — one failed attempt delays weeks
  • Business name = legal or DBA name exactly. No keywords stuffed in ("Kyle's Epoxy Flooring Miami Best"). Keyword-stuffed names get suspended
  • Service-area business with no walk-in location → hide the address, set service areas (cities/counties, max ~20). Address hidden ≠ address wrong — the registered address still anchors ranking
  • Primary category = what they make the most money on. 2–5 secondary categories. Get this from intake, don't guess
  • NAP: name, address, phone identical across GBP, website footer, Sunbiz, and the top 5 directories (Yelp, Facebook, BBB, Nextdoor, Angi). Log every listing URL in Drive
  • Phone: tracking number primary, real number additional (DEL-04)
  • Website link with UTM: ?utm_source=google&utm_medium=organic&utm_campaign=gbp so we can prove GBP traffic
  • Services: one per service with a 2–3 sentence description. Hours including holidays. Attributes (veteran-owned, etc.)
  • Photos: 20+ at launch — logo, cover, team, trucks, 10+ real jobs. Geo-tagging is a myth; real photos of real work are not
  • Q&A: seed 5 questions with answers (pricing range, service area, licensed/insured, timeline, financing). Owners can post and answer their own
  • Messaging OFF unless the owner will answer in under an hour. An unanswered GBP message hurts more than no messaging

VAOngoing (part of the Growth Plan)

  • 2 posts/month minimum (Growth Plan promise), ideally weekly: one job photo with a one-line caption + CTA, one offer/seasonal. Posts expire — keep them coming
  • Upload 2–4 new job photos monthly (ask the owner for them in the monthly report reply)
  • Reply to every review within 48h (DEL-09)
  • Check for unauthorized edits monthly (Google accepts public "suggestions" that change hours/categories). Revert immediately
  • Log GBP calls, direction requests, website clicks into the monthly report (DEL-15)

Edge cases

  • Suspended profile. Don't panic-edit. Identify the cause (name stuffing, address change, ineligible category, PO box), fix it, submit one reinstatement request with proof (license, utility bill, Sunbiz). Multiple appeals get ignored. Timeline 1–4 weeks
  • Duplicate listings. Claim both, mark the duplicate as "permanently closed / duplicate," request merge
  • Client wants to rank in a city 40 miles away. Map pack is proximity-anchored to the registered address. City landing pages help organic, not map pack. Set the expectation now, not after they complain. A second real location is the only fix
  • Client moved / new address. Update GBP, then every directory, then website — same day. Re-verification likely
  • Video verification fails twice. Escalate via Business Profile support chat with license + insurance docs
  • Client posts their own stuff on GBP. Great — encourage it. Just check it's not a keyword-stuffed name change
11Calendar + booking

Two-way synced, reminded, no-show protected

One GHL calendar synced to the owner's real calendar. Booking link everywhere. Reminders at 24h and 1h. No-shows get one text.

OwnerMKWhenBuild week; test on QA day 23Done whenA test booking appears on the owner's phone calendar, reminders fire, and a blocked time on their calendar is unbookable
MKCL

MKSetup

  • Connect the owner's Google or iCloud calendar under their GHL user, two-way sync, conflict calendar = their personal calendar so blocked time is respected
  • One calendar: "Estimate / Consultation." Duration from intake (usually 30–60 min). Buffer 15 min before and after. Slot interval 30 min
  • Availability = business hours minus lunch. Minimum notice 2h, max 30 days out. Same-day bookings ON only if the owner wants them
  • Booking form: name, phone, address (for on-site), service. Consent checkbox again
  • Confirmation SMS + email immediately. Reminders at 24h and 1h with the address and "reply R to reschedule." Owner gets the same reminders
  • Booked → opportunity moves to Booked automatically
  • No-show workflow: appointment status = no-show (owner marks it) → one SMS with reschedule link → tag no-show. One attempt, then stop
  • Booking link stored as custom value; used in DEL-07 messages, the website CTA, and the GBP appointment link

Edge cases

  • Double-booked. Conflict calendar not set, or the owner's calendar isn't the one they actually use. Ask which calendar has their kid's soccer schedule — that's the real one
  • Two techs. Round-robin calendar with both users. Each connects their own calendar. Don't put two people on one Google calendar
  • Owner works 24/48 shifts (firefighter clients). Use the calendar's availability override per day, or block shift days on their personal calendar. Ask on kickoff — half our clients will have this
  • Lead books then ghosts the reminder. Not a no-show until the owner marks it. VA checks past-dated Booked weekly (DEL-08)
  • Client wants online payment/deposit at booking. Rung 3+ feature, Stripe on the sub-account. Quote it
12Domain, DNS, hosting, email

Cloudflare in front, email never breaks

Every client domain moves to Cloudflare DNS. Site on Cloudflare Pages. GHL sends from a dedicated subdomain. Export before you touch anything.

OwnerMK (JM for hosting deploy)WhenDNS prep day 15–22; cutover day 29Done whenSite resolves on https with valid cert, www redirects, email still sends/receives, sending domain verified, uptime monitor green
MKJM

MKBefore cutover (day 15–22)

  • Export the full current DNS zone from the registrar. Screenshot too. Save to Drive. This is rule 8
  • Identify email: MX records, SPF TXT, DKIM CNAME/TXT, DMARC. Google Workspace, Microsoft 365, or GoDaddy email are the common three. Write down which
  • Add the domain to the agency Cloudflare account. Cloudflare imports records — verify every email record imported before changing nameservers. Add anything missing manually
  • Lower TTLs to 300s at the current registrar 24h before cutover
  • Create the GHL dedicated sending domain: mail.clientdomain.com. Add its CNAME/TXT records in Cloudflare. Verify in GHL. Set as default sending address with the owner's name as From and reply-to = owner's real email
  • DMARC: if none exists, add v=DMARC1; p=none; rua=mailto:dmarc@[agency domain]. Never set p=reject on a client domain we didn't audit

JMHosting

  • Deploy to Cloudflare Pages under the agency account, project named after the client. Custom domain attached under the project's domains settings — both apex and www
  • www → apex 301 (or apex → www, pick one per client and stay consistent with GBP)
  • Old site URLs: list every indexed URL (Search Console or site: search) and 301 each to the closest new page. No 404s on launch day
  • Repo: one GitHub repo per client, private, under the agency org. Michael has read access for config values

MKCutover (day 29) + after

  • Change nameservers at the registrar to Cloudflare. Propagation 1–24h; usually under an hour
  • Immediately: send a test email TO the client's address and FROM it. Both must work. If either fails, revert nameservers first, diagnose second
  • SSL: Full (strict). Confirm the padlock on mobile Safari and Chrome
  • Search Console: add the property (domain-level via DNS TXT), submit sitemap, request indexing on every page
  • Uptime monitor (UptimeRobot or Cloudflare health check) on the homepage, 5-min interval, alerts to support@
  • Registrar auto-renew ON, our support@ as a secondary contact. A lapsed domain is the second unforgivable mistake

Edge cases

  • Domain registered through Wix/Squarespace/GoDaddy website builder. Nameserver change is still possible; the old site just goes dark. Cancel their old builder subscription only after 30 days live — sometimes the domain is bundled
  • Client's email is at the old web host (cPanel mail). It dies when hosting is cancelled. Migrate them to Google Workspace first — quote it as a small project, or leave the old host running for email only
  • Registrar won't let us change nameservers (transfer lock, 60-day lock after a recent transfer). Use the registrar's own DNS temporarily: point A/CNAME records at Cloudflare Pages directly
  • Client has no domain. Register in the client's name at Cloudflare Registrar, on their card, with us as a contact. Never on our account
  • DMARC already p=reject and GHL emails bounce. The GHL sending subdomain needs its own DKIM aligned. Verify in GHL, then test to Gmail + Outlook + Yahoo before launch
  • Site down alert. Check Cloudflare status first, then the last deploy. Roll back to the previous deployment in Pages (one click). Then tell Josh
13Compliance: TCPA + Florida

Florida is stricter than federal. Build to Florida.

Prior express written consent for automated texts, 8am–8pm only, 3 attempts max, STOP honored instantly, two-party call recording. Every workflow follows this by default.

OwnerEveryoneWhenAlwaysDone whenEvery workflow in the snapshot passes the checklist below
MKJMVA

MKRules baked into every workflow

  • Consent: automated marketing texts require prior express written consent (the unchecked checkbox, with the disclosure text). Store the consent timestamp + source on the contact. An inbound text or call from the lead is consent for a reply; it is not consent for a marketing sequence
  • Hours: lead-facing automated texts and calls only between 8am and 8pm in the lead's local time. Every sequence uses a hold-until-window step. (Federal allows until 9pm; Florida says 8pm; we use 8pm everywhere)
  • Frequency: max 3 automated attempts per 24h on the same matter. Our sequences are 3 total over 2 days — well under
  • Opt-out: STOP/UNSUBSCRIBE/QUIT/CANCEL/END → DND on all channels instantly, confirm once ("You're unsubscribed"), never message again. GHL handles keywords natively — verify it's on per sub-account
  • Identify: every automated message includes the business name. First message in any sequence includes opt-out language
  • Recording: Florida is all-party consent. Recording notice plays on every recorded call (DEL-04). No exceptions, no "just for training"
  • Reactivation campaigns (rung 3): only to contacts with documented consent or an established business relationship within the last ~18 months. Purchased lists never. Cold lists never
  • Reviews: no gating, no incentives (DEL-09)
  • Records: consent evidence (form submission with timestamp, IP, checkbox state) retained. GHL keeps form submissions; the backup email to support@ is the second copy

Edge cases

  • Client says "just text my whole customer list about the sale." Only contacts with consent + relationship. Everyone else gets email (still needs unsubscribe link) or nothing. Client pushes back → Josh explains that FTSA suits are per-message statutory damages and the client is the one named
  • Lead texts STOP then calls next week. The call is fine. The owner can text back manually from GHL — a human reply to an inbound isn't a marketing text. Automation stays off for that contact until they re-consent on a form
  • Out-of-state lead. Florida rules still apply to us as the sender; other states may add their own. 8am–8pm in their timezone covers almost everyone
  • Client wants to text from their personal cell using our templates. Fine — that's them, not automation. Same consent common sense, but not our system
14Support + edit queue

One inbox, 48-hour turnaround, clear line between edit and quote

support@ is the only door. Every request is a ticket. Small edits ship in 48h. Anything bigger gets a price from Josh.

OwnerMK → VA by client #10WhenHandoff (day 31) → foreverDone whenZero tickets older than 48h at Friday close
MKVAJM

MKThe system

  • support@ shared inbox. Every request — email, text to Josh, DM to Michael — gets forwarded there and acknowledged from there within 4 business hours: "Got it, you'll have it by [day]"
  • Ticket = one row in the support board (GHL pipeline named Support, or a simple Notion/Sheet): client, request, type (edit / quote / bug), owner, due, done
  • Small edit (included): text changes, photo swaps, hours, adding a review card, one GBP post, a new team member bio, a price change. Ships within 48h, no charge, no limit within reason (10+/month → Michael has a conversation)
  • Quote (Josh prices): new page, new service section, redesign, new integration, new form, landing page, anything touching workflows beyond copy. Michael replies "this one's a quote — Josh will send it today," tags Josh
  • Bug (ours): form broken, site down, SMS not sending. Top priority, same day, no charge, root cause logged in the snapshot changelog
  • Edits to the site: Josh or a VA with repo access makes the change in the repo → deploy. Nobody edits production directly
  • Close every ticket with a reply that includes a link to the change. Client never has to ask "is it done?"

VADaily (15 min)

  • Clear support@ to zero: acknowledge, ticket, assign
  • Ship every small edit you can do yourself (GBP post, review reply, copy change via repo)
  • Anything at 36h not done → ping the owner in Slack

Edge cases

  • Client texts Josh directly at 11pm. Josh replies "on it — forwarding to support so it's tracked" and forwards. Josh does not make the change himself. Training the client takes 3 repetitions
  • Client wants to edit the site themselves. The answer is the product: "That's what the Growth Plan is — you text, we ship in 48h." If they insist, it's a redesign on a platform they can edit, quoted, and the retainer drops to hosting-only. Rare; usually a sales-time red flag we should have caught
  • "Small" edit that's actually big ("just add a page for each of our 15 services"). It's a quote. Michael decides, Josh prices, nobody argues with the client — just price it
  • Client is unhappy and wants to cancel. Not a ticket. Escalate to Josh same day (DEL-16)
  • Request contradicts compliance or Google policy. Decline in writing with the reason. Offer the compliant version
15Monitoring + monthly report

Catch it before the client does. Prove it every month.

Weekly checks that nothing silently broke. A monthly report with three numbers up top that justifies the $500.

OwnerVA (MK reviews)WhenMondays weekly · 1st of the monthDone whenMonitoring sheet filled for every client; every report sent by the 3rd
VAMK

VAMonday health check (per client, 10 min)

  • Submit a test lead on the live site → confirm contact, pipeline, SMS, owner notification, backup email. Delete the test contact after
  • Call the tracking number, let it go to voicemail → confirm missed-call log + textback
  • Uptime monitor: any downtime last 7 days? Cloudflare Pages last deploy successful?
  • GHL: A2P campaign still Approved, number not flagged, email sending domain still verified, any workflow errors
  • GBP: no pending edits/suggestions, no new unanswered reviews, no suspension notice
  • Pipeline audit (DEL-08)
  • Log all of it: one row per client per week in the monitoring sheet. Red cell = ping Michael same day

VAMonthly report (send by the 3rd)

  • Three numbers at the top, big: Calls (GHL + GBP calls), Leads (form + textback conversations), New reviews (+ current rating)
  • Below: GBP views/direction requests/website clicks, missed calls recovered by textback, appointments booked, quoted value and won value from the pipeline, site visits (Cloudflare analytics)
  • One sentence of interpretation, one ask ("send us 3 photos from this month's jobs")
  • Sent from support@ as an email with the numbers in the body (not just an attachment). GHL report template or a one-page PDF from the sheet
  • Month 2+: include "vs last month." Month 3+: include "since launch" totals — that's the number that stops churn
  • If the numbers are bad, say so and say what we're changing. A bad month reported honestly retains better than silence

Edge cases

  • Pipeline is empty because the owner doesn't move cards. Report calls + leads (we have those regardless) and add: "We can't show quoted/won value because the pipeline isn't being updated — want us to do that from a weekly text from you?"
  • Zero leads for a month. Escalate to Josh before sending. Check tracking first (broken form? number flagged?). If tracking is fine, it's a demand or ranking problem — Josh frames the conversation and the rung 3/4 pitch
  • Client asks for the raw data. Give it. Export CSV from GHL. Transparency retains
16Offboarding

Clean exit, nothing held hostage

They keep everything that's theirs. We turn off what's ours. 30 days, documented, no drama. How we exit is how we get referred.

OwnerJM decides · MK executesWhenOn written cancellation, after the 12-month minimumDone whenChecklist complete, final invoice paid, access removed, folder archived
JMMKCL

JMFirst 48 hours

  • One call. Ask what changed. If it's ours (missed edits, bad numbers, silence), fix it and offer one free month. If it isn't, accept it gracefully. No second discount, no begging
  • Confirm the contract terms in writing: 30-day notice, last billing date, what they keep, what turns off, code buyout option
  • Tag the sub-account offboarding with the end date

MKWhat they keep (deliver by the end date)

  • Domain — it's theirs. Remove us as contact. Leave nameservers on Cloudflare only if they take over the Cloudflare account; otherwise give them the DNS export and 7 days of overlap
  • GBP — theirs. Remove our manager role. Restore their real number as primary before the tracking number is released
  • Contacts — full CSV export from GHL, including consent fields and conversation history. Their customers are their customers
  • Reviews, photos, Search Console, Analytics — all in their accounts already. Remove our user
  • Tracking number — offer to port it to them (they may have it on things by now). If they decline, release it 30 days after end date

MKWhat turns off

  • Website: goes offline on the end date unless they buy the code (set the buyout price in the contract — e.g. 3× monthly). Bought → transfer the repo to their GitHub and a Cloudflare project in their account. Not bought → they get a static HTML export of the public site anyway (goodwill, and it's not our design system, just their pages)
  • GHL sub-account: workflows OFF on the end date, sub-account deleted 30 days later. A2P campaign closed
  • Email sending domain removed from DNS
  • Vault folder archived, access matrix marked closed. Drive folder archived
  • Final invoice for any outstanding month + buyout. Retainer billing cancelled in Stripe with the correct final date

Edge cases

  • Cancelling inside the 12-month minimum. Remaining months are due per contract. Josh decides whether to enforce — usually settle for 50% and a clean reference. Never hold the domain or GBP over it. That's the line between a business and a hostage-taker
  • Unpaid invoices. Website goes offline on the end date (it's ours). Everything that's theirs still gets handed over. Send to collections only above $1,000 and only after 60 days
  • They're moving to another agency. Cooperate. Export everything, answer the new agency's DNS questions once. The next referral from that owner depends on this
  • They come back. Happens. New contract, no setup fee if within 6 months, sub-account restored from the deleted-account grace window if still there