Events
Gordon CRM's Events module lets you create events, track registrations, and automate follow-up communication — all within your existing CRM workspace. Events can be created manually inside the dashboard or imported from Eventbrite via the OAuth integration.
Event Sources
Every event in the CRM has a source determined by how it was created:
| Source | Created By | Registration Managed By |
|---|---|---|
| Manual | Dashboard → Events → New Event | Gordon CRM's native registration form |
| Eventbrite | Dashboard → Events → Import from Eventbrite | Eventbrite (via webhook sync) |
Both sources share the same events table and registration model. The external_event_id column is the
discriminator — it is NULL for manual events and populated with the Eventbrite event ID for imported events.
Key distinction: Manual events accept registrations through Gordon CRM's public registration page. Eventbrite events do not — they redirect registrants to the Eventbrite listing. Registrations for Eventbrite events are synced into the CRM automatically via webhooks.
The Event Model
Each event is stored in the events table with the following key fields:
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
workspace_id | uuid | Tenant isolation |
name | text | Internal event name |
description | text | Rich-text event description |
location | text | Physical location or "Online" (nullable for virtual events) |
start_time | timestamptz | Event start (stored in UTC) |
end_time | timestamptz | Event end, nullable for open-ended events |
timezone | text | IANA timezone string (e.g. America/Chicago) |
external_event_id | text | Eventbrite event ID, or NULL for manual events |
is_published | boolean | Whether the event appears on the public registration page and events API |
website_title | text | Optional public-facing title (falls back to name) |
website_description | text | Optional public-facing description |
ticket_price | numeric(10,2) | Lowest buyer-facing ticket price, tax-inclusive (imported from Eventbrite ticket classes) |
ticket_class_count | integer | Number of paid ticket types for the event. Used for "From $X" display logic. Defaults to 1. |
eventbrite_url | text | Direct link to the Eventbrite listing |
last_synced_at | timestamptz | When the event was last synced from Eventbrite. NULL for manual events. |
Timezone Handling
Events store times in UTC at the database level, but each event carries its own timezone column
for display purposes:
- Manual events — The user selects a timezone when creating the event. Defaults to
America/Chicago. - Eventbrite events — The timezone is automatically populated from the Eventbrite venue data during import.
- Display — All date/time rendering in the dashboard and public pages uses the event's
timezonefield to format times in the correct local zone viadate-fns-tz.
The Registration Model
Registrations are stored in the event_registrations table:
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
workspace_id | uuid | Tenant isolation |
event_id | uuid | FK → events |
contact_id | uuid | FK → contacts |
status | text | registered or cancelled |
amount_paid | numeric(10,2) | Payment amount (0 for free events) |
external_attendee_id | text | Eventbrite attendee ID for per-ticket deduplication |
created_at | timestamptz | Registration timestamp |
Deduplication
The deduplication strategy differs by event source:
-
Manual events — A composite unique constraint on
(event_id, contact_id)prevents a contact from registering twice. If a previously cancelled registration exists, the status is flipped back toregisteredinstead of inserting a new row. -
Eventbrite events — A unique index on
(workspace_id, external_attendee_id)deduplicates by the Eventbrite attendee ID. This supports multi-ticket orders — a single contact can hold multiple tickets for the same event, each tracked as a separate registration row.
Event Publishing
Events have a built-in publishing system that enables two public interfaces:
-
Public Registration Page — A hosted page at
/rsvp/[eventId]where visitors can register for manual events. See Registration & Public Pages. -
Headless Events API — A JSON endpoint at
/api/v1/public/eventsthat returns all published, upcoming events for a workspace. Authenticated via the workspace's Public API Key, this feed can power event listings on external websites.
An event must have is_published = true to appear on either surface. Unpublished events are only visible
within the dashboard.
Automation Integration
Event registrations are deeply integrated with the Automations engine via the
event_registration trigger type.
When a contact registers for an event — whether through the native registration form or an Eventbrite
webhook — the system calls processAutomationTrigger with:
| Parameter | Value |
|---|---|
trigger_type | event_registration |
trigger_id | The CRM event UUID |
contactId | The registering contact's ID |
triggerEventId | The event UUID (used for date-based campaign timing) |
This means the same automation rule fires regardless of how the registration originated. You can configure automations to:
- Add a tag — e.g. tag all registrants with "Spring Gala 2026"
- Enroll in a campaign — e.g. start a "Pre-Event Reminder" drip sequence
For date-based campaigns, the
triggerEventIdparameter is critical: it links the enrollment to the event'sstart_time, allowing campaign steps to usebefore_eventtiming mode (e.g. "Send reminder 24 hours before the event"). See Automations → Triggers for the full trigger specification.
Refund Handling & Campaign Cancellation
When an Eventbrite refund is processed, the webhook handler cancels the registration and checks whether the contact has any remaining active tickets for the event. If the count reaches zero, the system automatically cancels any active campaign enrollments linked to that event — preventing post-event communication from going out to refunded attendees.
Automation Relationships
Each event's detail page includes an inline automation relationships card that shows all automation rules connected to the event — including campaign enrollments, tag assignments, task creation, and agreement dispatch. Relationships are rendered as natural-language sentences.
This replaces the legacy "Campaigns" section that previously only showed enroll_campaign actions.
The automation relationships card covers all action types. See
Automations → Dependency Tracking for details.
Deleting Events
Deleting an event permanently removes the event, all its registrations, and any automation rules triggered by this event.
If automation rules reference the event, the delete confirmation dialog warns:
"This event is used in N automation rule(s). Deleting it will also remove those rules. Continue?"
RBAC
Events follow Gordon CRM's standard role-based access control:
- Owner & Admin — Full CRUD access to events, registrations, and publishing settings.
- Member — Can view events and registrations but cannot create, edit, or delete.
Contact Source Tracking
Gordon CRM tracks how contacts enter the CRM. Event-related contacts are tagged with distinct sources:
| Source Value | Created Via |
|---|---|
event | Native registration form (/rsvp/[id]) |
eventbrite | Eventbrite webhook (order.placed) |
This allows you to filter and segment contacts by how they were acquired.
Next Steps
- Registration & Public Pages — The public registration form, contact upsert logic, and the headless events API.
- Eventbrite Integration — OAuth connection, webhook sync, multi-ticket mapping, and historical attendee import.