Makeover Operations — Claim to Delivery
For agents and team: Operational manual for moving a makeover from intent-click to delivered + billing-started across four surfaces: Makeover v2 Sheet, Comms Dept admin (Firestore + Cloud Functions), Help Scout, and The Fort (Nucleus admin). Sequence content: follow-up-sequence and delivery-sequence. Timing rules: delivery-rules.
New-customer pipeline only. Existing Nucleus customers use the parallel pipeline (secondary CTA on Screen 8 → email → Existing Users sheet) — see v2-eligibility-and-tracking and existing-customer-reply-scripts.
Surfaces involved
| Surface | What it is | Who edits | Role in the flow |
|---|---|---|---|
| Makeover v2 Sheet | Google Sheet, ID 1Hk23n-djh4S0xiz_UsV9JMlTeTAMTZfaAGvvSpYyuM4 | Team (manual) + Cloud Functions (system columns only) | Team’s editing surface for the entire claim → delivery pipeline. The team’s day-to-day workspace. |
| The Comms Dept admin | Firestore (churches/* docs) + Cloud Functions + admin app at admin.thecommsdept.com | Cloud Functions (writes); admins (manual triggers) | Lifecycle truth, drip sequences, delivery trigger. Mirrors operational fields from the sheet. |
| Help Scout | Shared inbox for hello@nucleus.church | The team (replies, drafts) | Where customer-facing email lives — replies to drip emails, support questions during the makeover, and incoming notifications from The Fort. |
| The Fort (Nucleus admin) | Nucleus dashboard for makeover production | The team, while building the makeover | Where the church actually signs up after the intent click and where their new website is built. External system — outside The Comms Dept codebase. |
Source of truth: Firestore for lifecycle, the sheet for operational fields the team owns (Application Date, Loom URL, Site URL, Plan, billing dates). Sync runs every 15 min, sheet → Firestore only. Help Scout and The Fort are external — both feed in via the team manually updating the V2 sheet.
Team cheat sheet
Chronological — your manual responsibilities across the entire claim → delivery → billing pipeline:
| Stage | When this happens… | You do this… | In this surface |
|---|---|---|---|
| 2 — Application received | A new-application notification from The Fort lands in Help Scout | Find the church’s row on the V2 sheet and fill column H (Application Date). Within 15 min the sync pauses the drip — assuming the row links cleanly. If not, you’ll get a Slack alert (next row). | Makeover v2 Sheet |
| 2 — Reconciliation (only if alerted) | Slack pings “Reconciliation Needed” — the sync ran after you filled H but couldn’t auto-link the row to a Firestore church | Open the Tasks tab and confirm/reject any candidate match, or paste a church ID into column V manually, or create a new church record. The next sync (≤15 min) picks up the link and finishes the pause. | The Comms Dept admin |
| 3 — Production | You finalize the new site URL on The Fort | Fill column F (Site URL) | Makeover v2 Sheet |
| 4 — Delivery | The makeover is ready to ship | Fire the delivery trigger on the church’s record. Enter Loom URL, Site URL, and planned First Bill date in the trigger UI. Columns J (Delivery Date — today) and K (Loom URL) auto-fill on the sheet — no manual sheet edits needed at delivery. | The Comms Dept admin |
| 5 — Billing | The church picks Standard or Complete and signs up | Fill column P (Plan) (and Q/R as applicable) | Makeover v2 Sheet |
| 5 — Billing | The first bill actually goes through | Fill column T (First Bill date) and column U (Active) | Makeover v2 Sheet |
| Throughout | A customer replies to any drip email | Reply | Help Scout |
Everything else — pausing the drip, advancing lifecycle stages, mirroring fields to Firestore, queueing the post-delivery sequence — happens automatically. Fill the sheet correctly and the rest drives itself.
The lifecycle, stage by stage
Each stage: What happens (narrative) → What you do (manual or automated) → Under the hood (technical).
Stage 0 — Audit delivered, drip running
What happens. Church submits the audit form (form-copy), the system validates it (Gate 1 + Gate 2), and the audit email ships after the 4-hour delay + delivery window (delivery-rules). The 7-day post-audit drip (follow-up-sequence) starts pushing toward “Claim Your Free Makeover.”
What you do. Nothing — fully automated.
Under the hood.
- Lifecycle stage:
audit_delivered. - Drip enqueued in
emailSchedulecollection. - Pre-flight Slack alerts fire only on real failures: Gate 1 rejection, Gate 2 quality-check failure, or analysis crash. Each rejection also queues a Help Scout draft so the team can manually respond to the church. Duplicate submissions are silent — the church sees the 409 form response with re-submit instructions, no team follow-up needed.
Stage 1 — Intent click (“Let’s Go”)
What happens. The church reads their audit and clicks Let’s Go on the makeover signup modal (makeover-offer). Two things happen at once:
-
The browser redirects the church to the signup form in a new tab. The redirect URL pre-fills their audit email and church name as query params, so they don’t have to re-type. Example:
https://app.nucleus.church/purchase/makeover?email=brady%2Bbethany%40prochurchtools.com&church-name=Bethany%20Community%20ChurchWhy the pre-fill matters: the email used in the audit is now also the email used on The Fort. That same email lands in column B of the V2 sheet (auto-added in the next step), which means The Comms Dept admin can auto-link the sheet row to the right Firestore church record without anyone reconciling by hand.
-
A behind-the-scenes ping fires to The Comms Dept admin (
processMakeoverClaimCloud Function) registering the intent. This auto-adds a row to the V2 sheet.
The audit drip keeps running at this point. Intent ≠ commitment.
What you do. Nothing. Intent click is silent on the team’s end — no Slack alerts fire.
The system records the intent invisibly: a row goes into the V2 sheet, lifecycle.stage becomes makeover_interested, and a reconciliation task is queued in Firestore if there’s a name+location candidate match. None of that surfaces to the team yet.
If the church follows through on Nucleus and submits their application, you’ll see their application notification in Help Scout (Stage 2). If they drop out, the row sits there, the audit drip continues nudging them, and no one’s time gets wasted reconciling churches who never finished.
Under the hood.
- Cloud Function:
processMakeoverClaim(HTTP, fire-and-forget POST from the audit page). - Match logic, in order: email match → name+location match (audit must be
delivered) → orphan. - Email match →
lifecycle.stage = makeover_interested, history entrytriggeredBy: auto_reconciliation. Drip stays active. - Name+location match →
taskscollection entry withtype: reconciliation_needed. - All paths → V2 sheet row appended (idempotent on email), column X (Intent Date) stamped today.
- Code:
functions/src/reconciliation/processMakeoverClaim.js.
Stage 2 — Application received on The Fort
What happens. The church fills out the signup form on Nucleus. The Fort emails the team — as a notification landing in Help Scout. This is the moment the church has truly claimed their makeover (vs. just clicking “Let’s Go” earlier).
What you do. This is the most important manual step in the entire pipeline.
- When you see The Fort’s new-application notification in Help Scout, open the V2 sheet and find the church’s row (search by their email in column B).
- Fill column H (Application Date) with today’s date ASAP. Otherwise, they’re at risk of receiving email/SMS messages urging them to claim their makeover when they already have.
That’s it for the happy path. Within 15 min, the sync will:
- Auto-link the row to a Firestore church (via column V if filled, else by email match).
- Advance lifecycle to
makeover_applied. - Pause the audit drip and cancel any queued sends.
If the row can’t be auto-linked, Slack pings “Reconciliation Needed” — that’s your cue to step in. The application has actually landed, but the system doesn’t know which church to attribute it to:
- If a candidate-match task exists in the Tasks tab, open it and confirm or reject. Confirming auto-fills column V.
- If no task exists (no candidate at all), find the matching Firestore church and paste its ID into V manually, or create a new church record and paste its ID into V.
The next 15-min sync picks up the now-linked row and completes the pause. You don’t have to do anything else.
Under the hood.
- Cron job:
syncMakeoverSheet, every 15 min. - For each row, if column H has a date AND
church.makeover.applicationDatein Firestore is empty:- Stamp
church.makeover.applicationDate(idempotency guard — won’t re-fire on subsequent ticks). - Set
lifecycle.stage = makeover_applied, history entrytriggeredBy: sync_application_filled. - Flip
church.emailSequence.active = falseandchurch.commsDeptSequence.active = false, both withremovalReason: makeover_applied. - Call
cancelPendingForSequenceformainandcomms_dept— flips remaining queuedemailSchedulerows tostatus: skipped,skippedReason: makeover_applied.
- Stamp
- If a row has column H filled but the sync can’t link it to a Firestore church (no V, no email match), the sync (a) ensures a
reconciliation_neededTask exists in the admin Tasks tab and (b) fires a “Reconciliation Needed” Slack alert ONCE. Dedup is keyed off the Task’smetadata.slackAlertedAtfield, so subsequent syncs see the alert was sent and stay silent. The Task itself is the persistent reminder — even if the Slack ping is missed, the unresolved row sits in the Tasks tab until the team reconciles it. - Code:
functions/src/sequences/syncMakeoverSheet.js.
Stage 3 — Production (the team builds the makeover)
What happens. The team builds the new website inside Nucleus. Production runs 14–45 days, 30 average. Customer questions during this window — “when’s my site going live?”, “can you change X?”, account fiddling — flow through Help Scout.
What you do.
- Build the site in Nucleus.
- Reply to customer questions in Help Scout as they come in.
- Fill column F (Site URL) on the V2 sheet once the new site URL is finalized.
That’s the only V2 column the team fills during production. The delivery trigger handles Loom URL (K) and Delivery Date (J) automatically at Stage 4. Plan / First Bill / Active land in Stage 5 when the church actually chooses + pays.
Under the hood.
syncMakeoverSheetcron mirrors any non-empty cell tochurch.makeover.{siteUrl, plannedDeliveryDate, loomUrl, plan, actualBillingDate}every 15 min.- Empty cells never overwrite — partial updates can’t wipe Firestore values.
- During production, in practice, only
siteUrlactually flows through this sync. - Same cron also writes a per-row entry to
makeoverPipelineEntries(matched and unmatched rows alike) so the admin pipeline UI at/makeovers/pipelinehas a fast Firestore source for every V2 row. Doc ID is the URL-encoded lowercased email. Rows removed from the sheet → the corresponding entry is deleted on the next sync.
Stage 4 — Delivery trigger (manual)
What happens. The build is done and the church-specific Loom (A-roll + unique B-roll) is finalized. The team fires the delivery trigger from the admin app. The trigger sends the delivery email, starts the 7-day post-delivery upsell drip, and auto-fills the V2 sheet — no manual sheet edits needed at delivery time.
What you do.
- Finalize the Loom for this church (add the B-roll to the shared A-roll, grab the unique walkthrough URL).
- Open the makeover pipeline at
admin.thecommsdept.com/makeovers/pipelineand find the church under “In production.” (You can also fire from the church’s individual record page.) - Click “Fire delivery.”
- Enter the inputs: Loom URL, Site URL, and the planned First Bill date (defaults to today + 7 days).
- Fire it.
That’s it. The trigger sends the delivery email, queues the 7-day upsell sequence (6 emails + 3 SMS), and writes columns J (Delivery Date — today) and K (Loom URL) to the V2 sheet for that church. Column F (Site URL) is intentionally left alone — you’ve already filled that during production, and the trigger doesn’t touch it (so a typo in the trigger UI can’t overwrite a correct production-time value).
Under the hood.
- Cloud Function:
startMakeoverDelivery(onCall, owner/admin only) → calls internalrunMakeoverDelivery({ churchId, loomUrl, siteUrl, billingDate, testMode? }). - Updates
church.makeover.{loomUrl, siteUrl, plannedBillingDate, deliveredAt, status: delivered}andchurch.makeoverDeliverySequence.active = true. - Enqueues 9 rows in
emailScheduleanchored at “now” — the post-delivery upsell sequence. - After delivery succeeds, calls a best-effort
autoFillMakeoverSheethelper that looks up the church’s row in the V2 sheet by email and writes columns J (Delivery Date — today) and K (Loom URL). F is deliberately not in this set — F is owned by the team during Stage 3. Failures are logged but don’t roll back the trigger. - Idempotent: doc IDs encode
sequenceType + stepKey, so re-firing won’t duplicate. - The
billingDatearg above is the planned first-bill date entered at trigger time. It’s stored onchurch.makeover.plannedBillingDateand is what the post-delivery email copy reads (“your hosting begins on X”). The team-filled column T (Stage 5) records the actual first-bill date once billing kicks off and lands onchurch.makeover.actualBillingDate— separate field so a slipped first bill (failed card, bank delay) doesn’t overwrite the planned date the emails reference. - Code:
functions/src/sequences/startMakeoverDelivery.js.
Stage 5 — Post-delivery sequence + billing kickoff
What happens. The 7-day post-delivery drip (delivery-sequence) sends 6 emails + 3 SMS, driving the Standard ($99/mo) → Complete ($199/mo) upgrade with the Pro Church Certified scholarship as the lever. Sends are gated by delivery-rules (time-of-day window, blackout days). The 4-hour delay rule applies to initial audit delivery only, not to this sequence — these emails fire on their scheduled day-offset within the day’s allowed window.
At the end of the sequence, the church picks a plan and signs up. That’s the moment billing actually starts — and that’s when the rest of the V2 sheet’s billing columns get filled.
What you do.
- Throughout the 7 days: reply in Help Scout to any responses.
- When the church chooses a plan and signs up:
- Fill column P (Plan) with their selection (Standard / Complete).
- Fill column Q (MRR) and column R (Annual) with the contract values.
- When the first bill actually goes through:
- Fill column T (First Bill date) with the actual date.
- Fill column U (Active) — they’re now a confirmed paying customer. (We hold off on Active until the first bill clears, not at plan-pick, since plan selection without billing isn’t yet a confirmed customer.)
Under the hood.
- The 7-day post-delivery sequence is gated on
church.makeoverDeliverySequence.active === true(set when the trigger fires), not onlifecycle.stage. - After Stage 4 delivery,
lifecycle.stagestays atmakeover_applied. There’s no automated transition tomakeover_in_progressormakeover_completedyet — those values exist in the model and are honored byMAKEOVER_STAGES = ['makeover_applied', 'makeover_in_progress', 'makeover_completed'](the “already-reconciled” set that makesprocessMakeoverClaima no-op on re-fire), but admins advance them manually if needed. - Stripe-side billing automation isn’t wired into the lifecycle yet. Plan / MRR / first-bill confirmation flows through the team filling V2 sheet columns P, Q, R, T, U; the Stripe webhook integration that would automate this is out of scope for now.
Reconciliation cases
When the V2 sheet and Firestore can’t auto-link a row, syncMakeoverSheet resolves the link in this order:
- Column V is filled — trust it. The row is permanently linked to that Firestore church ID. Manual reconciliation via column V always wins.
- Email match — auto-link. System writes the matched church ID to column V on first sync so subsequent syncs lock onto it without a query.
- No match — the row stays
unmatchedin the sync. The team reconciles by either:- Pasting the right church ID into column V, or
- Updating the church’s
contact.emailin Firestore to match the row’s email.
The intent-click path (Stage 1) silently queues a reconciliation task in the Tasks tab whenever it finds a name+location candidate match. The task sits in Firestore until the team resolves it — but no Slack alert fires at intent time, and no one needs to act on the task until Stage 2.
The only Slack ping for reconciliation fires from syncMakeoverSheet when an unmatched row has its column H (Application Date) filled. That’s the system saying “an application has actually landed but we can’t link this church — fix it now.” Until column H is filled on an unmatched row, the row sits silent.
Source of truth (code paths)
When the runtime behavior changes, update this doc to match.
functions/src/reconciliation/processMakeoverClaim.js— intent-click HTTP handler. Owns Stage 1.functions/src/sequences/syncMakeoverSheet.js— every-15-min sheet → Firestore sync. Owns Stage 2 pause trigger, Stage 3 mirroring, and themakeoverPipelineEntriescollection that powers the admin pipeline UI.functions/src/sequences/startMakeoverDelivery.js— onCall delivery trigger + internalrunMakeoverDelivery. Owns Stage 4.functions/src/integrations/sheetsClient.js— shared Sheets client,MAKEOVER_COLindex, append/find/update helpers.functions/src/sequences/enqueue.js—enqueueSequence(used by Stage 4) andcancelPendingForSequence(used by the Stage 2 pause).apps/admin/src/pages/MakeoverPipelinePage.jsx+hooks/useMakeoverPipeline.js+components/makeovers/FireDeliveryModal.jsx— admin pipeline UI that readsmakeoverPipelineEntriesand firesstartMakeoverDeliveryinline.
Related
- README — makeover funnel overview
- follow-up-sequence — post-audit drip (Stage 0)
- makeover-offer — the offer modal (Stage 1)
- delivery-sequence — post-delivery upsell (Stage 5)
- delivery-rules — timing rules that gate every send across the funnel