Gathr
Core platform
Multi-tenant architecture, auth, and data integrity rules that everything else builds on.
Magic link authentication
One-click login via email. Supabase auth chain: auth.uid() → person → membership → tenant. Auth callback activates membership on entry.
Multi-tenant hierarchy
org_group → tenant → event → event_session. Tenant slug unique per org group. All PKs are UUID. Tenant branding injected via useTenantBranding().
Row-level security
RLS enabled across all tables. Service role for admin operations, auth.uid() for all client queries. No bypass paths in production code.
Soft delete everywhere
deleted_at timestamptz on every table. No hard deletes. All queries filter WHERE deleted_at IS NULL.
PWA — add to home screen
Installable on iOS and Android. Web manifest, apple-touch-icon, and favicon all in place. Add-to-home nudge included in invitation and welcome emails.
Organizer tools
Everything an organizer needs from the moment they create an event to after it ends.
Organizer dashboard
Full event management: create, edit, publish, track RSVPs. Desktop sidebar + mobile bottom tab nav (Home · Members · Events · More). Multi-org switcher for organizers with more than one tenant.
UP NEXT event card
Directive-first layout: title / status / date up top, then a single guidance line telling the organizer exactly what to do next and why. Share link, RSVP counts, declined member list. Card-level field editing with one Save.
Contact import
Drag-and-drop email import. Parsed, deduped, and added to roster. Invitation send triggered from the same flow.
Share link + join flow
Prospective members submit a join request via share link. Known roster members are fast-tracked: membership activated, magic link generated, redirected straight to their dashboard — no approval queue.
Organizer approval flow
Approve or deny join requests from the dashboard. Acknowledgment email sent on submission; welcome email (with magic link) sent on approval.
Guidance & data engine
The nudge engine is the beginning of the "software plans, you approve" stage. It surfaces the right action at the right time — and logs every decision in a form the system can eventually learn from.
decision_inputs
Every guidance decision records the exact inputs and threshold values that drove it — RSVP rate, weeks out, roster size, last reminder date. When an outcome lands later, the inputs are there to correlate against it.
Actor types
actor_type = 'system' when the platform surfaces an action. actor_type = 'person' when a human clicks. This distinction is what makes outcome analysis trainable — conflating them corrupts the signal.
Named thresholds
Every numeric rule is a named constant: REMINDER_COOLDOWN_DAYS, URGENCY_WEEKS_THRESHOLD, INTEREST_RATIO_THRESHOLD. Constants are the handles the AI layer needs to adjust them per-organizer over time.
Activity logging
nudge.shown / nudge.dismissed / nudge.acted logged to activity_event on every interaction. Conversion rate queryable today; becomes the training signal tomorrow.
In-app notification bell
Supabase realtime subscription. Unread count badge, dismissal, View CTA. Shown in mobile top bar and desktop header. Cron (compute-guidance-notifications) confirmed running weekly.
Pure function contract
getGuidanceLine never queries the database. All state is passed in. Any decision is replayable by reconstructing the inputs — a requirement for reproducible AI evaluation.
Email flows
All flows send from invitations@mail.getgathr.co via Resend. Every outbound message is recorded in communication + communication_recipient for delivery tracking and multi-sender support.
| Flow | Trigger | Variants |
|---|---|---|
| Invitation | Organizer imports + sends | Magic link embedded directly. Add-to-home-screen nudge included. |
| Join acknowledgment | Member submits via share link | Single variant — "request received, under review." |
| Approval welcome | Organizer approves request | Single variant — "you're in." Magic link included. |
| Publish notification | Event goes live | Active members only. Does not create invitation records. |
| Manual reminder | Organizer sends from UP NEXT card | Confirmed: "see you soon." Undecided: "RSVPs still open." Declined excluded. |
| Day-before reminder | Cron, 14:00 UTC daily | Confirmed: "see you tomorrow." Undecided: "are you coming?" Declined excluded. |
| Post-event recap | Organizer sends after last session | Full recap text + "see the event page" CTA. All active members who haven't opted out. |
planwithgather.com in all footers.
Member experience
The member dashboard and join flow — built to make someone feel like they belong to something specific, not like they received a blast email.
Member dashboard
Event cards with RSVP flow (yes / decline), session cards, post-event recap display. Mobile bottom nav: Home · People · Events. Design system tokens applied throughout.
Profile gate
First-login screen collects name, photo, phone, and SMS opt-in. Skippable (session-snoozed), re-shown until confirmed. Logs member.profile_completion nudge events.
Fast-track join for known members
Roster members who hit the share link are recognized, membership activated, and redirected via magic link — no waiting for approval. New members go through the standard queue.
SMS
The routing logic is built and tested. What's missing is carrier approval, not code.
SMS routing in send-event-reminder
Members with sms_opt_in = true and a phone number on file receive reminders via Twilio SMS. All others get email. Both manual and day-before reminder flows use this routing.
Twilio credentials
Account SID, auth token, and from-number (+13176614390) are set as Supabase secrets. Sending fails with Error 30034 — unregistered number — until A2P 10DLC brand and campaign registration is complete in Twilio Trust Hub. Toll-free verification is an alternative path.
Not in this release
Apple Wallet passes
Button exists but is commented out. Not production-ready.
Google & Facebook OAuth
Supabase auth supports it; not yet wired in the app.
Per-commit preview deployments
Cloudflare Pages preview URLs still point at prod Supabase. Dev environment is seeded but the env var wiring for preview builds is unresolved.
Stripe Connect / payments
Architecture decided, tables set up. Implementation not started.
Rollback reference
To restore this exact state
To roll back: reset main to this branch and push, or use Cloudflare Pages → Deployments → Rollback to restore the build instantly without touching git.