Skip to main content

Knowledge > Processes > Customer Onboarding Flow

Customer Onboarding Flow

This document describes every step from when a church clicks "Get Started" on the pricing page through to their first time using the admin dashboard.


Overview

Pricing Page ──> Onboard Form (3 steps) ──> Stripe Checkout ──> Webhook Activation
│ │
│ auto-provisions chatbot
│ sends welcome email
│ │
└──── Pastor clicks magic link ──> Admin Dashboard (first visit)

Step 1: Pastor Selects a Plan

  1. Pastor visits churchwiseai.com/pricing and picks a plan (e.g., "Pro Chat").
  2. Clicks "Get Started" -- this links to /onboard?plan=pro_chat.
  3. The onboard page validates the plan query parameter against known plan keys.
  4. IF the plan key is missing or invalid, redirect the pastor back to /pricing.
  5. The page renders the OnboardForm component with the selected plan.

Billing toggle: If the plan supports annual billing (chat plans only), the pastor can toggle between monthly and annual. Voice and bundle plans are monthly-only.


Step 2: Three-Step Onboard Form

The OnboardForm walks the pastor through three sub-steps:

Step 2a: Search for Existing Church

  1. Pastor enters their church name (required), city (optional), and state (optional).
  2. Clicks "Search" -- form calls GET /api/churches/search?q=...&city=...&state=....
  3. The API searches the churches table (218K+ visible listings) using text search.
  4. Results are displayed as cards showing church name, city, state, denomination.

Step 2b: Select or Create

The pastor sees search results and has three choices:

  • Select an existing church -- clicks a result card.
    • IF that church already has an active subscription at an equal or lower price: show an inline "Already claimed" message. Do NOT proceed.
    • IF that church has an active subscription at a lower price AND the new plan costs more: treat as an upgrade. Notify existing admin via /api/onboard/notify and redirect to checkout.
    • IF the church has no active subscription (or only a "preview" status): proceed to contact info step.
  • "My church isn't listed" -- proceed to contact info step and create a new church record.

Step 2c: Contact Information

  1. Pastor fills in:
    • Contact name (required)
    • Email address (required, validated with regex)
    • Phone number (optional)
    • Church name (if creating new, pre-filled from search query)
    • Backup owner name, email, phone (optional second admin contact)
  2. Turnstile captcha token is captured (non-blocking -- never reject a paying customer over captcha failure).

Step 3: Create Premium Record (POST /api/onboard)

When the contact form is submitted, the frontend calls POST /api/onboard:

  1. Rate limit check -- 3 requests per minute per IP. Reject with 429 if exceeded.
  2. Turnstile verification -- verify the captcha token. If it fails, log a warning but allow through (rate limiter is the primary defense).
  3. Validate inputs -- church name (if new), contact name, email format, plan key.
  4. Resolve church record:
    • IF existingChurchId is provided: verify it exists in churches table.
    • IF creating new: insert a row into churches with a generated slug (church-name + random hex). Retry up to 3 times on slug collision.
  5. Duplicate email check (unless forceCreate is true):
    • Query premium_churches for any other church with the same admin_email.
    • IF found: return a 409 email_exists error with the existing church name. Also fire-and-forget resend the existing dashboard link to that email.
    • The frontend shows the pastor a choice: go to their existing dashboard OR force-create a second church.
  6. Upsert premium_churches:
    • Set status = 'preview', plan and channel from the selected tier.
    • Set admin_name, admin_email, admin_phone, preview_created_at.
    • The admin_token is auto-generated by the database default (UUID).
    • If the DB did not generate a token, generate one manually and update.
  7. Send pre-checkout welcome email (fire-and-forget):
    • Uses sendPremiumWelcomeEmail via Resend.
    • Subject: "Your ChurchWiseAI dashboard is ready -- {churchName}".
    • Contains a magic link: churchwiseai.com/auth/magic?t={admin_token}.
    • Sent NOW so the pastor has their dashboard link even before paying.
  8. Create identity records (non-blocking):
    • Create primary owner identity in church_identities table.
    • Assign admin role in church_identity_roles.
    • Create a session token.
    • If backup contact was provided, create a second identity with admin role.
    • Log an audit event (action: "onboard").
  9. Sync to MailerLite (fire-and-forget):
    • Add subscriber to cwa-trial group with church name, plan, city, state fields.
  10. Return { ok: true, churchId }.

The frontend then redirects to /onboard/checkout?cid={churchId}&tier={plan}.


Step 4: Stripe Checkout

  1. The /onboard/checkout page loads, validates the church identity:
    • Looks up premium_churches by church_id (preferred) or admin_token or session cookie.
    • If no valid record found, redirect back to /onboard.
  2. Renders the CheckoutForm client component with Stripe Embedded Checkout.
  3. The CheckoutForm calls POST /api/stripe/checkout-embedded with token and tier.
  4. The API creates a Stripe Checkout Session:
    • Maps the tier to a Stripe Price ID.
    • Sets metadata.church_id and metadata.tier on the session.
    • Chat plans include a 14-day free trial.
    • Returns clientSecret for Stripe Embedded Checkout.
  5. Stripe Embedded Checkout renders inline (card number, billing address).
  6. Pastor completes payment.
  7. Stripe redirects to /onboard/return?session_id={id}.

Step 5: Post-Checkout Return

  1. The /onboard/return page retrieves the Stripe session by ID.
  2. IF session status is complete:
    • Look up premium_churches by church_id from session metadata.
    • Redirect to /thank-you?email={email}&token={admin_token}.
  3. IF session not found or incomplete:
    • Show an error/retry page.

Step 6: Stripe Webhook Activates the Church

When Stripe fires checkout.session.completed:

  1. Extract metadata: church_id, tier, customer ID, subscription ID.
  2. Idempotency check: if stripe_subscription_id already matches, skip (prevents duplicate activations from retried webhook events).
  3. Activate the church via activateChurch():
    • Update premium_churches: set status = 'active', plan, channel, stripe_customer_id, stripe_subscription_id, activated_at.
    • Update churches: set is_premium = true.
  4. Auto-provision chatbot via provisionChatbot():
    • Skip if organization_settings already exists for this church (idempotent).
    • Look up church details (name, address, denomination, phone, website, hours).
    • Detect the church's theological lens from denomination mapping.
    • Build default chatbot config (personality, welcome message, suggested questions, knowledge base settings).
    • Resolve agent config based on plan tier.
    • Upsert into organization_settings table.
    • Update premium_churches: set chatbot_enabled = true, chatbot_agent_id.
    • Provision denomination starter packs -- insert canned FAQ responses from universal + denomination-specific packs into canned_responses table, with embeddings generated for each question.
    • IF provisioning fails: send an alert email to the founder (non-fatal).
  5. Send welcome email (the webhook may send a second welcome email; the pre-checkout email ensures the pastor already has their link).
  6. Voice agent alert: IF the plan includes voice, send sendVoiceSetupAlertEmail to the founder for manual Twilio number provisioning.
  7. Sync to MailerLite (fire-and-forget): add subscriber to cwa-customers group with plan and subscription status.

  1. Pastor opens the welcome email and clicks "Open Your Dashboard".
  2. The magic link URL is churchwiseai.com/auth/magic?t={admin_token}.
  3. The magic endpoint sets an HttpOnly session cookie and redirects to /admin/{token}.

Step 8: First Dashboard Visit

  1. The admin page at /admin/[token] loads.
  2. Server component calls resolveToken(token):
    • Checks premium_churches.admin_token and church_team_members.access_token in parallel.
    • Returns the user's role (e.g., admin), church data, premium record, and team members.
  3. If token is invalid, show 404.
  4. Dashboard renders with the Overview tab active by default:
    • Completeness donut -- shows how much of the church profile is filled in (staff, hours, description, etc.).
    • Metrics -- call count, prayer requests, visitor contacts, chatbot conversations (all zero for new churches).
    • Activity feed -- recent events (empty on first visit).
  5. The pastor sees a guided prompt to complete their profile:
    • Add service hours in the Training tab.
    • Add staff members.
    • Add a church description.
    • Upload a logo.
    • Customize the chatbot welcome message.

What Is Auto-Provisioned vs. Manual

ItemAuto-provisioned at activation?Details
premium_churches rowYes (created at onboard step, activated by webhook)Status goes preview -> active
organization_settings (chatbot config)YesDefault personality, welcome message, suggested questions
canned_responses (FAQ starter packs)YesUniversal + denomination-specific packs with embeddings
chatbot_enabled flagYesSet to true, chatbot is live immediately
Church is_premium flagYesEnables premium badge in directory
church_voice_agents rowNo -- manual setup requiredFounder must create row + configure Twilio number forwarding
Voice agent phone numberNo -- manualRequires Twilio number purchase and forwarding to Cartesia agent
Team membersNoAdmin invites team via Settings tab after activation

Error Recovery

FailureWhat happensRecovery
Stripe checkout abandonedpremium_churches stays in preview statusPastor can return later; preview record persists
Webhook delivery failureStripe retries up to 3 daysIdempotent activation handles retries safely
Chatbot provisioning failsAlert email sent to founderFounder can manually provision via Supabase
Welcome email failsLogged as non-fatal errorPastor can use /api/onboard/resend-link to get a new magic link
Slug collision on church insertRetried up to 3 times with new random hexExtremely rare; returns 500 only after 3 failures
Duplicate email (existing church)Returns 409 with existing church namePastor chooses: go to existing dashboard OR force-create second church