Events & Ticketing Module
Events & Ticketing Module
Overview
The Events & Ticketing module provides comprehensive event management capabilities within the Fundraising Hub. It supports a wide range of event types from simple appointment scheduling to complex mission trips and fundraising galas with ticketing, registration, custom fields, checklists, and payment processing.
Route / navigation: /fundraising, fundraisingTool = events-ticketing (hub card Events & Ticketing). See 00-FUNDRAISING-HUB.md.
Location: Fundraising Hub → Events & Ticketing
Access: Parent org and fund users with write permissions to the Fundraising Hub
---
Table of Contents
1. Feature Summary 2. Event Types 3. Components 4. URL Structure 5. Database Schema 6. Edge Functions 7. User Workflows 8. Integration Points 9. Configuration 10. Troubleshooting
---
Feature Summary
Core Capabilities
| Feature | Description |
|---------|-------------|
| **Event Creation** | Multi-step wizard for creating events with dynamic steps based on event type |
| **Ticket Management** | Multiple ticket types with pricing, inventory, and limits |
| **Registration** | Public registration pages with custom fields and promo codes |
| **Payment Processing** | Stripe Checkout integration for paid events |
| **Waitlist** | Automatic waitlist management with promotion when spots open |
| **Checklists** | Pre-event requirements (waivers, uploads, confirmations) |
| **Promo Codes** | Percentage or fixed discounts with usage limits |
Event Types Supported
| Type | Use Case | Key Features |
|------|----------|--------------|
| **Simple** | Shower signup, counseling | Time slots, single capacity |
| **Standard** | Speaker events, courses | Tickets, custom fields |
| **Retreat** | Weekend retreats | Tickets, custom fields, guests, waitlist |
| **Camp** | Horse camps, youth camps | Tickets, checklist requirements, waitlist |
| **Trip** | Mission trips | Tickets, extensive checklists, deposits |
| **Gala** | Fundraising galas | Tickets, tax-deductible portions, table seating |
| **Auction** | Silent/live auctions | Bidding, card-on-file, winner charging |
---
Components
Admin Components
EventsManager (src/features/events/components/EventsManager.tsx)
The main list view for managing events.
Features:
- Event list with status badges (Draft, Published, Closed, Canceled)
- Search and filter by status, type, and time (upcoming/past)
- Quick actions: View, Edit, Copy URL, Publish, End Registration, Delete
- Registration count and revenue display
Props: None (uses context for entity selection)
State Management:
- Uses
useEventshook for data fetching with filters - Uses
useDeleteEvent,usePublishEvent,useCloseEventmutations
EventBuilder (src/features/events/components/EventBuilder.tsx)
Multi-step wizard for creating and editing events.
Steps (dynamic based on event type): 1. Basics - Name, dates, location, capacity, waitlist settings 2. Tickets - Ticket types with pricing and inventory 3. Fields - Custom registration fields (text, select, checkbox, etc.) 4. Checklist - Pre-event requirements (waivers, uploads, confirmations) 5. Promo - Promo codes with discounts 6. Review - Summary and publish
Key Features:
- Step validation blocks forward navigation until requirements met
- Auto-generates URL slug from event name
- In-Person/Virtual radio selection for location type
- Ticket type management with add/edit/delete
- Custom field builder with multiple field types
Props:
interface EventBuilderProps {
eventId?: string; // For editing existing events
onClose: () => void; // Callback when closing builder
onSave: () => void; // Callback after successful save
}Public Components
PublicEventPage (src/features/events/components/PublicEventPage.tsx)
Public-facing event registration page.
Features:
- Hero section with event image or gradient
- Event details (date, time, location/virtual)
- Ticket selection with quantity controls
- Promo code input and validation
- Registration form with custom fields
- Order summary with Stripe checkout integration
- Sold out and waitlist states
Props:
interface PublicEventPageProps {
config: EventConfig; // Event data from API
}EventRegistrationSuccess (src/features/events/components/EventRegistrationSuccess.tsx)
Confirmation page after successful registration.
Features:
- Confirmation number, event details, tickets/amount, QR ticket block when applicable
- Add to Calendar (ICS) + Web Share API (no “Back to Event Page” on the confirmed-success view)
- Fund contact strip: *For questions about this event, contact {organizationName}.* then
PublicFooter(layout aligned with giving page:min-h-screenflex column,mt-autofooters) - Polling / recovery UI for
get-registration-details(seeevents/05-PUBLIC-EVENT-PAGE.md,06-EVENT-REGISTRATION-SUCCESS.md) - Checklist notice for events with requirements
Props:
interface EventRegistrationSuccessProps {
config?: EventSuccessConfig;
registrationId?: string;
sessionId?: string;
}PublicEventLoader (src/features/events/components/PublicEventLoader.tsx)
Wrapper component that handles URL parsing and data fetching.
Features:
- Parses URL segments (canonical
public_org_keyor temporary legacy UUID prefix, org slug, event slug) - Fetches event data from Edge Function
- Handles loading, error, success, and canceled states
- Routes to appropriate view (event page or success page)
ChecklistPortal (src/features/events/components/ChecklistPortal.tsx)
Magic link access for registrants to complete checklist items.
Features:
- Accessed via
/checklist/:tokenURL - Displays all checklist items for the registration
- Complete confirmation items (checkbox)
- Upload files for upload items
- Waiver signing fallback for legacy registrations
- Progress bar showing completion status
- Validates token and expiration
> Registration-time waiver update (Mar 2026): Required event waiver items are now signed during registration on PublicEventPage before payment completion. Checklist portal signing remains available for backward compatibility and non-required/legacy completion flows.
Props:
// Uses URL param :token to fetch dataCheck-in & Reporting Components
EventCheckIn (src/features/events/components/EventCheckIn.tsx)
Real-time check-in interface for event day operations.
Features:
- Stats cards showing total attendees, checked in, remaining, and progress percentage
- Search by name or email
- Filter by check-in status (all, checked in, not checked in)
- Individual registrant check-in with one click
- Guest check-in support (expandable rows)
- Undo check-in capability
- Check-in timestamps displayed
- Refresh button to sync latest data
Props:
interface EventCheckInProps {
event: EventWithDetails;
onBack: () => void;
}UI Layout: 1. Header with event name and refresh button 2. Four stats cards (Total, Checked In, Remaining, Progress) 3. Search and filter controls 4. Registrations list with expandable guest rows
EventDashboard (src/features/events/components/EventDashboard.tsx)
Event metrics and reporting dashboard.
Features:
- Overview Tab:
- Ticket breakdown by type with counts and revenue
- Registration activity chart (last 7 days)
- Registrations Tab:
- Full registration list with details
- Export to CSV functionality
- Refresh button
- Tickets Tab:
- Ticket type cards with inventory status
- Revenue per ticket type
- Progress bars for capacity
Quick Stats:
- Total attendees (with capacity percentage)
- Total revenue (with average per ticket)
- Check-in count and rate
- Registration count (with canceled count)
Props:
interface EventDashboardProps {
event: EventWithDetails;
onBack: () => void;
onCheckIn: () => void;
onEdit: () => void;
}Actions:
- Copy URL - copies public event URL to clipboard
- Edit Event - navigates to EventBuilder
- Check-In - navigates to EventCheckIn
---
URL Structure
Public URLs (No Authentication)
Format: /events/{org-key-or-prefix}/{org-slug}/{event-slug}
| Segment | Description | Example |
|---------|-------------|---------|
| `org-key-or-prefix` | Canonical `public_org_key` for newly emitted links; temporary legacy fallback is the first 6 UUID characters | `abc123def456` |
| `org-slug` | Organization slug (human-readable) | `infocus-ministries` |
| `event-slug` | Event slug (auto-generated from name) | `annual-gala-2025` |
Examples:
https://alignmint.app/events/abc123/infocus-ministries/annual-gala-2025
https://alignmint.app/events/abc123/infocus-ministries/annual-gala-2025/successURL Generation (in code):
// organization_slug is populated via join in fetchEvents/fetchEvent
const orgKeyOrPrefix = event.organization_public_key || event.organization_id.substring(0, 6);
const publicUrl = `https://alignmint.app/events/${orgKeyOrPrefix}/${event.organization_slug}/${event.slug}`;> Note: The organization_slug field on EventWithDetails is populated by joining the organizations table in fetchEvents() and fetchEvent() in db.ts. This ensures the public URL is always constructed with the correct organization slug.
Routes Configuration (src/main.tsx)
<Route path="/events/:orgIdPrefix/:orgSlug/:eventSlug" element={<PublicEventLoader />} />
<Route path="/events/:orgIdPrefix/:orgSlug/:eventSlug/success" element={<PublicEventLoader />} />---
Database Schema
Tables
events
Core event record with metadata, dates, capacity, and accounting links.
| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `organization_id` | UUID | FK to organizations |
| `name` | TEXT | Event name |
| `slug` | TEXT | URL-safe identifier |
| `description` | TEXT | Event description |
| `event_type` | TEXT | 'simple', 'standard', 'retreat', 'camp', 'trip', 'gala', 'auction' |
| `start_date` | TIMESTAMPTZ | Event start |
| `end_date` | TIMESTAMPTZ | Event end |
| `timezone` | TEXT | Timezone (e.g., 'America/Chicago') |
| `location_name` | TEXT | Venue name |
| `location_address` | TEXT | Full address |
| `is_virtual` | BOOLEAN | Virtual event flag |
| `virtual_url` | TEXT | Meeting link |
| `capacity` | INTEGER | Max attendees (null = unlimited) |
| `waitlist_enabled` | BOOLEAN | Enable waitlist |
| `waitlist_capacity` | INTEGER | Max waitlist size |
| `status` | TEXT | 'draft', 'published', 'closed', 'canceled' |
| `total_registrations` | INTEGER | Denormalized count |
| `total_revenue` | DECIMAL | Denormalized revenue |
| `fund_id` | UUID | FK to organizations (for director access) |
| `income_account_id` | UUID | FK to chart_of_accounts |
event_ticket_types
Pricing tiers for events.
| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `event_id` | UUID | FK to events |
| `name` | TEXT | Ticket name (e.g., "General Admission") |
| `description` | TEXT | Ticket description |
| `price` | DECIMAL | Ticket price |
| `quantity_available` | INTEGER | Inventory (null = unlimited) |
| `quantity_sold` | INTEGER | Tickets sold |
| `max_per_order` | INTEGER | Limit per registration |
| `tax_deductible_amount` | DECIMAL | Tax-deductible portion (for galas) |
| `is_active` | BOOLEAN | Available for purchase |
| `sort_order` | INTEGER | Display order |
event_custom_fields
Dynamic form fields for registration.
| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `event_id` | UUID | FK to events |
| `name` | TEXT | Field label |
| `field_type` | TEXT | 'text', 'textarea', 'select', 'multiselect', 'checkbox', 'date', 'number' |
| `required` | BOOLEAN | Required field |
| `options` | JSONB | Options for select/multiselect |
| `applies_to` | TEXT | 'registrant', 'guest', 'both' |
| `sort_order` | INTEGER | Display order |
event_checklist_items
Pre-event requirements.
| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `event_id` | UUID | FK to events |
| `name` | TEXT | Item name |
| `description` | TEXT | Instructions |
| `item_type` | TEXT | 'waiver', 'upload', 'confirmation', 'link' |
| `waiver_id` | UUID | FK to waivers (for waiver type) |
| `external_url` | TEXT | Link URL (for link type) |
| `required` | BOOLEAN | Must complete before event |
| `requires_review` | BOOLEAN | Staff must approve |
| `due_date` | DATE | Deadline |
| `sort_order` | INTEGER | Display order |
event_promo_codes
Discount codes.
| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `event_id` | UUID | FK to events |
| `code` | TEXT | Promo code (uppercase) |
| `discount_type` | TEXT | 'percentage', 'fixed' |
| `discount_value` | DECIMAL | Amount (20 = 20% or $20) |
| `max_uses` | INTEGER | Usage limit (null = unlimited) |
| `times_used` | INTEGER | Current usage count |
| `valid_from` | TIMESTAMPTZ | Start validity |
| `valid_until` | TIMESTAMPTZ | End validity |
| `is_active` | BOOLEAN | Currently active |
event_registrations
Registration records.
| Column | Type | Description |
|--------|------|-------------|
| `id` | UUID | Primary key |
| `event_id` | UUID | FK to events |
| `donor_id` | UUID | FK to donors (unified CRM) |
| `registrant_email` | TEXT | Email |
| `registrant_first_name` | TEXT | First name |
| `registrant_last_name` | TEXT | Last name |
| `registrant_phone` | TEXT | Phone |
| `status` | TEXT | 'pending', 'confirmed', 'waitlisted', 'cancelled', 'refunded' |
| `payment_status` | TEXT | 'pending', 'paid', 'refunded', 'failed' |
| `amount_paid` | DECIMAL | Total paid |
| `stripe_payment_intent_id` | TEXT | Stripe reference |
| `promo_code_id` | UUID | FK to event_promo_codes |
| `custom_field_responses` | JSONB | Custom field values |
| `checked_in_at` | TIMESTAMPTZ | Check-in timestamp |
| `access_token` | TEXT | Magic link token for checklist portal |
Database Functions
| Function | Purpose |
|----------|---------|
| `update_event_totals(event_id, amount)` | Increment registration count and revenue |
| `increment_ticket_quantity(ticket_id, quantity)` | Update tickets sold |
| `decrement_event_totals(event_id, amount)` | For refunds/cancellations |
| `check_event_availability(event_id)` | Check capacity and waitlist status |
| `promote_from_waitlist(event_id)` | Auto-promote next waitlisted registrant |
Triggers
| Trigger | Table | Purpose |
|---------|-------|---------|
| `promote_waitlist_on_cancel` | event_registrations | Auto-promote from waitlist when registration cancelled |
---
Edge Functions
create-event-checkout
Creates or resumes event checkout for both embedded PaymentElement and legacy redirect modes.
Location: supabase/functions/create-event-checkout/index.ts
Request:
{
eventId: string;
eventSlug: string;
organizationId: string;
tickets: Array<{ ticketTypeId: string; quantity: number }>;
registrantEmail: string;
registrantFirstName: string;
registrantLastName: string;
registrantPhone?: string;
guests?: Array<{ firstName: string; lastName: string; email?: string }>;
customFields?: Array<{ fieldId: string; value: string }>;
promoCode?: string;
successUrl?: string;
cancelUrl?: string;
collectSalesTax?: boolean;
embedded?: boolean;
action?: 'cancel_pending_registration' | 'finalize_free_registration';
registrationId?: string;
}Response:
{
// embedded mode
clientSecret?: string;
paymentIntentId?: string;
registrationId?: string;
paymentStatus?: string;
finalizeRegistration?: boolean;
free?: boolean;
freePending?: boolean;
// legacy redirect mode
sessionId?: string;
url?: string;
}Features:
- Validates ticket availability
- Applies promo code discounts
- Uses embedded PaymentElement flow by default in current frontend
- Supports idempotent retry/resume for pending embedded payment attempts
- Handles pending-registration cancellation (
action: cancel_pending_registration) - Supports free registration finalization after required waiver signing (
action: finalize_free_registration)
Stripe payment routing (Connect vs platform — Mar 2026): Implemented via `resolvePaymentTargetForOrg` in `supabase/functions/_shared/stripe-connect-context.ts` — the same helper as `create-payment-intent` and `create-checkout-session`. Ancestor-chain Connect (stripe_account_id + stripe_charges_enabled) → express (Stripe-Account header + 0.5% application fee). Else, if the event org is anywhere under `DIRECT_STRIPE_PAYOUT_ORG_ID` and the platform account can charge → legacy_direct (no Stripe-Account, no application fee). If neither applies, the function returns `403` with `connect_not_ready` or `charges_not_enabled`. Webhook handling is unchanged: stripe-webhook uses session/PI metadata (source: event_registration, organization_id, etc.) for both platform- and connected-account events.
After Connect onboarding (reverting off platform direct charges): When the parent or an ancestor org completes Stripe Connect and stripe_account_id + stripe_charges_enabled are set, new event checkouts automatically use that connected account—no code change. In-flight pending registrations keep their existing PaymentIntent on whichever account created them. Optional cleanup: ensure DIRECT_STRIPE_PAYOUT_ORG_ID remains set for payout settlement JEs even when all orgs use Connect; removing the secret does not switch routing by itself—routing is driven by DB Connect fields first.
get-public-event
Fetches public event data for registration page.
Location: supabase/functions/get-public-event/index.ts
Request (query params):
?org={org-slug}&slug={event-slug}Response:
{
id: string;
slug: string;
organizationId: string;
organizationSlug: string;
organizationName: string;
organizationLogo?: string;
name: string;
description?: string;
event_type: string;
start_date: string;
end_date?: string;
timezone: string;
location_name?: string;
location_address?: string;
is_virtual: boolean;
virtual_url?: string;
capacity?: number;
total_registrations: number;
waitlist_enabled: boolean;
image_url?: string;
status: string;
ticket_types: TicketType[];
custom_fields: CustomField[];
waiver_requirements?: Array<{
checklist_item_id: string;
waiver_id: string;
name: string;
description?: string;
required: boolean;
}>;
has_checklist: boolean;
}stripe-webhook (Updated)
Handles Stripe webhook events for event registrations.
Location: supabase/functions/stripe-webhook/index.ts
Event Handling:
checkout.session.completedwithmetadata.source = 'event_registration'- Creates registration record with
access_tokenfor checklist portal - Updates ticket quantities sold
- Creates guest records
- Updates promo code usage
- Updates event totals
- Links to donor record (creates if needed)
- Sends confirmation email with magic link
- Creates dashboard notifications for org admins
- Checks and notifies if event reaches capacity
get-checklist-portal
Fetches registration and checklist items via magic token.
Location: supabase/functions/get-checklist-portal/index.ts
Request (query params):
?token={access_token}Response:
{
registration: RegistrationDetails;
event: EventDetails;
checklistItems: ChecklistItem[];
checklistStatus: ChecklistStatus[];
}update-checklist-status
Updates checklist item completion status.
Location: supabase/functions/update-checklist-status/index.ts
Request:
{
registrationId: string;
checklistItemId: string;
status: 'pending' | 'completed' | 'approved';
token?: string; // Magic link token for verification
}Features:
- Validates token matches registration
- Upserts status record
- Sends notification when all required items complete
upload-checklist-file
Handles file uploads for checklist items.
Location: supabase/functions/upload-checklist-file/index.ts
Request: FormData with file and metadata
Response:
{
success: boolean;
fileUrl: string;
fileName: string;
}send-event-confirmation
Sends confirmation email with event details and checklist guidance.
Location: supabase/functions/send-event-confirmation/index.ts
Request:
{
registrationId: string;
eventId: string;
organizationId: string;
registrantEmail: string;
registrantFirstName: string;
registrantLastName: string;
eventName: string;
eventDate: string;
eventLocation?: string;
isVirtual?: boolean;
accessToken?: string;
hasChecklist?: boolean;
amountPaid?: number;
}Features:
- Organic theme styling matching email campaigns
- Hosted by line with the fund/organization display name
- Logo: custom logo for Pro/Enterprise on the fund; if the fund has no logo, uses the parent organization logo when the parent is Pro or Enterprise
- View event details button to the public event page (
APP_URLorhttps://alignmint.app, same URL pattern as checkout success pages) - Add to calendar: Google Calendar and Outlook buttons
- Checklist guidance (required waivers may already be completed during registration)
- Event details and confirmation number; ticket QR block
- Logs to email_logs table
---
User Workflows
Creating an Event
1. Navigate to Marketing Hub → Events & Ticketing 2. Click "Create Event" 3. Select event type (determines available steps) 4. Complete each step:
5. Click "Publish" to make event live
- Basics: Name, dates, location, capacity
- Tickets: Add ticket types with pricing
- Fields: Add custom registration fields (optional)
- Checklist: Add pre-event requirements (optional)
- Promo: Add promo codes (optional)
- Review: Verify and publish
Managing Registrations
1. From event list, click on event to view details 2. View registration list with status filters 3. Actions available:
- View registration details
- Check in attendees
- Process refunds
- Manage waitlist
Public Registration Flow
1. User visits public event URL 2. Selects tickets and quantities 3. Optionally applies promo code 4. Fills in registration form (name, email, custom fields) 5. Proceeds to Stripe Checkout (or completes directly if free) 6. Redirected to success page with confirmation 7. Receives confirmation email
---
Integration Points
Stripe Integration
- Uses existing Stripe account connected via Payment Integrations
- Creates Checkout Sessions for paid registrations
- Webhook processes successful payments
- Supports refunds through Stripe Dashboard
Unified CRM (Donors Table)
- Registrants are linked to donor records
- Creates new donor if email doesn't exist
- Updates donor totals after registration
Accounting Integration
- Events can be linked to income accounts
- Revenue tracked in event totals
- Journal entries created by the server-side event revenue flow in
supabase/functions/_shared/event-revenue-journal.ts
Waiver System
- Checklist items can reference existing waivers
- Reuses waiver signing UI
- Tracks completion status per registrant
- Event Builder (admin): Checklist Item Name for saved events uses debounced persistence and optimistic React Query updates so fast typing does not corrupt names — see `events/02-EVENT-BUILDER.md` (Recent Updates, Mar 22, 2026).
---
Configuration
Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| `STRIPE_SECRET_KEY` | Yes | Stripe API key for checkout |
| `STRIPE_WEBHOOK_SECRET` | Yes | Webhook signature verification |
| `SUPABASE_URL` | Yes | Supabase project URL |
| `SUPABASE_SERVICE_ROLE_KEY` | Yes | Service role key for Edge Functions |
Stripe Dashboard Setup
1. Go to Developers → Webhooks 2. Add endpoint: https://{project-ref}.supabase.co/functions/v1/stripe-webhook 3. Select events: checkout.session.completed, invoice.paid 4. Copy signing secret to STRIPE_WEBHOOK_SECRET
---
Troubleshooting
Common Issues
Event not appearing on public page
- Check event status is "published"
- Verify URL format includes the canonical public org key for newly generated links
- Check organization slug matches
Tickets showing as sold out
- Verify
quantity_availablevsquantity_sold - Check if tickets are marked as active
Promo code not working
- Verify code is uppercase
- Check
is_activeflag - Verify validity dates
- Check usage limits
Payment not completing
- Check Stripe Dashboard for failed payments
- Verify webhook is receiving events
- Check Edge Function logs in Supabase
Logs
- Edge Function logs: Supabase Dashboard → Edge Functions → Logs
- Stripe events: Stripe Dashboard → Developers → Events
- Browser console: Check for API errors
---
Future Enhancements
Completed in Phase 5
- [x] Check-in interface (EventCheckIn component)
- [x] Event metrics dashboard (EventDashboard component)
- [x] Export registrations to CSV
Completed in Phase 6 (Dec 17, 2025)
- [x] Checklist portal (magic link access for registrants)
- [x] Email confirmation with magic link
- [x] Dashboard notifications for org admins
- [x] Donor/Contact/Prospect integration
- [x] Waiver integration in checklist
Planned for Future Phases
- [ ] Auction bidding system (real-time bidding, bid history, winner notification)
- Components:
AuctionBidding.tsx,place-auction-bidEdge Function - Features: Real-time bid updates via Supabase Realtime, bid history, winner notification
- Estimated complexity: Medium-High (~500-800 lines)
- [x] Signed QR self-check-in via normal phone camera apps
- [ ] Table seating for galas
- [ ] Calendar integration (Google, Outlook)
- [ ] Bulk check-in operations
- [ ] Check-in kiosk mode (full-screen, simplified UI)
Synced from IFMmvp-Frontend documentation: pages/fundraising/06-EVENTS-TICKETING.md
Ready to Get Started?
See how Alignmint can simplify your nonprofit's operations. Schedule a free demo with our team and we'll walk you through everything.
Questions? Email us at steven@getalignmint.org