Skip to main content

Payment welcome cutover

Prepared for code deployment. The welcome remains inactive until the live cutover below. No workflow publication, customer send, or survey submission was performed during the October 2, 2026 review.

Verified existing configuration

  • Agency Location: HUjFTz0AbjoNOcWuS1h7.
  • Published FM Closers Signed Agreement Workflows: cdc89371-97e7-4a7f-a7c3-f05d1e781485. Annual/monthly document-completed triggers currently send the welcome email and its companion SMS. Other actions include pipeline updates, Slack notification, contact copying, and a member tag. Leave those actions in place. The copy destination was not established by the UI.
  • Existing template: 69bc798be2e963a9d5a3e2dd. Its button targets the public onboarding root. Its click condition adds agency tag onboarding link clicked. The user approved retiring that click tag on October 2, 2026. The cloud welcome records opens without propagating the agency tag. Existing tag history is retained; Operations contact IDs must never be used as agency IDs.
  • Operations Location: ReoQmuh4ooNuHeE5stlj.
  • Published Create Onboarding Opp on New Subscription Payment: aa7de117-009f-43f6-80fd-013bc8bbbceb. Both triggers require Payment status Success. Products are Funding Machine SaaS - V2 + Setup and MFM SaaS - V2 + Leads + Setup. Find Opportunity matches the Onboarding pipeline and contact full name. It does not establish contact identity; the app separately verifies the returned card’s contact ID.
  • Z - Onboarding Funnel, page mAfOovWqrNep7g3bdGAt, publishes https://onboarding.myfundingmachine.com/pre-onboarding-step-1-page. It embeds General | Pre Onboarding Form - V2, survey CzZVhTZnyHwIuT4nNcDo. The funnel overrides the survey’s thank-you message with a redirect to {{custom_values.schedule_tech_call_url}} and existing first/last name, phone, email, agency name, agency owner email, and relationship ID parameters. Sticky Contact and partial-submission contact creation were off. Preserve the survey and this redirect. No contact-ID binding contract was verified. A browser visit with only synthetic first/last-name query values confirmed those fields prefill in this exact published page. No contact details or survey answers were submitted; full submission-to-contact matching still needs a designated test.

Implementation

POST /api/webhooks/ghl-workflow/onboarding-payment authenticates a dedicated ONBOARDING_PAYMENT_WEBHOOK_TOKEN bearer before reading its bounded JSON body:
Use the workflow variable picker and a reviewed test execution to establish the exact transaction and found/created opportunity variables. Do not substitute Stripe charge IDs or guess merge-tag names. Both opportunity branches need the webhook. The endpoint returns 202 only after durable enrollment; verification failures return 503 for visible staff review/retry. Do not assume the workflow retries automatically. Provider reads verify the exact transaction, completed order, approved product ID, live/test mode, positive non-refunded amount, new order and opportunity dates, open onboarding pipeline, and Operations contact. Manual payments, signature events, failed payments, old orders/cards, and zero-dollar trials do not enroll. The welcome is deduplicated per Operations contact; a different episode requires staff review. No product-account mapping is required before this initial welcome. Later messages retain their existing stricter product-account mapping checks. Read-only production shape checks confirmed successful order transactions and matching order/contact/Location IDs. The transaction-by-ID endpoint returned a single-element array; the CRM client now normalizes only that endpoint and rejects ambiguous arrays. Orders returned objects with items[].product._id. No customer payload was saved. The existing cloud cron processes at most five welcome records per pass. It rechecks provider state, current contact DND, hold status, and CRM form-submitted date. The first pass sends E1; a later pass sends W1-welcome-sms. Existing durable claims, receipts, pause fencing and uncertain-send reconciliation remain authoritative. The app controls sending; Operations CRM conversations remain the delivery provider. The email/SMS use a 30-day HMAC link. The redirect validates the enrollment, generation, expiry and pause state, reads the scoped contact, records its first open, and redirects to the existing funnel with encoded prefill plus UTM attribution. It does not send a raw contact-ID override or forward the bearer token to the funnel. Email/phone matching remains with the CRM. It never treats a click as a submission; link scanners may also generate opens. Prefill appears in the destination URL/history.

Slack before product provisioning

Enrollment now durably queues onboardingSlackPaid.refresh. The existing Slack reconciliation cron processes at most five due payment enrollments per pass. The payment worker rereads transaction/order/contact/opportunity evidence and hold status before claiming and immediately before each provider write. It requires send mode, enabled persisted live state, ONBOARDING_ENGINE_LIVE_ENABLED=true, workspace mode, provisioning and CRM writes. A configured Location pilot list denies pre-product writes. Compute mode can prepare the welcome for review without an existing channel or provider writes. No new production flag is enabled by deployment. For the new-paid-only rollout, keep ONBOARDING_SLACK_PROVISION_ENABLED=false and set ONBOARDING_SLACK_PAID_PROVISION_ENABLED=true only at the authorized cutover. The paid switch requires an exact Operations payment enrollment on or after the policy cutoff, including after a product Location adopts the job. It does not grant provisioning to older accounts without an eligible enrollment. Turn the paid switch off to stop new writes while retaining channels and receipts. FM Bot posts the personalized welcome only after channel membership is confirmed. Its account link uses the verified product Location. If the member joins before account linking, welcomePendingAccountLink records that the same welcomeTs needs a link update. Updates retry against that message; uncertain initial sends are never automatically reposted. Legacy messages without the pending flag are preserved. The onboarding owner specified the customer destination as https://app.myfundingmachine.com/v2/location/{locationId}/custom-menu-link/fundingmachine. On October 7, 2026, a read-only authenticated browser inspection of an existing customer tab at this route confirmed the Funding Machine dashboard iframe for the same Location, including its Onboarding & Setup link. The older app.leadbuilt.com/.../ugflmc links elsewhere are not the requested customer-agency destination. No live send was performed for this verification. The existing onboardingSlackProvisioning reservation can initially have a welcomeId without locationId. That welcome is scoped to Operations; Operations is never stored as a synthetic customer Location. A unique opportunity reservation and lease serialize both entry points. Later verified product linking atomically adopts it, preserving channel ID, recovery name, Operator ID and invitation/welcome receipts. Active leases, conflicting identities/channels and paused enrollment prevent adoption. Product pauses, Labs and agency checks remain in force. Pre-product channels never enter account-scoped support ingestion; humans can still respond in Slack. With workspace mode enabled, E1 and its SMS wait for confirmed channel/staff setup and a usable current invitation. They include a signed /api/onboarding/welcome link with destination=slack. The redirect uses the same enrollment, generation, expiry and pause checks, and reads the current approved invitation. It never takes a redirect URL from the caller, records an intake-form open for a Slack click, or forwards the signed token to Slack. Pausing or rotating an invitation takes effect on the next click. For recovery, inspect onboardingSlackProvisioning.health, message, nextCheckAt and the write-attempt receipts for the exact opportunity. Pre-reservation failures log the welcome ID, error class and provider HTTP status without response bodies or secrets. The enrollment’s slackNextCheckAt retries those failures independently of email timing. The sweep also initializes at most five older welcome records per pass without changing send receipts. Once a verified Location job owns retries, the welcome leaves the Slack queue. Failures before reservation use the same bounded backoff as saved jobs: three five-minute retries, then 30 minutes, then six hours. Stale failures cannot overwrite newer progress, active leases, paused enrollments or changed account mappings. Reissue/resume requeues the welcome’s Slack check; neither clears write receipts. Expired enrollments require staff review/reissue. Already linked jobs continue through the normal product checker.

Activation prerequisites and exact live changes

  1. Click-tag retirement is approved: the replacement welcome does not add the agency onboarding link clicked tag. Disable the old welcome sends at cutover as below; do not delete historical tags or add cross-Location propagation.
  2. Deploy the reviewed app and Convex changes while the welcome remains unconfigured. This requires explicit production deployment authorization under AGENTS.md.
  3. Configure ONBOARDING_PAYMENT_WEBHOOK_TOKEN in the app and its matching bearer header in the Operations workflow; configure a separate high-entropy ONBOARDING_WELCOME_LINK_SECRET (at least 32 characters) in Convex. Use the existing Operations-scoped ONBOARDING_ENGINE_LIVE_PIT; verify payment read scopes and conversation send permission. Never print or commit credentials.
  4. Pause the existing engine and finish its lease. Preserve its reviewed config, imported history and cohort. Call onboardingWelcomeStore.configure with an admin actor, review reason and policy JSON containing: mode: "compute", a future UTC millisecond startsAt, liveMode: true, exact approved Operations productIds, nativeOwnershipEvidence, verified Operations senderName/senderEmail, and intakeOrigin set to the HTTPS app origin. Product IDs and sender address must be read from the actual Operations configuration; display names are not IDs. The cutoff is immutable once configured and avoids historical backfill.
  5. Add the authenticated webhook after finding/creating the card in both branches of the exact Operations payment workflow above. Keep the success/product filters. Confirm the synthetic/test execution supplies the correct IDs and receives 202.
  6. Disable only Email and SMS in the exact agency signed-agreement workflow. Keep the workflow and its other actions. Preserve the linked template as a rollback reference. Confirm no enrolled contact remains queued for those sends.
  7. Before send mode, confirm workspace settings, the current Never expires invitation, bot/staff configuration, provisioning and CRM-write gates. A Location-only pilot cannot create a pre-product channel: obtain broader-rollout approval before removing that restriction. Rehearse payment → channel with no sub-account → workspace join → account linking, confirming one channel and one welcome. Pause/resume, rejected or refunded payments, uncertain writes and invitation rotation must also be checked.
  8. After compute evidence and an explicitly authorized test send to a designated test contact confirm sender, SMS destination, prefill and calendar handoff, pause, configure the same policy as mode: "send", and resume the engine. Do not enable sending while the old welcome actions can still fire.
The future cutoff, new-order check, and one-contact ledger prevent renewal/backfill messages; they do not reconcile a welcome previously sent in the agency before cutover. Review any signed-before-cutover/paid-after-cutover customers and suppress their enrollment with manage: pause before enabling send mode. A historical agency receipt must never be inferred merely from matching an email address.

Staff inspection and recovery

  • onboardingWelcomeStore.find({ locationId, contactId }) returns the enrollment ID, paid/open/expiry timestamps, pause state and opportunity ID. It excludes form data.
  • onboardingEngineLiveStore.inspect and evidence expose run reports and accepted or uncertain communication claims. Existing resolve requires a paused engine, exact attempt and evidence. Do not turn an uncertain result into a resend without confirming the provider did not accept it.
  • onboardingWelcomeStore.manage accepts pause, resume, or reissue plus admin actor and reason. Reissue requires a paused engine with no active lease, invalidates the old link and returns the replacement link for controlled delivery. It never clears receipts or automatically resends an accepted message. Treat that returned link as a bearer credential, not something to put in logs or tickets.
  • Rollback: pause the cloud engine and disable both general and paid Slack provisioning first, preserve all enrollment and dispatch history, then review affected contacts before restoring native sends. Never run both welcome senders simultaneously or bulk-reenroll existing customers.
API references: Transaction by ID, Order by ID.