Capture eventbrite attendees in SFDC campaigns (#42788)
- Add documentation for historical events and salesforce campaigns. - Moved and consolidated "Connecting Eventbrite registrations to Salesforce campaigns (event ID key)"
This commit is contained in:
@@ -3,6 +3,54 @@
|
||||
|
||||
## Automation
|
||||
|
||||
### Capture Eventbrite attendees in Salesforce campaigns
|
||||
|
||||
> _*TL;DR: It's not working, Who should I call and what should I check?*_
|
||||
> DRI: @Sampfluger88
|
||||
> - Does the Eventbrite page have an "order form" attached? If so, remove it! « This breaks the flow by adding another required form submission not tied to the `New Attendee Registered` action. Attendee name and email will be returned as "Info Requested".
|
||||
> - Does the SFDC campaign exists?
|
||||
> - Is the `Event_key` populated correctly on the corresponding SFDC campaign?
|
||||
|
||||
_*Purpose*_
|
||||
|
||||
Create a reliable, repeatable way to associate Eventbrite registrations with the correct Salesforce contact and campaign. Each event has a unique identifier (`event_key`). We store that identifier on the corresponding Salesforce campaign creating a 1:1 relationship between the published event and the Salesforce campaign.
|
||||
|
||||
This approach “connects” Eventbrite to Salesforce campaigns by using the **`Event_key` as the system-of-record key**. Salesforce Campaigns store that key, and Clay uses it to automatically route registrations to the right Campaign and create/update Campaign Members—cleanly, invisibly, and in a way that can later support additional event platforms.
|
||||
|
||||
_*High-level workflow*_
|
||||
|
||||
1. A new registration occurs and is captured by Zapier (workflow: [Eventbrite - Event registration » Clay](https://zapier.com/editor/355884186/published)).
|
||||
2. Zap captures and sends the following info to Clay:
|
||||
2. `fullName`
|
||||
2. `firstName`
|
||||
2. `lastName`
|
||||
2. `Email`
|
||||
2. `providedNotes`: "`EVENT_NAME` - `EVENT_URL`"
|
||||
2. `Event_key`: "Eventbrite-"`EVENT_ID` (This is used to identify the correct Salesforce campaign to add the contact to.)
|
||||
2. `campaignMemberStatus`: "Registered" « (Hardcoded)
|
||||
3. Clay (table: https://app.clay.com/workspaces/315782/workbooks/wb_0t4mlesfmwB8E6W357B/tables/t_0t90w56wNMpfCnCnfFm/views/gv_0t90w56hCPwZrpWtyC6) receives the payload.
|
||||
3. The `Event_key` is used to find the correct campaign.
|
||||
3. A [historical event](https://fleetdm.com/handbook/finance/gtm-architecture#historical-events-sfdc) gets created with a `relatedCampaign` matching the `Event_key`. Creating a historical event will also create the contact/account if it doesn't already exist.
|
||||
3. The name and email is used to pull the correct LinkedIn. If a LinkedIn profile is found, Clay updates the following data in Salesforce:
|
||||
3. Job title
|
||||
3. Mailing address: (City, State/Province, Country)
|
||||
3. Primary buying situation « TODO Document
|
||||
3. Role « TODO Document
|
||||
3. Sends the following message to the [#help-gitops-workshops](https://fleetdm.slack.com/archives/C0ALY0LJD39) Slack channel.
|
||||
|
||||
```
|
||||
NEW GITOPS REGISTRATION
|
||||
|
||||
_*`fullName`*_ signed up for `proviededNotes`
|
||||
|
||||
- CONTACT:
|
||||
_*`fullName`*_ (`finalLinkedInProfile`)
|
||||
`CRMLink`
|
||||
|
||||
- ACCOUNT:
|
||||
`Rating` - _*`accountName`*_ (`finalLinkedInCompanyUrl`)
|
||||
```
|
||||
|
||||
|
||||
### LinkedIn comments from tracked posts
|
||||
|
||||
@@ -19,6 +67,105 @@ We track certian social posts from the [LinkedIn company page](https://www.linke
|
||||
<img width="1410" height="1174" alt="image" src="https://github.com/user-attachments/assets/da2dccaa-e5ac-4373-9d93-d02b2a1bd8cd" />
|
||||
|
||||
|
||||
## Salesforce
|
||||
|
||||
|
||||
### Campaigns (SFDC)
|
||||
|
||||
TODO
|
||||
|
||||
#### For event campaigns (SFDC)
|
||||
|
||||
- **Event platform** (Picklist) – identifies the source platform
|
||||
- Options: `Eventbrite`, `Luma`, etc.
|
||||
|
||||
- **External event ID** (Text) – stores the platform-specific event identifier
|
||||
- Example: Eventbrite event ID `123456789`
|
||||
|
||||
- **Event key** (Formula) – composite key for matching integrations
|
||||
- Formula: `"Event platform"&"-"&"External event ID"`
|
||||
- Example output: `Eventbrite-123456789`
|
||||
|
||||
|
||||
### Historical events (SFDC)
|
||||
|
||||
Historical events (`fleet_website_page_views__c`) is a custom Salesforce object that records timestamped interactions a contact has with Fleet across the website and other channels. Each Historical event record is associated with both a **Contact** and an **Account** in Salesforce, creating a per-contact activity log that the GTM team uses to understand engagement over time.
|
||||
|
||||
|
||||
#### What historical events do
|
||||
|
||||
Historical events serve as the single source of truth for tracking how contacts engage with Fleet. Every time a meaningful interaction occurs — whether it's a website page view, a LinkedIn reaction, a newsletter subscription, or a form submission — a Historical event record is created in Salesforce. This gives GTM teams a chronological view of engagement that helps with:
|
||||
|
||||
- Measuring psychological progression of contacts and accounts.
|
||||
- Prioritizing accounts for [research](https://fleetdm.com/handbook/marketing#research-an-account) and outreach.
|
||||
- Identifying contacts that would benefit from a [POV conversation](https://fleetdm.com/handbook/company/go-to-market-operations#proof-of-value-pov).
|
||||
|
||||
|
||||
#### Historical event types and intent signals
|
||||
|
||||
There are two types of Historical event records:
|
||||
|
||||
| Event type | Description |
|
||||
|:---|:---|
|
||||
| **Website page view** | Logged when a signed-in user visits a page on fleetdm.com. Includes the page URL and, when available, the ad attribution that brought them to the site. |
|
||||
| **Intent signal** | Logged when a contact takes a specific high-value action. |
|
||||
| **Warm-up action** | Logged when a Fleetie takes a specific high-value action toward a contact. |
|
||||
|
||||
The following intent signals are tracked:
|
||||
|
||||
- Followed the Fleet LinkedIn company page
|
||||
- LinkedIn comment, share, or reaction
|
||||
- Fleet channel member in MacAdmins Slack or osquery Slack
|
||||
- Implemented a trial key
|
||||
- Signed up for a Fleet event
|
||||
- Registered for a conference
|
||||
- Engaged with Fleetie at event
|
||||
- Attended a Fleet happy hour
|
||||
- Starred, forked, or contributed to the fleetdm/fleet repo on GitHub
|
||||
- Subscribed to the Fleet newsletter
|
||||
- Attended a Fleet training course
|
||||
- Submitted the "Send a message" form
|
||||
- Scheduled a "Talk to us" or "Let's get you set up" meeting
|
||||
- Submitted the "GitOps workshop request" form
|
||||
- Signed up for a fleetdm.com account
|
||||
- Requested whitepaper download
|
||||
- Created a quote for a self-service Fleet Premium license
|
||||
|
||||
|
||||
#### How historical events are triggered
|
||||
|
||||
Historical event records are created automatically by the Fleet website backend (`website/api/helpers/salesforce/create-historical-event.js`). The helper is called from several code paths:
|
||||
|
||||
| Trigger | Code path | Event type |
|
||||
|:---|:---|:---|
|
||||
| Signed-in user views a page on fleetdm.com | `website/api/hooks/custom/index.js` | Website page view |
|
||||
| Clay webhook receives LinkedIn activity data | `website/api/controllers/webhooks/receive-from-clay.js` | Intent signal |
|
||||
| User subscribes to the Fleet newsletter | `website/api/controllers/create-or-update-one-newsletter-subscription.js` | Intent signal |
|
||||
| User submits the "Send a message" contact form | `website/api/controllers/deliver-contact-form-message.js` | Intent signal |
|
||||
| User requests a whitepaper download | `website/api/controllers/deliver-whitepaper-download-request.js` | Intent signal |
|
||||
| User creates a self-service quote | `website/api/controllers/customers/create-quote.js` | Intent signal |
|
||||
| User submits the "GitOps workshop request" form | `website/api/controllers/deliver-gitops-workshop-request.js` | Intent signal |
|
||||
| User signs up for a fleetdm.com account | `website/api/controllers/entrance/signup.js` | Intent signal |
|
||||
|
||||
In every case, the website first calls `updateOrCreateContactAndAccount` to ensure the contact and account exist in Salesforce, then calls `createHistoricalEvent` with the returned `salesforceContactId` and `salesforceAccountId`.
|
||||
|
||||
|
||||
#### Historical event fields
|
||||
|
||||
| Salesforce field API name | Description |
|
||||
|:---|:---|
|
||||
| `Contact__c` | Lookup to the related Contact record. |
|
||||
| `Account__c` | Lookup to the related Account record. |
|
||||
| `Event_type__c` | The type of event: "Website page view" or "Intent signal". |
|
||||
| `Intent_signal__c` | The specific intent signal (only for Intent signal events). |
|
||||
| `Content__c` | Free-text content associated with the event (e.g. a LinkedIn comment or form message). |
|
||||
| `Content_url__c` | URL of the content (e.g. a LinkedIn post URL). |
|
||||
| `Interactor_profile_url__c` | The LinkedIn profile URL of the person who interacted. |
|
||||
| `Page_URL__c` | The fleetdm.com page URL (only for Website page view events). |
|
||||
| `Website_visit_reason__c` | Ad attribution string, if the user arrived via an ad within the last 30 minutes. |
|
||||
| `Related_campaign__c` | Related Salesforce campaign, if applicable. |
|
||||
|
||||
> Historical event records are only created in the production environment. When deleting a contact's data (e.g. for a data deletion request), any related Historical event records associated with that contact are also automaticly deleted.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -735,106 +735,6 @@ create_sub_issue "6. Post-Mortem & Follow-Up"
|
||||
echo "Done."
|
||||
```
|
||||
|
||||
## **Connecting Eventbrite registrations to Salesforce campaigns (event ID key)**
|
||||
|
||||
#### **Purpose**
|
||||
|
||||
We need a reliable, repeatable way to associate each Eventbrite registration with the correct Salesforce Campaign **without adding any visible fields to the attendee experience**. This approach uses the Eventbrite **Event ID** as the canonical key to map registrations to Campaigns in Salesforce.
|
||||
|
||||
#### **Core idea**
|
||||
|
||||
Each Eventbrite event has a unique identifier (`event_id`). We store that identifier on the corresponding Salesforce Campaign. When a new registration occurs, our integration (e.g., Clay) reads the `event_id` from the registration payload, finds the matching Campaign, then creates/updates the Campaign Member.
|
||||
|
||||
This creates a clean 1:1 relationship:
|
||||
|
||||
**1 Eventbrite Event → 1 Salesforce Campaign → Many Campaign Members (registrants)**
|
||||
|
||||
#### **Why this approach**
|
||||
|
||||
* **Invisible to attendees:** No hidden checkout questions or user-facing “tags.”
|
||||
* **Stable and unambiguous:** Event IDs are unique and don’t depend on event names.
|
||||
* **Easy to operationalize:** Simple to document and enforce as a process.
|
||||
* **Scalable to other platforms:** The same pattern could be extended to Lu.ma later using a platform-specific ID or key (if we ever want to use Lu.ma)
|
||||
|
||||
### **Data model (Salesforce)**
|
||||
|
||||
#### **Campaign fields**
|
||||
|
||||
Add the following fields to **Campaign**:
|
||||
|
||||
* **Event platform** (Picklist) – identifies the source platform
|
||||
* Options: `Eventbrite`, `Luma`, etc.
|
||||
|
||||
* **External event ID** (Text) – stores the platform-specific event identifier
|
||||
* Example: Eventbrite event ID `123456789`
|
||||
|
||||
* **Event key** (Formula) – composite key for matching integrations
|
||||
* Formula: `"Event platform"&"-"&"External event ID"`
|
||||
* Example output: `Eventbrite-123456789`
|
||||
|
||||
The composite key pattern lets us use one matching field across platforms and avoid collisions if we ever want to use [Lu.ma](http://Lu.ma) or others.
|
||||
|
||||
|
||||
### **Operational workflow**
|
||||
|
||||
##### **1\) Capture the Eventbrite event ID**
|
||||
|
||||
The Event ID can be sourced from:
|
||||
|
||||
* Eventbrite event page URL (contains the ID), or
|
||||
* Event settings / Event details in Eventbrite, or
|
||||
* Eventbrite API / integration payload
|
||||
|
||||
**Important:** Do not use event name as a key (names can change and are not guaranteed unique).
|
||||
|
||||
##### **2\) Create the Salesforce Campaign**
|
||||
|
||||
* Create a Campaign for the event.
|
||||
* Set:
|
||||
* **Event platform**: `Eventbrite` (or the appropriate platform)
|
||||
* **External event ID**: `{event_id}` (e.g., `123456789`)
|
||||
* **Event key**: Auto-populated by formula (no manual entry needed)
|
||||
|
||||
##### **3\) Integration logic (Clay)**
|
||||
|
||||
When Clay receives a new Eventbrite registration/attendee record:
|
||||
|
||||
1. **Extract** `event_id` from the Eventbrite payload
|
||||
* Clay matches the `Event key` from Zapier to the corresponding campaign.
|
||||
2. **Find Campaign** in Salesforce where:
|
||||
* *`Event key = Eventbrite-{event_id}`*
|
||||
3. **Create/update Person Record**
|
||||
* Match/Create the Contact
|
||||
4. **Create/Update Campaign Member**
|
||||
* Add the person as a Campaign Member on the matched Campaign
|
||||
* Optionally set Campaign Member Status (e.g., `Registered`, `Attended`, `No Show`) if we later sync those states
|
||||
|
||||
|
||||
### **Assumptions / scope**
|
||||
* **One ticket type per Campaign** (i.e., we do not need ticket-type-level mapping).
|
||||
* **One event maps to exactly one Salesforce Campaign**.
|
||||
* We are focusing on **registrations** (Campaign Members).
|
||||
|
||||
### **Governance & quality controls**
|
||||
To keep the system clean and prevent broken mappings:
|
||||
|
||||
* **Required fields:** Ensure Campaigns intended for Eventbrite syncing have `Event Key` populated.
|
||||
* **Uniqueness guardrails:** Prevent multiple Campaigns from sharing the same Event Key (via process, reporting, or validation rules).
|
||||
* **Monitoring:** Have a Clay/Salesforce report for “registrations received with no matching Campaign” to catch missing IDs early.
|
||||
|
||||
### **(FUTURE) Extending this to Lu.ma (future)**
|
||||
|
||||
If we adopt Lu.ma later, we can follow the same model:
|
||||
|
||||
* Set **Event platform** to `Luma`
|
||||
* Set **External event ID** to Lu.ma’s stable event identifier (ID or slug)
|
||||
* **Event key** formula automatically generates the composite key: `Luma-{external_event_id}`
|
||||
* Clay uses this Event key to map inbound registrations to the correct Campaign
|
||||
* No attendee-visible fields required.
|
||||
|
||||
### **Summary**
|
||||
|
||||
This approach “connects” Eventbrite to Salesforce Campaigns by using the **Eventbrite Event ID as the system-of-record key**. Salesforce Campaigns store that key, and Clay uses it to automatically route registrations to the right Campaign and create/update Campaign Members—cleanly, invisibly, and in a way that can later support additional event platforms.
|
||||
|
||||
|
||||
<meta name="maintainedBy" value="johnjeremiah">
|
||||
|
||||
Reference in New Issue
Block a user