← Design HQ · HTML on GitHub · PRD — prd-as-event-booking.md
Event Booking — Discover, set it up, go live, then the guest thread.
The Event 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.
Event Booking (TMPL-EVBK01) turns an event host's WhatsApp DMs into confirmed spots. A guest taps the founder's booking link — or just texts — and the assistant captures the RSVP/ticket request right in the chat (name, which event, headcount), relays the founder's own ticket link exactly as she gave it, and once payment is learned — her one-tap Mark paid — sends the Meta-approved confirmation and reminds the guest 1 day + 1 hour before it starts. Free RSVPs confirm the moment they're captured — no payment leg at all. $1.75 per confirmed booking; MystFlo relays the ticket link 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.
full_prepay · free_rsvp) — v1 is in_chat capture only, no own_form (that radio, and the roster-sheet mechanics that come with it, is Workshop Booking / Retreat Booking scope, not this card's).workshop-booking.html: a tick-one-or-both checkbox group, one QR-image bubble with the link in its caption when both are ticked). Confirmation on Mark paid (free RSVP confirms on capture); reminders 1 day + 1 hour before start (shared engine — Workshop card AC-WSBK-02/03 ↗).own_form / a founder's own booking form + roster sheet is Workshop Booking's v1 scope, not Event's; one event per activation — two events = two assistants; no write-back to founder sheets, no Airtable/Notion.resolveFlowIntroText) · multi-event-on-one-number disambiguation (resolveBookingPhase0 / pickBookingMatchScored) · QR option disabled with a coming-soon badge (#4159 / PR #4201).## Catalog Copy section verbatim (T1144 drift guard keeps the two in lockstep via check-catalog-copy.sh).
┌ CONFIG (Personalize) ────────────┐ ┌ BRAIN ──────────────┐ ┌ CATALOG COPY (DB row) ────────┐
│ event name · date* · price/ │ │ voice · policies · │ │ description · tldr · │
│ ticket · currency · spots · │ │ contact sign-off │ │ benefits[4] · details.flow │
│ payment_mode · link/keywords · │ └──────────┬──────────┘ │ (5 nodes) — sec-1/sec-2 only │
│ copy fields · payment vehicle │ │ └───────────────────────────────┘
└────────────────┬─────────────────┘ │ grounds answers
reads ▼ ▼
guest WhatsApp thread — capture (Flow form fronting typed capture, in_chat only)
│ name · contact · spots
▼
cohort_bookings row
│ booked → paid → confirmed (or, free_rsvp: booked → confirmed, no paid leg)
├─► locked templates: confirmation (on Mark paid) + reminder (1d + 1h)
└─► Pulse metrics · attention rows · Attendees columns · activity feed
payment_mode (full_prepay/free_rsvp only) · generated wa.me link + conditional keywords · founder-editable copy (booking invite, payment ask).event_booking_confirmation — own Meta name) + Night reminder (class_reminder — name frozen, body shared byte-for-byte with the rest of the booking family) submitted at activation via event-templates.ts submitEventTemplateSet, Meta-approved, never editable (C1). Every sec-9 ledger tags each message's source with one vocabulary: template founder field ai-composed native.cohort_bookings): name · contact · spots · status · paid-source (Mark paid only — no roster tick, v1 has no own_form). 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).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. Unlike
Workshop Booking, v1 has no booking_surface fork — every guest is captured in_chat;
the real branch worth drawing here is at Payment, where payment_mode splits full-prepay from
free RSVP.
┌ 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 (event date/price per ticket/currency/spots · payment │
│ mode · locked message copy) → Connect (WA number · template-approval │
│ notice — no conditional Sheets pick, v1 has no own_form) → Try │
└──────────────────────────┬─────────────────────────────────────────────┘
▼
┌ 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
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.
Column 6 is the honest column. Two of six lanes end in raw ✗ — 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.
booking_surface radio at all — Connect never shows a conditional roster-sheet pick, and there is no booking_form_url field. That gating logic (Workshop's AC-WSBK-15 ↗) does not apply to this card.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.AC-* ↗) live in prd-as-event-booking.md §Test Plan.own_form, so there is no conditional Google Sheet requirement on this card at all.workshop-booking.html → 3s / 6s; Pulse in assistant-lifecycle.html → 8f.Event Booking captures the spot straight from WhatsApp — name, which event, how many tickets — then relays your ticket link exactly as you gave it. Free RSVPs confirm on the spot; paid spots wait for your “mark paid” tap. Either way, the guest gets a confirmation and a reminder the day before and an hour before it starts. Your ticket link stays yours; MystFlo never touches the money.
ticket link, policy, voice — from your Brain
picks an event, gives headcount
one tap, or free RSVP confirms on capture
Confirms + reminds
instant confirm, then 1 day + 1 hour before
description, long_description, details.tldr, and all 4 benefit cards are copied from prd-as-event-booking.md §Catalog Copy, seeded by migration 20260813130000_evbk01_event_booking_card.sql and kept in lockstep by check-catalog-copy.sh (T1144).HowItWorksFlow idiom, nodes/subs/branch VERBATIM from the seeded details.flow (20260813130000_evbk01_event_booking_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).Superseded by the master picker (Peyton 2026-09-28: one master design + per-assistant variations): one dropdown labelled “Which event is this for?”, placeholder “Pick an event”, fields on pick, plus the loading and couldn’t-load states — drawn once in workshop sec-3. The radio list below is the pre-dropdown frame.
Picking fills Name, Price, Where and Spots below — and Date and Time when your Brain knows them. Every one stays editable. Edit what you sell in your Brain →
A start, or a range — 18:00 or 18:00–21:00. An end at or before the start is flagged under the field, never blocks Continue.
So reminders fire at the right moment for a nomad host — never changes the time guests see.
Where guests show up — fills the confirmation, the reminders, and the invite. Starts as your business address and Maps link from your Brain; change either here if this event is somewhere else. With a map link guests get a one-tap “Open in Maps” button; without it they still see the address.
Edits here change only this event — 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 and Time fill from the pick only when the offering carries them; Timezone is always yours.
Retreat-style deposits live on Retreat Booking, not here.
Tick one or both. Whatever you tick goes out with your payment ask.
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.
Add your QR to activate — or untick it.
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.
The card never hides — it lights back up the moment Connect finishes.
The AI reads intent — “any spots left tuesday?” books a spot just like “book” does. Nothing to configure.
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.
However they arrive — link tap or a direct message — it's one path in. v1 is in_chat only — no “where guests book” radio: WhatsApp's own tap-through form always fronts typed capture. Your booking list lives in Attendees — on this assistant, in Pulse. (Workshop Booking's own-form + roster-sheet radio is that card's v1 scope, not this one's.)
Shown above the invite button. Fills in your event name, date, time, and location automatically. No date set yet → the name-only line, never a blank.
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.
Which channels the night reminder can use — connect the app for each in Connect. WhatsApp on by default; the booking flow itself always runs in chat regardless.
No “05 · When I need to step in” category on this card — that's Workshop Booking's own aspirational, not-built PRD debt (its AC-WSBK-36/37/38), not confirmed for Event Booking. Handover to the founder still works in-thread — see sec-12.
Works the same whether they tapped your wa.me link or typed this directly — the AI reads intent, not just the word. No second, dimmed “own booking form” preview here — v1 has only this one path.
own_form and no step-in category). This frame exists only to show the same content at the desktop split-pane width, alongside the live preview column.cohort_name + price the pick fills are exactly what the Flow form’s title and date · price line render (shared engine — Workshop’s AC-WSBK-22 ↗).TMPL-EVBK01) — 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.brain_profile.services[] (name · price · capacity) tops 01; a pick fills Name, Price + Currency, Where (and Date · Time when the offering carries them), Spots — all stay visible and 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); the picker is the shipped OfferingPicker.tsx (T1092) widened from RTBK01 to this card, minus COHORT_PICKER_FIELD_KEYS filtering (ConfigureStep.tsx:267).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 (“18:00” or “18:00–21:00”) with Timezone beside it.configuration only; the Brain is never written from Personalize. Date · start/end time · timezone are never quick-filled.OfferingPicker.tsx (T1092) verbatim: radio rows ≥44px, name text-sm white/80 + price sub-line white/40 (“price on ask” when empty), ✓ on the picked row, “Edit what you sell in your Brain →” microline; vertical-first, no modal (kit 2026-06-12 lock). Only the last row is new: dashed “+ Something new” replaces the inline name/price composer — typing happens in the real fields below, not a nested mini-form.workshop-booking.html sec-3b is the same contract in workshop words; grade Event pixels against THIS file.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 (largely legacy under prepaid — price_per_run is the debitable amount, per the PRD's Billing section).own_form-conditional acceptance criteria on this card — v1 has no roster-sheet gate, no booking_form_url, no conditional Storage withholding. Workshop's AC-WSBK-15 ↗ does not apply here.+1 555-0142 ▾ control on the WhatsApp pick-row. Every row carries its ROLE label (ROLE_IDS, required-roles.ts): “AI Model” → MystFlo AI · Connected, “Storage” → MystFlo Storage · Connected — this card has exactly one Storage state (always in-chat), so the row is drawn plainly connected, no caret, no withheld/conditional variant to document.design-decisions.md 2026-08-05/07) — Personalize's “04b · Reminder channels” field is a DECISION only (WhatsApp always required, Email optional), and the caret expands into one pick-group per channel. Drawn NOT-CONNECTED for Email here deliberately — Event's booking confirmation and Night reminder are locked, Meta-approved WhatsApp templates (04a) with no Resend-side equivalent copy yet; picking “Email” without that copy existing is a real build gap, flagged not solved in this pass.Off unless you switch it on. Shows only when 01 used “+ Something new” — hidden when the name is already in your Brain.
Runs on your WhatsApp from the moment you activate. Pause anytime.
workshop-booking.html → 3s and applies here unchanged except for the card name: a one-line heads-up on Personalize, On-Brand Support as a selected, locked add-on in Connect (Active if already live), a read-back of both assistants, and “Activate both”.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).brain_profile.services[] or an existing offering was picked.services[] once, at activation; OFF → the Brain is untouched. Re-activation never re-asks for a name that now exists.Superseded by the master picker (Peyton 2026-09-28: one master design + per-assistant variations): one dropdown labelled “Which event is this for?”, placeholder “Pick an event”, fields on pick, plus the loading and couldn’t-load states — drawn once in workshop sec-3. The radio list below is the pre-dropdown frame.
Picking fills Name, Price, Where and Spots below — and Date and Time when your Brain knows them. Every one stays editable. Edit what you sell in your Brain →
A start, or a range — 18:00 or 18:00–21:00.
Starts as your business address and Maps link from your Brain — change either if this event is somewhere else.
Edits here change only this event — your Brain stays as it is.
Field names match your Brain’s offering fields one-to-one — Name · Price · Date · Time · Where · Spots.
Works the same whether they tapped your wa.me link or typed this directly — the AI reads intent, not just the word. No second, dimmed “own booking form” preview — v1 has only this one path.
Type it below — or add what you sell to your Brain →
Still starts as your business address — the one thing the Brain can fill without an offering.
Retreat-style deposits live on Retreat Booking, not here.
Tick one or both. Whatever you tick goes out with your payment ask.
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.
Add your QR to activate — or untick it.
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.
The card never hides — it lights back up the moment Connect finishes.
The AI reads intent — “any spots left tuesday?” books a spot just like “book” does. Nothing to configure.
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.
However they arrive — link tap or a direct message — it's one path in. v1 is in_chat only — no “where guests book” radio. Your booking list lives in Attendees — on this assistant, in Pulse.
Shown above the invite button. Fills in your event name, date, time, and location automatically. No date set yet → the name-only line, never a blank.
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.
One preview, not two — the 1-day and 1-hour sends use the identical body.
No “05 · When I need to step in” category on this card — that's Workshop Booking's own aspirational, not-built PRD debt (its AC-WSBK-36/37/38), not confirmed for Event Booking.
free_rsvp skips the payment-ask leg entirely, straight to confirmed (shared engine — Workshop's AC-WSBK-05 ↗).payment_mode renders as a real radio the founder can select — no booking_surface radio on this card (Workshop's AC-WSBK-17 ↗ only partly applies — the payment half, not the surface half).wa.me link drops straight into the booking flow (shared engine — Workshop's AC-WSBK-01 ↗).in_chat only, Attendees on Pulse is the one destination. No “05 · when I need to step in” category — that's Workshop's own aspirational debt, not confirmed here.cond-block. Dead controls omitted outright: deposit_balance (a Retreat Booking concept, per the PRD's own Constraints section)..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.{{program}} aliases {{workshop_name}} per the PRD's Guest receipts section, #3789); Booking confirmation + Night reminder = Meta-approved locked-tpl previews, copy verbatim (C1; ONE reminder body covers both offsets). Payment vehicles (#4703, spec change — not built) — folded into 02 · How guests pay under the pay-structure radio (Peyton, preview review 2026-09-10); Payment ask stays in 04a with the invite: a checkbox GROUP of two vehicles — Payment link (URL nested under it) · QR code (upload nested under it, #3153 shipped) — tick one or both; both ticked → both required, one bubble (QR image, caption carries the link). Mirrors workshop-booking.html sec-3/3b verbatim (shared engine); rationale + guard-rail states live there. The sec-8c locked mirror below still draws the shipped three-option radio — it follows once the PRD locks the field shape.pv-chat (invite → Flow form → payment ask → Mark-paid beat → confirmation → reminder placeholder; scrolls in the ~460px shell, position:sticky in the build) — one preview only, no second dimmed own-form phone (that's Workshop's AC-WSBK-14 ↗, not this card's). Event date required, date-only, gates Try/activation.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 two alternate states drawn as a second full phone (nothing in your Brain · + Something new) — the one-offering and picked-then-edited states differ only by row count / one field value, so they ride the desktop call-out.Payment · Messages · Brain rows and the two “Trying it” notices are unchanged from sec-5 — cropped here.
Runs on your WhatsApp from the moment you activate. Pause anytime.
Runs on your WhatsApp from the moment you activate. Pause anytime.
About 20 seconds. Stay on this screen.
assistant-lifecycle.html / the activation-wizard PRD), not restated here — this card contributes no new criterion for the generic loader.assistant-lifecycle.html frame 7a) — no assistant-specific rewording, same 5 grouped steps.It's capturing spots on your WhatsApp now. Watch bookings fill in Pulse.
assistant-lifecycle.html) — same shell as every Pulse card, only the metrics line and feed content are Event-Booking-specific.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).
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. ──
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 (A · B) — v1's only mode
Flow form submitted
│ name · email · spots
▼
┌───────────────────┐
│ MystFlo ledger │ full_prepay → Mark paid is the billable event
│ (cohort_bookings)│ free_rsvp → confirms on capture, no paid leg
└─────────┬─────────┘
▼
┌───────────────────────────────┐
│ Pulse · unpaid bookings only │ paid → disappears from view
└───────────────────────────────┘
✗ no write-back to any sheet ✗ no Airtable / Notion
✗ no export (own-form / roster-sheet is
Workshop Booking's v1 scope)
in_chat only. Airtable, Notion, and a plain export don't exist yet either.own_form path, not this card's.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: Event 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.
nestedBackdrop · ✕ / Escape close THIS sheet only · ?assistant=4L9&records=open · same sheet as master assistant-lifecycle.html #sec-9esortable — hidden at ≤1 page (20 rows/page); drawn here for shape only‹ PrevPage 1 of 1Next ›ModalWrapper presentation="sheet", contentWidth="wide", nestedBackdrop, broadcastCloseAll={false}.?assistant=…&records=open), not a route.FilterChipGroup, PaginationBar), never a third style.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.
?records=openone component, noun is a proprow geometry identical in every state4 of 6 · 2 on the waitlist · needs-verdict first, then newest. Full context: the Pulse tab in 8b.
Your booking link is live. Share it and the first name lands here — usually within a day of posting.
Your booking link is live. Share it and the first name lands here — usually within a day of posting.
aria-busy="true" on the table; the shimmer is suppressed under prefers-reduced-motion.Nothing's lost — this is a connection problem on our end. Your bookings are safe.
Or ping us if it keeps happening.
?assistant=…&records=open), opened by the wrong personWe'll bring you straight back to this list.
<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.?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.?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.)?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.)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).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=.email: null bug — discarding the one channel not rate-limited by WhatsApp's 24h window.Bookings and payment relay keep working. Only the reminder is blocked until the wording is fixed.
Resubmitting goes back into review — same one-to-two-day window.
assistant-lifecycle.html frame 7e), adapted to this assistant's two templates.Your assistant is live. Bookings and reminders are paused until your event details save.
Pauses the assistant and opens Personalize so you can fix the details there. Bookings and reminders start the moment they save.
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..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.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..att-row grammar (! icon · title · outline button) this file's own sec-8 already draws for the pending-templates row; tapping Retry reopens 6c.Your assistant is live. Bookings and reminders are paused until your event details save.
Pauses the assistant and opens Personalize so you can fix the details there. Bookings and reminders start the moment they save.
03b where-guests-book choice) don't match Workshop's (Price per seat/Total seats, a real 03b radio) closely enough for the master's literal field values to stand in for this card. Personalize now gets its own frame — 8c ↓ — using the SAME locked-mirror skeleton and treatment the master's 9c established (categories, .pz-locked on the form column only, preview column carried over identically from pre-activation, nothing clickable), with Event's own field labels and values.Same locked-mirror skeleton and treatment as the master's assistant-lifecycle.html 9c/13c: same .sm group headings, .field-label/.field/.opt-radio row anatomy, .pz-locked wash on the form column only (never the preview), nothing clickable. Not pointed at the master this time — Event's own Personalize surroundings differ from Workshop's closely enough (below) that the master's literal field VALUES can't stand in for this card. Drawn on the Weekly sunset mixer instance (ASST-4L9) already live in this file's own Pulse frames (8, 8b) — 10 spots, live weekly, $1.75 per run.
This is what's set for this round — nothing here can be edited in place. To change any of it, start fresh with a new assistant → (the ⋯ menu, top right). Brain readiness lives on Connect →
ConfigFieldRenderer.tsx read-only mirror.) Timezone and Where are not yet drawn on this mirror — pre-#4478 crop.Both options still show — the picked one just reads selected, not tappable.
All three options still show — the picked one just reads selected, not tappable.
Runs on demand — whenever a guest messages this number.
Works the same whether they tapped your wa.me link or typed this directly — the AI reads intent, not just the word. No second, dimmed “own booking form” preview here — v1 has only this one path.
This is what's set for this round — nothing edits in place. Start fresh with a new assistant → (⋯, top right). Brain readiness lives on Connect →
Works the same whether they tapped your wa.me link or typed this directly — the AI reads intent, not just the word. No second, dimmed “own booking form” preview — v1 has only this one path.
Tablet 768: not drawn — this flow's own contract is desktop + 375 only (see sec-3b/sec-3 above); undrawn here for the same reason, not invented.
booking_surface field genuinely offers two options (in_chat/own_form), so its locked mirror shows both, one selected. Event Booking's booking_surface field has the SAME key name but only ONE option (in_chat) — the migration strips own_form outright (supabase/migrations/20260813130000_evbk01_event_booking_card.sql:476, DELETE ... WHERE f->>'key'='booking_surface' AND o->>'value'='own_form') and eventBookingCategories.ts never even reads the key (no findField(fieldByKey, 'booking_surface') call in its regroup function) — there is no choice to render, locked or otherwise. This file's own pre-activation sec-3/3b already says so explicitly ("v1 is in_chat only — no 'where guests book' radio") and its AC note (AC-WSBK-17 ↗) says the same field-parity criterion "only partly applies — the payment half, not the surface half." A same-day brief for this gap first described the two cards as sharing an identical two-choice Personalize skeleton; checking the schema directly (above) shows that's true for payment, not for booking surface — corrected here rather than drawing a fictional second option.payment_mode: full_prepay/free_rsvp)..pz-locked sits on .form-col only — never on .pv-col — per Peyton's same-day ruling that the preview is a static design visualisation, identical pre- and post-activation ("just have it stay the same"). The .pv-col here is byte-for-byte this file's own pre-activation preview (sec-3b desktop / sec-3 mobile) — same Beach Sunset Mixer illustrative sample, same "once your Flow is live" copy, not restyled, not re-derived from the instance's own saved values (Weekly sunset mixer) — same rule the master fix applied, see assistant-lifecycle.html #sec-9c ↗ for the full rationale.payment_method radio entirely, reasoning that the master's own 9c also skips Workshop's equivalent field. Checked against the real question — "does this field render pre-activation?" — and it does: eventBookingCategories.ts's evbk_payment_message group ("04b · Payment message") renders payment_method + payment_link + copy_payment_ask together, and the field is REAL and shipped (supabase/migrations/20260814121000_booking_payment_vehicle.sql, applied to BOTH TMPL-EVBK01 and TMPL-WSBK01, 2026-08-14). Drawn from that migration's actual current labels/options ("How guests pay" — Card on your ticketing page / Stripe payment link / QR code), not from this file's own pre-activation sec-3b/sec-3 — those predate the Aug 14 migration and still show an older, stale two-option "Payment method · not built · #3153" version (Payment link / QR only). "Payment ask" moved here from 04a to match its real category (it was drawn under "04a · Booking messages" in the first pass, copying master 9c's own mislabeling of the same field).workshop-booking.html's equivalent sec-3b/sec-3, and the master's own 9c (which still omits Workshop's 04b payment-method radio from its locked mirror the same way this frame's first draft did). All three should get the same fix this frame just got — real 3-option field, current labels — but that's a wider, separate pass across files this session isn't scoped to make unilaterally.payment_method (Workshop) and payment_mode (Event) were the same booking-choice concept under two drifted key names. Wrong — they're two legitimately distinct fields both cards carry: payment_mode is the payment STRUCTURE (full_prepay/free_rsvp — in both the WSBK01 and EVBK01 card migrations), payment_method is the payment VEHICLE (ticketing_page/stripe_link/qr — added to BOTH TMPL-EVBK01 and TMPL-WSBK01 by the shared supabase/migrations/20260814121000_booking_payment_vehicle.sql). No unification work needed.Spec: prd-as-event-booking.md (field source: eventBookingCategories.ts + the EVBK01 migration). Archetype: assistant-detail-tabs.md § 2026-09-04 locks. Master pattern: assistant-lifecycle.html #sec-9c / #sec-13c-m.
Every bubble below draws the in_chat
branch — v1's only path. A guest DMs, the assistant captures name/event/headcount, and relays the
founder's ticket 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). There is no own_form branch to draw here — that guest
path is Workshop Booking's v1 scope, not this card's.
pending_payment booking row this reply represents.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-event wording changes — the three-field capture contract itself is unchanged and correct.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 · Night 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.
{{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.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.
Unlike Workshop Booking, there is no “where
guests book” setting here — v1 is in_chat only. The only real fork is payment
structure: a founder picks full_prepay or free_rsvp. That's two shipped modes,
plus Mode D (concurrent events on one number) and Mode E (not built).
payment structure (in_chat is the only surface — no own_form fork)
│
├── full_prepay (default) ──► MODE A — shipped
│
├── free_rsvp ────────────────► MODE B — shipped
│
└── 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 events to pick │
│ from. NOT BUILT — two real blockers, drawn in Part III · Future. │
└─────────────────────────────────────────────────────────────────────────────────┘
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.
pending_payment row and the assistant relays the founder's payment ask.wa.me only pre-fills "book") · 2 invite = copy_flow_intro founder field (default names the program + date, #3789) over WhatsApp's native form (Event's own persona-worded Flow (event_booking Meta name, “Reserve my ticket” — hard-gated at activation per the PRD's WhatsApp templates table); unpublished → typed capture, nothing waits) · 4 = ONE message, thank-you first (runtime string, composePaidBookingThankYou — "spot 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 = event_booking_confirmation template, ≤30s after Mark paid · 6/7 = ONE class_reminder body, both offsets.shouldCaptureCohortBooking) — no row without both; spots re-checked at submit, not send. Mark paid is the ONLY door to confirmed on this mode — the assistant never takes the guest's word.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.
free_rsvp event skips the payment-ask leg entirely and goes straight to confirmed.captureCohortBooking calls learnPaid on submit, same meter, same exactly-once; no "awaiting payment" state ever appears in Pulse.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 event_booking_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.cohorts.start_date, not payment. Free RSVP is the one mode needing no nudge machinery (no unpaid state to chase).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.
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).resolveBookingPhase0, whatsapp-routing.ts).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.
guest asks something off the booking script
│
▼
┌ can Event Booking answer it? ┐
│ from the event 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
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.prd-as-event-booking.md §Test Plan (AC-WSBK-* / AC-BILL-*) · DB migration 20260813130000_evbk01_event_booking_card.sql · ConfigureStep.tsx isBookingFamily gate · no E2E coverage (tests/e2e/assistants/tmpl-evbk01.spec.ts was never written — verified absent 2026-09-05, #4476).Business name ("The Offline Club Studio"), guest names, and phone numbers are illustrative. This file draws the proposed target.
event-templates.ts copy with example vars resolved; nothing invented.Only shown when you have On-Brand Support running on this number.
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 Event Booking card knows about exactly one event, so it cannot offer its own list.
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.
own_form path (a founder's own booking form MystFlo can't see into). v1 is in_chat only, so every capture MystFlo makes is one MystFlo can also see the outcome of.in_chat, so the assistant always knows paid from unpaid.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.
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.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.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.
email: null bug (sec-13).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.
No own-form/Google-Sheets conditional row on this card (v1 has no own_form — that's Workshop Booking's frame). Wallet · Brain Check · template notice unchanged from sec-4 — omitted here, this frame exists for the one new Stripe row.
learnPaid settle path (host Mark-paid on this card; other booking cards also have a roster tick), all metering identically — never a second billing path. Deferred post-beta by CPO ruling; tracked as #3152.The recurring-schedule question (sec-3) is resolved, not deferred — single-session ships now; the lightweight repeating-session increment lives in #2798, tracked separately.