Events — Technical Reference
This page covers the data models, background sync engines, Eventbrite webhook handlers, and public APIs behind the Events module.
Data Models
events
Stores event records from both manual and external Eventbrite sources.
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
workspace_id | uuid | Tenant isolation — FK → workspaces.id (ON DELETE CASCADE) |
name | text | Internal event name |
description | text | Rich-text HTML event description |
location | text | Physical location or "Online" |
start_time | timestamptz | Start time (stored in UTC) |
end_time | timestamptz | End time (stored in UTC) |
timezone | text | IANA timezone string (e.g., America/Chicago) |
external_event_id | text | Eventbrite event ID, or NULL for manual events |
is_published | boolean | Flag to display event on public RSVP page / API (default false) |
website_title | text | Optional public heading (falls back to name) |
website_description | text | Optional public description (falls back to description) |
ticket_price | numeric(10,2) | Lowest paid ticket price, tax-inclusive |
ticket_class_count | integer | Number of paid ticket types |
eventbrite_url | text | Direct link to the Eventbrite event page |
last_synced_at | timestamptz | Timestamp of the last Eventbrite sync |
created_at | timestamptz | Record creation timestamp |
updated_at | timestamptz | Last updated timestamp |
event_registrations
Logs contact RSVPs and tickets.
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
workspace_id | uuid | Tenant isolation — FK → workspaces.id (ON DELETE CASCADE) |
event_id | uuid | Associated event — FK → events.id (ON DELETE CASCADE) |
contact_id | uuid | Matched contact — FK → contacts.id (ON DELETE CASCADE) |
amount_paid | numeric(10,2) | Ticket price paid |
status | text | 'registered' or 'cancelled' |
external_attendee_id | text | Unique Eventbrite attendee ID |
created_at | timestamptz | Record creation timestamp |
updated_at | timestamptz | Last updated timestamp |
eventbrite_connections
Stores credentials for workspace Eventbrite integrations.
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
workspace_id | uuid | Tenant isolation — FK → workspaces.id (ON DELETE CASCADE), unique |
access_token | text | OAuth access credentials |
organization_id | text | Linked Eventbrite organization ID |
organization_name | text | Name of the organization |
webhook_id | text | Eventbrite webhook subscription identifier |
auto_subscribe_attendees | boolean | Flag to auto-subscribe webhook registrants |
connected_at | timestamptz | When the connection was established |
updated_at | timestamptz | Last updated timestamp |
Integrations & Sync Engines
OAuth Connection Flow
The connection is established via the standard OAuth 2.0 flow:
- Redirects user to Eventbrite authorization.
- Callback endpoint
/api/auth/eventbrite/callbackexchanges authorization codes for long-lived access tokens. - Queries Eventbrite API to discover the Organization ID.
- Registers an organization-level webhook on Eventbrite for events
order.placed,order.refunded, andattendee.updated. - Persists tokens in
eventbrite_connections.
Eventbrite Ticket Ingestion
Ticket pricing is derived dynamically during imports or sync events:
- Checks the
ticket_classesarray of the Eventbrite event. - Extracts
(cost + tax + fee) / 100(buyer-facing total price) to find the lowest paid ticket option. - Stores the total count of paid ticket classes as
ticket_class_count.
Webhook Sync Engine
Real-time attendee updates route to:
POST /api/webhooks/eventbriteBackground Processing
The webhook handler responds with HTTP 200 immediately to prevent Eventbrite retry timeouts. Processing runs asynchronously in the background using the Next.js after() API.
Webhook Event Actions
1. order.placed
- Fetches order details expanded with attendee lists.
- Matches
external_event_idto resolve the target CRM event. - Upserts contact profiles matching email. If email is blank (e.g. "Info requested"), falls back to the buyer's details.
- Inserts
event_registrationsrows mappingexternal_attendee_id(idempotent unique index). - Auto-subscribes the contact (setting
is_subscribed = true,opt_in_source = 'eventbrite_auto_subscribe', and clearing non-complaint suppressions) ifauto_subscribe_attendeesis enabled. - Fires
event_registrationautomations (deduplicated in-memory to execute once per contact).
2. attendee.updated (Transfers & Details Updates)
- If the ticket email changes (transfer), creates a new contact, updates the
contact_idon the registration, and fires the registration automation rules. Ifauto_subscribe_attendeesis enabled, the new contact is also auto-subscribed. - If only name details change, updates the contact record.
3. order.refunded (Cancellation)
- Sets the registration status to
'cancelled'and zeroesamount_paid. - Queries total active tickets for the contact. If the count reaches
0, cancels any active campaign enrollments linked to that event.
Server Actions
getEvents
The getEvents server action retrieves a paginated list of events for the active workspace, applying search and status filters.
export interface EventListParams {
page?: number;
pageSize?: number;
search?: string;
upcoming?: boolean | null;
published?: boolean | null;
sortBy?: string;
sortDir?: "asc" | "desc";
}Filtering & Sorting Parameters:
upcoming: Tri-state time filter (incorporating live/ongoing events):true: Retrieves upcoming and active events (where the event has not concluded:start_time >= NOW()orend_time >= NOW()).false: Retrieves concluded past events (whereend_time < NOW(), or if no end time is specified,start_time < NOW()).null/undefined: Retrieves all events.
published: Tri-state publishing filter:true: Filters for published events (is_published = true).false: Filters for unpublished events (is_published = falseorNULL).null/undefined: Bypasses publishing restrictions and returns both.
sortBy: Specifies the column to sort by. Supported values:"name": Sorts alphabetically by event name (name)."created_at": Sorts by event record creation date."start_time"(default): Sorts chronologically by event start date (start_time).
sortDir: The sort direction ("asc"or"desc").search: Optional text string. Filters name or location using a case-insensitive SQLilikepartial match (%search%).
Public APIs
1. RSVP Page Validation Flow
The hosted RSVP page (/rsvp/[eventId]) performs checks before rendering the registration form:
- Validates the event UUID exists in the workspace.
- Enforces
is_published = true. - Rejects requests if
start_timeis in the past. - Rejects if
external_event_idis present (directing users to the Eventbrite listing).
2. Contact Upsert Rules
The RSVP page API (POST /api/rsvp/[eventId]) applies the following contact update policies:
- New Contact: Creates a contact profile setting
source = 'event'and marketing consent variables. - Existing Contact: Updates consent parameters (
is_subscribed,opt_in_timestamp,opt_in_source,opt_in_ip), clearsunsubscribed_at, and clears non-complaint suppressions. It never overwrites existing contact names.
3. Headless Events API
Retrieves published schedules:
GET /api/v1/public/events- Authenticates using the Bearer token or
?key=query. - Returns JSON array of events where
is_published = trueandend_time > UTC_NOW(). - Sorts ascending by
start_time. - Adds
Access-Control-Allow-Origin: *to CORS headers. - Each event object includes pricing fields:
ticket_class_count(integer): Number of paid ticket classes.display_price(string): pre-computed display price string (e.g."Free","From $15.00", or"$30.00").
Performance & Hydration Optimization
Server-Side Pre-fetching & Lazy Loading
- Server Component (
pages/events/page.tsx): Pre-fetches the active workspace, page 1 events list, and Eventbrite integration status in parallel on the server. - Client Component (
events-client.tsx): Coordinates tabular list rendering, pagination, sorting columns, and segmented filters (Time and Website). - Dynamic imports: Dynamically imports and lazy loads heavy creation dialogs (
CreateEventDialogandImportEventbriteDialog) withssr: false. - Sync Gates: Uses
isFirstQueryRefto prevent initial mounting waterfalls andinitialWorkspaceIdRefstate synchronization hook.
Row-Level Security (RLS)
All tables enforce tenant-level isolation using the get_user_workspace_ids() helper.
| Table | SELECT | INSERT | UPDATE | DELETE |
|---|---|---|---|---|
events | Workspace members | Admin+ | Admin+ | Admin+ |
event_registrations | Workspace members | System API | System API | Admin+ |
eventbrite_connections | Workspace members | Admin+ | Admin+ | Admin+ |