fleet/handbook/marketing/event-execution.md
johnjeremiah 82355473f8
Adding Eventbrite-SFDC connection details (#41336)
Adds section about how to integrate Eventbrite to sfdc campaigns with
clay
2026-03-10 12:27:25 -05:00

36 KiB
Raw Blame History

Fleet events

We sponsor and participate in events so that we can support, connect, engage, and grow the Fleet community. We need to be thoughtful about what events we sponsor or host, and we need to be disciplined in how we run events so we can be efficient and effective.

How to propose an event:

It's simple Open an issue: Propose an event

Current Events

QTR Event Name Date Location
Q1 GitOps Workshop 2026-03-12 Chicago
Q1 * Gartner Digital Workspace 2026-03-23 San Diego
Q2 GitOps Workshop 2026-04-07 Minneapolis
Q2 HappyHour-Apple 2026-04-TBD Austin
Q2 * MACAd-UK 2026-04-21 Brighton, UK
Q2 * MacAdmins Europe 2026-04-30 Leiden, Netherlands
Q2 * MacDevOpsYVR 2026-06-25 Montreal
Q2 * PSU MacAdmin 2026-07-07 PennState
Q3 * JNUC 2026 Gurrilla 2026-09-23 Kansas City
Q4 * MacSysAdmin 2026-09-29 XX

Event Process

There are three phases to running an event at FleetDM,

  • Phase 1: Propose, review and approve new events
  • Phase 2: Manage and and execute approved events
  • Phase 3: Event postgame

Phase 1 Propose, review and approve new events

Objective: To ensure that the organization is aligned with the investment of time and resources to execute an event This process is managed through Fleet Issues and is summarized in a detailed tracking spreadsheet.

See the section "Settle event strategy" below for the process.

Settle event strategy (approve proposed events)

Anyone at Fleet can propose a future event. Fleet's Head of DemandGen serves as the project manager for managing the event approval process. Events are settled in advance to provide ample time for strategy and planning. This includes any event that Fleet pays to attend or sponsor.

  • Field/sales events and GitOps workshops are settled 1 sprint in advance.
  • Conferences are settled 1 quarter in advance.

Once events have been settled for a particular time frame, i.e, decided in a previous event strategy session, Fleet does not make changes except in extreme circumstances.

  1. Add all upcoming proposed in issues using the template (Propose an event: EVENT_NAME - CITY - YYYY-MM-DD). Approval is tracked and recorded in the "🫧 Proposed events (not yet settled)" tab of the 🎪 Events spreadsheet (confidential doc).
  2. Proposed events will include the following information:
  • Event Priority (Scale 1 - 10) where 1 is a top priority
  • Event Name
  • Event Location
  • Event DRI
  • Event Dates
  • Type of Event
  • Theme
  • Event Registration
  • Who from Fleet will attend?
  • Which talk proposal will Fleet submit?
  • Estimated budget, including sponsorship or airfare, and lodging for attendees.
  1. Set up and attend a 30m meeting with the CMO, Head of Demand Generation, or Manager of Training and Enablement.
  • First, during this meeting, Marketing will decide which field/sales events and GitOps workshops Fleet will execute in the following sprint.
  • Next, Marketing will decide which conferences in the following quarter the company will invest time or money into.
  1. After the meeting, the Content Specialist will communicate the settled events by
  • Moving all settled events to the "All 🎪 Official (planned & settled events)" tab of the 🎪 Events spreadsheet (confidential doc).
  • Using the following template, post a message in the #oooh-events Slack channel.
  1. Close all proposed event issues that weren't able to be prioritized with a comment explaining why.

Phase 2 Manage and and execute approved events

Objective To efficiently plan, organize,track and complete the tasks in order to execute an event. This covers everything from once an event is approved to when the event is finished. From detail planning to promotion, staff assignments to logistics, events can be complicated projects that need focused attention.

Event execution needs to plan and track the detail decisions supporting:

  1. All Event Logistics. (Location, Venue, Start and End Date/Time, Event Website, Shipping, Staff schedules, costs, and more)
  2. Speaking sessions: (Location, time, talk metadata(title, abstract, etc), av requirements, and more)
  3. Event Pre Promotion: (landing page, blog, social, email, customers, prospects, etc)
  4. Lead capture plans and process: (scanning process, qualifying questions)
  5. Booth: Design, messaging, staffing hours and assignments, attire, swag, power, etc.)
  6. Key vendor relationships: (Event Organizer, booth builder, site logisitcs, scanning tech, av tech)

This will be managed in a structure central document for each event so that attendees and organizers have a central place to find information and collaborate.

[Planning Doc/Tracking Template](https://docs.google.com/document/d/1Td1XtFClRlOMDuoojXUkJvU8f6MUEjsBacMVRqEJbQQ/edit?tab=t.uych0uenb12p#heading=h.qhf7mkrao68w0

Phase 3 Event postgame

Objective To consistently wrap up an event, gather lessons learned, and ensure the organization follows through with our new relationships.

After the event there are three important activites that need to be completed.

  1. Update CRM: The CRM is our single source of truth about our relationships. So, it is critical that all the information from what happened at the event is promptly updated in the CRM.
  2. Follow up: When we make new friends and connections at an event, we must be prompt in follow through and connecting with them after the event. The CRM is the main way to prompt the right person at Fleet to reach back out and follow up.
  3. Post Mortem: Learn and improve from the previous event. (gather feedback, review lessons learned, and update processes and strategy)

Event execution process

This page outlines the execution process for Fleet events. It builds upon our general event strategy and goals outlined in the Fleet events section of the handbook.

Tools and single source of truth

To keep event planning organized, we separate event information from actionable tasks:

  1. Google Docs (Event overview): Event status and information are tracked in a single event overview document. This is the single source of truth (SSOT) for the event. It includes key questions and answers, as well as working notes. It does not contain tasks. Events Working Doc
  2. GitHub issues: Event tasks are tracked in GitHub. We use parent/child issues for specific tasks to execute an event, tracking the execution from the initial planning stages all the way to completion.

All child tasks in GitHub (e.g., draft and finalize talk title/abstract, design booth, order swag, ship booth kit, promote event) should reference back to the event overview document.

GitHub labels

We use GitHub labels to organize the difference between overall event issues and detailed execution tasks, allowing us to filter and track between overview issues only, and specific events only. The color coding will help us to visually tell the difference between events. Note the specific event labels have 6 possible colors defined. These should get re-used, as events are completed.

Label Color Hex Code Definition (When to use it)
:mktg-event Orange #F97316 The standard label for all events.
:mktg-event:tp Dark Rust #9A3412 Indicates this issue is part of event execution in general.
:mktg-event:overview Light Peach #FFDED2 The parent issue for the event.
:mktg-event:detail Amber #F59E0B Used for detailed tasks (children) of the overall event.
:mktg-event:YYMM-eventname-city Sunset Red #EF4444 A first custom label created for each specific event to group a family of tasks together.
:mktg-event:YYMM-eventname-city Tangerine #FF8A65 2nd color custom lable for specific events
:mktg-event:YYMM-eventname-city Marigold #FBBF24 3rd color custom lable for specific events
:mktg-event:YYMM-eventname-city Terracotta #C2410C 4th color custom lable for specific events
:mktg-event:YYMM-eventname-city Salmon #FA8072 5th color custom lable for specific events
:mktg-event:YYMM-eventname-city Brick #B91C1C 6th color custom lable for specific events

Event plans

We utilize two general event plans, which act as templates depending on the scale and type of the event:

  1. Conference: Used for large conferences and events where we have a booth, speaking slots, lead scanning, and other major logistical needs.
  2. Workshop/Happy hour: Used for our GitOps workshop series (which often includes happy hours). This smaller template can be used for the full workshop or for bespoke, standalone happy hours.

Execution process

Once an event is approved, a Marketing directly responsible individual (DRI) is assigned. From there, the process is divided between the Marketing DRI and the Onsite DRI.

Marketing DRI responsibilities

  • Create the event overview planning doc.
  • Create the parent and child execution issues in GitHub.
  • Assign the execution issues to themselves or the Onsite DRI for parts of the plan. (In many cases, a workshop has been planned locally by the Account Executive; this is where specific issues would be assigned out).
  • Ensure leads are uploaded and properly accounted for in Salesforce (SFDC) post-event.

Onsite DRI responsibilities

  • Manage the details of the facility.
  • Set up and configure the booth, swag, and lead capture tools.
  • Coordinate AV, facilities, and catering.
  • Ensure leads and attendance are actively captured during the event.
  • Pack up the event kit and ship it back.
  • Coordinate with the Marketing DRI to get leads uploaded and processed.

Definition of done

An event's execution is not complete until the Definition of Done is met: the Event Overview Doc must be fully updated with post-event outcomes, notes, and final details.

How to automate event creation

Since the tracking process uses github issues and subissues to track tasks, it can be tedious to create the structure for a new event. Here are the steps to automate the creation of the event execution issues in github.

Setup

We will use a local script that executes commands on the local GitHub command line interface (CLI). In order to get started you need to have the GitHub CLI installed.

  1. Install Homebrew. Homebrew is a package installer and the simplest way to get the GitHub CLI installed..

    1. Navigate to https://brew.sh/ and copy the installation script.
    2. Open a terminal window, paste and run the script. You will be prompted for your local password
  2. Install GitHub CLI (GH CLI) Using homebrew, tell it to install the GitHub command line interface brew install gh

  3. Authenticate / connect with GitHub

    1. Enter the command: gh auth login and follow the steps to authenticate with the repository
  4. Update the CLI permissions to include projects

    1. Enter the command gh auth refresh -s project and follow the steps to authenticate.

TODO - add a test set up section where a user has a simple issue script they test

Event Template Process and Script

Creating a new event group is now simple.

  1. copy the script below and save as NewEvent.sh
  2. Edit the script.

First - CHANGE THESE THREE THINGS.

Nothing else needs to change

  • EVENT_SLUG - will be the name of the event and part of the label for the event
  • PLANNING_DOC_URL - is the link to the google doc where we're keeping the latest status of the event plans
  • REQUEST_ISSUE - is thte number of the issue that proposed the event

For example:

EVENT_SLUG="2606-MacDevOpsYVR-Montreal"
PLANNING_DOC_URL="https://docs.google.com/document/d/1Td1XtFClRlOMDuoojXUkJvU8f6MUEjsBacMVRqEJbQQ/edit?tab=t.afz38t4pwdka"
REQUEST_ISSUE = "#14599"   

Save the changed file NewEvent.sh

And then execute the script. ./NewEvent.sh

That's it. This should create the events in GitHub to manage the event.

here's the script

#!/bin/bash
# --- Event specifics / details - change this
EVENT_SLUG="2606-MacDevOpsYVR-Montreal"
PLANNING_DOC_URL="https://docs.google.com/document/d/1Td1XtFClRlOMDuoojXUkJvU8f6MUEjsBacMVRqEJbQQ/edit?tab=t.afz38t4pwdka"
REQUEST_ISSUE = "#14599"

# No need to change anything else to run the script

# --- Static Configuration ---
ORG="fleetdm"
REPO="confidential"
PROJECT_NUMBER="94"

# 1. Define Labels (Fixed with leading colons)
NEW_LABEL=":mktg-event:${EVENT_SLUG}"
PARENT_LABELS=":mktg-event,:mktg-event:overview,:mktg-event:tp"
CHILD_LABELS=":mktg-event,:mktg-event:detail,:mktg-event:tp"

# Ensure the specific event label exists
echo "1. Ensuring label '${NEW_LABEL}' exists..."
# gh label create "$NEW_LABEL" --repo "$ORG/$REPO" --color "1D76DB" --force >/dev/null 2>&1 || true
gh label create "$NEW_LABEL" --repo "$ORG/$REPO" --force >/dev/null 2>&1 || true


# ==========================================
# STEP 1: CREATE THE PARENT ISSUE
# ==========================================
echo "2. Creating Parent Issue (Overview)..."

PARENT_TITLE="${EVENT_SLUG} Execution Overview"

# Using a Heredoc for clean, WYSIWYG Markdown formatting
PARENT_BODY=$(cat << EOF
Master tracking issue for ${EVENT_SLUG}.

EXECUTION for request $REQUEST_ISSUE

## Executive Snapshot & Key Decisions 

Use this section for a quick overview. If someone only reads this part, they should understand the scope and scale of our presence. 

| Category | Details |
|---|---|
| Event Name | [e.g., KubeCon NA 2026] |
| Dates | [Start Date] to [End Date] |
| Location | [City, State, Venue Name] |
| Event Website | [Link to official site] |
| Budget Estimate | [Total estimated cost] |
| Primary Goal | [e.g., Lead Gen (500 scans), Brand Awareness, Recruiting] |
| Booth Size | [e.g., 10x20, Island, Tabletop] |
| Speaking Slot? (details below) | Yes or No |
| Workshop? | Yes or No |
| DRI | [Name of person responsible] |
| Onsite DRI | [Name of person responsible] |
| Planing Doc | $PLANNING_DOC_URL |

- [ ] Finalize sponsorship agreements
- [ ] Assign issues/tasks

## Progress Tracker
- [ ] 1. Speaking Session & Workshop Details
- [ ] 2. Promotion & Marketing Plan
- [ ] 3. Booth Strategy & Messaging
- [ ] 4. Staffing & Travel Logistics
- [ ] 5. Execution, Logistics & Swag
- [ ] 6 Lead Capture & Follow-Up Strategy
- [ ] 7. Post-Mortem & ROI Analysis
EOF
)

# Create Parent
PARENT_URL=$(gh issue create \
  --repo "$ORG/$REPO" \
  --title "$PARENT_TITLE" \
  --body "$PARENT_BODY" \
  --label "$PARENT_LABELS,$NEW_LABEL")

# CRITICAL CHECK: Did the parent issue actually get created?
if [ -z "$PARENT_URL" ]; then
    echo "❌ ERROR: Failed to create Parent Issue. Check if labels exist in the repo."
    exit 1
fi

# Extract Issue Number from URL
PARENT_NUM=$(echo "$PARENT_URL" | awk -F/ '{print $NF}')
echo "   ✅ Parent Created: #$PARENT_NUM"

# Get the Global Node ID of the Parent (Needed for GraphQL linking)
PARENT_NODE_ID=$(gh api graphql -f query='
  query($owner:String!, $repo:String!, $number:Int!) { 
    repository(owner:$owner, name:$repo) { 
      issue(number:$number) { id } 
    } 
  }' -f owner="$ORG" -f repo="$REPO" -F number="$PARENT_NUM" --jq '.data.repository.issue.id')

if [ -z "$PARENT_NODE_ID" ] || [ "$PARENT_NODE_ID" == "null" ]; then
    echo "❌ ERROR: Could not fetch GraphQL Node ID for Parent #$PARENT_NUM"
    exit 1
fi

echo "   🔹 Parent Node ID: $PARENT_NODE_ID"

# Add Parent to Project
gh project item-add "$PROJECT_NUMBER" --owner "$ORG" --url "$PARENT_URL" >/dev/null 2>&1


# ==========================================
# STEP 2: HELPER FUNCTION FOR CHILD ISSUES
# ==========================================
# This function handles the creation and linking of all sub-issues
create_sub_issue() {
    local TITLE="$1"
    local BODY="$2"
    
    CHILD_URL=$(gh issue create \
      --repo "$ORG/$REPO" \
      --title "$TITLE: ${EVENT_SLUG}" \
      --body "$BODY" \
      --label "$CHILD_LABELS,$NEW_LABEL")
      
    if [ -n "$CHILD_URL" ]; then
        CHILD_NUM=$(echo "$CHILD_URL" | awk -F/ '{print $NF}')
        
        # Get Child Node ID
        CHILD_NODE_ID=$(gh api graphql -f query='
          query($owner:String!, $repo:String!, $number:Int!) { 
            repository(owner:$owner, name:$repo) { 
              issue(number:$number) { id } 
            } 
          }' -f owner="$ORG" -f repo="$REPO" -F number="$CHILD_NUM" --jq '.data.repository.issue.id')

        echo "   ✅ Created Child: #$CHILD_NUM ($TITLE)"
        
        # Link as Sub-Issue via GraphQL
        LINK_RESULT=$(gh api graphql -f query='
          mutation($parentId: ID!, $childId: ID!) {
            addSubIssue(input: {issueId: $parentId, subIssueId: $childId}) {
              clientMutationId
            }
          }
        ' -f parentId="$PARENT_NODE_ID" -f childId="$CHILD_NODE_ID" 2>&1)
        
        if [[ $? -eq 0 ]]; then
             echo "      🔗 Linked as Sub-issue to Parent #$PARENT_NUM"
        else
             echo "      ⚠️ Failed to link. Error details:"
             echo "$LINK_RESULT"
        fi

        # Add to Project
        gh project item-add "$PROJECT_NUMBER" --owner "$ORG" --url "$CHILD_URL" >/dev/null 2>&1
    else
        echo "   ❌ Failed to create child: $TITLE"
    fi
}


# ==========================================
# STEP 3: DEFINE & CREATE CHILD ISSUES
# ==========================================
echo "3. Creating and Linking Child Issues..."

# --- Child 1 ---
BODY=$(cat << EOF
**Description**
Track all details, deadlines, and requirements for any speaking slots or workshops we are hosting before, during, or after the event.

- [ ] Confirm speaking session details (Title, Speaker, Date/Time, Room)
- [ ] Submit Abstract Link and AV Requirements
- [ ] Confirm workshop hosting and timing
- [ ] Update Workshop Planning Doc, Registration Link, and Capacity
- [ ] Update the $PLANNING_DOC_URL
EOF
)
create_sub_issue "1. Speaking Session & Workshop Details" "$BODY"


# --- Child 2 ---
BODY=$(cat << EOF
**Description**
Manage how we are driving traffic to our booth, session, or workshop.

- [ ] Schedule Pre-Event Email Blast 
- [ ] Schedule LinkedIn and Twitter/X Posts
- [ ] Create Speaker Promo Graphics and Blog Post 
- [ ] Assign Customer Invites
- [ ] Assign Live Social Coverage during event 
- [ ] Schedule Event App Push Notification
- [ ] Update the $PLANNING_DOC_URL
EOF
)
create_sub_issue "2. Promotion & Marketing Plan" "$BODY"


# --- Child 3 ---
BODY=$(cat << EOF
**Description**
Define the core purpose, layout, and messaging for our physical footprint on the show floor.

- [ ] Document Booth Number and Exhibit Hall Hours 
- [ ] Define Core Messaging/Theme 
- [ ] List Key Demos 
- [ ] Document Key Requirements (internet, scanners, monitors)
- [ ] Update the $PLANNING_DOC_URL
EOF
)
create_sub_issue "3. Booth Strategy & Messaging" "$BODY"


# --- Child 4 ---
BODY=$(cat << EOF
**Description**
Manage who is going, where they are staying, and when they are working the booth.

- [ ] Assign Staff Manager and Attire 
- [ ] Select Suggested Hotel 
- [ ] Set Arrival and Departure Requirements 
- [ ] Complete Staff Assignments table 
- [ ] Create Booth Staffing Schedule
- [ ] Update the $PLANNING_DOC_URL
EOF
)
create_sub_issue "4. Staffing & Travel Logistics" "$BODY"


# --- Child 5 ---
BODY=$(cat << EOF
**Description**
This section is for the operations team to handle on-site setup, booth build, and shipping.

- [ ] Track Shipping & Handling deadlines and tracking numbers 
- [ ] Create Return Shipping Label 
- [ ] Confirm Booth Vendor, Graphics Deadline, and Furniture/Electrical 
- [ ] Order Premium Swag, General Swag, and Raffle/Contest items 
- [ ] Complete Key Points of Contact table
- [ ] Update the $PLANNING_DOC_URL
EOF
)
create_sub_issue "5. Execution, Logistics & Swag" "$BODY"


# --- Child 6 ---
BODY=$(cat << EOF
**Description**
Crucial for ROI. Track how we capture data and what happens next.

- [ ] Define Capture Mechanics, Method, and Device Rental 
- [ ] Define Incentive to Scan 
- [ ] Write Qualifying Questions for Booth Staff 
- [ ] Assign Lead Upload Owner and SLA 
- [ ] Define Follow Up Strategy and Nurture Sequence
- [ ] Update the $PLANNING_DOC_URL
EOF
)
create_sub_issue "6. Lead Capture & Follow-Up Strategy" "$BODY"


# --- Child 7 ---
BODY=$(cat << EOF
**Description**
To be filled out within 1 week of event conclusion to analyze performance and ROI.

- [ ] Record The Numbers (Leads, MQLs, Spend, CPL) 
- [ ] Complete Retrospective (What went well/wrong) 
- [ ] Document Competitor Intel 
- [ ] Upload Photo Archive
- [ ] Update the $PLANNING_DOC_URL
EOF
)
create_sub_issue "7. Post-Mortem & ROI Analysis" "$BODY"

echo "Done."

How to automate workshop creation

The workshop tracking process uses the same GitHub parent/child issue structure as conferences, but with a smaller, workshop-specific set of tasks. Use this script instead of the conference script when running a GitOps workshop (with or without a happy hour).

If you haven't set up the GitHub CLI yet, follow the Setup steps in the How to automate event creation section above before continuing.

Workshop template process and script

Creating a new workshop issue group is straightforward.

  1. Copy the script below and save it as NewWorkshop.sh
  2. Edit the script.

First — CHANGE THESE THREE THINGS.

Nothing else needs to change.

  • WORKSHOP_SLUG — will be the name of the workshop and part of the GitHub label
  • PLANNING_DOC_URL — link to the Google Doc where the latest workshop status is tracked
  • REQUEST_ISSUE — the number of the issue that proposed the workshop

For example:

WORKSHOP_SLUG="2606-GitOps-Workshop-Montreal"
PLANNING_DOC_URL="https://docs.google.com/document/d/YOUR_PLANNING_DOC_ID/edit"
REQUEST_ISSUE="#00000"

Save the changed file NewWorkshop.sh, then run it:

./NewWorkshop.sh

This will create a parent overview issue and six linked child issues in GitHub to manage the full workshop lifecycle.

Here's the script:

#!/bin/bash
# --- Workshop specifics / details - change this
WORKSHOP_SLUG="2606-GitOps-Workshop-Montreal"
PLANNING_DOC_URL="https://docs.google.com/document/d/YOUR_PLANNING_DOC_ID/edit"
REQUEST_ISSUE="#00000"

# No need to change anything else to run the script

# --- Static Configuration ---
ORG="fleetdm"
REPO="confidential"
PROJECT_NUMBER="94"

# 1. Define Labels
NEW_LABEL=":mktg-event:${WORKSHOP_SLUG}"
PARENT_LABELS=":mktg-event,:mktg-event:overview,:mktg-event:tp"
CHILD_LABELS=":mktg-event,:mktg-event:detail,:mktg-event:tp"

# Temp file for issue bodies (avoids heredoc/subshell parenthesis conflicts)
BODY_FILE=$(mktemp)
cleanup() { rm -f "$BODY_FILE"; }
trap cleanup EXIT

# Ensure the specific workshop label exists
echo "1. Ensuring label '${NEW_LABEL}' exists..."
gh label create "$NEW_LABEL" --repo "$ORG/$REPO" --force >/dev/null 2>&1 || true


# ==========================================
# STEP 1: CREATE THE PARENT ISSUE
# ==========================================
echo "2. Creating Parent Issue (Overview)..."

PARENT_TITLE="${WORKSHOP_SLUG} Workshop Overview"

cat > "$BODY_FILE" << EOF
Master tracking issue for the GitOps Workshop: ${WORKSHOP_SLUG}.

EXECUTION for request $REQUEST_ISSUE

## Executive Snapshot & Key Decisions

Use this section for a quick overview. If someone only reads this part, they should understand the scope and scale of our workshop.

| Category | Details |
|---|---|
| Workshop Name | [e.g., GitOps Workshop — Atlanta] |
| Date | [Date] |
| Location | [City, Venue Name] |
| Capacity | [Max Attendees] |
| Lead Instructor | @[Name] |
| Onsite DRI | @[Name] |
| Marketing DRI | @[Name] |
| Happy Hour? | Yes or No |
| Planning Doc | $PLANNING_DOC_URL |

- [ ] Confirm workshop date and venue
- [ ] Assign issues/tasks to DRIs

## Progress Tracker
- [ ] 1. Workshop Promotion & Registration Launch
- [ ] 2. Venue Selection & Logistics
- [ ] 3. Happy Hour Planning & Promotion
- [ ] 4. Workshop Catering
- [ ] 5. Travel & Staffing
- [ ] 6. Post-Mortem & Follow-Up
EOF

PARENT_URL=$(gh issue create \
  --repo "$ORG/$REPO" \
  --title "$PARENT_TITLE" \
  --body-file "$BODY_FILE" \
  --label "$PARENT_LABELS,$NEW_LABEL")

if [ -z "$PARENT_URL" ]; then
    echo "❌ ERROR: Failed to create Parent Issue. Check if labels exist in the repo."
    exit 1
fi

PARENT_NUM=$(echo "$PARENT_URL" | awk -F/ '{print $NF}')
echo "   ✅ Parent Created: #$PARENT_NUM"

PARENT_NODE_ID=$(gh api graphql -f query='
  query($owner:String!, $repo:String!, $number:Int!) {
    repository(owner:$owner, name:$repo) {
      issue(number:$number) { id }
    }
  }' -f owner="$ORG" -f repo="$REPO" -F number="$PARENT_NUM" --jq '.data.repository.issue.id')

if [ -z "$PARENT_NODE_ID" ] || [ "$PARENT_NODE_ID" == "null" ]; then
    echo "❌ ERROR: Could not fetch GraphQL Node ID for Parent #$PARENT_NUM"
    exit 1
fi

echo "   🔹 Parent Node ID: $PARENT_NODE_ID"
gh project item-add "$PROJECT_NUMBER" --owner "$ORG" --url "$PARENT_URL" >/dev/null 2>&1


# ==========================================
# STEP 2: HELPER FUNCTION FOR CHILD ISSUES
# ==========================================
create_sub_issue() {
    local TITLE="$1"
    # Body is already written to $BODY_FILE by the caller

    CHILD_URL=$(gh issue create \
      --repo "$ORG/$REPO" \
      --title "$TITLE: ${WORKSHOP_SLUG}" \
      --body-file "$BODY_FILE" \
      --label "$CHILD_LABELS,$NEW_LABEL")

    if [ -n "$CHILD_URL" ]; then
        CHILD_NUM=$(echo "$CHILD_URL" | awk -F/ '{print $NF}')

        CHILD_NODE_ID=$(gh api graphql -f query='
          query($owner:String!, $repo:String!, $number:Int!) {
            repository(owner:$owner, name:$repo) {
              issue(number:$number) { id }
            }
          }' -f owner="$ORG" -f repo="$REPO" -F number="$CHILD_NUM" --jq '.data.repository.issue.id')

        echo "   ✅ Created Child: #$CHILD_NUM ($TITLE)"

        LINK_RESULT=$(gh api graphql -f query='
          mutation($parentId: ID!, $childId: ID!) {
            addSubIssue(input: {issueId: $parentId, subIssueId: $childId}) {
              clientMutationId
            }
          }
        ' -f parentId="$PARENT_NODE_ID" -f childId="$CHILD_NODE_ID" 2>&1)

        if [[ $? -eq 0 ]]; then
            echo "      🔗 Linked as Sub-issue to Parent #$PARENT_NUM"
        else
            echo "      ⚠️ Failed to link. Error details:"
            echo "$LINK_RESULT"
        fi

        gh project item-add "$PROJECT_NUMBER" --owner "$ORG" --url "$CHILD_URL" >/dev/null 2>&1
    else
        echo "   ❌ Failed to create child: $TITLE"
    fi
}


# ==========================================
# STEP 3: DEFINE & CREATE CHILD ISSUES
# ==========================================
echo "3. Creating and Linking Child Issues..."

# --- Child 1 ---
cat > "$BODY_FILE" << EOF
**Description**
Get the main workshop event live to start gathering leads.

Note: You can launch with "Venue TBD" or "Downtown [City]" if the specific room isn't booked yet.

**Who:** Marketing DRI

- [ ] Create Workshop Landing Page on Eventbrite or Luma
- [ ] Schedule Email Blast to target audience
- [ ] Schedule LinkedIn and Twitter/X posts and request speaker graphics
- [ ] Notify AEs and Partners to drive personal invites
- [ ] Monitor registration — watch for waitlists or low attendance and adjust promo if needed
- [ ] Update the $PLANNING_DOC_URL with "Promotion & Marketing Plan" details and registration link
EOF
create_sub_issue "1. Workshop Promotion & Registration Launch"


# --- Child 2 ---
cat > "$BODY_FILE" << EOF
**Description**
Secure the physical space for the workshop. Once confirmed, notify attendees.

**Who:** Onsite DRI

- [ ] Secure venue — confirm availability for workshop date
- [ ] Power audit — confirm every seat has access to power, or plan to bring extension cords
- [ ] AV check — confirm projector/HDMI availability
- [ ] Update Workshop Landing Page with the specific venue name and address
- [ ] Update the $PLANNING_DOC_URL with "Venue Details" section
EOF
create_sub_issue "2. Venue Selection & Logistics"


# --- Child 3 ---
cat > "$BODY_FILE" << EOF
**Description**
Plan the post-workshop networking. This is treated as a separate event to allow for broader networking — invite people who couldn't make the workshop itself.

**Who:** Onsite DRI

- [ ] Secure venue — find a bar/restaurant within a 5-minute walk of the workshop area
- [ ] Confirm menu/tab — decide on Open Bar vs. Fixed Menu and set the budget cap
- [ ] Create Happy Hour registration page on Eventbrite or Luma and promote separately
- [ ] Schedule LinkedIn/Twitter posts promoting the Happy Hour
- [ ] Update the $PLANNING_DOC_URL with "Post-Event Happy Hour" section including venue and registration link
EOF
create_sub_issue "3. Happy Hour Planning & Promotion"


# --- Child 4 ---
cat > "$BODY_FILE" << EOF
**Description**
Finalize in-room food and drink orders.

Wait to complete this until ~1 week before the event so you have an accurate headcount.

**Who:** Onsite DRI

- [ ] Check registration count — confirm headcount from Registered and Waitlist to avoid over-ordering
- [ ] Order food and drinks
- [ ] Update the $PLANNING_DOC_URL with "Catering" section and order details
EOF
create_sub_issue "4. Workshop Catering"


# --- Child 5 ---
cat > "$BODY_FILE" << EOF
**Description**
Ensure the instructor and support staff can get to the city and are prepared for the event.

**Who:** Onsite DRI, Marketing DRI, Attendees

- [ ] Book flights for Lead Instructor and TA if traveling
- [ ] Book hotel — ensure proximity to the venue
- [ ] Confirm staffing assignments and attire
- [ ] Update the $PLANNING_DOC_URL with "Staff Travel" and "Logistics" sections
EOF
create_sub_issue "5. Travel & Staffing"


# --- Child 6 ---
cat > "$BODY_FILE" << EOF
**Description**
To be completed within 48 hours after the event. Close the loop on leads and technical feedback.

- [ ] Calculate stats — record Registered, Attended, and No-Show rates for both Workshop and Happy Hour
- [ ] Log technical issues — document any WiFi drops or firewall blockers for future reference
- [ ] CRM upload — upload attendee list to Salesforce/HubSpot
- [ ] Send follow-up email with slides and repo links
- [ ] Update the $PLANNING_DOC_URL with completed "Post-Mortem & Follow-Up" section
EOF
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 dont 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 field to Campaign:

  • Event Key (Text) = composite key of the platform and event id:
    • eventbrite:{event_id}
    • luma:{event_id}

The composite key pattern lets us use one matching field across platforms and avoid collisions if we ever want to use 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 Key eventbrite:{event_id}
3) Integration Logic (Clay)

When Clay receives a new Eventbrite registration/attendee record:

  1. Extract event_id from the Eventbrite payload
    • Clay knows its coming from Eventbrite, so it looks up the event_id and then is able to create the composite key “eventbrite:{event_id}”
  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:

  • Store Lu.mas stable event identifier (ID or slug) on the Salesforce Campaign.
  • Use 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.