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
- Pastor visits
churchwiseai.com/pricingand picks a plan (e.g., "Pro Chat"). - Clicks "Get Started" -- this links to
/onboard?plan=pro_chat. - The onboard page validates the
planquery parameter against known plan keys. - IF the plan key is missing or invalid, redirect the pastor back to
/pricing. - The page renders the
OnboardFormcomponent 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
- Pastor enters their church name (required), city (optional), and state (optional).
- Clicks "Search" -- form calls
GET /api/churches/search?q=...&city=...&state=.... - The API searches the
churchestable (218K+ visible listings) using text search. - 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/notifyand 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
- 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)
- 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:
- Rate limit check -- 3 requests per minute per IP. Reject with 429 if exceeded.
- Turnstile verification -- verify the captcha token. If it fails, log a warning but allow through (rate limiter is the primary defense).
- Validate inputs -- church name (if new), contact name, email format, plan key.
- Resolve church record:
- IF
existingChurchIdis provided: verify it exists inchurchestable. - IF creating new: insert a row into
churcheswith a generated slug (church-name + random hex). Retry up to 3 times on slug collision.
- IF
- Duplicate email check (unless
forceCreateis true):- Query
premium_churchesfor any other church with the sameadmin_email. - IF found: return a 409
email_existserror 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.
- Query
- Upsert
premium_churches:- Set
status = 'preview',planandchannelfrom the selected tier. - Set
admin_name,admin_email,admin_phone,preview_created_at. - The
admin_tokenis auto-generated by the database default (UUID). - If the DB did not generate a token, generate one manually and update.
- Set
- Send pre-checkout welcome email (fire-and-forget):
- Uses
sendPremiumWelcomeEmailvia 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.
- Uses
- Create identity records (non-blocking):
- Create primary owner identity in
church_identitiestable. - Assign
adminrole inchurch_identity_roles. - Create a session token.
- If backup contact was provided, create a second identity with
adminrole. - Log an audit event (action: "onboard").
- Create primary owner identity in
- Sync to MailerLite (fire-and-forget):
- Add subscriber to
cwa-trialgroup with church name, plan, city, state fields.
- Add subscriber to
- Return
{ ok: true, churchId }.
The frontend then redirects to /onboard/checkout?cid={churchId}&tier={plan}.
Step 4: Stripe Checkout
- The
/onboard/checkoutpage loads, validates the church identity:- Looks up
premium_churchesbychurch_id(preferred) oradmin_tokenor session cookie. - If no valid record found, redirect back to
/onboard.
- Looks up
- Renders the
CheckoutFormclient component with Stripe Embedded Checkout. - The
CheckoutFormcallsPOST /api/stripe/checkout-embeddedwithtokenandtier. - The API creates a Stripe Checkout Session:
- Maps the tier to a Stripe Price ID.
- Sets
metadata.church_idandmetadata.tieron the session. - Chat plans include a 14-day free trial.
- Returns
clientSecretfor Stripe Embedded Checkout.
- Stripe Embedded Checkout renders inline (card number, billing address).
- Pastor completes payment.
- Stripe redirects to
/onboard/return?session_id={id}.
Step 5: Post-Checkout Return
- The
/onboard/returnpage retrieves the Stripe session by ID. - IF session status is
complete:- Look up
premium_churchesbychurch_idfrom session metadata. - Redirect to
/thank-you?email={email}&token={admin_token}.
- Look up
- IF session not found or incomplete:
- Show an error/retry page.
Step 6: Stripe Webhook Activates the Church
When Stripe fires checkout.session.completed:
- Extract metadata:
church_id,tier,customerID,subscriptionID. - Idempotency check: if
stripe_subscription_idalready matches, skip (prevents duplicate activations from retried webhook events). - Activate the church via
activateChurch():- Update
premium_churches: setstatus = 'active',plan,channel,stripe_customer_id,stripe_subscription_id,activated_at. - Update
churches: setis_premium = true.
- Update
- Auto-provision chatbot via
provisionChatbot():- Skip if
organization_settingsalready 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_settingstable. - Update
premium_churches: setchatbot_enabled = true,chatbot_agent_id. - Provision denomination starter packs -- insert canned FAQ responses from universal + denomination-specific packs into
canned_responsestable, with embeddings generated for each question. - IF provisioning fails: send an alert email to the founder (non-fatal).
- Skip if
- Send welcome email (the webhook may send a second welcome email; the pre-checkout email ensures the pastor already has their link).
- Voice agent alert: IF the plan includes voice, send
sendVoiceSetupAlertEmailto the founder for manual Twilio number provisioning. - Sync to MailerLite (fire-and-forget): add subscriber to
cwa-customersgroup with plan and subscription status.
Step 7: Pastor Clicks Magic Link
- Pastor opens the welcome email and clicks "Open Your Dashboard".
- The magic link URL is
churchwiseai.com/auth/magic?t={admin_token}. - The magic endpoint sets an HttpOnly session cookie and redirects to
/admin/{token}.
Step 8: First Dashboard Visit
- The admin page at
/admin/[token]loads. - Server component calls
resolveToken(token):- Checks
premium_churches.admin_tokenandchurch_team_members.access_tokenin parallel. - Returns the user's role (e.g.,
admin), church data, premium record, and team members.
- Checks
- If token is invalid, show 404.
- 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).
- 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
| Item | Auto-provisioned at activation? | Details |
|---|---|---|
premium_churches row | Yes (created at onboard step, activated by webhook) | Status goes preview -> active |
organization_settings (chatbot config) | Yes | Default personality, welcome message, suggested questions |
canned_responses (FAQ starter packs) | Yes | Universal + denomination-specific packs with embeddings |
chatbot_enabled flag | Yes | Set to true, chatbot is live immediately |
Church is_premium flag | Yes | Enables premium badge in directory |
church_voice_agents row | No -- manual setup required | Founder must create row + configure Twilio number forwarding |
| Voice agent phone number | No -- manual | Requires Twilio number purchase and forwarding to Cartesia agent |
| Team members | No | Admin invites team via Settings tab after activation |
Error Recovery
| Failure | What happens | Recovery |
|---|---|---|
| Stripe checkout abandoned | premium_churches stays in preview status | Pastor can return later; preview record persists |
| Webhook delivery failure | Stripe retries up to 3 days | Idempotent activation handles retries safely |
| Chatbot provisioning fails | Alert email sent to founder | Founder can manually provision via Supabase |
| Welcome email fails | Logged as non-fatal error | Pastor can use /api/onboard/resend-link to get a new magic link |
| Slug collision on church insert | Retried up to 3 times with new random hex | Extremely rare; returns 500 only after 3 failures |
| Duplicate email (existing church) | Returns 409 with existing church name | Pastor chooses: go to existing dashboard OR force-create second church |