Design preview · Workshop Booking (TMPL-WSBK01) · 8-section structure · updated 2026-08-06

← Design HQ  ·  HTML on GitHub  ·  PRD — prd-as-workshop-booking.md

Workshop Booking — the flow, organized by journey

Workshop Booking — Discover, set it up, go live, then the guest thread.

SizePortrait scrolls 375 · sideways is a slide deckNotes
Design brief — overview · scope · journey (closed)

The Workshop Booking flow, every state, in journey order — the build contract; ACs cited by id, never restated. Companion: assistant-lifecycle.html (master) · on-brand-support.html (token/component source). This file is the canonical structural reference for per-assistant flow mockups (canon §“Assistant-flow mockup structure”).

8 sections: overview → scope → content → journey → desktop → mobile → components → future Discover → Evaluate → Activate → Monitor (founder) Trigger → Interact (forks on booking_surface) → Outcome → Fallback (guest) amber = pending/action-needed · emerald = fixed/healthy/resolved Flow / Components / Future (canon 2026-08-01)

Widths here sample a fluid continuum — content scales continuously; structure steps only at sm 640 · md 768 · lg 1024. Kit: design-canon.md U8.

Workshop Booking (TMPL-WSBK01) turns a workshop host's WhatsApp DMs into confirmed, paid seats. A guest taps the founder's booking link — or just texts — and the assistant captures the booking right in the chat (name, session, headcount), relays the founder's own payment link or QR exactly as she wrote it, and once payment is confirmed — her one-tap Mark paid, or a tick in her own roster sheet — sends the Meta-approved confirmation and reminds the guest 1 day + 1 hour before class. $1.75 per confirmed booking; MystFlo relays payment asks but never touches the money. Bookings live in MystFlo's own ledger and surface on Pulse (metrics, attention rows, the Attendees list); when the assistant can't answer, the founder takes over in the same WhatsApp thread — no second app.

In this build sections 4–7 = the Active contract
  • Full founder journey: Explore card → Overview → Personalize (375 + desktop split) → Connect → Try → Activating / Activated → Pulse + Attendees destination.
  • Two payment structures (full_prepay · free_rsvp) × two booking surfaces (in_chat — WhatsApp Flow form fronting typed capture · own_form — founder's form link relayed, read-only roster sheet).
  • Payment relay only: link or QR today (#3153 shipped 2026-09-09; the QR upload nests under its option per #4701); link + QR together is specified-not-built (#4703, Peyton 2026-09-10 — drawn INSIDE Personalize 02 · How guests pay, a tick-one-or-both vehicle group directly under the pay-structure radio — Peyton, preview review 2026-09-10: “combine 04b with section 02 instead of creating another below”; one QR-image bubble with the link in its caption when both are ticked). Confirmation <30s of payment learned; reminders 1 day + 1 hour (AC-WSBK-02/03 ↗).
  • Template-approval honesty states (sec-4 banner, sec-6 amber notice) and the rejected-template fix path (sec-6b — drawn as contract, not built in code; the shipped rejected screen draws a different lifecycle moment — see parity ledger).
  • Handover in the same thread (WhatsApp Coexistence) + one-way Slack mirror (sec-12); the “05 · When I need to step in” Personalize category is proposed, not built.
  • Attendees records destination (M1, approved 2026-07-28, frontend-only, not built — sec-13 is its build contract).
Explicitly out drawn only in Future design (Part III), never a build target
  • F1 workshop picker (Mode E) · F2 quiet-guest follow-ups · F3 waitlist capture promise · F4 Stripe auto-settle (#3152, post-beta CPO ruling).
  • Not this assistant at all: deposit + balance is Retreat Booking (TMPL-RTBK01); one workshop per activation — two workshops = two assistants; no write-back to founder sheets, no Airtable/Notion.
Build status Beta · code-verified in parity/parity-workshop-booking.md
  • Shipped: capture engine + Modes A/B/C end to end (sec-9 MATCHES) · payment-relay fix #2796 · entry-link fields #2821 · 03a reach-you regroup (#3205, render-verified) · deposit_balance radio removal (#3192) · Connect two-role rows — StorageRoleRow/AiModelPicker (#3198, PR #3209) · MystFlo Storage provider (#3178, closed).
  • Drawn ahead of code: sec-6b rejected-template path · Nudge/attention actions (sec-8) · Attendees M1 (sec-13) · step-in category 05 · link + QR both, the 02 vehicle group #4703.
  • Catalog copy is FINAL here — live details.benefits/details.flow have drifted (sec-2 PARTIAL); the build PR ships the migration + PRD together (check-catalog-copy.sh).
┌ CONFIG (Personalize) ────────────┐  ┌ BRAIN ──────────────┐  ┌ CATALOG COPY (DB row) ────────┐
│ name · start_date* · price ·     │  │ voice · policies ·  │  │ description · tldr ·          │
│ seats · payment_mode ·           │  │ contact sign-off    │  │ benefits[4] · details.flow    │
│ booking_surface · link/keywords ·│  └──────────┬──────────┘  │ (5 nodes) — sec-1/sec-2 only  │
│ copy fields · booking_form_url   │             │             └───────────────────────────────┘
└────────────────┬─────────────────┘             │ grounds answers
         reads   ▼                               ▼
        guest WhatsApp thread — capture (Flow form / typed / own-form relay)
                 │  name · contact · seats
                 ▼
        cohort_bookings row  ◄─── one-way roster read (own_form: her paid tick = billable event)
                 │  booked → paid → confirmed
                 ├─► locked templates: confirmation (<30s) + reminder (1d + 1h)
                 └─► Pulse metrics · attention rows · Attendees columns · activity feed
The four data shapes
  • Config schema (drawn in sec-3/3b, categories 01–05): workshop facts (start_date required — gates activation, AC-WSBK-16 ↗) · payment_mode · booking_surface · generated wa.me link + conditional keywords · founder-editable copy (booking invite, payment ask) · booking_form_url required on all save paths under own_form (AC-WSBK-15 ↗).
  • Locked message copy: confirmation + reminder bodies from workshop-templates.ts, Meta-approved, never editable (C1). Every sec-9 ledger tags each message's source with one vocabulary: template founder field ai-composed native.
  • Booking record (cohort_bookings): name · contact · seats · status · paid-source (Mark paid / roster tick). Read + verdict, never edit; the read-back enum is closed in code (_shared/flow-patterns.ts), and the Attendees columns come from the same per-assistant records contract (sec-13).
  • Roster sheet (own_form only): the founder's own columns, mapped by header name (_shared/cohort-roster.ts) — read one way, never written; a missing required column pauses reminders with a named-column message, never a silent failure.

Founder vocabulary (Discover → Evaluate → Activate → Monitor) and guest vocabulary (Trigger → Interact → Outcome → Fallback) are the same pair every MystFlo assistant's Scope Council uses — reproduced here, not invented for this file. The one real branch worth drawing is at Interact, where booking_surface splits the guest experience in two; everywhere else is one direction.

┌ DISCOVER ────────────────────────────────────────────────────────┐
│ Founder browses Explore — sees the card, its price, and the      │
│ one required app (WhatsApp Business).                            │
└──────────────────────────┬─────────────────────────────────────────┘
                           ▼
┌ EVALUATE ──────────────────────────────────────────────────────────┐
│ Opens the Overview tab — what it does, how it works, key benefits. │
└──────────────────────────┬─────────────────────────────────────────┘
                           ▼
┌ ACTIVATE ──────────────────────────────────────────────────────────────┐
│ Personalize (workshop date/price/seats · payment + booking-surface     │
│ mode · locked message copy) → Connect (WA number · conditional Sheets  │
│ pick · template-approval notice) → Try (read-back)                     │
└──────────────────────────┬─────────────────────────────────────────────┘
                           ▼
┌ MONITOR — founder, ongoing ─────────────────────────────────────────────┐
│ Activating (loader) → Activated (live, honest about pending templates) │
│ → Pulse: bookings roll in, attention rows, live feed                   │
└──────────────────────────┬─────────────────────────────────────────────┘
                           ▼   once live, per guest DM
                           ▼   the guest side runs across every scenario below
The guest side — every scenario across one spine

One waterfall could only draw one path. This reads left-to-right along the same six beats every guest passes through, with one lane per scenario — so the differences between modes, and the places where a lane simply stops, are visible in one look. Amber lanes are not built. Full detail per lane: sec-9.

Scenario
1 · Trigger
2 · Interact
3 · Payment
4 · Confirm
5 · Remind
6 · If they go quiet
A · Full payment, in chatfull_prepay · in_chat
the default
Taps the link, or just DMs. The AI reads intent — no keyword needed.
Assistant sends the native Flow form — invite names the program + date. Not approved yet → typed capture instead.
Thank-you first (“seat is held”), then the founder's own link as a Pay now button. Never inside the form.
Founder taps Mark paid → template lands <30s.
1 day + 1 hour before. One template, both times.
✗ Nothing. Seat held forever, founder never told.
B · Free RSVP, in chatfree_rsvp · in_chat
same as A
same as A — form drops the price/date field
— no payment leg at all
Automatic on submit — in-chat “you're in” note. The locked template stays for Mark paid.
same as A
Nothing to chase — the only clean lane.
C · Their own booking formeither payment · own_form
roster sheet required
same as A
Assistant relays the founder's form link and steps aside. No capture.
On the founder's own form — MystFlo never sees it.
Waits on a manual roster tick. Hours, not seconds.
same as A — roster bookings are reminder-eligible
✗ Nothing — and MystFlo can't even tell paid from unpaid.
Sold out → waitlistany mode · seats re-checked
at submit, not at send
same as A
Form submitted, but the last seat went. Guest is added to the waitlist and told so.
— no charge
✗ No confirmation — there's no seat
✗ No reminders
✗ Promise broken — "you'll be first to know" is never sent by anything.
Asks something elseany mode · already built
zero founder setup
mid-thread, any time
Answers from workshop details + Brain. Can't → On-Brand Support tries once, fact-checked.
unchanged
unchanged
unchanged
Still fails → founder takes over in the same thread. See sec-12.
D · Two live cards, one numberany payment · 2+ booking cards
shipped — see Mode D
“what's on?” or a bare “book” when 2+ booking cards share the number. Supported product usage, not a misconfiguration.
Deterministic name + date list — never the wrong Flow, never Brain improvisation. A unique full program name goes straight to that card's form.
then exactly A, B, or C
then exactly A, B, or C
then exactly A, B, or C
inherits the picked lane
E · Picks from a listNOT BUILT · 2 blockers
see sec-10
Just DMs — no link, no keyword. Intent reading already works.
Offers the open workshops; guest taps one. This is the missing piece.
then exactly A or B
then exactly A or B
then exactly A or B
inherits whatever A/B does

Column 6 is the honest column. Four of seven lanes end in ✗ — see sec-11. Founder-side fallback (a template Meta rejects) is unchanged and lives at sec-6b; bookings and payment relay keep working throughout that.

Sequencing principle BINDING
  • Personalize's start_date gates Try and activation — no real date, no activation (AC-WSBK-16 ↗, closing the silent-failure gap where reminders never scheduled and no one was told).
  • booking_surface='own_form' gates two things at once: Connect's roster-sheet pick becomes required, and Personalize's booking_form_url becomes required on all three save paths — activation, update-config, and reactivation (AC-WSBK-15 ↗).
How the booking form is sent
  • Both entry points are one trigger path, not two. A wa.me link can't open a Flow form directly — it only pre-fills the guest's “book” message. The form is always sent BY the assistant, as an interactive flow message, once intent is caught. Link-tap and direct-DM converge on the identical mechanism.
  • The Flow fronts the typed capture — it doesn't replace it. The native form sends when the founder's Flow is published; otherwise the assistant falls back to typed capture, unchanged. A Workshop Booking instance behaves as typed-capture until its Flow is Meta-approved & published.
  • Payment is never inside the form. WhatsApp in-Flow payment is India/Singapore only — not the US. After the form submits, the assistant relays the founder's own external payment link, exactly like the typed path.
  • The form is auto-built per founder — no Meta Business Suite step. It's created, uploaded, and published on the founder's own WABA at activation. One published Flow per founder, version-swapped on config edits. Founders never open Flow Builder.
Part I — The flow every journey state, desktop then mobile, grouped by the PRD's phases — the 1:1 build contract, matched pixel for pixel. Parts live at full size in Part II · Components; deferred material in Part III · Future design, never a build target. Acceptance ids (AC-* ↗) live in prd-as-workshop-booking.md §Test Plan.
Discover
Discover “is this worth a tap?” · sec-1 the Explore card
Workshop Booking
⤴♡
Takes the booking, relays your payment link, and reminds them before class — so seats don't go empty and DMs don't pile up.
$1.75 per bookingBeta WhatsApp Business
Runs with On-Brand Support · each charged only when used
Personalize Connect Try
tap for detailsActivate
Frame 1 · Explore card [Discover]
  • Story — As a founder scrolling Explore, I see the real price and the one required app before I ever tap in, so there's no surprise once I start.
Design notes
  • Price is $1.75 per confirmed booking, flat fee. Only WhatsApp Business is a required app — the own_form Google Sheet is a conditional, backend-enforced requirement (sec-4 note / F4), not a card-level chip.
  • Pairing line (#5311, Peyton 2026-09-25 — provisional copy): every booking card says it runs with On-Brand Support before the tap. It comes from the booking-family rule in code, not from description — catalog copy is untouched, and no price is written into the line. It shows even when Support is already live (the card doesn’t know the number yet). Activation: 3s.
768 not yet drawn for this flow. Desktop and 375 are the contract.
This step is 1180. No 375 drawing — use the Desktop size.
Evaluate
Evaluate “what does it actually do?” · sec-2 the Overview tab
Workshop Booking
Takes the booking, relays your payment link, and reminds them before class — so seats don't go empty and DMs don't pile up.
✕
$1.75 per booking
Runs with On-Brand Support — Support answers your guests; booking takes the booking. Both run together, and each is charged per run.
Overview Personalize Connect Try
What it does

Workshop Booking captures the seat straight from your WhatsApp DMs — name, session, headcount — then relays your own payment link or bank details exactly as you wrote them. Once you mark it paid (or your roster shows it), the guest gets an instant confirmation and a reminder before class. You decide every payment; MystFlo never touches the money.

How it works
01
You
Teach it once

payment link, policy, voice — from your Brain

02
Customer
DMs to book a seat

picks a session, gives headcount

03
Assistant ✦
Captures + relays
✦AItakes the booking, sends your payment ask
free RSVP mode -> skips the payment ask, straight to confirms + reminds
04
You
Marks it paid

one tap, or your sheet ticks it

05
Assistant

Confirms + reminds

instant confirm, then 1 day + 1 hour before

Key benefits
Captures the booking while you're mid-class
The guest asks, Support answers; say yes and the seat's booked — you're not typing price-and-link between classes.
Relays your real payment link — every time
The same link or bank details you wrote, sent exactly as you wrote them. You decide every payment.
Reminds them before class
One reminder template, sent before the session — fewer no-shows without you typing a word.
Never invents a date or price
If it isn't in your setup, it says so and brings you in.
Frame 2 · Overview tab [Evaluate]
  • Story — As a founder, I read exactly what it does and how it works before I configure anything, so Personalize never asks me to decide something I don't understand yet.
Design notes
  • "Relays your payment link — every time" is the promise this Overview tab makes — see sec-7 for the payment relay fix that makes it true in the default in-chat mode.
  • Catalog copy is FINAL here — "a reminder before class" replaces the live long_description; ships via migration + PRD in the same PR (guard-railed, check-catalog-copy.sh) — the build PR must carry it. Exception (#5311, provisional): benefit 1’s body is Chief Product’s 2026-09-25 draft (Support answers, booking books), and the header pairing line is UI from the booking-family rule, not a DB field — both pending Peyton’s copy approval.
  • "How it works" = production HowItWorksFlow idiom, nodes/subs/branch VERBATIM from the seeded details.flow (20260710080000_2012_b4_wsbk01_card.sql, 5 nodes · 1 branch); skeleton + ✦ only on the seed's one ai:true node; outcome tile = the single lit object. Panel at 720 for the ≥768px horizontal row; <768px renders the vertical rail (not drawn).
768 not yet drawn for this flow. Desktop and 375 are the contract.
This step is 1180. No 375 drawing — use the Desktop size.
Activate
Activate “set it up once” · sec-3/3b Personalize · sec-4 Connect · sec-5 Try
dimmed Explore page peeks — revealGap · app header stays visible above
Workshop Booking
Takes the booking, relays your payment link, and reminds them before class — so seats don't go empty and DMs don't pile up.
$1.75 per booking
✕
Overview› Personalize› Connect › Try
01 · Your workshop
Which workshop is this for? dropdown · #4824 · not built

Edit what you sell in your Brain →

Name * required
Saturday Pottery Workshop
Price
42
Currency
USD · EUR/GBP/IDR/AUD/SGD/CAD/NZD

Same class, new date? Just set when and where.

Date * required
Pick a date
Time * required
10:00 or 10:00–11:15

Recurring, so this starts empty — set this run’s start, or a range — 10:00 or 10:00–11:15. An end at or before the start is flagged under the field, never blocks Continue.

Timezone
America/New_York

So reminders fire at the right moment for a nomad host — never changes the time guests see.

Where * required always shown · #5249 · not built pre-fill · open for Peyton
Ember Clay Studio, 142 Bank St
https://maps.app.goo.gl/8fQz2Rk9pL7… · map link, optional

Where guests show up — fills the confirmation, the reminders, and the invite. Starts as the offering’s venue from your Brain, or your business address and Maps link when it has none; change either here if this run is somewhere else. With a map link guests get a one-tap “Open in Maps” button; without it they still see the address.

Spots
10

Edits here change only this workshop — your Brain stays as it is.

Field names match your Brain’s offering fields one-to-one — Name · Price · Date · Time · Where · Spots (Peyton 2026-09-23). Date always starts empty after a pick — same class, new run; Timezone is always yours.

Other states of 01 — one dropdown, fields appear only after a pick; the main frame is a recurring class. Drawn in full at 375 in sec-3; the desktop is the same column, wider.
Nothing picked yet · only the dropdown
Pick a workshop
No empty form under it. 02 · How guests pay and the rest of the tab carry on below as usual.
Loading · the trigger waits, nothing below it
Which workshop is this for?
Loading what you sell…
Disabled until your offerings arrive. No fields, no spinner — the words carry it.
Couldn’t load · retry, or just type
Which workshop is this for?
Couldn’t load what you sell.Retry
Name *
e.g. Saturday Pottery Workshop
Attention tone (amber dot + text), never red. The fields reveal empty underneath — same as + Something new — so a failed load never blocks setup. Retry is a 44px target; ↻ is decorative.
Open · your offerings, then + Something new
Pick a workshop
Saturday Pottery Workshop
Recurring · $42 · 10 spots
Wheel Throwing Class
$65 · 6 spots
Kids’ Clay Afternoon
One-time · Sat 12 Oct · price on ask
+ Something new
Each row carries your Brain’s answer to “One-time or recurring?”. Not answered yet (Wheel Throwing Class) → price · spots only.
One-time picked · everything the Brain has fills in
Kids’ Clay AfternoonOne-time · Sat 12 Oct
Date Sat 12 Oct · Time 14:00–16:00 · Where filled; Price stays blank because the Brain says “price on ask”. No hint — there is no new date to set. Drawn in full at 375.
Not answered yet · fills whatever the Brain has, no hint
Wheel Throwing Class fills Name · Price · Spots · Where; Date and Time fill only if the Brain holds them. The Brain’s “One-time or recurring?” question stays open until you answer it there.
+ Something new picked · same fields, empty
Something new
Name · Price · Date · Spots start empty; Time empty too. Where still starts as your business address. Only this path gets the “Add it to what you sell?” row on Try (#4824, sec-5).
Nothing in your Brain yet · no dropdown
Type it below — or add what you sell to your Brain →
The empty fields show straight away — there is nothing to pick from.
Picked, then edited · price changed for this date
Saturday Pottery WorkshopRecurring · $42 · 10 spots
Price
38
Currency
USD
Edits here change only this workshop — your Brain stays as it is.
The dropdown keeps saying $42 — that is the Brain. This date runs at $38. No badge, no undo link: the quiet line is the whole explanation.
02 · How guests pay
Payment structure
Full prepay
Default. Guest pays in full to hold the seat.
Free RSVP
No payment — just a headcount.

Retreat-style deposits live on Retreat Booking, not here.

Where they pay spec change · #4703 · not built

Tick one or both. Whatever you tick goes out with your payment ask.

Payment link *
Any page guests can pay on — your ticketing page, Stripe, PayPal.
https://buy.stripe.com/8wM4kb…
QR code *
Upload your bank or PayNow QR once — sent as an image, guests scan and pay directly.
paynow-qr.png
Saved — sent with every payment ask
Replace Remove

Both are sent together — guests scan or tap, whichever suits them. With both ticked, the link and the QR are both required before you can activate.

Other states of this control — same two rows, only what’s ticked changes. Each input shows only while its vehicle is ticked (#4701).
One ticked · link only — today’s default
Payment link *
https://buy.stripe.com/8wM4kb…
QR code
Unticked → its input folds away. A QR you saved earlier is kept, just not sent.
Both ticked · QR still missing → blocks activation
QR code *
Drop your QR here — PNG or JPG

Add your QR to activate — or untick it.

Nothing ticked in a paid mode → both rows stay open with one line under them: Pick at least one way to get paid. Free RSVP → this whole group folds away — nothing to send.
Canvas fallback · file still saved · #4913
QR code *
paynow-qr.png
Saved — sent with every payment ask
ReplaceRemove

We couldn't convert this image — if the QR doesn't reach guests, re-upload from a different browser.

Upload succeeded; this is a warn, not a block; founder can Replace from another browser.

03a · How guests reach you — two ways in
1
Your booking link — share it anywhere
Bio, launch post, story, DM — one tap opens the chat.
Your booking link
Download QR
State — WhatsApp not connected
Your booking link
Connect WhatsApp to generate your booking link and QR.

The card never hides — it lights back up the moment Connect finishes.

2
They just text you — no link needed
Guests who already have your number, or reply to a story.

The AI reads intent — “any spots left tuesday?” books a seat just like “book” does. Nothing to configure.

Tell assistants apart conditional
book · count me in

Only if more than one assistant shares this number — these tell your assistants apart. Nothing to do with reading intent; most founders never see this field.

03b · Where guests book — what they see next

However they arrive — link tap or a direct message — it's one path in. What they see next is your call:

In this chat
Default. WhatsApp's own tap-through form — no typing back and forth.
Your booking list lives in Attendees — on this assistant, in Pulse.
Your own booking form
The assistant shares your form link; bookings land in your sheet, you tick paid there.
Your roster sheet is the record — MystFlo creates a pre-structured one in your Drive (default), or links the one you already have. Either way, we only read it.
Linking your own sheet? Any table works — you map your columns in Connect, and MystFlo has no information about a booking until it next reads your sheet.

Shows the form once WhatsApp approves it; guests text back and forth until then — nothing to set up on your end.

04a · Messages — in the order your guest gets them
Photo — optional · shown above the invite in the same bubble
+
No photo yet — drop one in, 1125×600 works best
1Booking invite — one field, same for every entry path · sent as a regular message, no approval needed
«workshop or event name» · «date» · «time» · «location» Tap below to grab your spot:

Shown above the invite button. Fills in your workshop name, date, time, and location automatically. No date set yet → the name-only line, never a blank.

2Booking form fixed — WhatsApp's own tap-through form (03b “In this chat”) · nothing to write
Name · Email · Party size · Date — filled from what you set in 01. “Your own booking form” (03b) sends your form link here instead.
3Payment ask — editable · sent as a regular message, no approval needed
Scan the QR to pay, or use the link below.

Sent right after the booking form, with your QR and/or link. The default follows what you ticked in 02 — QR only: “Scan the QR to pay.” · link only: “Use the link below to pay.” — and your edits stay. Lives here with the invite because it is your copy, not a setting.

4Booking confirmation locked
Meta-approved · not editable
Header: "You're booked — Wheel Throwing Class"
Hi Maya, it's Priya — your seat for the Wheel Throwing class is confirmed. We'll send a reminder before class. Reply here with any questions.
5Class reminder locked
Meta-approved · not editable one preview, not two
Hi Maya, it's Priya — just a quick reminder, your spot's all set. If anything comes up, reply here and let me know. See you soon!
04b · Reminder channels — required
WhatsAppEmail

Which channels the class reminder can use — connect the app for each in Connect. WhatsApp on by default; the booking flow itself always runs in chat regardless.

05 · When I need to step in not built · proposed
If the assistant can't answer conditional
Let On-Brand Support try first
Uses your full knowledge base. Costs one extra AI run per question — only when it's actually used.
Come straight to me
No second attempt, no extra cost.

Only shown when you have On-Brand Support running on this number.

What guests hear while they wait — editable
I'm connecting you with Priya — she'll come back to you shortly.
Where you get pinged
WhatsApp — this thread
Always on. Reply from your phone as usual.
Connected ⌄
Slack
A copy in your channel. On the app? You reply in WhatsApp. API-only number? Reply right in the thread — it reaches your guest.
Connect
·· preview — “In this chat” · once your Flow is live ··
hi! any spots left for tuesday's class?
1 · Booking invite
Wheel Throwing Class
· Jul 28, 2026
· 10:00–11:15
· Ember Clay Studio, 142 Bank St
Tap below to grab your spot:
When a photo is set (Photo field, above), this SAME bubble sends with the photo as its image header:
photo header · 1.91:1
Wheel Throwing Class
· Jul 28, 2026
· 10:00–11:15
· Ember Clay Studio, 142 Bank St
Tap below to grab your spot:
2 · Booking form — fixed
Wheel Throwing Class
Tue 28 Jul · $42 per seat · 3 seats left
Name
Email
Party size
2026-07-28
Confirm booking
Real form fields after activation — never faked here
3 · Payment ask — link only shown; QR / both variants below
Your details are in, Maya. Your seat for Wheel Throwing Class is held — it's confirmed the moment your payment lands.

Use the link below to pay.
↗ Pay now
This rail shows the variant matching what’s ticked in 02 · How guests pay. Link only = the bubble above (URL lifted onto the Pay now button). QR only — this bubble sends instead:
your QR as an image — caption: the thank-you + “Scan the QR to pay.”
Both ticked (#4703) — ONE bubble, no button (an image can’t carry one): your QR as the image, the caption = thank-you + payment ask + your link on its own line, tappable:
Your details are in, Maya. Your seat for Wheel Throwing Class is held — it's confirmed the moment your payment lands.

Scan the QR to pay, or use the link below.
https://buy.stripe.com/8wM4kb…
done — just sent it!
you tap Mark paid — or your roster shows it → 4 · Booking confirmation
You're booked — Wheel Throwing Class
Hi Maya, it's Priya — your seat for the Wheel Throwing class is confirmed. We'll send a reminder before class. Reply here with any questions.
5 · Class reminder — same thread, 1 day + 1 hour before class

Works the same whether they tapped your wa.me link or typed this directly — the AI reads intent, not just the word.

·· preview — “Your own booking form” (03b) ··
hi! any spots left for tuesday's class?
Yes! Here's where to book: forms.emberclay.com/wheel — you'll pick your spot and pay there. Shout if anything's unclear.
That relay is the whole guest experience — no capture, no payment ask (AC-WSBK-14). Confirmation + reminders fire from your roster tick.
Frame 3b · Personalize (desktop split) [Activate]
  • Story — As a founder on desktop, I fill the same Personalize fields while watching a live preview of how the assistant will actually reply — same fixes, wider canvas.
Acceptance
  • Same criteria as sec-3 — AC-WSBK-16 ↗, AC-WSBK-05 ↗, AC-WSBK-17 ↗ — this frame draws the desktop width of the identical fix, not a second behavior.
Design notes
  • See sec-3 for the full rationale on every fix shown here (recurring-schedule resolution, required date, dead-toggle removal, pay/book split, radio bug, entry-link parity #2821, the 03a/03b reach-vs-book split, the conditional “Tell assistants apart” block, the cancellation-policy removal, the 04 Messages block, the #4703 payment-vehicle checkbox group folded into 02 · How guests pay (link · QR · both — #4701 nesting kept) with three guest-sees previews, and the 05 step-in category pulled in from sec-12 — Peyton 2026-08-04). This frame exists only to show the same content at the desktop split-pane width, alongside the live preview column.
  • #4913 fallback note: see sec-3 — drawn in the Other-states call-out; happy path stays quiet; no-frame gap closed.
01 · Which workshop is this for? — one dropdown, then the fields [Activate · Peyton 2026-09-25 · #4824]
  • Story — As a host who runs the same class every week, I pick it from one dropdown and its standing details fill in — I only set when and where this run happens, without touching my Brain.
  • One-time or recurring decides what fills (Peyton 2026-09-25). The Brain asks the founder per offering “One-time or recurring?” — nothing is inferred from a date (“sometimes they are just not ready with the full details but they know it is one time or recurring”). The dropdown row prints the answer (“Recurring” / “One-time · Sat 12 Oct”). One-time → everything the Brain has fills, Date · Time · Where included; blanks stay blank. Recurring (this frame) → the standing content fills (Name · Price · Currency · Spots · Where); Date and Time start empty under “Same class, new date? Just set when and where.” Not answered yet → fills whatever the Brain has, no hint. Where always falls back to the business address + Maps link (#5249). Open for Peyton: Where pre-filled on a recurring pick (drawn filled, chipped pre-fill · open for Peyton) — the alternative is leaving it empty with Date + Time, for hosts whose weekly class moves venue. The Brain question is drawn in brain.html 13.4-sell-open.
Acceptance
  • AC-WSBK-22 ↗ — the cohort_name + price the pick fills are exactly what the Flow form’s title and date · price line render.
  • AC-COHB-28 ↗ · AC-COHB-31 ↗ — Personalize location + map_url are answerable mid-chat and outrank the Brain’s business location, so both always render here — never hidden because the Brain has an address.
  • NEW (PRD debt — #4824, amended Peyton 2026-09-25) — a single dropdown of brain_profile.services[] (name · price · spots per row, then a separator, then “+ Something new”) tops 01; before a pick NOTHING else in 01 renders — no empty form. Picking an offering reveals the fields, filled by the offering’s one-time / recurring answer (above): one-time fills everything the Brain holds, recurring leaves Date + Time empty, unanswered fills whatever is there; “+ Something new” reveals the same fields empty (Where still starts as the business address). All stay editable, grouped Name · Price · Date · Time · Where · Spots (Peyton 2026-09-23 — the shared Brain offering vocabulary, one-to-one; supersedes the #4478 field order); retires COHORT_PICKER_FIELD_KEYS filtering (ConfigureStep.tsx:267).
  • NEW (PRD debt — #5249) — Where (address, map link beneath it as a second field) always renders, prefilled from the Brain (contact_info.address · maps_url) when known; today Location hides when the Brain has an address. Start time + End time collapse to ONE Time field (“10:00” or “10:00–11:15”) with Timezone beside it.
  • NEW (PRD debt — #4824) — a pick writes wizard configuration only; the Brain is never written from Personalize. Timezone is never quick-filled; Date and Time fill only for a one-time offering (or an unanswered one that holds them).
  • NEW (PRD debt — #4824) — the Try-step “Add it to what you sell?” row renders only when 01 was “+ Something new” (or the Brain had nothing to pick); a picked offering never shows it.
Design notes
  • Control = the platform Select (src/components/ui/select.tsx), not the laid-out OfferingPicker.tsx radio rows (Peyton 2026-09-25): bordered trigger with ChevronDown and a 2px white ring when open; the menu is surface-elevated glass, rows ≥44px with name + a white/40 price · spots sub-line (“price on ask” when empty), the picked row gets the left ✓, one separator, then “+ Something new” last. The closed trigger shows the picked name (desktop adds the price · spots meta). “Edit what you sell in your Brain →” sits under the trigger. Still vertical, no modal (kit 2026-06-12 lock). The long list became one line, so the page opens short.
  • Reveal on pick: the fields appear only after a choice — the dropdown IS the first question. One founder-voice hint sits above Date on a recurring pick only: “Same class, new date? Just set when and where.” — it tells the weekly host exactly which fields are theirs to change. A one-time pick has nothing to set, so no hint.
  • One quiet line, once: “Edits here change only this workshop — your Brain stays as it is.” sits under Spots — the last of the six quick-filled fields, now contiguous (Name · Price · Date · Time · Where · Spots); Where keeps its own helper naming the Brain source, and a 9.5px review line under the quiet line states the one-to-one match with the Brain’s offering fields. No per-field “from your Brain” ticks (kit 2026-06-30 cheap-tick lock).
  • Currency is drawn now — shipped since #4478 (WSBK01 gained currency, type currency); the old bare $42 field was stale.
  • States: nothing picked · loading (disabled trigger “Loading what you sell…”, nothing below) · couldn’t load (“Couldn’t load what you sell.” + 44px Retry ↻ in attention tone, never red; fields revealed so she can still type) · list open · recurring picked (this frame) · one-time picked · not answered yet · + Something new · nothing in the Brain (no dropdown, empty fields straight away, invite line) · picked-then-edited — the first five as full phones on sec-3, all six summarised in the dashed call-out here; the Try-step ask is sec-5 / 5m. The preview rail keeps its sample conversation; the build renders the real date once one is set. 768: single column at every width, no separate frame (canon U8).
dimmed Explore page peeks — revealGap · app header stays visible above
Workshop Booking
Takes the booking, relays your payment link, and reminds them before class — so seats don't go empty and DMs don't pile up.
$1.75 per booking
✕
Overview› Personalize › Connect› Try
01 · Connect apps
Communication WhatsApp Business ▴
Matches your Reminder channels from Personalize — WhatsApp always required for booking chat
Connected ⌄
WhatsApp
◉WhatsApp Business
Which number should this assistant answer on?
+1 555-0142 ▾
Email — if picked in Personalize
○Resend
Not required — Email wasn't picked as a reminder channel
Not connected
AI Model MystFlo AI
Connected by default. Choosing your own AI model is coming later.
Connected ⌄
Storage MystFlo Storage ▾
Your Attendees list on Pulse — every booking lands there, captured in chat or read from your roster. Nothing external to connect.
Connected ⌄
Storage — when “Your own booking form” is picked (03b) conditional

In own_form mode the Storage row above is withheld entirely — no row renders, not even grayed (shipped: internalStorageWithheldForOwnForm, ConnectStep.tsx, #3178 CTO/Design review + #3184). The roster lives in her own Sheet, so “MystFlo Storage · Connected” would be a false claim in that mode. The own-sheet requirement stays backend-enforced (CONDITIONAL_REQUIRED_STORAGE, four paths) with no Connect-tab pick UI yet — separately tracked; per canon U9 that future roster-pick row is not drawn in this Active frame (its contract lives only in the F4 duplicated frame below).

Two message templates are waiting on approval Your booking confirmation and class reminder need one-time approval from Meta before they can send — usually resolved within a day or two of activation. Bookings still capture and your payment link still relays in the meantime.
02 · Wallet
Wallet balanceEach confirmed booking draws $1.75 from your prepaid balance $86.40
03 · Brain Check
Your business
✓Business name
Confirmed
Edit in Brain →
Where to find you
✓Contact
For sign-off · Confirmed
Edit in Brain →
Branding
✓Brand voice
Confirmed
Edit in Brain →
How you run
✓Cancellation policy
Confirmed
Edit in Brain →
4 of 4 ready — it can answer guests
Gap closed (2026-08-07)
  • This frame was TOC'd under the desktop Activate group (alongside sec-3b/sec-5) but only ever drawn as a 560px .panel — no true 1180-width realization existed. Re-drawn here using the exact wrapper this file already established for a real desktop frame (sec-3b's .flowgrid > .flowstep > .pageframe > .desktop-scroll > .desktop, reduced-zoom chrome) plus the master's two-column split (apps+wallet left flex-1, Brain Check right 400px). Same rows/copy as before (including this session's Storage-row and caret fixes), nothing invented. The "Continue to Try" button is omitted from this crop.
Frame 4 · Connect tab [Activate]
  • #5311 (2026-09-25): 01 · Connect apps now ends with the On-Brand Support add-on, selected and locked (or Active if Support is already live). It’s drawn in 3s B / D, and this frame predates it.
  • Story — As a founder, I connect the WhatsApp number this assistant should answer on, see that my bookings' home is already connected with nothing to set up, and see plainly that two templates are waiting on Meta before I activate.
Acceptance
  • AC-BILL-04 ↗ — activation is blocked unless both a stripe_meter_id and a stripe_price_id exist on this template's row; the Wallet row above surfaces the same wallet that guard protects.
  • AC-WSBK-15 ↗ — own_form requires booking_form_url; the roster-sheet requirement itself is backend-enforced (CONDITIONAL_REQUIRED_STORAGE) — its Connect-side face is not yet built, so this frame documents only the withheld Storage row (cond-block above); the future pick contract is drawn in F4.
Design notes
  • A tethered WhatsApp-number picker is required when the connected account has more than one number. The dimmed cond-block documents the own_form state — the Storage row is withheld there, not re-badged; with "In this chat" (the default) the lit row above is what renders. Every row carries its ROLE label (ROLE_IDS, required-roles.ts): “AI Model” → MystFlo AI · Connected (AiModelPicker.tsx shape) and “Storage” → MystFlo Storage · Connected — one Storage role, drawn ONLY in the default in-chat mode; under own_form it disappears entirely (shipped internalStorageWithheldForOwnForm, #3178 CTO/Design review — “Connected” would be a false claim when the roster lives in her own Sheet). MystFlo Storage is a real, shipped provider value (#3178, closed — STORAGE_PROVIDER_KEYS includes mystflo_storage, status pill via the shared ConnectionStatusButton primitive, never hand-rolled text); what remains not built is the Attendees destination it points at (sec-13, M1).
  • The badge is the picker, and the caret is now structural on every row but AI Model (B9, kit design-decisions.md 2026-08-05, caret scope amended 2026-08-07, Peyton-locked; code #3198/PR #3209): label = the role (“Storage”), badge = the connected app carrying the ▾/▴, and tapping it expands the picker group in place as a hairline list. The DestinationPicker expansion (Create a new one — default, Template-first 2026-05-22 / Link a sheet I already have, with the how-this-works tip living under the Link option only) belongs to the FUTURE own_form roster-pick row — no longer drawn in this Active frame (the shipped code withholds the Storage role entirely under own_form; see the cond-block note above); its contract row lives in F4 only, per canon U9. Deviation from the master's generic three-group default, flagged not silently resolved: the master's 2026-08-07 ruling defines the Storage picker as three groups (MystFlo Storage / Google Sheet / Your Supabase) everywhere, but that future row's real gate is provider-specific (CONDITIONAL_REQUIRED_STORAGE names google_sheets only for own_form) — drawing a Supabase group there would promise a destination the backend validator would reject. This mockup keeps the two-group contract (Google Sheet only) rather than fabricate a third group the code doesn't support; reconciling this file's provider-specific gate with the master's generic default is an open follow-up, not decided in this pass. Caret rule, corrected: the default-mode MystFlo Storage row now carries the caret too (fixed in this pass — it was previously missing even the badge/chip wrapper). AI Model remains the one exception — no caret, no coming-soon row, deliberate product gate (BYOK_AI_HIDDEN). Master contract: assistant-lifecycle.html B9/frame 5b.
  • Communication now carries the caret too (2026-08-07, supersedes the "keeps its own control" reading below) — Personalize's new "04b · Reminder channels" field is a DECISION only (WhatsApp always required, Email optional), and the caret expands into one pick-group per channel: WhatsApp (the existing which-number control now nests INSIDE this group, unchanged in function — see next bullet) and Email (Resend). Drawn NOT-CONNECTED here deliberately — Workshop's booking confirmation and class reminder are locked, Meta-approved WhatsApp templates (04a) with no Resend-side equivalent copy yet; picking "Email" in Personalize without that copy existing is a real build gap, not a design decision, so the Email group renders dimmed/optional rather than pretending parity. Building real Email reminders here means writing a second, non-WhatsApp version of the class-reminder copy — flagged, not solved in this pass.
  • The WhatsApp-number picker keeps its own control, now nested inside the WhatsApp group. When the connected account has more than one number, the founder picks which one this assistant answers on via the right-aligned +1 555-0142 ▾ control — a real, already-functioning picker for a different sub-choice (which number) than the label-badge caret's job (which channel/provider). It now lives on the WhatsApp pick-row rather than the outer conn-row, since the outer badge's caret has a real second group (Email) to open.
  • One connect list, no badge tier (Peyton 2026-08-04): MystFlo AI and MystFlo Storage are real rows in 01 · Connect apps, not an “Included” group. AI is Connected by default (a future model choice is named, future-tense only — never “bring your own key” today). MystFlo Storage is Connected with zero clicks in the default in-chat mode. Superseded in part (2026-08-05, #3178 CTO/Design review + #3184): under own_form the Connect-tab row is WITHHELD entirely — “Connected” would be a false claim when the roster lives in her own Sheet — and the conditional Google Sheets roster row is not built yet (backend-only gate; future contract in F4). Attendees as the in-app data home in both modes (roster-read bookings land there too) remains true at the data layer (sec-13, M1) — it just isn't claimed as a Connect row under own_form.
  • The roster sheet is READ-only (re-affirmed Peyton 2026-08-04): MystFlo reads one way — the roster tick IS the billable event (marks the booking confirmed, fires the $1.75 charge), so the read can never be optional. When the own_form Connect-side pick ships (separately tracked), it adopts the shipped DestinationPicker (T472, “Template-first, never blank” 2026-05-22): Create a new one (default — the MystFlo template roster cloned into her Drive at activation, zero structure concerns) vs Link a sheet I already have — the ONLY path where column mapping applies; the how-this-works tip (no data until read · header-name mapping per _shared/cohort-roster.ts · missing column pauses honestly) lives under that option, never as a blanket. Until then the requirement is backend-only — an own_form founder meets it at the activation gate, not on this tab.
  • Template-approval banner: plain, factual, no internal names — repeated at success (sec-6). Brain check reads four sections: business name + brand voice, cancellation policy, founder contact info.
Overview Personalize Connect Try
What you're activating
WorkshopSunday Glaze Clinic · new
Date2026-07-28
Price · Spots$38 · 8 spots
PaymentFull prepay · in this chat
Messagesconfirmation + reminder — locked, pending Meta
Brain✓ 4 of 4 ready
Wallet$86.40 · $1.75 per booking
Trying it
Replies are triggered by real customer messages Message your WhatsApp after activation to see it live. There's no way to fake a guest message before then — so we don't pretend to.
Send yourself the first one Right after you activate, message your own business number and go through the booking flow yourself — pick a spot, see the confirmation, the real thing.
Name this one
Sunday glaze clinic
Add it to what you sell? #4824 · not built
Add “Sunday Glaze Clinic” to what you sell?
Your other assistants will quote it. Edit anytime in Brain.

Off unless you switch it on. Shows only when 01 used “+ Something new” — hidden when the name is already in your Brain.

Other states of this row.
Switched on
Add “Sunday Glaze Clinic” to what you sell?
Added when you activate — $38 · 8 seats lands in What you sell.
The Brain write happens at activation, in the same pipeline step as the workshop seed — never at pick time, never on Continue.
Hidden · you picked an existing offering, or typed a name already in your Brain
No row, not even greyed — there is nothing to decide. “Name this one” sits directly above Activate, as before.
Activate — $1.75 per booking

Runs on your WhatsApp from the moment you activate. Pause anytime.

Frame 5 · Try tab [Activate]
  • Story — As a founder, I see exactly what I'm about to turn on — date, price, payment/booking mode, message status, Brain readiness, wallet — in one read-back before I commit.
Acceptance
  • AC-WSBK-16 ↗ — the required date shown in the read-back is what Try/activation actually gates on.
  • AC-BILL-04 ↗ — the wallet balance shown here is what the same-name guard checks before activation succeeds.
Design notes
  • Honest test — same pattern as every WhatsApp assistant: no pre-activation test run exists for chat-triggered assistants; the master contract (assistant-lifecycle.html frame 6) covers this once for every assistant type instead of restating it here. The read-back's "Messages" row already flags the pending Meta approval, so activating isn't a surprise when the success screen repeats it (sec-6).
  • #5311 (2026-09-25): this read-back predates the Support pairing — the target Try read-back is 3s C (Support not live) / E (already live); Connect gains the locked add-on (3s B / D). The two “Trying it” notices below carry over unchanged. #3138 — self-test nudge is booking-specific: "Send yourself the first one" tells the founder to go through the actual booking flow (pick a spot, see the confirmation) rather than the master contract's generic "message your own number" line — a single reply doesn't prove booking capture works, walking the flow does. First-time activation must never show a real "Run test" button or an "activate without a test run?" confirm dialog for this template — both notices above are the whole Try experience, on every activation path.
“Add it to what you sell?” — the one explicit ask [Activate · Peyton 2026-09-14 / 2026-09-23 · #4824]
  • Story — As a founder who typed a workshop my Brain didn’t know, I decide on the last step whether it joins what I sell — never silently, never pre-ticked.
Acceptance
  • NEW (PRD debt — #4824) — when 01 used “+ Something new”, one row renders directly above Activate: an OFF-by-default toggle labelled “Add ‘[name]’ to what you sell? Your other assistants will quote it. Edit anytime in Brain.”
  • NEW (PRD debt — #4824) — the row is hidden (not greyed) when the typed name already exists in brain_profile.services[] or an existing offering was picked.
  • NEW (PRD debt — #4824) — ON → the offering (name · price · currency · capacity) is appended to services[] once, at activation; OFF → the Brain is untouched. Re-activation never re-asks for a name that now exists.
Design notes
  • This frame now draws the “+ Something new” path (read-back gains a Workshop row, $38 · 8). The picked-existing path is this same screen minus the ask row — Name this one sits directly above Activate.
  • A toggle, not a checkbox row: a switch reads as a decision with a default; the OFF default is Peyton’s 2026-09-14 ruling (explicit ask, never pre-ticked). Copy is the ruled string verbatim — no “recommended” nudge.
  • Hidden, not greyed, when not applicable — deliberate exception to the kit 2026-08-10 “grey and explain” lock, on Peyton’s 2026-09-23 instruction: when nothing is being added there is no impossible action to explain.
  • 375: 5m. 768: same single column.
768 not yet drawn for this flow. Desktop and 375 are the contract.
Nothing picked yet — only the dropdown, no empty form
dimmed Explore peeks — tap to return
Overview Personalize Connect Try
01 · Your workshop
Which workshop is this for? dropdown · #4824

Edit what you sell in your Brain →

02 · How guests pay
Payment structure
Full prepay
Guest pays in full to hold the seat. Default.

The rest of the tab is unchanged — see “Scrolled” below.

Loading — the dropdown waits, nothing below it
dimmed Explore peeks — tap to return
Overview Personalize Connect Try
01 · Your workshop
Which workshop is this for?
Loading what you sell…
02 · How guests pay

The rest of the tab renders as usual — only 01 waits.

Couldn’t load — retry, or type it in
dimmed Explore peeks — tap to return
Overview Personalize Connect Try
01 · Your workshop
Which workshop is this for?
Couldn’t load what you sell.Retry
Name * required
e.g. Saturday Pottery Workshop
Price
e.g. 42
Currency
USD
Date * required
Pick a date
Time * required
10:00 or 10:00–11:15
Where * required
Ember Clay Studio, 142 Bank St
Spots
e.g. 10

Edits here change only this workshop — your Brain stays as it is.

Attention tone, never red. Retry is 44px; ↻ is decorative. Timezone and the map link sit where they always do — cropped here.

Dropdown open — what you sell, then + Something new
dimmed Explore peeks — tap to return
Overview Personalize Connect Try
01 · Your workshop
Which workshop is this for? dropdown · #4824
Pick a workshop
Saturday Pottery Workshop
Recurring · $42 · 10 spots
Wheel Throwing Class
$65 · 6 spots
Kids’ Clay Afternoon
One-time · Sat 12 Oct · price on ask
+ Something new

Each row shows your Brain’s answer — Recurring, or One-time with its date; Wheel Throwing Class hasn’t been answered yet, so it shows price · spots only. Rows are 44px tall; the list opens under the field and pushes nothing. Long lists scroll inside the menu; + Something new stays last.

Picked a recurring class — standing details fill in, Date and Time are yours
dimmed Explore peeks — tap to return
Overview Personalize Connect Try
01 · Your workshop
Which workshop is this for? dropdown · #4824

Edit what you sell in your Brain →

Name * required
Saturday Pottery Workshop
Price
42
Currency
USD

Same class, new date? Just set when and where.

Date * required
Pick a date
Time * required
10:00 or 10:00–11:15

A start, or a range.

Timezone
America/New_York
Where * required always shown · #5249 pre-fill · open for Peyton
Ember Clay Studio, 142 Bank St
https://maps.app.goo.gl/8fQz2Rk9pL7… · map link, optional

The offering’s venue from your Brain, or your business address and Maps link — change either if this run is somewhere else.

Spots
10

Edits here change only this workshop — your Brain stays as it is.

Field names match your Brain’s offering fields one-to-one — Name · Price · Date · Time · Where · Spots.

·· preview — “In this chat” · once your Flow is live ··
hi! any spots left for tuesday's class?
1 · Booking invite
Wheel Throwing Class
· Jul 28, 2026
· 10:00–11:15
· Ember Clay Studio, 142 Bank St
Tap below to grab your spot:
When a photo is set, this SAME bubble sends with the photo as its image header (see desktop frame).
2 · Booking form — fixed
Wheel Throwing Class
Tue 28 Jul · $42 per seat · 3 seats left
Name
Email
Party size
2026-07-28
Confirm booking
Real form fields after activation — never faked here
3 · Payment ask — link only shown; QR / both variants below
Your details are in, Maya. Your seat for Wheel Throwing Class is held — it's confirmed the moment your payment lands.

Use the link below to pay.
↗ Pay now
This rail shows the variant matching what’s ticked in 02 · How guests pay. Link only = the bubble above (URL lifted onto the Pay now button). QR only — this bubble sends instead:
your QR as an image — caption: the thank-you + “Scan the QR to pay.”
Both ticked (#4703) — ONE bubble, no button (an image can’t carry one): your QR as the image, the caption = thank-you + payment ask + your link on its own line, tappable:
Your details are in, Maya. Your seat for Wheel Throwing Class is held — it's confirmed the moment your payment lands.

Scan the QR to pay, or use the link below.
https://buy.stripe.com/8wM4kb…
done — just sent it!
you tap Mark paid — or your roster shows it → 4 · Booking confirmation
You're booked — Wheel Throwing Class
Hi Maya, it's Priya — your seat for the Wheel Throwing class is confirmed. We'll send a reminder before class. Reply here with any questions.
5 · Class reminder — same thread, 1 day + 1 hour before class

Works the same whether they tapped your wa.me link or typed this directly — the AI reads intent, not just the word.

·· preview — “Your own booking form” (03b) ··
hi! any spots left for tuesday's class?
Yes! Here's where to book: forms.emberclay.com/wheel — you'll pick your spot and pay there. Shout if anything's unclear.
That relay is the whole guest experience — no capture, no payment ask (AC-WSBK-14). Confirmation + reminders fire from your roster tick.
Picked a one-time class — everything your Brain has fills in
dimmed Explore peeks — tap to return
Overview Personalize Connect Try
01 · Your workshop
Which workshop is this for? dropdown · #4824

Edit what you sell in your Brain →

Name * required
Kids’ Clay Afternoon
Price
e.g. 30
Currency
USD

Your Brain says “price on ask” — add one here, or leave it and guests ask you.

Date * required
2026-10-12
Time * required
14:00–16:00
Timezone
America/New_York
Where * required always shown · #5249
Ember Clay Studio, 142 Bank St
https://maps.app.goo.gl/8fQz2Rk9pL7… · map link, optional
Spots
12

Edits here change only this workshop — your Brain stays as it is.

One-time: Date and Time come straight from the Brain, so there is no “new date” hint. Anything the Brain doesn’t hold stays blank.

+ Something new picked — the same fields, empty
dimmed Explore peeks — tap to return
Overview Personalize Connect Try
01 · Your workshop
Which workshop is this for? dropdown · #4824

It stays with this workshop — on Try you can add it to what you sell.

Name * required
e.g. Sunday Glaze Clinic
Price
38
Currency
USD
Date * required
Pick a date
Time * required
10:00 or 10:00–11:15
Timezone
America/New_York
Where * required
Ember Clay Studio, 142 Bank St
Map link, optional

Still starts as your business address — you can change it.

Spots
8

Edits here change only this workshop — your Brain stays as it is.

Nothing in your Brain yet — no dropdown, just the fields
dimmed Explore peeks — tap to return
Overview Personalize Connect Try
01 · Your workshop

Type it below — or add what you sell to your Brain →

Name * required
e.g. Sunday Glaze Clinic
Price
38
Currency
USD
Date * required
Pick a date
Time * required
10:00 or 10:00–11:15
Timezone
America/New_York
Where * required
Ember Clay Studio, 142 Bank St
Map link, optional

Still starts as your business address — the one thing the Brain can fill without an offering.

Spots
8

Nothing to pick from, so the fields show straight away. The Try tab asks whether to add it to what you sell (5m).

Scrolled — payment, booking surface, message copy
dimmed Explore peeks — tap to return
02 · How guests pay
Payment structure
Full prepay
Guest pays in full to hold the seat. Default.
Free RSVP
No payment — just a headcount.

Retreat-style deposits live on Retreat Booking, not here.

Where they pay spec change · #4703 · not built

Tick one or both. Whatever you tick goes out with your payment ask.

Payment link *
Any page guests can pay on — your ticketing page, Stripe, PayPal.
https://buy.stripe.com/8wM4kb…
QR code *
Upload your bank or PayNow QR once — sent as an image, guests scan and pay directly.
paynow-qr.png
Saved — sent with every payment ask
Replace Remove

Both are sent together — guests scan or tap, whichever suits them. With both ticked, the link and the QR are both required before you can activate.

Other states of this control — same two rows, only what’s ticked changes. Each input shows only while its vehicle is ticked (#4701).
One ticked · link only — today’s default
Payment link *
https://buy.stripe.com/8wM4kb…
QR code
Unticked → its input folds away. A QR you saved earlier is kept, just not sent.
Both ticked · QR still missing → blocks activation
QR code *
Drop your QR here — PNG or JPG

Add your QR to activate — or untick it.

Nothing ticked in a paid mode → both rows stay open with one line under them: Pick at least one way to get paid. Free RSVP → this whole group folds away — nothing to send.
Canvas fallback · file still saved · #4913
QR code *
paynow-qr.png
Saved — sent with every payment ask
ReplaceRemove

We couldn't convert this image — if the QR doesn't reach guests, re-upload from a different browser.

Upload succeeded; this is a warn, not a block; founder can Replace from another browser.

Scrolled further — how guests reach & book you, message copy, when to step in
dimmed Explore peeks — tap to return
03a · How guests reach you — two ways in
1
Your booking link — share it anywhere
Bio, launch post, story, DM — one tap opens the chat.
Your booking link
Download QR
State — WhatsApp not connected
Your booking link
Connect WhatsApp to generate your booking link and QR.

The card never hides — it lights back up the moment Connect finishes.

2
They just text you — no link needed
Guests who already have your number, or reply to a story.

The AI reads intent — “any spots left tuesday?” books a seat just like “book” does. Nothing to configure.

Tell assistants apart conditional
book · count me in

Only if more than one assistant shares this number — these tell your assistants apart. Nothing to do with reading intent; most founders never see this field.

03b · Where guests book — what they see next

However they arrive — link tap or a direct message — it's one path in. What they see next is your call:

In this chat
WhatsApp's own tap-through form — no typing back and forth, no separate app. Default.
Your booking list lives in Attendees — on this assistant, in Pulse.
Your own booking form
The assistant shares your form link; bookings land in your sheet, you tick paid there.
Your roster sheet is the record — MystFlo creates a pre-structured one in your Drive (default), or links the one you already have. Either way, we only read it.
Linking your own sheet? Any table works — you map your columns in Connect, and MystFlo has no information about a booking until it next reads your sheet.

Shows the form once WhatsApp approves it; guests text back and forth until then — nothing to set up on your end.

04a · Messages — in the order your guest gets them
Photo — optional · shown above the invite in the same bubble
+
No photo yet — drop one in, 1125×600 works best
1Booking invite — one field, same for every entry path · sent as a regular message, no approval needed
«workshop or event name» · «date» · «time» · «location» Tap below to grab your spot:

Shown above the invite button. Fills in your workshop name, date, time, and location automatically. No date set yet → the name-only line, never a blank.

2Booking form fixed — WhatsApp's own tap-through form (03b “In this chat”) · nothing to write
Name · Email · Party size · Date — filled from what you set in 01. “Your own booking form” (03b) sends your form link here instead.
3Payment ask — editable · sent as a regular message, no approval needed
Scan the QR to pay, or use the link below.

Sent right after the booking form, with your QR and/or link. The default follows what you ticked in 02 — QR only: “Scan the QR to pay.” · link only: “Use the link below to pay.” — and your edits stay. Lives here with the invite because it is your copy, not a setting.

4Booking confirmation locked
Meta-approved · not editable
Header: "You're booked — Wheel Throwing Class"
Hi Maya, it's Priya — your seat for the Wheel Throwing class is confirmed. We'll send a reminder before class. Reply here with any questions.
5Class reminder locked
Meta-approved · not editable
Hi Maya, it's Priya — just a quick reminder, your spot's all set. If anything comes up, reply here and let me know. See you soon!

One preview, not two — the 1-day and 1-hour sends use the identical body.

05 · When I need to step in not built · proposed
If the assistant can't answer conditional
Let On-Brand Support try first
Uses your full knowledge base. Costs one extra AI run per question — only when it's actually used.
Come straight to me
No second attempt, no extra cost.

Only shown when you have On-Brand Support running on this number.

What guests hear while they wait — editable
I'm connecting you with Priya — she'll come back to you shortly.
Where you get pinged
WhatsApp — this thread
Always on. Reply from your phone as usual.
Connected ⌄
Slack
A copy in your channel. On the app? You reply in WhatsApp. API-only number? Reply right in the thread — it reaches your guest.
Connect
Frame 3 · Personalize (375) [Activate]
  • Story — As a founder, I set my workshop's date, price, seats, how guests pay and book, and my message copy in one place — the assistant needs nothing else to run.
Acceptance
  • AC-WSBK-16 ↗ — required start_date gates Try and activation.
  • AC-WSBK-05 ↗ — a workshop configured free_rsvp skips the payment-ask leg entirely, straight to confirmed.
  • AC-WSBK-17 ↗ — payment_mode and booking_surface render as real radio controls the founder can select.
  • AC-WSBK-01 ↗ — a guest tapping the workshop's shareable wa.me link drops straight into the booking flow.
  • NEW (PRD debt — #4703 PRD row in flight) — 02 · How guests pay lets the founder tick link, QR, or both (directly under Pay in full / Free RSVP; Free RSVP folds the group away); both ticked → activation is blocked until BOTH the link and the QR are saved, and delivery is ONE bubble: the QR image with caption = thank-you + payment ask + the tappable link on its own line. Payment-ask default copy follows the ticked vehicles: “Scan the QR to pay, or use the link below.” / “Scan the QR to pay.” / “Use the link below to pay.”
Design notes
  • Categories 01 · 02 · 03a/03b · 04a/04b · 05: 03a “how guests reach you” = two equal entry cards (booking link · they-just-text, AI reads intent — nothing to configure); 03b “where guests book” radios = WhatsApp’s own tap-through Flow form (default) vs the founder’s own form, each with its destination line (Attendees on Pulse · a template roster created in her Drive — or a linked sheet — read-only); cancellation policy holds NO slot (Brain read, Connect owns it); 05 step-in is proposed/not built (why: sec-12 · parts: c-stepin).
  • Conditional grammar: “Tell assistants apart” keywords (routing disambiguation ONLY — never intent; renders only with 2+ assistants on the number) and 05’s On-Brand-Support radio pair draw as dimmed cond-blocks; 05’s holding line + ping rows always render. Dead controls omitted outright: five no-runtime-reader toggles, deposit_balance (a Retreat Booking concept).
  • 04a is one flow, in guest order (Peyton, preview review 2026-09-10 addendum): 1 Booking invite (editable) → 2 Booking form (fixed — WhatsApp’s own) → 3 Payment ask (editable; default “Scan the QR to pay, or use the link below.” — QR only “Scan the QR to pay.”, link only “Use the link below to pay.”; helper “Sent right after the booking form, with your QR and/or link.”) → 4 Booking confirmation (locked) → 5 reminder (locked). Each numbered row carries the same number as the .beat above the bubble it produces in the phone rail, so a founder reads the form top-to-bottom and watches the chat happen in that order. The old “Here’s how to pay…” default is superseded in these frames only — sec-7/sec-9 still draw today’s shipped copy until the PRD reseeds it.
  • Messages: Booking invite + Payment ask = regular session messages, stated in their fine print (never submitted to Meta); the invite's default names the program + date before the Book-now button ("{{workshop_name}} — {{date}}. Tap below to grab your spot:", reseeded #3789 — founders see the «name» / «date» labels, guests see the filled line); Booking confirmation + Class reminder = Meta-approved locked-tpl previews, copy verbatim (C1; ONE reminder body covers both offsets); Payment vehicles (#4703, spec change — not built) live in 02 · How guests pay, not here — Peyton (preview review 2026-09-10): one section answers “how guests pay” end to end, so the vehicle group sits directly under the Pay in full / Free RSVP radio, no separate 04b title; Free RSVP folds the group away. Payment ask stays here in 04a, right after the invite in chat order — it is founder copy, and founder copy lives together under Messages (Peyton 2026-08-04). The group = a checkbox GROUP of two vehicles, not a radio: Payment link (URL field nested under it) · QR code (nested upload — saved row: thumbnail · Replace · Remove; empty = dashed drop box). Tick one → that one is sent; tick both → both expand, both required, one helper line. The ask is the caption/body in every case, only the delivery vehicle changes (AC-WSBK-34 ↗ · AC-WSBK-35 ↗; gap #6b bridge must be verified in the same PR).
  • Why a checkbox group, not a third “Both” radio (#4703): what’s ticked IS what’s sent — one control, one truth, and the #4701 rule (each input lives under its own option, expanded only while it’s selected) carries over unchanged; a Both radio would have to expand two inputs under one option and invents a third state to explain. Guard rails drawn in the dashed call-out: link-only (today’s default; unticked QR folds its input but keeps the saved file), both-ticked-QR-missing (dashed drop box + the blocking line “Add your QR to activate — or untick it”), nothing ticked in a paid mode (“Pick at least one way to get paid”), Free RSVP (nothing required — today’s required_when). Schema implication for the build: today’s three-value payment_method (ticketing_page / stripe_link / qr) collapses to two vehicles — the PRD owns the field shape. 768: no separate frame — the control is a single column at every width; 768 draws the 375 stack at wider measure inside the desktop shell (canon U8 sm 640 step, same rule as the time pair).
  • #4913 fallback note is drawn in the Other-states call-out; happy path stays quiet; no-frame gap closed.
  • Preview = the full conversation in ONE pv-chat (invite → Flow form → payment ask with the QR and QR + link variants as one dashed call-out (the rail shows whichever matches 02) → Mark-paid beat → confirmation → reminder placeholder; scrolls in the ~460px shell, position:sticky in the build), plus a second dimmed phone for the own_form state (relay is the whole guest experience — AC-WSBK-14 ↗). start_date required, date-only, gates Try/activation (AC-WSBK-16 ↗); repeating sessions = #2798.
01 · Which workshop is this for? — 375 [Activate · Peyton 2026-09-25 · #4824]
  • Story — Same as the desktop note on sec-3b: pick the class from one dropdown, its standing details fill in, set only when and where this run happens.
  • Acceptance — identical to sec-3b (AC-WSBK-22 ↗ · AC-COHB-28 ↗ · AC-COHB-31 ↗ · the NEW (PRD debt) rows, incl. the one-time / recurring fill rule); this tier draws the same contract at 375, not a second behaviour.
  • 375 rules: eight phones, left to right — nothing picked (dropdown only; 02 carries on below, no empty 01 form) · loading · couldn’t load (fields revealed, Retry 44px) · list open (menu rows ≥44px, each sub-line leads with the Brain’s answer — Recurring / One-time · date — + Something new last after a separator; long lists scroll inside the menu) · recurring picked (standing details filled, Date + Time empty under the “Same class, new date?” hint) · one-time picked (everything filled, no hint; “price on ask” leaves Price blank) · + Something new (same fields, empty) · nothing in your Brain (no dropdown). The trigger is full-width, 44px, and shows the name only — price · spots live in the open list; Price + Currency share one row (the same sm 640 pair rule as the time pair) and stay side by side even at 375 because both values are short; the quiet line follows the Spots field; Time and Timezone stack at 375 (the shipped sm 640 pair rule) and the map link sits under Where as a second field. The one-offering and picked-then-edited states differ only by row count / one field value, so they ride the desktop call-out.
Off — the default; nothing is added unless you say so
dimmed Explore peeks — tap to return
Overview Personalize Connect Try
What you're activating
WorkshopSunday Glaze Clinic · new
Date2026-07-28
Price · Spots$38 · 8 spots
Wallet$86.40 · $1.75 per booking

Payment · Messages · Brain rows and the two “Trying it” notices are unchanged from sec-5 — cropped here.

Name this one
Sunday glaze clinic
Add it to what you sell?
Add “Sunday Glaze Clinic” to what you sell?
Your other assistants will quote it. Edit anytime in Brain.
Activate — $1.75 per booking

Runs on your WhatsApp from the moment you activate. Pause anytime.

On — added when you activate
dimmed Explore peeks — tap to return
Overview Personalize Connect Try
Name this one
Sunday glaze clinic
Add it to what you sell?
Add “Sunday Glaze Clinic” to what you sell?
Added when you activate — $38 · 8 seats lands in What you sell.
Activate — $1.75 per booking

Runs on your WhatsApp from the moment you activate. Pause anytime.

State — hidden
Picked an existing offering, or typed a name already in your Brain → no row at all; Name this one sits directly above Activate.
Frame 5m · Try (375) — the ask row [Activate · #4824]
  • Story — Same as sec-5: I decide on the last step, on my phone, whether the workshop I typed joins what I sell.
  • Only after “+ Something new” (Peyton 2026-09-25) — or when the Brain had nothing to pick from. Picking an existing offering in the 01 dropdown never shows this row: it is already in what you sell, and a new date is not a new offering.
  • Acceptance — identical to the sec-5 “Add it to what you sell?” block (three NEW (PRD debt — #4824) rows).
  • 375 rules: the row is ≥44px with the switch as the only control, label wraps to two lines, the sub-line changes with the state (ask copy off · “Added when you activate” on); the row sits directly above Activate with nothing between them. Off and On are both drawn; hidden is stated, not drawn — there is nothing to draw.

Copy on these frames is provisional — Chief Product draft 2026-09-25, pending Peyton’s approval (#5311). Placement and structure are the contract; strings may still change.

A · Support not live yet — Personalize opens with a one-line heads-up
dimmed Explore page peeks — revealGap · app header stays visible above
Workshop Booking
Takes the booking, relays your payment link, and reminds them before class — so seats don't go empty and DMs don't pile up.
$1.75 per booking
✕
Overview›Personalize›Connect ›Try
Booking comes with Support It switches on with this booking assistant — you’ll see it under Connect.
01 · Your workshop
Workshop name * required
Saturday Pottery Workshop
Workshop date * required
2026-07-28

… the rest of Personalize is unchanged — 3b (desktop) · 3 (375).

B · Support not live yet — Connect: the add-on is selected and locked
dimmed Explore page peeks — revealGap · app header stays visible above
Workshop Booking
Takes the booking, relays your payment link, and reminds them before class — so seats don't go empty and DMs don't pile up.
$1.75 per booking
✕
Overview›Personalize ›Connect›Try
01 · Connect apps
Communication WhatsApp Business
+1 555-0142 · one number, both assistants
Connected
AI Model MystFlo AI
Connected by default
Connected
Storage MystFlo Storage
Your Attendees list on Pulse
Connected
Add-on
On-Brand Support+$0.50/session

Answers your guests’ questions from your Brain · a booking link or QR tap goes straight to the form.

Booking runs with Support — each is charged only when used.

… template-approval notice, 02 · Wallet and 03 · Brain Check are unchanged — frame 4.

C · Support not live yet — Try reads back both, one Activate
dimmed Explore page peeks — revealGap · app header stays visible above
Workshop Booking
Takes the booking, relays your payment link, and reminds them before class — so seats don't go empty and DMs don't pile up.
$1.75 per booking
✕
Overview›Personalize ›Connect ›Try
What you’re activating
Workshop Booking
Workshop date2026-07-28
Price / seats$42 · 10 total
PaymentFull prepay · in this chat
Messagesconfirmation + reminder — pending Meta
Switching on with it
On-Brand SupportAnswers your guests’ questions · a booking link or QR tap goes straight to the form
Greeting“Hi! You’ve reached Ember Clay — how can we help?”
Routes to yourefunds · anything it can’t answer
NameEmber Clay support
Braincovers both — Support reads every section✓ 7 of 7 ready
Wallet$86.40 · $1.75 per booking · $0.50 per Support session
Other Support states — same layout, one line changes Paused (possible only while no booking assistant is live) → the second box heads “Resuming with it”; CTA stays “Activate both”.   Stopped (in Past) → “Switching back on with it”, its saved setup comes back (quick resume).   Brain short for Support → the Brain row reads “5 of 7 — 2 more for Support” with the existing Finish in Brain gate; Activate waits, as for any Brain gate.
Trying it

Unchanged — the two notices in frame 5 (real messages only · send yourself the first one).

Name this one
Weekly wheel-throwing class
Activate both

Both run on your WhatsApp from the moment you activate. Each is charged only when used.

D · Support already live — no Personalize notice; Connect row reads Active
dimmed Explore page peeks — revealGap · app header stays visible above
Workshop Booking
Takes the booking, relays your payment link, and reminds them before class — so seats don't go empty and DMs don't pile up.
$1.75 per booking
✕
Overview›Personalize ›Connect›Try
01 · Connect apps
Communication WhatsApp Business
+1 555-0142 · one number, both assistants
Connected
AI Model MystFlo AI
Connected by default
Connected
Storage MystFlo Storage
Your Attendees list on Pulse
Connected
Add-on
On-Brand Support+$0.50/session
Active

Answers your guests’ questions from your Brain · a booking link or QR tap goes straight to the form.

Already live on this number as “Ember Clay support” — nothing to switch on.

… template-approval notice, 02 · Wallet and 03 · Brain Check are unchanged — frame 4.

E · Support already live — Try: one quiet row, CTA unchanged
dimmed Explore page peeks — revealGap · app header stays visible above
Workshop Booking
Takes the booking, relays your payment link, and reminds them before class — so seats don't go empty and DMs don't pile up.
$1.75 per booking
✕
Overview›Personalize ›Connect ›Try
What you’re activating
Workshop Booking
Workshop date2026-07-28
Price / seats$42 · 10 total
PaymentFull prepay · in this chat
Messagesconfirmation + reminder — pending Meta
On-Brand SupportEmber Clay support answers questions · link and QR taps book directlyAlready live
Brain✓ 7 of 7 ready
Wallet$86.40 · $1.75 per booking
Trying it

Unchanged — the two notices in frame 5 (real messages only · send yourself the first one).

Name this one
Weekly wheel-throwing class
Activate — $1.75 per booking

Runs on your WhatsApp from the moment you activate. Pause anytime.

Frame 3s · Booking comes with Support [Activate · #5311 · not built]
  • Story — As a founder turning on a booking assistant, I’m told before I fill in a single field that On-Brand Support comes with it, I see it already selected as an add-on in Connect, and I switch both on with one Activate.
Acceptance
  • AC-WSBK-61 ↗ · AC-EVBK-10 ↗ · AC-COHB-35 ↗ — Support is never spawned silently; it shows as its own add-on row, its own read-back box and its own Pulse card. Amended 2026-09-25: the add-on is REQUIRED — selected and locked, never deselectable (NEW (PRD debt) until the three PRDs carry it).
  • NEW (PRD debt) — Connect ’01 · Connect apps’ ends with the On-Brand Support add-on row, selected and locked, with the one-line reason. If Support is already live, the row shows Active instead of a switch.
  • NEW (PRD debt) — the Personalize notice renders only when Support is not live, and it points to the Connect row instead of restating it.
  • NEW (PRD debt) — the Brain gate is the union: Support’s sections count before “Activate both” enables (AC-WSBK-61 ↗, “OBS Brain gates still apply”).
  • NEW (PRD debt) — both or neither: if Support can’t switch on, neither goes live and the error says why (approved by Peyton 2026-09-25).
Design notes
  • Where Support shows up (Peyton 2026-09-25): as an add-on in Connect, using the existing AddOnCard in its enabled state (stronger hairline, switch on), with the switch locked and a reason line. Build delta: AddOnCard needs a locked + lockReason prop, a price suffix read from the template’s unit (“/session”, not a hard-coded “/run”), and a mount in ConnectStep (today it only mounts in Personalize).
  • Upfront, but said once. The Personalize .notice is a headline plus a pointer, with no lantern because it informs rather than asks. The full reason lives on the Connect row, and the card and Overview carry the pairing line one step earlier.
  • Support starts on its Brain defaults (greeting and routing), which the founder edits later from its own Pulse card. Connect adds no fields of its own: one WhatsApp number serves both assistants.
  • Prices render only from price_per_run (the add-on price, the Wallet row and the CTA). They never appear in notice or reason copy.

Copy on these frames is provisional — Chief Product draft 2026-09-25, pending Peyton’s approval (#5311). Placement and structure are the contract; strings may still change.

768 samples the U8 continuum; it isn’t a third layout. The notice, the Connect rows with the add-on, and the read-back are all single-column at every width, so tablet is the 375 stack at a wider measure inside the desktop sheet chrome (24px gutters, column capped at 720). A to C are drawn here; D and E follow the same rule.

A · Personalize — heads-up
tablet 768 — same sheet chrome, 24px gutters, one column
Workshop Booking
Takes the booking, relays your payment link, and reminds them before class — so seats don't go empty and DMs don't pile up.
$1.75 per booking
✕
Overview›Personalize›Connect ›Try
Booking comes with Support It switches on with this booking assistant — you’ll see it under Connect.
01 · Your workshop
Workshop name * required
Saturday Pottery Workshop
Workshop date * required
2026-07-28

… the rest of Personalize is unchanged — 3b (desktop) · 3 (375).

B · Connect — locked add-on
tablet 768 — same sheet chrome, 24px gutters, one column
Workshop Booking
Takes the booking, relays your payment link, and reminds them before class — so seats don't go empty and DMs don't pile up.
$1.75 per booking
✕
Overview›Personalize ›Connect›Try
01 · Connect apps
Communication WhatsApp Business
+1 555-0142 · one number, both assistants
Connected
AI Model MystFlo AI
Connected by default
Connected
Storage MystFlo Storage
Your Attendees list on Pulse
Connected
Add-on
On-Brand Support+$0.50/session

Answers your guests’ questions from your Brain · a booking link or QR tap goes straight to the form.

Booking runs with Support — each is charged only when used.

… template-approval notice, 02 · Wallet and 03 · Brain Check are unchanged — frame 4.

C · Try — both, one Activate
tablet 768 — same sheet chrome, 24px gutters, one column
Workshop Booking
Takes the booking, relays your payment link, and reminds them before class — so seats don't go empty and DMs don't pile up.
$1.75 per booking
✕
Overview›Personalize ›Connect ›Try
What you’re activating
Workshop Booking
Workshop date2026-07-28
Price / seats$42 · 10 total
PaymentFull prepay · in this chat
Messagesconfirmation + reminder — pending Meta
Switching on with it
On-Brand SupportAnswers your guests’ questions · a booking link or QR tap goes straight to the form
Greeting“Hi! You’ve reached Ember Clay — how can we help?”
Routes to yourefunds · anything it can’t answer
NameEmber Clay support
Braincovers both — Support reads every section✓ 7 of 7 ready
Wallet$86.40 · $1.75 per booking · $0.50 per Support session
Other Support states — same layout, one line changes Paused (possible only while no booking assistant is live) → the second box heads “Resuming with it”; CTA stays “Activate both”.   Stopped (in Past) → “Switching back on with it”, its saved setup comes back (quick resume).   Brain short for Support → the Brain row reads “5 of 7 — 2 more for Support” with the existing Finish in Brain gate; Activate waits, as for any Brain gate.
Trying it

Unchanged — the two notices in frame 5 (real messages only · send yourself the first one).

Name this one
Weekly wheel-throwing class
Activate both

Both run on your WhatsApp from the moment you activate. Each is charged only when used.

Story, acceptance, and design notes are shared with the desktop tier (one note for every tier).

Copy on these frames is provisional — Chief Product draft 2026-09-25, pending Peyton’s approval (#5311). Placement and structure are the contract; strings may still change.

A · Personalize — heads-up
dimmed Explore peeks — tap to return
Workshop Booking
$1.75 per booking
OverviewPersonalizeConnect Try
Booking comes with Support It switches on with this booking assistant — you’ll see it under Connect.
01 · Your workshop
Workshop name * required
Saturday Pottery Workshop
Workshop date * required
2026-07-28

… the rest of Personalize is unchanged — 3b (desktop) · 3 (375).

B · Connect — locked add-on
dimmed Explore peeks — tap to return
Workshop Booking
$1.75 per booking
OverviewPersonalize ConnectTry
01 · Connect apps
Communication WhatsApp Business
+1 555-0142 · one number, both assistants
Connected
AI Model MystFlo AI
Connected by default
Connected
Storage MystFlo Storage
Your Attendees list on Pulse
Connected
Add-on
On-Brand Support+$0.50/session

Answers your guests’ questions from your Brain · a booking link or QR tap goes straight to the form.

Booking runs with Support — each is charged only when used.

… template-approval notice, 02 · Wallet and 03 · Brain Check are unchanged — frame 4.

C · Try — both, one Activate
dimmed Explore peeks — tap to return
Workshop Booking
$1.75 per booking
OverviewPersonalize Connect Try
What you’re activating
Workshop Booking
Workshop date2026-07-28
Price / seats$42 · 10 total
PaymentFull prepay · in this chat
Messagesconfirmation + reminder — pending Meta
Switching on with it
On-Brand SupportAnswers your guests’ questions · a booking link or QR tap goes straight to the form
Greeting“Hi! You’ve reached Ember Clay — how can we help?”
Routes to yourefunds · anything it can’t answer
NameEmber Clay support
Braincovers both — Support reads every section✓ 7 of 7 ready
Wallet$86.40 · $1.75 per booking · $0.50 per Support session
Other Support states — same layout, one line changes Paused (possible only while no booking assistant is live) → the second box heads “Resuming with it”; CTA stays “Activate both”.   Stopped (in Past) → “Switching back on with it”, its saved setup comes back (quick resume).   Brain short for Support → the Brain row reads “5 of 7 — 2 more for Support” with the existing Finish in Brain gate; Activate waits, as for any Brain gate.
Trying it

Unchanged — the two notices in frame 5 (real messages only · send yourself the first one).

Name this one
Weekly wheel-throwing class
Activate both

Both run on your WhatsApp from the moment you activate. Each is charged only when used.

D · Already live — Connect row Active
dimmed Explore peeks — tap to return
Workshop Booking
$1.75 per booking
OverviewPersonalize ConnectTry
01 · Connect apps
Communication WhatsApp Business
+1 555-0142 · one number, both assistants
Connected
AI Model MystFlo AI
Connected by default
Connected
Storage MystFlo Storage
Your Attendees list on Pulse
Connected
Add-on
On-Brand Support+$0.50/session
Active

Answers your guests’ questions from your Brain · a booking link or QR tap goes straight to the form.

Already live on this number as “Ember Clay support” — nothing to switch on.

… template-approval notice, 02 · Wallet and 03 · Brain Check are unchanged — frame 4.

E · Already live — Try
dimmed Explore peeks — tap to return
Workshop Booking
$1.75 per booking
OverviewPersonalize Connect Try
What you’re activating
Workshop Booking
Workshop date2026-07-28
Price / seats$42 · 10 total
PaymentFull prepay · in this chat
Messagesconfirmation + reminder — pending Meta
On-Brand SupportEmber Clay support answers questions · link and QR taps book directlyAlready live
Brain✓ 7 of 7 ready
Wallet$86.40 · $1.75 per booking
Trying it

Unchanged — the two notices in frame 5 (real messages only · send yourself the first one).

Name this one
Weekly wheel-throwing class
Activate — $1.75 per booking

Runs on your WhatsApp from the moment you activate. Pause anytime.

Story, acceptance, and design notes are shared with the desktop tier (one note for every tier). Buttons are full width. The locked switch keeps a 44px hit area and announces “on, locked” along with the reason line.
Live

Copy on these frames is provisional — Chief Product draft 2026-09-25, pending Peyton’s approval (#5311). Placement and structure are the contract; strings may still change.

Activating "Weekly wheel-throwing class"

About 30 seconds. Stay on this screen.

✓Checking your setup
✓Confirming billing & terms
✓Setting up your assistant
◍Switching on On-Brand Support~10s
Running a live test
Going live
✓
Workshop Booking and On-Brand Support are live

Support answers your guests’ questions; a booking link or QR tap goes straight to the form. Watch both in Pulse.

✓ WhatsApp connected — one number, both assistants ✓ Brain confirmed — 7 of 7 sections
Confirmations & reminders — pending Meta approval Unchanged from frame 6 — bookings capture now; the two templates send once Meta approves.
View in Pulse Done
Frame 6s · Activating both → both live [Monitor · #5311 · not built]
  • Story — As a founder, I watch Support switch on as one real step and land on one success screen that names both and says who talks to my guests first.
Acceptance
  • Loader grammar = master frame 7a (assistant-lifecycle.html) — not restated. NEW (PRD debt): one added step, “Switching on On-Brand Support”, only when Support wasn’t live.
  • NEW (PRD debt) — success names both assistants, and Pulse gains two cards (never one merged card).
Design notes
  • Support already live → frames 6a / 6 exactly as drawn; this strip never renders.
  • The Meta-approval amber notice is unchanged — it belongs to the booking templates, not to Support.

Loader and success are a centered dialog column at every width (U8): 768 draws the same 420 / 460 column centered over the dimmed page.

tablet 768 — page dims behind the centered column
✓
Workshop Booking and On-Brand Support are live

Support answers your guests’ questions; a booking link or QR tap goes straight to the form. Watch both in Pulse.

✓ WhatsApp connected — one number, both assistants ✓ Brain confirmed — 7 of 7 sections
Confirmations & reminders — pending Meta approval Unchanged from frame 6 — bookings capture now; the two templates send once Meta approves.
View in Pulse Done
bottom sheet — full width
Activating "Weekly wheel-throwing class"

About 30 seconds. Stay on this screen.

✓Checking your setup
✓Confirming billing & terms
✓Setting up your assistant
◍Switching on On-Brand Support~10s
Running a live test
Going live
bottom sheet — full width
✓
Workshop Booking and On-Brand Support are live

Support answers your guests’ questions; a booking link or QR tap goes straight to the form. Watch both in Pulse.

✓ WhatsApp connected — one number, both assistants ✓ Brain confirmed — 7 of 7 sections
Confirmations & reminders — pending Meta approval Unchanged from frame 6 — bookings capture now; the two templates send once Meta approves.
View in Pulse Done
Story · acceptance · design notes — shared with the desktop tier.
Monitor “is it working?” · sec-6a Activating · sec-6 Activated · sec-8 Pulse
Activating "Weekly wheel-throwing class"

About 20 seconds. Stay on this screen.

✓Checking your setup
✓Confirming billing & terms
✓Setting up your assistant
◍Running a live test~15s
Going live
Frame 6a · Activating loader [Monitor]
  • Story — As a founder, I watch real setup steps complete in order — not a fake spinner — so waiting feels like something is actually happening.
Acceptance
  • Covered by the master lifecycle contract's own criteria (assistant-lifecycle.html / the activation-wizard PRD), not restated here — this card contributes no new criterion for the generic loader.
Design notes
  • Same C2 pipeline as every assistant: wording ported verbatim from the master loader (assistant-lifecycle.html frame 7a) — no assistant-specific rewording, same 5 grouped steps.
✓
Workshop Booking is live

It's capturing seats on your WhatsApp now. Watch bookings fill in Pulse.

✓ WhatsApp connected ✓ Brain confirmed — 4 of 4 sections
Confirmations & reminders — pending Meta approval Two message templates are still waiting on Meta's one-time approval — usually a day or two. Bookings capture and your payment link relays right now; confirmations and reminders will start sending once approval comes through. If either is rejected instead, you'll get a clear fix step here — never a silent stall (see sec-6b below).
View in Pulse Done
Frame 6 · Activated / success [Monitor]
  • Story — As a founder, the moment activation finishes I know it's live, what's already true (WhatsApp connected, Brain confirmed), and exactly what's still pending — never a surprise later.
Acceptance
  • AC-WSBK-18 ↗ — when templates are pending Meta approval at activation success, the screen states that honestly and never claims confirmations/reminders already send; bookings and payment relay keep working underneath.
Design notes
  • Checklist carries only always-true facts (WhatsApp connected, Brain confirmed); the pending-Meta wait is its own amber notice, never folded into the green list. Headline hedges; "usually a day or two" stays in sub-copy (matches sec-4's banner).
Active ▶⏸⏹ Details →
Weekly wheel-throwing class ASST-4L9
Workshop Booking
1 connected · reminders pending Meta approvalⓘ
Bookings
9 confirmed · 2 awaiting payment · $15.75 · 2h ago
Attendeeswho's coming, who still owes you7 booked · 3 left ›
Needs your attention
!
+1 ••• ••• 7742 — booked, not yet paid
Booked 44 min ago · payment ask sent, no reply yet
Nudge
!
2 templates pending Meta approval
Confirmations & reminders hold until approved
Details
Today
Booking confirmed — Priya M.+1 ••• ••• 2210 · paid $42 6:14 PM
Payment link relayed+1 ••• ••• 7742 · awaiting payment 5:30 PM
HeldClass reminder queued — pending template approvalWill send once Meta approves tonight 6 PM
Booking confirmed — Daniel R.+44 •• •• 9031 · paid $42 2:05 PM
Frame 8 · Pulse [Monitor]
  • Story — As a founder, I watch bookings and payments roll in and see exactly who still needs a nudge, without depending on push notifications that don't exist yet.
Acceptance
  • AC-WSBK-06 ↗ — a payment signal arriving after the session's start time is flagged for the founder to confirm/decline, never auto-confirmed — this is what an "attention" row like the unpaid booking represents.
  • AC-WSBK-07 ↗ — duplicate captures from the same phone don't create a second row, so the feed never double-counts one guest.
Design notes
  • Honest fallback while notifications aren't built: even though the push-notification toggles are hidden (sec-3 — no runtime reader exists), Pulse itself always shows unpaid/pending bookings the moment the founder opens it. Real detection, not the broken notify system standing in for it.
  • Card geometry, controls, and feed pattern reuse the master's fixed zone map (assistant-lifecycle.html) — same shell as every Pulse card, only the metrics line and feed content are Workshop-Booking-specific. Support paused or off while this card is live (#5311) is drawn once in the master: assistant-lifecycle.html → 8f.
  • Attendees is a slot IN the card (2026-09-02, master frame 8d): last zone after Bookings — noun · "7 booked · 3 left ›" — collapses to zero on a non-records card, never renders for guests, taps into the detail sheet with the records sheet open (sec-13). Not built.
  • Meta line uses the platform's D-3 convention: amber lives on the meta line only, never the status badge — "Active" stays green because bookings and payment relay already work; only the outbound reminder send is waiting.

Two things sit behind “route to the founder”: how the handover physically works (settled — it happens in the same WhatsApp thread, no second app needed) and what the founder can decide about it (nothing today — and one of those decisions costs her money, so it can't stay silent).

MechanismIt happens in the same chat — Slack is only a mirror
shippedWhatsApp Coexistence · Meta, May 2025
   ONE number — the founder's own. Two things talk on it at once.

   ┌───────────────────────────────┐        ┌──────────────────────────────┐
   │  WhatsApp Business APP        │◄──────►│  Cloud API (MystFlo)         │
   │  on Priya's phone             │ mirror │  the assistants              │
   │  she types here, by hand      │ in     │  they answer here            │
   └───────────────┬───────────────┘ real   └───────────────┬──────────────┘
                   │                 time                   │
                   └────────────────────┬────────────────────┘
                                        ▼
                              ┌──────────────────┐
                              │  the guest's     │   one thread.
                              │  WhatsApp chat   │   one number.
                              └──────────────────┘   no handoff seam.

   escalation fires ──► assistant goes SILENT (human mode)
                   ├──► guest told "I'm connecting you with Priya"
                   ├──► Priya opens WhatsApp and replies herself
                   └──► Slack (optional): a copy lands in her channel
                        ── one-way. She CANNOT reply from Slack. ──
Design notes
  • Since May 2025, WhatsApp Coexistence lets the founder's own number run the Business App and the Cloud API at once, mirroring in real time — the founder keeps her phone and replies by hand in the same thread the assistant was just in. No second app is needed.
  • Slack is a one-way mirror today: escalations and guest messages post into the founder's channel, and she cannot answer from there.
  • A founder whose number is Cloud-API-only (migrated before coexistence existed, or onboarded API-only) has no Business App to reply from — for that class of founder, “route to the founder” currently has no way for her to actually answer.
  • The holding line — “I'm connecting you with Priya” — should degrade to what's true for founders who have no reply path yet, rather than promise a human who can't currently speak.
Gap → placedThe founder can't set any of this — now a real Personalize category (05)
NOT BUILT · drawn in place as Personalize 05 (sec-3 / sec-3b)
Design notes
  • Category drawn in the real Personalize frames — sec-3b / sec-3, 05 · When I need to step in; full-size reference: Part II · c-stepin.
  • OBS second attempt is a PAID AI run that fires automatically today with no opt-in — the radio makes it a founder choice; default ON, cost stated per-question, never monthly.
  • Holding line is hard-coded today ("I'm connecting you with {founder} who can assist you further. They'll respond shortly!") — becomes an editable field.
  • Slack ping already wired, optional, ONE-WAY — presented as a choice for the first time; the WhatsApp-thread reply path is always on.

Every booking lands in MystFlo's own ledger. Nothing flows outward — not to Sheets, not to Airtable, not to Notion, not to a file. And the founder's only view of that ledger shows bookings that are still unpaid — the moment one is paid, it leaves her screen; the Attendees block + sheet below fix that (re-cut 2026-09-02 — Peyton: the list lives IN the Pulse tab and rises as a nested sheet, no longer a standalone page).

  IN-CHAT MODES (A · B)                        OWN-FORM MODE (C)

  Flow form submitted                          founder's own form
        │ name · email · seats                       │ (MystFlo never sees it)
        ▼                                            ▼
  ┌───────────────────┐                     ┌──────────────────────┐
  │  MystFlo ledger   │◄────── reads ───────│  her roster Sheet —  │
  │  (cohort_bookings)│      one way        │  MystFlo TEMPLATE in │
  └─────────┬─────────┘                     │  her Drive (default) │
            │                               │  or a linked sheet   │
            │                               └──────────────────────┘
            ▼                                  she ticks paid — the
                                               tick is the billable
                                               event
  ┌───────────────────────────────┐
  │ Pulse · unpaid bookings only  │   paid → disappears from view
  └───────────────────────────────┘

        ✗ no write-back to her Sheet        ✗ no Airtable / Notion
        ✗ no export
Design notes
  • Bookings flow straight into MystFlo's own ledger — name, email, seats, amount, status. It does not reach the founder's own tools, and today she cannot get it out.
  • Sheets read into MystFlo today but never write back; Airtable, Notion, and a plain export don't exist yet.
  • Own-form: her form collects the booking off-platform, her sheet is the record; ticking a row paid is the billable event — it confirms the booking, fires the $1.75 charge, and triggers the same confirmation + 1d/1h reminders (AC-WSBK-11 ↗). No payment integration on this path.
  • A founder who picks own-form has a full attendee list — her own sheet. A founder who picks the default in-chat mode has no list until Attendees (M1) ships: Pulse shows unpaid bookings until they're paid, then nothing.
Approved 2026-07-28 · re-cut 2026-09-02 (Peyton) · roster-first amendment 2026-09-03 (R37)Attendees — a block IN the right slot + a nested sheet; not a tab, no longer a page

Every field is already stored on the booking, and all three verdict actions already ship in the booking EF. Re-cut 2026-09-02 (Peyton ruling), amended 2026-09-03 (R37 — "right column as principle"): Attendees is a block that is the FIRST item in the Pulse tab's right slot (noun-titled, ≤5 rows, "See all N ›"), the booking link/share/flyer sit under it, and the full list rises as a nested, scrollable sheet over that tab — not a new tab (R30/R31 hold), not a full-width band, and no longer a routed page. The way in from the wall is a slot inside the Pulse card, not a row hanging under it. This supersedes the 2026-07-28 "Pulse row → full-screen destination" contract and the #2924 route lock; the shipped AssistantRecordsPage + RecordsEntryRow are now drift, not the contract. Deep link survives as a URL param on the sheet: ?assistant=<id>&records=open (param name = build decision, CTO); the old route becomes a redirect. Naming is per-assistant: Workshop shows Attendees, Checkout Orders, Waitlist Signups — one component, one records contract. "Who's coming" survives as the group heading inside. Event Booking (TMPL-EVBK01) rides these frames (design decision 2026-08-26) — this re-cut IS the Event Booking update; only the program nouns change.

NOT BUILTM1 shipped as a routed page (#2924) — now driftread + verdict, never editin-card slot → Pulse-tab block → nested sheet · R30/R31 unamended · R36not built · 2026-09-02
the post detail sheet (8b, Pulse tab) recedes behind — nestedBackdrop · ✕ / Escape close THIS sheet only · ?assistant=4L9&records=open · same sheet as master assistant-lifecycle.html #sec-9e
Attendees · Weekly wheel-throwing class
Desktop ≥1024 · 10 seats · 7 booked · 3 left · synced 2 min ago
✕
Awaiting paymentWaitlistFilterChipGroup — zero selected = everyone, no "All" chip
NameContactSeatsBookedStatus
Maya Chenmaya@studio.co226 Julpaid
Tom Reyestom@reyes.co126 Julpaid
Aisha Koneaisha@studio.co3link sent 27 Julawaiting paymentMark paid⋯
Devi Raodevi@rao.studio1roster tickpaid
Waitlist · 2
Sam Oyelaranphone only—joined 27 Julwaiting
PaginationBar sortable — hidden at ≤1 page (20 rows/page); drawn here for shape only‹ PrevPage 1 of 1Next ›
Columns come from the per-assistant records contract — the same declaration that names the destination. The read-only columns match the mirror sheet 1:1, so in-app and her spreadsheet are the same picture. Read + verdict only — no cell is ever editable. Sheet chrome: ModalWrapper presentation="sheet", contentWidth="wide", nestedBackdrop, broadcastCloseAll={false}.
Story
  • As a founder on the morning of my workshop, I open the assistant and see exactly who is coming, who still owes me, and who's waiting for a spot — without opening a spreadsheet or scrolling WhatsApp.
Design notes
  • Lives on the assistant, never a global list — cross-assistant merged tables and phone/email identity joins are banned; deep-link entry required (workshop morning is repeat-entry) — now a URL param on the sheet (?assistant=…&records=open), not a route.
  • Nested sheet, not a page (2026-09-02). Rises over the Pulse tab the founder was reading; ✕ / Escape close only the sheet and she is back on the tab. Header band = noun · instance name, subtitle = seats · booked · left · synced-ago, utility ✕. Filter chips and pagination are the platform idioms (FilterChipGroup, PaginationBar), never a third style.
  • Every status stays listed — unpaid is called out by dot + verdict, never by being the only rows shown; waitlist rows group beneath (a spot opens → "who's next" is human-answerable).
  • Read + verdict, never edit — contact fixes happen in the founder's own sheet, returning through the narrow read-back enum.
  • The live sheet mirror is M2, opt-in at activation, never auto-created.
Approved 2026-07-28 · re-cut 2026-09-02Attendees — the way in (slot + block), and every state the populated frame doesn't draw

The frame above draws one populated table at one width. A founder meets six other screens before or instead of it: the in-card slot and the in-tab block that open it, the same rows on a phone (as a bottom sheet), the day-one empty list, the fetch in flight, the fetch that failed, and the deep link opened by someone who isn't signed in — or isn't the owner. Mobile leads. The 6-column grid is a lg 1024 structure only; below it the identical rows render stacked, and no column is dropped — every field stays visible, the layout changes, the data does not.

NOT BUILTnested sheet · deep-linkable via ?records=openone component, noun is a proprow geometry identical in every state
13a · The way in — a SLOT IN the Pulse card + a BLOCK IN the Pulse tab (not a row under the card, not a page, not a tab) not built · 2026-09-02
Active ▶⏸⏹ Details →
Weekly wheel-throwing class ASST-4L9
Workshop Booking
1 connected · next class Sat 10:00
Bookings
9 confirmed · 2 awaiting payment · $15.75 · 2h ago
Attendeeswho's coming, who still owes you7 booked · 3 left ›
Needs your attention
!
+1 ••• ••• 7742 — booked, not yet paid
Booked 44 min ago · payment ask sent, no reply yet
Nudge
375 · Pulse wall — the Attendees line is the card's LAST zone (after Runs, before any footer action); nothing hangs under the tile, so the wall stays a wall. Never renders for guests. Tap → detail sheet on Pulse + the records sheet.
Desktop · the in-card slot's two seat states, then the in-tab block
Attendeeswho's coming, who still owes you7 booked · 3 left ›
Attendeeswho's coming, who still owes yousold out · 2 waiting ›
Attendees 10 seats · 7 booked · 3 left · synced 2 min agoSee all 6 ›
Aisha Kone3 seats · link sent 27 Jul · aisha@studio.coawaiting paymentMark paid⋯
Maya Chen2 seats · booked 26 Jul · maya@studio.copaid
Tom Reyes1 seat · booked 26 Jul · tom@reyes.copaid
Devi Rao1 seat · roster tick · devi@rao.studiopaid

4 of 6 · 2 on the waitlist · needs-verdict first, then newest. Full context: the Pulse tab in 8b.

Never amber-tinted as a whole row — sold-out is a state, not a fault. Status is a dot; the row stays monochrome. The block sits between the metrics and the activity feed (unified Pulse-tab template order: exception row → metrics · quick actions → records block → activity) and collapses entirely for a non-records assistant. No lantern on the block — an unpaid booking is pending (amber), a verdict is a domain action; D-5's "Waiting on you" flag keeps the tab's one lantern moment when present.
13b · Populated — the sheet below 1024 (375 bottom sheet · 768 shell), rows stacked
detail sheet dims behind · nested sheet
Attendees
10 seats · 7 booked · 3 left · synced 2 min ago
✕
Awaiting paymentWaitlist
Who's coming · 4
Maya Chenpaid
maya@studio.co · 2 seats · booked 26 Jul
Tom Reyespaid
tom@reyes.co · 1 seat · booked 26 Jul
Aisha Koneawaiting payment
aisha@studio.co · 3 seats · link sent 27 Jul
Mark paid⋯
Waitlist · 2
Sam Oyelaranwaiting
phone only · — seats · joined 27 Jul
375 · bottom sheet, full height · stacked rows — every column survives, none is dropped · pagination hidden at ≤1 page
detail sheet dims behind · nested sheet · rail 64px stays under it
Attendees
10 seats · 7 booked · 3 left · synced 2 min ago
✕
Awaiting paymentWaitlist768 · sheet takes the desktop band · content still stacked — the grid engages at 1024, not here
Who's coming · 4
Maya Chenmaya@studio.copaid
2 seats · booked 26 Jul
Aisha Koneaisha@studio.coawaiting paymentMark paid⋯
3 seats · link sent 27 Jul
Tablet takes its SHELL from desktop (the sheet's full-width band, rail still under it) and its CONTENT REFLOW from mobile (rows stay stacked) — the only thing extra width buys is the contact moving up onto line 1 and the verdicts moving inline.
13c · Empty — activation day, nobody has booked yet
detail sheet dims behind · nested sheet
Attendees
10 seats · 0 booked · 10 left
✕
◦
No one's booked yet

Your booking link is live. Share it and the first name lands here — usually within a day of posting.

Copy your booking link
375 · no chips — nothing to filter, so the control isn't drawn disabled, it isn't drawn
detail sheet dims behind · nested sheet
Attendees
Desktop ≥1024 · 10 seats · 0 booked · 10 left
✕
◦
No one's booked yet

Your booking link is live. Share it and the first name lands here — usually within a day of posting.

Copy your booking link
Column headers are NOT drawn over an empty body — a header row with nothing under it reads as a broken table, not as an empty one.
13d · Loading — the fetch in flight
detail sheet dims behind · nested sheet
Attendees
✕
375 · four skeleton rows at the real row height — never a centred spinner, never a height jump on resolve
detail sheet dims behind · nested sheet
Attendees
✕
NameContactSeatsBookedStatus
Column headers ARE drawn during load — they're structure the fetch can't change, and drawing them stops the header sliding in late. Screen readers get aria-busy="true" on the table; the shimmer is suppressed under prefers-reduced-motion.
13e · Error — the fetch failed
detail sheet dims behind · nested sheet
Attendees
10 seats
✕
!
Couldn't load your attendees

Nothing's lost — this is a connection problem on our end. Your bookings are safe.

Try again Close

Or ping us if it keeps happening.

375 · red is earned here — a fetch failure is a genuine failure, not a pending state · Close returns to the Pulse tab beneath
detail sheet dims behind · nested sheet
Attendees
Desktop ≥1024 · 10 seats
✕
last loaded 6 min ago
Couldn't refresh — showing the list from 6 minutes agoRows below may be out of date. Try again
Maya Chenpaid
Tom Reyespaid
Two error shapes, one rule: cold failure (no rows ever loaded) takes the full-surface state on the left; warm failure (a refresh failed over rows already on screen) keeps the stale rows, dims them, and names their age — never blanks a list the founder is reading. Verdict buttons are removed from stale rows: acting on data we know is stale is how a seat gets double-released. The in-tab block behind the sheet dims and strips its verdicts the same way — one hook, one stale flag.
13f · The deep link (?assistant=…&records=open), opened by the wrong person
◍
Sign in to see your attendees

We'll bring you straight back to this list.

Sign in
375 · NOT signed in — Pulse is a <ProtectedRoute>; sign-in first. Return-to back to the sheet is a build gap (no platform-wide return-to exists — named, not invented). Never a guest preview: a roster has no public shape.
Pulse
Your active assistants
ActiveDetails →
Her own assistant ASST-1A1
On-Brand Support
Runs
12 · 100% healthy · 1d ago · $1.10
375 · signed in, NOT the owner — ?assistant= resolves nothing she owns (RLS returns no row), so both params are dropped and Pulse renders her own wall. No sheet, no count, no name, no "permission" wording — byte-identical to a deleted assistant. Replaces the old in-surface "This list isn't here" state: there is no surface without the assistant.
Story
  • As a founder, whatever state the list is in — empty, still loading, broken, or not mine — I always know what happened and what to do next, and I never see a blank screen with no way forward.
Acceptance
  • NEW (PRD debt) — amendment pending, replaces the AC-WSBK-23 read — Given the in-card slot ("Attendees · N booked · M left ›"), the in-tab block's "See all", or the URL ?assistant=<id>&records=open / When resolved / Then the records sheet opens stacked over the post detail sheet's Pulse tab; ✕ and Escape close only the sheet; refresh, bookmark, second device reopen it. The post-activation tab set stays Pulse · Overview · Personalize · Connect. (AC-WSBK-23's "same full-screen destination" wording no longer matches — never edited here.)
  • AC-WSBK-24 ↗ — paid rows stay listed; unpaid are called out by dot + verdict, never by being the only rows present.
  • AC-WSBK-25 ↗ — the only controls on a row are mark paid · extend · release; no state adds an editable cell, and the stale-rows error state removes verdicts rather than adding any.
  • AC-WSBK-27 ↗ — waitlist rows group beneath attendees in every tier; on 375 they keep the same stacked row shape, Seats reads "—".
  • AC-WSBK-28 ↗ — a paused assistant renders these states unchanged; pausing is not an error state.
  • NEW (PRD debt) — amendment pending, replaces the AC-WSBK-31 read — Given a signed-in non-owner opens the deep link / When ?assistant= resolves no owned assistant / Then Pulse renders their own wall with no sheet, no count, no name, no permission wording — byte-identical to a deleted assistant. (AC-WSBK-31's "This list isn't here" state no longer exists as a surface.)
  • NEW (PRD debt) — Given the attendee fetch fails after rows have already rendered / When the founder is looking at the list / Then the stale rows stay on screen, dimmed and labelled with their age, with verdict controls removed — the list is never blanked and a stale row is never actionable.
  • NEW (PRD debt) — Given a signed-out visitor opens the Attendees deep link / When the route resolves / Then they are sent to sign-in with a return-to back to this list — the surface has no guest or preview mode.
Design notes
  • Nested sheet, not a routed page (Peyton 2026-09-02 — supersedes the "Routed page, not a slide-over" bullet and #2924). The list rises over the Pulse tab as a ModalWrapper sheet (nestedBackdrop, broadcastCloseAll={false}, contentWidth="wide"), so the founder never loses the Pulse context — ✕ returns her to the tab. The deep-link reason that justified the route survives as a URL param on the sheet (?assistant=<id>&records=open; name = build decision, CTO): bookmark, refresh, second device all reopen it. The old /pulse/:assistantId/records route becomes a redirect. Still not a PageSlideOver — a data-review-and-decide panel is a sibling sheet (2026-07-01 lock).
  • Structure steps at lg 1024 only (6-column grid; below it the same fields stack, nothing dropped); row geometry identical across all four data states (56px min mobile / 40px grid) so the surface never jumps height on resolve. Fluid per U8; radius/hairlines constant.
  • Status is a dot, the row stays monochrome — emerald paid (C12), amber awaiting/sold-out, white/35 waiting. Red appears only on a genuine load failure, never on an unpaid booking.
  • One component, three nouns. Attendees / Orders / Signups is a prop plus a per-assistant column contract; every state above is authored once and reused. The empty-state copy is the only per-assistant string.
  • Build gaps (2026-09-02): route redirect + retire AssistantRecordsPage · in-card slot in AssistantSummaryCardPost + retire the RecordsEntryRow mount · records block in the post modal's Pulse tab · RecordsSheet on ModalWrapper over AssistantModalShell (z-index above the shell) · warm-stale flag shared by block + sheet · ?records=open persisted like ?assistant=.
What the waitlist actually stores
  • ✓ A real row IS written — name, phone, joined-at, subscribed status.
  • ✗ Email is collected and validated, then written email: null bug — discarding the one channel not rate-limited by WhatsApp's 24h window.
  • ✗ Rows are tagged as campaign overflow — a different assistant's concept reporting.
  • ✗ Seat cap exists ONLY on the Flow-form path overbooking — typed capture (what every instance runs until Meta approves its Flow) has no seat check; a 10-seat workshop can confirm 15.
Fallback “what happens when Meta says no?” · sec-6b template rejected · sec-6c workshop details didn't save
!
WhatsApp turned down your workshop reminder

Bookings and payment relay keep working. Only the reminder is blocked until the wording is fixed.

What they flagged "Too promotional for a utility message." WhatsApp is strict about wording on automated reminders sent outside the customer's active reply window.
Rewrite and resubmit
Use our approved wording

Resubmitting goes back into review — same one-to-two-day window.

Frame 6b · Template rejected [Fallback]
  • Story — As a founder, if Meta rejects a template I get the exact reason and two ways to fix it — while bookings and payment relay keep running underneath.
Acceptance
  • AC-WSBK-19 ↗ — a rejected template shows a named, actionable fix path (rewrite & resubmit, or use MystFlo's approved wording), never a dead end; other capabilities (capture, payment relay) stay unaffected.
Design notes
  • A real state Workshop Booking can hit, unlike On-Brand Support: On-Brand Support has no WhatsApp template dependency and correctly draws no rejected-template state. Workshop Booking depends on Meta approving two templates (confirmation + reminder), so rejection is a real outcome — design-canon's D-3 rule requires red-status failure states to always show a named fix path, never a dead end. Modeled directly on the master's own rejected-template twin (assistant-lifecycle.html frame 7e), adapted to this assistant's two templates.
  • Deliberately NOT drawn here: the generic live-test rollback (B6 — WhatsApp connection failure, unrelated to template wording) already covered once by the master contract. This frame is the template-specific twin only.
!
Your workshop details didn't save

Your assistant is live. Bookings and reminders are paused until your workshop details save.

What happened Your workshop details didn't save. Your WhatsApp, templates and payment setup are fine. Try again.
Try saving again
Check the details first

Pauses the assistant and opens Personalize so you can fix the details there. Bookings and reminders start the moment they save.

Frame 6c · Your workshop details didn't save [Fallback]
  • Story — As a founder, if the workshop details behind my assistant don't save, I'm told plainly and I can retry in one tap — never a silent "live" that quietly can't book anyone.
Acceptance
  • Not yet built — filed as #4634. Today activate-assistant treats a failed seedWorkshopCohort as non-blocking (index.ts:1915-1918) and the founder sees the ordinary Frame 6 success screen with no cohorts row underneath — no bookings, no reminders, no signal. This frame is the target state once #4634 (B2) forwards a cohortSeedFailed flag the same way workshopTemplatesPending/workshopTemplatesFailed already ride the activation response.
Design notes
  • Card shape reused verbatim from Frame 6b (template rejected): same .fail-badge, same .notice block ("What happened" instead of "What they flagged"), same stacked primary/outline buttons, same fine-print line — no new card style, colour, or icon introduced. Red badge because this blocks the founder's actual capability (bookings), not just one channel — a stricter case than 6b's reminder-only block.
  • Replaces Frame 6 entirely — the founder never sees the green checklist over a workshop that isn't actually bookable, matching how 6b fully replaces 6 rather than layering on top.
  • Desktop drawn here; 375 is the one exception carved out of the Monitor phase's mobile tier below (#sec-try-seed-failed-m) — 6a/6/8/6b stay desktop-only since nothing else in this phase changes shape at 375, but this is the fix path a founder is most likely to hit first, right on their phone, right after tapping Activate.
  • On a second failed retry: same card, same copy — only the fine print changes to "Still not saving. Pause and edit your workshop details, or message us" with a real Message us link (wa.me/<MYSTFLO_SUPPORT_NUMBER>, config value, never hardcoded, run reference prefilled — same pattern as the master's assistant-lifecycle.html sec-7 "(b) Failed" sub-frame); no new panel, the card just updates in place.
  • Successful retry returns straight to Frame 6 (sec-6) — Frame 6 IS the confirmation; no separate success card is drawn (canon U13: state changes are one-shot, not a new persistent screen).
  • Never shows an error code or blames the founder — matches this file's D-3 fallback tone (6b) and the platform-wide error copy standard (honest, helpful, calm).
RECURRING SURFACE · Pulse hand-back, drawn once in the master
Dismissing 6c doesn't dismiss the problem: the assistant is still live with no bookable workshop underneath, so the exception carries into Pulse's Needs your attention inbox (master 8c) as a standing row until it's fixed — the founder is never left with no way back to the fix.
assistant-lifecycle.html #sec-8c ↗
  • Row copy: “Your workshop details didn't save · Retry” — same .att-row grammar (! icon · title · outline button) this file's own sec-8 already draws for the pending-templates row; tapping Retry reopens 6c.
768 not yet drawn for this flow. Desktop and 375 are the contract.
6a/6/8/6b use Desktop. No 375 drawing for those — 6c below is this phase's one exception.
!
Your workshop details didn't save

Your assistant is live. Bookings and reminders are paused until your workshop details save.

What happened Your workshop details didn't save. Your WhatsApp, templates and payment setup are fine. Try again.
Try saving again
Check the details first

Pauses the assistant and opens Personalize so you can fix the details there. Bookings and reminders start the moment they save.

Same content and copy as the desktop draw (#sec-try-seed-failed) — story, acceptance, and design notes are not repeated here.
8b · Pulse tab — RECURRING SURFACE, drawn once in the master R30 · R37 ↑ top
Revisit · 2026-09-04 (#4432 Track 2, updated same day — locked mirror supersedes the ledger) — header + Personalize are drawn ONCE in the masterEvery header below now carries the shared four-line hierarchy (assistant-lifecycle.html frame 9g / 13g: subject · “Workshop Booking Assistant · id” · status · price · live since · activity; no health line, no repeated type word). This template's post Personalize tab is master frame 9c / 13c — the locked mirror: same visual structure as this file's own pre-activation Configure step (sec-3b's 01 · Your workshop / 02 · How guests pay / 03a / 03b / 04a / 04b categories), every field showing its saved state, nothing clickable — with booking_keywords hidden. Not redrawn here on purpose: one drawing, one contract.
RECURRING SURFACE · drawn once in the master
Workshop Booking's Pulse tab is the master's BOOKING (records) fill of the ONE Pulse-tab template — same instance (Weekly wheel-throwing class · ASST-4L9) the master already draws, roster FIRST in the right slot.
assistant-lifecycle.html #sec-9b-r ↗
Twins rule (shell-language #7) and the R28/R29/R30 Overview/Personalize/Connect notes carry over unchanged from the prior drawing — see this file's own sec-2 (Overview) and sec-5 (Try/read-back) for the source copy the post tabs mirror word-for-word. sec-13 keeps the full records-sheet columns contract; its caption points at master frame 9e.
Guest
This step is 375. No 1180 drawing — use the Mobile size.
768 not yet drawn for this flow. Desktop and 375 are the contract.
Guest journey — Trigger · Interact · Outcome what the guest sees on WhatsApp · sec-7, forked on booking_surface

Every bubble below draws the in_chat branch (the default) — a guest DMs, the assistant captures name/session/headcount, and relays the founder's payment link. When the founder's Flow is published, this typed capture is replaced by the native in-chat form — drawn in the Secondary sub-frame below (payment still relays outside the form; the US has no in-Flow payment). The own_form branch (guest is relayed the founder's own booking-form link and books there instead — AC-WSBK-14) has no equivalent bubble frame drawn yet; see the note at the end of this section rather than a screen invented for this pass.

Booking capture — before/after the copy-mismatch fix [Trigger → Interact, in_chat]
Current — retreat-flavored (wrong)
Proposed — class-flavored (fixed)
Sub-frame · Booking capture
  • Story — As a guest, I ask about a class in the founder's own voice and get an answer about MY class — not a retreat cohort I never signed up for.
Acceptance
  • AC-WSBK-01 ↗ — the capture step (name, session pick, headcount) is what creates the pending_payment booking row this reply represents.
Design notes
  • Confirmed, not hypothetical: today's actual AI prompt is worded for retreats, not workshops/classes: it says "our retreat," asks for "name, email, how many seats," and treats the guest as "joining a retreat cohort." Verified against the live prompt — this is real, shipped, user-visible copy damage, not a guess.
  • Email stays in the fixed bubble too — it isn't a stylistic choice. The real capture gate (shouldCaptureCohortBooking, shared by this booking engine) requires name AND email before a booking is persisted or the payment ask is shared; only the retreat-vs-class wording changes — the three-field capture contract itself is unchanged and correct.
Payment relay — before/after #2796 [Interact, in_chat]
Current — broken (#2796)
Proposed — fixed
Sub-frame · Payment relay
  • Story — As a guest, once I say I'm in, I get the founder's real payment link right away — not a hand-off promise that goes nowhere.
Acceptance
  • AC-WSBK-01 ↗ — "the assistant relays the host's payment ask" is this criterion's own language; #2796 was this exact promise silently failing in the in-chat runtime.
Design notes
  • Root cause of #2796: copy_payment_ask is bridged to cohorts.payment_instruction only at activation, and only the WA-Flows path reads it at runtime — the default in-chat path never does, so the AI falls back to a generic hand-off line instead of relaying the real link. The proposed fix makes the in-chat runtime read the same field, closing the gap.

The two locked, Meta-approved template bodies (Booking confirmation · Class reminder) live at full size in Part II · Components — seated in the flow at Personalize 04a (sec-3/3b) and in every sec-9 thread.

Secondary — the WhatsApp Flow seat-booking form [Interact, alternate rendering]
Sub-frame · Secondary WhatsApp Flow form
  • Story — As a guest, when the founder's Flow is published, I book through a native in-chat form instead of typing my details one message at a time.
Design notes
  • When the Flow is published, guests get this native in-chat form instead of typed capture; when not published, capture falls back cleanly to plain chat.
  • The intro line is a Personalize field, editable, same mechanism as Payment ask — not the runtime's hardcoded string. Approved 2026-07-28. Default reseeded 2026-08-21 (#3789, CPO): "{{workshop_name}} — {{date}}. Tap below to grab your spot:" — the invite names the program + date before the Book-now button; {{program}} aliases {{workshop_name}} (the Event card's seeded token now resolves), and a missing date degrades to the name-only line, never a literal token or a blank.
  • What triggers this form is not literal keyword matching — it's the AI's own classification of the message's intent, so free-form text ("any spots left tuesday?") triggers it exactly like a link-tap's pre-filled "book" does. The conditional "Tell assistants apart" keywords field (Personalize 03a) is a separate, earlier-stage mechanism: it only disambiguates which assistant an inbound message routes to when a founder runs more than one WhatsApp assistant on the same number.
own_form branch — cited, not drawn [Interact]
  • AC-WSBK-14 ↗ — once booking_surface='own_form', in-chat capture never fires; the assistant relays the booking-form link and sets intent FORM_RELAY instead.
  • AC-WSBK-11 ↗ — a roster-sourced booking (no chat thread ever existed for that guest) is still eligible for the 1d/1h reminder exactly like a chat-captured booking.
  • AC-WSBK-15 ↗ — booking_form_url is required the moment own_form is picked, enforced on all three save paths (activation, update-config, reactivation).
  • No WhatsApp bubble existed for what the guest actually sees in this mode when this frame was written — the founder's booking-form link relay. That gap is closed by sec-9 · Mode C, which draws the own_form thread end to end, and by the Personalize preview's own second state (sec-3/3b, round-4): a dimmed phone under the main preview draws the relay as the whole guest experience, keyed to the “Your own booking form” radio.

Section 7 shows individual messages being fixed. This section shows the whole thread, unbroken, for every mode a founder can pick — and, beside each thread, exactly where every message's words come from and where the founder meets them in Personalize and the live preview. One card per mode; nothing here is invented — every bubble traces to a named template, config field, or the real runtime string, and anything not built lives in Part III · Future, never here.

First: this is a 2×2, not a list of five

Payment structure and where guests book are two independent settings, not one menu. A founder picks one of each — so the five things that feel like five options are really four shipped combinations plus one unbuilt entry path. Drawing them as one list would imply choices that don't exist (and hide ones that do).

                     where guests book ──►   in_chat (default)     own_form
  payment structure                        ┌──────────────────┐ ┌──────────────────┐
        │                                  │  MODE A          │ │                  │
        ├── full_prepay  (default) ────────│  shipped         │ │  MODE C          │
        │                                  └──────────────────┘ │  shipped         │
        │                                  ┌──────────────────┐ │  (payment shape  │
        ├── free_rsvp ─────────────────────│  MODE B          │ │   changes only   │
        │                                  │  shipped         │ │   one word)      │
        │                                  └──────────────────┘ └──────────────────┘
        │
        └── deposit_balance ── hidden on this card — a retreat concept, lives on Retreat Booking

  ┌ MODE D — 2+ booking cards live on ONE WhatsApp number. Supported product usage ──┐
  │ (a founder running concurrent programs), not a misconfiguration. "what's on?"    │
  │ or a bare "book" → deterministic name+date list; a unique full program name      │
  │ routes straight to that card's form. SHIPPED (#3789) — drawn below.              │
  └─────────────────────────────────────────────────────────────────────────────────┘

  ┌ MODE E — guest DMs and the AI already reads booking intent. What's missing is ───┐
  │ the CHOICE within ONE assistant: offering its several open workshops to pick     │
  │ from. NOT BUILT — two real blockers, drawn in Part III · Future.                 │
  └─────────────────────────────────────────────────────────────────────────────────┘
meta template locked, Meta-approved — founder reviews, never edits founder field a real Personalize input, sent verbatim ai-composed written live in the founder's voice from the Brain native form WhatsApp's own UI, not text
Mode AFull payment, booked in this chat

The default, and the one most founders will run. The guest never leaves WhatsApp; the money does — payment happens on the founder's own link, because WhatsApp's in-form payment is India/Singapore only.

payment_mode = full_prepay booking_surface = in_chat shipped
Story
  • As a guest, I ask about a class, fill a short form, pay on the founder's own link, and get a confirmation and two reminders — all in one thread, without ever being handed off.
Acceptance
  • AC-WSBK-01 ↗ — capture creates the pending_payment row and the assistant relays the founder's payment ask.
  • AC-WSBK-02 ↗ — confirmation within 30s of payment being learned. AC-WSBK-03 ↗ — reminder fires at both offsets.
Design notes
  • Message sources, 1→7: 1 guest (intent-classified — wa.me only pre-fills "book") · 2 invite = copy_flow_intro founder field (default names the program + date, #3789) over WhatsApp's native form (per-founder Flow from flow-json/workshop-booking.ts; unpublished → typed capture, nothing waits) · 4 = ONE message, thank-you first (runtime string, composePaidBookingThankYou — "seat is held, confirmed the moment your payment lands", P2: pay AFTER the form), then the founder's Payment ask (Brain policies.payment wins when set); a single URL in the ask is lifted onto a native Pay now button (sendWhatsAppCtaUrl) · 5 confirmation = workshop_booking_form_confirmation template, ≤30s after Mark paid · 6/7 = ONE class_reminder body, both offsets.
  • Capture gate: name + email (shouldCaptureCohortBooking) — no row without both; seats re-checked at submit, not send. Mark paid (or a roster tick) is the ONLY door to confirmed — the assistant never takes the guest's word.
  • Four kinds of words in one thread (runtime string · founder field · Meta template · live AI) — the source-chip vocabulary is proposed to carry INTO Personalize so "why can't I edit this one?" answers itself.
  • The only unbounded gap is 3 → 4 → Mark paid — nothing chases an unanswered payment link; see Part III · F2. Not drawn as solved because it isn't.
Mode BFree RSVP, booked in this chat

Same thread as Mode A with the money taken out — and taking the money out removes two steps, not one. There is no payment ask, and no Mark paid: the booking confirms itself the moment the form comes back.

payment_mode = free_rsvp booking_surface = in_chat shipped
Story
  • As a guest at a free session, I say I'm coming, fill in three fields, and I'm confirmed on the spot — nobody asks me for money and nobody makes me wait.
Acceptance
  • AC-WSBK-05 ↗ — a free_rsvp workshop skips the payment-ask leg entirely and goes straight to confirmed.
Design notes
  • Delta from Mode A: trigger/form/submit identical (form softens to "Save your place", price/date field hidden — same Flow JSON, one conditional field); payment ask AND Mark paid legs removed — captureCohortBooking calls learnPaid on submit, same meter, same exactly-once; no "awaiting payment" state ever appears in Pulse.
  • P3 revised (#3789, CPO Option B): bubble 4 is an in-window session message (composeFreeBookingConfirm — "you're in / free / see you {date}"), NOT the locked Meta template. learnPaid runs with skipWhatsApp (honored only for free_rsvp) — it still meters, emails, and confirms; the locked workshop_booking_form_confirmation template is reserved for Mark paid on the paid card. Typed capture sends the same session confirm; an already-confirmed retry never re-texts the guest.
  • Proposed proposed: the "Payment ask" field hides when Free RSVP is picked — an editable sentence that never sends is the dead-control problem this rescope removed five toggles for.
  • Reminders unchanged — they key off cohorts.start_date, not payment. Free RSVP is the one mode needing no nudge machinery (no unpaid state to chase).
Mode CThe founder's own booking form

The assistant hands over the founder's own form link and steps aside — no capture, no payment ask, no in-chat form. Everything that happens next happens somewhere MystFlo cannot see, and comes back only through the roster sheet.

payment_mode = full_prepay or free_rsvp booking_surface = own_form booking_form_url · required Google Sheets roster · required shipped
Story
  • As a founder who already has a booking form I trust, I keep it — the assistant sends people to it, and once I tick them off as paid it takes the confirming and reminding off my plate.
Acceptance
  • AC-WSBK-14 ↗ — in-chat capture never fires; the assistant relays the link and sets FORM_RELAY. AC-WSBK-15 ↗ — booking_form_url required on all three save paths.
  • AC-WSBK-11 ↗ — roster-sourced bookings get the 1d/1h reminders like any other.
Design notes
  • Message sources: same entry/intent classification as every mode; bubble 2's link = booking_form_url, its sentence = copy_booking_form_link (or AI-written around the real link when blank); confirmation + reminders = the same Meta templates, with the confirmation landing hours later because it waits on the founder.
  • The blind stretch is the honest limit: booking + payment happen on the founder's form — no webhook, no way to tell bookmarked from paid; the roster tick (booking-roster-sync) is the ONLY door a booking enters through, manual by design — and it is the billable event (confirms the booking, fires the $1.75 charge). Reminders are the part that stays automatic.
  • full_prepay vs free_rsvp only shapes wording here ("pick your spot and pay there" → "pick your spot there") — MystFlo never asks for or learns payment independently in this mode.
Mode DTwo live cards, one number — who answers?

Several booking cards Active on one WhatsApp number is supported product usage — a founder running concurrent programs — not a misconfiguration. Every booking link prefills the same generic keyword by design, so when the message doesn't uniquely name a program, the assistant answers with a deterministic list instead of guessing — never the wrong Flow, never Brain improvisation.

2+ booking cards · one WhatsApp number any payment_mode shipped · #3789
Three deterministic rules — never a guess
1
Unique full program name → that card's form router
"book Wheel Throwing Class" — or the name inside a what's-on ask — opens that card's Flow directly, no list. A single stray word ("friday") is never enough: keyword-free unique match requires the full program name, so it can't steal the turn from On-Brand Support.
2
"what's on?" or a bare "book" → the name+date list runtime string
Bubble 2 is composeBookingCatalogReply — sorted by start date then name, one bullet per live card. Deterministic, not AI-composed: two cards can't be told apart by the shared "book" prefill, and guessing sent an Event guest into a Workshop Flow (live test, 2026-08-20).
3
Ties break the same way every time router
Sibling names sharing a word tie-break by longer (more specific) name, then most-recently-created — every input order resolves identically (resolveBookingPhase0, whatsapp-routing.ts).
Story
  • As a guest texting a studio that runs two programs at once, I ask "what's on?" and get a short, real list with dates — I name the one I want and the right booking form appears, never the wrong one.
Design notes
  • The design story is the concurrent one. QA walks that stop one card before testing another are a test constraint (isolation), not the product: the router exists precisely so founders can keep several cards live on one number.
  • Delta from Mode E (F1): this is 2+ cards on one number, each still one program — shipped. Mode E is one assistant holding several workshops itself — still not built, and still On-Brand Support's job (see F1).
  • iPhone curly apostrophes in "what's on?" are normalized before matching — the ask can't miss on typography.
Every modeWhen the guest asks something else

This is already built, and the answer to "route to the owner or swap in the on-brand assistant?" is both, in that order — the handover is automatic and the founder never configures it.

shippedchat-handler.ts · fall-through gate
  guest asks something off the booking script
                 │
                 ▼
      ┌ can Workshop Booking answer it? ┐
      │  from the workshop details or   │──── yes ──► answers, thread continues
      │  the Brain                      │
      └────────────┬────────────────────┘
                   │ no  (intent = UNSURE)
                   ▼
      ┌ is On-Brand Support live on ────┐
      │  this same WhatsApp number?     │──── no ───┐
      └────────────┬────────────────────┘           │
                   │ yes                            │
                   ▼                                │
      ┌ Support takes one more shot with ┐          │
      │ the full Brain + FAQ, and the    │── fails ─┤
      │ answer is fact-checked before    │          │
      │ it is allowed out                │          │
      └────────────┬─────────────────────┘          │
                   │ passes                          ▼
                   ▼                    ┌ founder gets the thread ─────┐
            guest gets a real           │ holding line to the guest ·  │
            answer — no handoff         │ human mode on · host pinged  │
                                        └──────────────────────────────┘

  "get me a human" / refund / date move ─────────────────────────► straight to founder,
                                                                    no Support attempt
Design notes
  • The ladder, A→E: answerable → answered in the founder's voice, thread continues · unanswerable → On-Brand Support gets ONE attempt, fact-checked before it can reach the guest · refunds / date moves / "get me a human" → straight to the founder, honest holding line, human mode (the assistant never tells a paid guest their booking is cancelled) · "I already paid!" → acknowledged, never believed — confirmed only when payment is learned · sold out → hard-coded waitlist line; seats re-counted at submit.
  • Nothing here needs building — it needs showing — the ladder is live and invisible; a Try-step / Explore-copy opportunity, not engineering.
  • The real gap: the holding line and the sold-out line are hard-coded — the two messages a founder most wants in her own words, and neither appears in Personalize (holding line now proposed as Personalize 05).
Master contract + sources
  • assistant-flows/assistant-lifecycle.html is the primary structural contract; assistant-flows/on-brand-support.html is the sibling exemplar this file borrows its token block and component classes from verbatim.
  • Sources: prd-as-workshop-booking.md §Test Plan (AC-WSBK-* / AC-BILL-*) · DB migration 20260710080000_2012_b4_wsbk01_card.sql · ConfigureStep.tsx isBookingFamily gate · tests/e2e/assistants/tmpl-wsbk01.spec.ts.

Business name ("Ember Clay Studio"), guest names, and phone numbers are illustrative. This file draws the proposed target.

Part II — Components the parts library at full size — reference only, nothing here is the ONLY place a thing appears (kit lock 2026-07-30).
C1 · Locked, Meta-approved templates ↑ top in the flow ↑
Meta-approved · lockedBooking confirmation
Header: "You're booked — Wheel Throwing Class"
Hi Maya, it's Priya — your seat for the Wheel Throwing class is confirmed. We'll send a reminder before class. Reply here with any questions.
Meta-approved · lockedClass reminder — one body, both offsets
Hi Maya, it's Priya — just a quick reminder, your spot's all set. If anything comes up, reply here and let me know. See you soon!
C1 · Locked templates seated: Personalize 04a (sec-3/3b) · every sec-9 thread
  • Story — As a guest, once I've paid I get a real confirmation, then a warm reminder before class — sent in the founder's own name.
Acceptance
  • AC-WSBK-02 ↗ — confirmation within 30s of payment learned (Mark paid or roster tick). AC-WSBK-03 ↗ — reminder fires at both offsets.
Design notes
  • Read-only previews, never editable — bodies are the approved workshop-templates.ts copy with example vars resolved; nothing invented.
  • Both deliberately link-free UTILITY templates (Peyton 2026-07-10); the reminder is time/location-agnostic, so ONE preview honestly covers both sends.
  • P3 revised (#3789): the confirmation template fires on Mark paid only (paid card). A free-RSVP booking confirms in-window with the session message instead (sec-9 · Mode B bubble 4) — this locked body never sends on the free path.
C2 · “When I need to step in” — the 05 category at full size not built ↑ top in the flow ↑
05 · When I need to step in proposed
If the assistant can't answer
Let On-Brand Support try first
Uses your full knowledge base. Costs one extra AI run per question — only when it's actually used.
Come straight to me
No second attempt, no extra cost.

Only shown when you have On-Brand Support running on this number.

What guests hear while they wait — editable
I'm connecting you with Priya — she'll come back to you shortly.
Where you get pinged
WhatsApp — this thread
Always on. Reply from your phone as usual.
Connected ⌄
Slack
A copy in your channel. On the app? You reply in WhatsApp. API-only number? Reply right in the thread — it reaches your guest.
Connect
C2 · Step-in category seated: Personalize 05 (sec-3/3b) · why: sec-12
  • Escalation radio pair renders only with On-Brand Support on the number (conditional block in the flow frames); holding line + ping rows always render.
Part III — Future design deferred — never a build target. Everything below is drawn so the shape can be judged, NOT so it can be built from. Nothing here is wired today; each item names what actually blocks it.
F1 · Mode E — the workshop picker (formerly 10) not built ↑ top

The guest hasn't tapped a link and hasn't said "book" — they just message the business, and want to choose between what's on offer. More than half of this already works: the assistant reads booking intent from plain English today (sec-9, Mode A design notes), and since #3789 a founder running 2+ booking cards on one number gets the deterministic name+date list — shipped, drawn as sec-9 · Mode D. What is still missing is the choice within one assistant: a single Workshop Booking card knows about exactly one workshop, so it cannot offer its own list.

Mode EGuest just DMs — assistant offers what's open
any payment_mode booking_surface = in_chat NOT BUILT — 2 blockers
What it would take — two named blockers
✓
Reading intent without a keyword ai-composed already works
The assistant classifies the message's intent, so "do you run classes?" already lands in the booking flow. The one caveat: on multi-assistant numbers, the keyword router can send a keyword-free message to On-Brand Support instead, which can answer but cannot book — though a catalog-style ask ("what's on?") now gets the deterministic booking list when 2+ booking cards share the number (#3789, Mode D).
1
Knowing what's open ai-composed blocker 1
A live "what's coming up" block — real dates, prices, remaining seats — is built and shipping for On-Brand Support, but the booking assistant never receives it.
2
One workshop per assistant structural blocker 2
An activated Workshop Booking assistant is bound to a single workshop — one date, one price, one seat count, seeded at activation. A picker needs one assistant that holds several workshops, which a single-workshop assistant can't do.
·
The picker widget itself native list
Not a blocker. Up to three options use WhatsApp's reply buttons, already built; more than three needs an interactive list message.
Direction — the picker is On-Brand Support's job, not this card's
  • When a founder runs several workshops, On-Brand Support — the assistant that holds the whole business — answers "what do you run?", presents the list, and routes the guest into the right booking flow. Workshop Booking stays one workshop, booked well.
  • The multi-card slice of this shipped without waiting for the picker (#3789): when several booking cards share the number, the engine itself answers a what's-on ask (or an ambiguous "book") with the deterministic name+date list — sec-9 · Mode D. That covers "two workshops = two activated assistants" today; this F1 frame remains only the one-assistant-many-workshops case.
  • "Two workshops = two activated assistants" (sec-3) stays true and untouched — each workshop keeps its own assistant, date, seat count, and billing. On-Brand Support already holds the business knowledge and the live dates-and-seats block that Workshop Booking is missing in blocker 1.
  • This makes On-Brand Support a recommended companion for any founder running more than one offering, and a real second charge (see sec-12).
  • Still worth doing on Workshop Booking regardless: letting it see its own live seat count, so "is Tuesday full?" is answered from data instead of guessed.
F2 · When the guest goes quiet (formerly 11) not built ↑ top

Today, none of this exists. Every mode above ends its automatic behaviour the moment the assistant sends the form or the payment link. If the guest reads it and does nothing, nothing happens — no nudge to them, no signal to the founder, ever. The three toggles that sound like they'd cover this ("notify me on unpaid timeout" and friends) are dead controls with no runtime reader, which is why this rescope removed them from Personalize rather than leave them lying.

The constraint that shapes every option below Meta policy, not a preference
  • Inside 24 hours of the guest's last message, the assistant can write a nudge freely, in the founder's own voice.
  • After 24 hours, only a pre-approved template can be sent — which means every distinct nudge below is a separate submission to Meta, with its own review wait and its own chance of rejection (sec-6b). Each nudge has a real cost, so the set should be small.
  • There is a working precedent to copy rather than invent: On-Brand Support already sends exactly one gentle nudge to a guest who goes quiet for 24h, capped at one per quiet stretch, and backs off automatically when the founder's number is near its sending limit.
CasesFour silences, and what each one deserves
NOT BUILT — proposed shape only
F1
Form sent, opened, never submitted Mode A · B
Today: nothing. Proposed — to the guest, once, at 24h: "Hi Maya, it's Priya — your spot for Tuesday's class isn't saved yet. The form's still open if you'd like it." To the founder: nothing individually — this is normal browsing, not a problem. Roll it into Pulse as a count, not an alert.
F2
Booked, payment link sent, never paid Mode A
Today: nothing — the seat sits held forever and the founder is never told; the chase machinery exists but is switched off for full-payment workshops by design. Proposed — to the guest, once, at 24h: "Hi Maya, it's Priya — your spot for Tuesday's class is still held. Here's the link again if you'd like to lock it in: …" To the founder: a Pulse attention row — a held seat is a seat not sold, and the founder is the only one who can release it.
F3
Own-form link sent, nothing came back Mode C
Today: nothing — and this one is different in kind. MystFlo cannot see the founder's form, so it cannot tell "never opened it" from "booked and paid but the founder hasn't ticked the roster yet." Nudging the guest is genuinely risky here — chasing someone who already paid is worse than staying silent. Proposed — say nothing to the guest, and tell the founder instead: "6 people got your booking link this week. 2 are on your roster." That's a true statement MystFlo can make, and it puts the judgement with the only person who has the missing half.
F4
Waitlisted, a spot opens all modes
Today: the guest is told they're on the waitlist, and then nothing ever happens — there is no sweep that notices a cancellation and tells them. The assistant makes a promise it cannot keep ("you'll be the first to know if a spot opens").
Where this lands in Personalize
  • One new category — "06 · Chasing" — with two toggles: nudge unpaid bookings (on by default), nudge unfinished forms (off by default), each with editable copy. This replaces the three dead "notify me" toggles removed elsewhere in this rescope.
  • No nudge toggle for Mode C — the assistant cannot tell paid from unpaid there, so Mode C gets the founder-facing count (F3) instead.
Design notes
  • The smallest honest version is F2 alone — one nudge, one template, one Pulse row, aimed at the only silence that costs the founder money. F1 is polish, F3 is a reporting line rather than a message, and F4 is a bug fix; they don't need to ship together.
  • Every nudge is one per quiet stretch, capped, never stacked with a reminder on the same day — matching the precedent On-Brand Support already runs.
F3 · The waitlist — what we actually capture (formerly 14) not built ↑ top

Whether the guest should see a WhatsApp Flow waitlist form depends on which of three sold-out paths they hit — they behave nothing alike, and only one of them is missing anything a form could fix. Paths 1 and 3 below are covered in sec-13 and are fixed in code, not redesigned here — this section is scoped to path 2 only.

1
Sold out at submit — Mode A/B form fix in code, not a form
The guest already filled the booking form — name, email, party size — before the seat check ran. A waitlist row IS written, but with email: null, discarding data validated two lines earlier (sec-13). No second form belongs here. Asking someone to fill another form immediately after “sorry, sold out” is friction with nothing to show for it — the fix is the one-line email bug, not new UI.
2
Sold out at send — guest just DMs, no form ever sent this is where a form earns its place
The workshop is already full when the guest asks, so no form is ever sent. A waitlist row is written — but it carries only the phone number, the WhatsApp display name if one is exposed, and email: null, because nothing was ever collected. This is the only path with no name and no email to lose — they were never asked for. That's what a form would add here, and why it belongs on this path and no other. Proposed form below.
3
Typed capture — every instance until Meta approves its Flow overbooks, doesn't waitlist
No seat check exists on this path at all (sec-13 — “the seat cap only exists on ONE path”) — sold-out is never detected, so it overbooks instead of waitlisting. A form is irrelevant here until the seat check ships; that's the real fix, and it's already in the parallel lane.
ProposedA native waitlist form — path 2 only
NOT BUILT same Flow mechanism as booking · new template/Flow

Same shape as the booking Flow (sec-9, Mode A) — a native in-chat form instead of typed capture — fired the moment the sold-out reply goes out, so joining the waitlist costs the guest one tap, not a typed back-and-forth.

Where each message comes from
1
Guest asks guest
Same intent classification as every other mode (sec-9) — no keyword needed.
2
Sold-out reply ai-composed
Already shipped, hard-coded, verbatim (sec-9, mode-q row E). Not editable today — same as now.
◇
Waitlist form native form proposed
A second Flow, published the same way as the booking form (provisionWorkshopFlow, sec-9) — not a reuse of the booking Flow, since it asks for less and fires on a different trigger. Two fields only: name + email.
3
Guest submits guest
Writes the waitlist row with a real name and email this time — the same row that exists today, minus the email: null bug (sec-13).
4
Join confirmation ai-composed proposed
Written live, inside the 24h window the guest just opened by messaging — no template needed for this turn.
Story
  • As a guest who DMs about a sold-out class, I get told I'm on the waitlist and I'm asked, in the same breath, for my name and email — one tap, not a typed back-and-forth.
Design call — why no party size
  • Left out, deliberately. The booking form (sec-9) asks for party size because it's committing a seat right now. A waitlist join isn't committing anything — the number that matters is whatever's true when a spot actually opens, which could be weeks later, so a party size collected today is stale by then. When the founder promotes someone off the waitlist she's messaging them directly anyway (sec-13's attendee-list proposal already shows waitlisted rows for exactly this) — that's the moment to ask, not now. Two fields, not three.
The honest limit
  • Better data collected here does not mean anyone gets told. Without the notify-sweep proposed as F4 in sec-11, a cleanly-filled waitlist is still a list nobody is ever messaged from. This form is the prerequisite for F4, not a substitute for it — email is the one channel that isn't rate-limited by WhatsApp's 24-hour window, so capturing it is what makes F4 reachable for this path.
Where it lands in Personalize
  • No new Personalize control. The sold-out reply is already not editable today; this form is the same class of message — structural, fired automatically, nothing for the founder to configure.
The cost
  • A second per-founder Flow means a second Meta review-and-publish cycle on top of the booking Flow's, with the same fallback-to-typed-capture safety net and the same named recovery path if Meta says no (sec-6b).
F4 · Stripe — auto-mark paid when their payment lands (formerly 15) not built ↑ top

The Connect tab (sec-4) duplicated with ONE tweak: a deferred Stripe row. CPO-ruled post-beta (#3152, ADR-019's phase-2 door) — per canon U9 it must NOT appear in the Active Connect frame, so it lives only here.

Overview Personalize Connect Try
01 · Connect apps
Communication WhatsApp Business
Which number should this assistant answer on?
+1 555-0142 ▾
Payment Stripe
Auto-mark paid when their payment lands. Nothing confirms until payment is real — MystFlo never touches the money.
Future · #3152
AI Model MystFlo AI
Connected by default. Choosing your own AI model is coming later.
Connected ⌄
Storage MystFlo Storage ▾
Your Attendees list on Pulse — every booking lands there, captured in chat or read from your roster. Nothing external to connect.
Connected ⌄
Storage — when “Your own booking form” is picked (03b) conditional

FUTURE own_form roster-pick row (separately tracked — the shipped Connect tab withholds the Storage row entirely under own_form and has no roster-pick UI yet; see sec-4's cond-block note). Per canon U9 it is drawn only here, never in the Active frame — collapsed; this frame otherwise exists for the Stripe row.

Storage Google Sheets ▾
Your booking roster — MystFlo reads it one way; the tick in your paid column is what confirms a booking
Connected ⌄

Wallet · Brain Check · template notice unchanged from sec-4 — omitted here, this frame exists for the one new row.

Frame F4 · Stripe auto-settle [Future]
  • Story — As a founder whose guests pay through my Stripe link, a landed payment marks the booking paid on its own — I stop being the human webhook.
Design notes
  • ADR-019 phase-2 door: a Stripe webhook becomes a THIRD door into the same learnPaid settle path (host Mark-paid · roster tick · Stripe webhook), all metering identically — never a second billing path. Deferred post-beta by CPO ruling; tracked as #3152.
  • Copy is locked ("Nothing confirms until payment is real — MystFlo never touches the money") — the row detects the founder's own Stripe payment landing; MystFlo still processes no money (ADR-019's payment-relay decision is unchanged).
  • Canon U9: this row never appears in the Active Connect frame (sec-4) until #3152 ships — this duplicated frame is the only place it's drawn.

The recurring-schedule question (sec-3) is resolved, not deferred — single-session ships now; the lightweight repeating-session increment lives in #2798, tracked separately.