---
name: conference-prep
description: >-
  Conference Prep: a workflow that finds conference attendees or speakers on
  LinkedIn, filters them against your ICP, enriches with MoltSets, and exports
  to HubSpot. Given any conference name or URL, runs an Apify LinkedIn post
  search (actor: harvestapi/linkedin-post-search) for people who publicly
  posted they're attending or speaking, matches them against Beezi AI's US/UK
  services-firm ICP, tiers them Strong/Partial/Not-ICP, then enriches top fits
  via MoltSets and creates linked Contacts and Companies in HubSpot. Outputs a
  pre-conference target list (before) or a warm follow-up list (after). ALWAYS
  ask two things first: (1) which conference, (2) preparing before or
  following up after. Triggers: "conference prep", "prep me for [event]",
  "who's going to [conference]", "find attendees of [event]", "build a target
  list for [event]", "match attendees to my ICP", "follow up after
  [conference]", "who did I miss at [event]". Also trigger when a summit,
  expo, or conference is mentioned with attendees or speakers.
---

# Conference Prep

A four-stage workflow for any conference or event:

**1. Find** attendees/speakers on LinkedIn (posts only) → **2. Filter** against
the Beezi AI ICP → **3. Enrich** with MoltSets → **4. Export** to HubSpot.

This skill analyzes LinkedIn **posts** and their **authors** only — people who
publicly wrote that they're attending or speaking. It does not scrape post
engagers (likers/commenters).

**Apify actor used throughout: `harvestapi/linkedin-post-search`** (HarvestAPI
LinkedIn Post Search) — the same actor as the comment-finder skill.

---

## Step 0 — Ask the two intake questions FIRST (do not skip)

Before doing anything else, ask the user these two questions. If the
`ask_user_input` tool is available, use it (tappable buttons); otherwise ask in
chat. Do **not** run any search until both are answered.

**Q1 — Which conference?**
Ask for the conference name, and a URL or the city/dates if they have it. If they
give only a name, you'll resolve details in Step 1.

**Q2 — Are you preparing *before* the conference, or following up *after*?**
Options:
- **Before** → build a target/meeting list: who to seek out, book, or DM ahead.
- **After** → build a warm follow-up list: people who attended, referencing their
  post/session.

Everything downstream (query recency, output columns, message angles) branches on
this answer. Capture both before continuing.

---

## Step 1 — Resolve conference details

Web-search the conference (start with the URL if given). Capture:
- Official name + the **exact hashtag(s)** the event uses.
- **Dates** and **venue / city**.
- Whether it's upcoming or already happened (sanity-check against `Before`/`After`).

These tokens make or break the search. Conference short-names often collide with
common words — for example, LinkedIn's search tokenizes a name like "Ai4" as
"AI" + "4", flooding results with generic AI posts. Distinctive hashtags and
venue/city names are the reliable signal. Note the collision risk before
building queries.

---

## Step 2 — FIND: run the Apify LinkedIn post search

Call `mcp__Apify__call-actor` with **`harvestapi/linkedin-post-search`**. Build
6–8 queries mixing **hashtags + attendance verbs + venue/city**. Template:

```json
{
  "actor": "harvestapi/linkedin-post-search",
  "waitSecs": 45,
  "input": {
    "searchQueries": [
      "#<ConferenceHashtag>",
      "#<ConferenceHashtag2>",
      "<Name> <Venue> <City>",
      "speaking at <Name>",
      "attending <Name> <City>",
      "excited to attend <Name>",
      "heading to <Name> <Venue>",
      "<Name> <Year> booth"
    ],
    "maxPosts": 25,
    "postedLimit": "<see below>",
    "sortBy": "relevance",
    "profileScraperMode": "short"
  }
}
```

`postedLimit` enum: `1h,24h,week,month,3months,6months,year,any` — an empty
string is rejected by the actor's schema, always pass a valid value.
- **Before** mode → use `month` (or `3months` for speaker announcements made early).
- **After** mode → use `week` (fresh recaps) up to `month`.

`sortBy: "relevance"` surfaces on-topic posts; `sortBy: "date"` if you want newest
first. If the run isn't `SUCCEEDED` within `waitSecs`, poll `get-actor-run` with
the `runId` every ~20s until terminal.

Then fetch results with `get-dataset-items`:
- `datasetId`: the run's dataset id
- `fields`: `linkedinUrl,type,content,author.name,author.info,author.linkedinUrl,postedAt.postedAgoShort,engagement.likes,engagement.comments`
- `limit`: 150, `clean`: true

If the payload is large it may be written to a file — parse it (the rows are
TOON/CSV-like; split on the row-start pattern and CSV-parse each row). Dedupe by
`linkedinUrl`.

**Fallback ladder — run in order, stop as soon as you have enough genuine hits:**
1. Widen `postedLimit` one step (e.g. `month` → `3months`).
2. Add more hashtag / venue / city query variants and re-run.
3. If after that you still have **no or only a few genuine this-year posts** (fewer
   than ~5) → **ASK the user** whether to run the prior-edition fallback. Do NOT run
   it automatically. Ask something like:

   > "I only found <N> genuine posts about <Conference> <ThisYear> — it may just be
   > early and people haven't started posting yet. Want me to search posts from
   > **last year's** <Conference> instead? Those are people who attended the previous
   > edition — you could reach out and ask if they're going again this year."

   Use `ask_user_input` (Yes, search last year / No, stick with what we found) if
   available. Only proceed to the prior-edition search on a yes. If they say no,
   present whatever genuine this-year hits exist (a thin result is a real answer).

   *Why this exists:* SDRs often start prepping **months in advance**, before anyone
   has posted about attending — an empty result usually means "too early," not "bad
   conference," and last year's attendees are the natural early prospecting pool.

### Keep only GENUINE attendee/speaker posts

The relevance search returns noise. Keep a post only if the **content** clearly
references the conference — look for the distinctive `\bName\b` token (case-aware),
the official hashtag, the venue/city, or dates. Drop:
- Generic posts that only matched on tokenization.
- Pure conference-promo from the event's own org / media / sponsors selling booths
  (unless they name an individual attendee/speaker who fits the ICP).
- Reposts with no added commentary, birthdays, generic career updates.

Signals of a real attendee/speaker post: "attending / heading to / see you at /
excited to speak / I'll be on the panel / visit us at booth / back at <Name>
this year".

### Fallback: prior-edition attendees (OPT-IN — only after the user says yes)

Run this **only after the user answered yes** to the question in rung 3 above.
Typical scenario: the user is prepping well before the event and nobody has posted
about attending yet. Last year's attendees are a strong early prospecting list:
many return, and "you were at <Conference> last year — going again this year?" is
a warm, natural opener.

**Mechanics (this is where the naive version breaks — get it right):**
- `postedLimit` only bounds recency *backward from today*; it cannot target a past
  window. So you pin the prior edition through the **query text**, not `postedLimit`.
- Build queries around the **previous-year hashtag and dates**, e.g. if it's 2026,
  search `#<Conference>2025`, `<Name> 2025`, plus the same venue/city, with
  **past-tense** verbs: "attended / was at / great time at / speaking at … 2025 /
  wrapped up / recap".
- Set `postedLimit`:
  - prior edition was **within ~12 months** → `postedLimit: "year"`.
  - prior edition was **more than ~12 months ago** → `postedLimit: "any"` (a `year`
    limit would never reach it and the run comes back empty — the exact silent
    failure to avoid).
- Then filter `content` to the **prior-year token** (`\b2025\b`, the prior hashtag,
  the prior dates/venue) so you don't re-surface this-year noise or the wrong year.

**Label them distinctly.** A prior-edition attendee is a *lead, not a confirmed
attendee for this year.* Tag every such row with **⏮ Prior-year attendee — not
confirmed for `<ThisYear>`** so it's never mistaken for a live hit. Carry that tag
through filtering, enrichment, export notes, and the final table. Then run these
authors through the normal ICP filter (Step 3) like any other.

This fallback matters most in **Before** mode — especially early prep, when nobody
has posted yet. In **After** mode it rarely applies (the event just happened), but
the same rule holds: if recaps are thin, ask before searching the prior edition,
and frame those people as returning-attendee leads, not confirmed attendees.

---

## Step 3 — FILTER: match authors against the Beezi ICP

**Beezi sells to SERVICE COMPANIES** (IT services, consultancies, custom software
shops, digital-transformation agencies, system integrators) in the **US and UK
only** — **not** SaaS product companies.

**Buyer titles (must be one of):** CEO / Founder / President / Co-founder · COO /
Chief Delivery Officer · Head/VP/Director of Delivery · Head of AI Practice / Head
of AI / AI Practice Lead / AI CoE · Chief Innovation Officer / VP Innovation · Head
of Engineering Practice / VP Engineering Services · Partner / Managing Director /
Managing Partner · Head of Pre-sales / Head of Solutions / VP Solutions · VP Sales /
Head of Sales (services firms) · CTO (services firms ONLY, never SaaS product cos).

**Hard geo filter:** author or company HQ must be US or UK. Drop others even if
otherwise strong. If location is missing, note it as "geo unconfirmed" rather than
dropping outright — flag for enrichment.

**Competitor employee block (drop):** EPAM, Softserve, Deloitte, EY, KPMG,
Accenture, Capgemini, IBM Consulting, TCS, Infosys, Wipro, Cognizant, HCL, Tech
Mahindra, Mphasis, PwC, McKinsey, BCG, Bain, 8090.ai. (Unless user opts in to GSIs.)

**Hard disqualifiers (drop):** recruiter, talent/HR, junior sales/AE, student,
intern, EA, comms/PR, journalist, VC/investor, coach, academia.

**Company type check:** SaaS/product company → NOT ICP (even if the person has a
buyer title). Services / consulting / IT services / custom software / digital
transformation → ICP-eligible. **Watch hybrids:** advisory firms with a
proprietary platform/protocol, or "consultants" who say "I build AI products",
lean product — mark Partial, not Strong. A personal-brand solo consultancy is
below the buyer bar even with a perfect title.

The author headline (`author.info`) usually reveals title + firm. If the firm's
type or geo is ambiguous, mark it **Partial — verify** rather than guessing.

### Tier the results

- **🟢 Tier A (Strong):** services/consulting firm + buyer title + US/UK confirmed.
- **🟡 Tier B (Partial):** right shape but title, company-type, or geo needs a quick
  check (MoltSets or web) before outreach.
- **🔴 Tier C (Not ICP):** attending but a product co / media / VC / academia /
  disqualified title — list them briefly so the user knows they were seen and why
  they're out.

Light scoring boosts within a tier: founder/CEO/COO (+15), names a Beezi competitor
or "we built our own AI platform" (+25), RFP/pre-sales/agentic-delivery themes
(+20), speaking/on a panel (+10), 15+ comments (+10).

**Expect Tier A to be a minority.** People who post loudly about conferences skew
product-company, media, VC, and academia; verify before promoting anyone to Tier A.
A couple of confirmed Tier A accounts from a 20-post pool is a normal, honest
result — don't pad Tier A with maybes.

Aim to surface the number the user asked for (default ~20 genuine attendee posts),
ordered Tier A → B → C, then present the tiered table before enrichment.

---

## Step 4 — ENRICH: MoltSets

Enrich Tier A by default; enrich Tier B when the user wants the maybes resolved.
All MoltSets calls go through the **`moltsets:moltsets_call`** tool (the MoltSets
local connector), passing a `tool_name` and a `params` JSON body. MoltSets
consumes account credits/usage — state the batch size and confirm with the user
before enriching.

Since this workflow always has the author's LinkedIn URL from the Apify scrape,
use the URL-based tools (most reliable):

1. **Person**: `reverse_linkedin_lookup` or `search_linkedin_profile` (by
   LinkedIn URL) → full profile: verified title, current company, location.
2. **Email**: `linkedin_to_best_email` → best email; fall back to
   `linkedin_to_business_email` / `linkedin_to_personal_email` if empty.
3. **Phone** (optional): `linkedin_to_mobile_phone`.
4. **Company**: `search_companies` (by domain or company name from step 1) →
   industry, headcount, revenue, HQ country → confirm services-firm + US/UK.
   Re-tier anyone whose enriched data changes their verdict (product co or
   non-US/UK → drop from export).

If MoltSets returns no match or a low-confidence result, say so and offer to
proceed to Step 5 with the LinkedIn-scraped data only, email blank and "email
pending MoltSets enrichment" in the notes. If multiple matches, pick the one
matching the scraped title + company; if uncertain, show the top 2 and ask.

*Fallback:* if the MoltSets connector's tools won't load in the session (a
connector enabled mid-chat may not register until a fresh conversation), say so,
offer web verification of company-type + geo instead, and proceed to Step 5
email-blank as above.

---

## Step 5 — EXPORT: HubSpot

1. Search HubSpot (`search_crm_objects`) for existing contact (by email or name)
   and company (by domain/name) to avoid dupes — update instead of duplicating.
2. `manage_crm_objects` → create/update Contact + Company and **associate them**
   (create companies first, then contacts with `associations` to the company IDs).
3. Present the proposed records in a confirmation table and wait for approval
   before writing.
4. Record the sourcing context in the contact notes: conference name, what they
   posted (attending/speaking), ICP verdict, and the post link.
5. Return the HubSpot record links when done.

---

## Step 6 — Present the mode-specific plan

### If BEFORE (pre-conference target list)
For each Tier A/B person, output a row plus a **pre-event play**:

| Person | Title / Firm | ICP verdict | Why now (from their post) | Pre-event play | Link |

- **Pre-event play** = one concrete action: "DM to book 15 min at their booth #507",
  "catch their panel Weds 2pm then intro", "warm connect referencing their RFP post".
- **For ⏮ prior-year attendees**, the play *confirms attendance first*: "reference
  their <PriorYear> post, ask if they're back this year, then book if yes" — never a
  play that assumes they'll be there.
- Optionally draft a short **connection/DM request** per Tier A person (use
  `message_compose_v1` if available) — never auto-send.

### If AFTER (post-conference follow-up list)
For each Tier A/B person, output a row plus a **follow-up angle**:

| Person | Title / Firm | ICP verdict | What they posted/said | Follow-up angle | Link |

- **Follow-up angle** = Hook (reference their specific post/session) → Value add →
  soft next step. Vary angles across people; never generic.
- Optionally draft the follow-up message per Tier A person (email or LinkedIn).
- **Never mention Beezi by name** in any suggested message — let positioning inform
  the angle (AI-native SDLC orchestration; win RFPs, protect margins, turn one-off
  engagements into retainers).

### Optional: post the plan to Slack
If the user wants the plan shared, post to Slack (`slack_send_message`, default
channel `C0BDG58MWLW` / #comment_finder unless they name another). Format: header
with conference + mode, then one block per Tier A/B person (name, title/firm, ICP
verdict, link, the play/angle in a copy-friendly code block).

---

## Critical rules

- **Always ask the two intake questions first** (conference + before/after).
- **Posts only.** This skill analyzes post authors; it never scrapes likers or
  commenters.
- **Watch the tokenization trap** — short conference names collide with common
  words; lead with hashtags + venue/city, then filter content for the real token.
- **US/UK services firms only.** Product cos, media, VCs, academia, competitor
  employees, and junior/support titles are not the buyer.
- **Be honest about thin results.** A handful of real fits is a real answer.
- **Prior-year fallback is opt-in.** When this-year results are few or zero, ASK the
  user whether to search last year's edition — never run it unprompted. Tag those
  people ⏮ and never present a prior-year attendee as confirmed for this year.
- **Confirm before spending MoltSets credits** and **never write to HubSpot**
  without showing the records and getting a yes.
- **Never auto-send** LinkedIn messages or emails.
- **Never name Beezi** in suggested outreach copy.
- **Cite the source post** (link) for every person surfaced.

## When the user pushes back
- "More people" → surface further down the ranked list (into Tier B), or widen
  `postedLimit` / add query variants and re-run.
- "Wrong ICP" → re-check the failing dimension (company type / geo / title) and ask.
- "Different angle" → rewrite just that person's play/message, keep the rest.
- "Switch to after / before" → re-run Step 6 in the other mode on the same pool
  (and adjust `postedLimit` recency if needed).

## See also
- `comment-finder-service-companies` — full Beezi ICP, scoring, Slack template.
- `gtm-linkedin-to-crm-moltsets` — the underlying MoltSets enrichment + HubSpot
  create/update/associate pattern, including the full MoltSets tool list.
