| name | event-lead-enrichment |
| platforms | ["claude-code"] |
| description | Enrich event booth-scan lead CSVs with Common Room firmographics, score them by ICP fit and conversation heat, flag Tiger Data customers, and produce a tiered xlsx for SDR follow-up. Use this skill when the user asks to enrich event leads, score booth scans, process a post-event lead list, or run lead enrichment for a conference/trade show. Also trigger when they mention 'GrafanCON leads', 'Hannover Messe leads', 'booth scan enrichment', 'tier the leads', or provide a lead CSV after an event. |
Event Lead Enrichment
Enrich post-event lead lists (booth scans, badge scans, form fills) against Common Room firmographics and the Tiger Data customer list. Produces a tiered xlsx ready for SDR follow-up and manual HubSpot import.
The skill runs in three checkpointed phases:
- Phase A — Scaffold: pre-flight → pull customer list → normalize CSV → dedupe + filter → emit summary for user review
- Phase B — Enrichment: per-domain CR lookup → merge firmographics → score → produce final xlsx
- Phase C — Combined rollup: after the last day of a multi-day event, harmonize per-day outputs into a single workbook
When to use this skill
- User says "enrich [event] Day N leads" + provides a CSV path
- User asks to process booth-scan leads after a conference/trade show
- User runs
/enrich-event or mentions 'GrafanCON leads', 'Hannover Messe leads', 'booth scan'
- User asks to build the combined rollup after the last day of an event
Dependencies
Step 0: Pre-flight check
Invoke the marketing-preflight skill via the Skill tool, passing this skill's name: field value (event-lead-enrichment) as the args. Do not proceed until it completes successfully. If it stops with an error, follow its instructions and stop.
If marketing-preflight returned a stale-connector footer note, append it verbatim at the end of your final response to the user.
Once Tiger Den is confirmed, fetch this skill's reference docs in one call:
get_marketing_context(slugs: ["event-lead-scoring-rubric", "event-lead-domain-aliases", "cr-customer-pull-runbook"])
Write the Markdown documents to:
<working-dir>/<event-slug>/rubric.md
<working-dir>/<event-slug>/aliases.md
<working-dir>/<event-slug>/customer-pull-runbook.md
Step 1: Confirm event metadata with the user
Ask for (or confirm):
- Event name (e.g. "GrafanCON 2026")
- Day number (for multi-day events)
- Path to the raw lead CSV
- Companies/email-domains to strip (host/internal — e.g. "Tiger Data,Grafana" for a Grafana-hosted event)
- Lead source type — ask explicitly: "Is this a Tradeshow, Webinar, Conference, or other?" This maps directly to the
--lead-source flag on the script. Do not default to "Tradeshow" — the value must come from the operator.
Create the event slug: lowercase, hyphenated, no year-month — e.g. "grafancon-2026".
Step 2: Pull customer list from Common Room
Read <working-dir>/<event-slug>/customer-pull-runbook.md (fetched in Step 0). Follow it end-to-end to:
- Build the Common Room filter and paginate through all results
- Classify each org as
Current, Previous, or skip
- Write the result to
<working-dir>/<event-slug>/customers.json
The runbook also defines expected counts and the soft-warning threshold for when the customer pull looks incomplete.
Step 2.5: Resolve the vendor mapping
Before running Phase A, translate the raw vendor export into canonical CSV shape:
-
Derive vendor_slug from the event name (kebab-case, year/day/region tokens stripped — e.g. "IoT Tech Expo NA 2026 Day 1" → iot-tech-expo). Confirm the derived slug with the operator before proceeding.
-
Look up the mapping. Try in order:
a. Local cache: ~/.tiger-event-mappings/<vendor_slug>.yaml
b. Repo: look in this skill's event-mappings/ directory (see references/event-mappings/hannover-messe.yaml for the file format). The file is named <vendor_slug>.yaml.
c. Auto-detect: if neither exists, read the source headers and check whether any repo mapping's event_signature is a subset of those headers (longest match wins; tie → ask the operator).
-
If a mapping is found, run:
python3 <plugin-path>/skills/event-lead-enrichment/scripts/apply_mapping.py \
--source <raw CSV/xlsx path> \
--mapping <resolved mapping yaml path> \
--output <working-dir>/<event-slug>/<event-slug>-day-<N>.canonical.csv
This produces a canonical CSV that Phase A consumes.
-
If no mapping is found (and auto-detect also returned None), halt with this exact message:
No mapping found for vendor <slug>.
The LLM scaffolding workflow that would automatically draft a mapping from the source headers is not yet implemented (Phase 3 of the rollout — see /Users/coreyfitz/Desktop/claude-projects/marketing-skills/docs/superpowers/specs/2026-05-19-event-lead-enrichment-mapping-system-design.md).
To proceed today, do one of:
- Hand-author a mapping at
~/.tiger-event-mappings/<slug>.yaml following the format in <plugin-path>/skills/event-lead-enrichment/references/canonical-schema.md. Use one of the existing mappings (hannover-messe.yaml, grafancon.yaml, iot-tech-expo.yaml) as a starting point.
- File an issue requesting the mapping be authored and blessed: https://github.com/timescale/marketing-skills-issues/issues/new
- Run the legacy
--schema=hannover|grafancon path on build_enriched.py if the source happens to match one of those formats verbatim.
Do not silently fall back. The operator must know that no scaffolding is available yet.
-
Pass --input <canonical CSV path> and --input-format canonical to build_enriched.py in Steps 3 and 4 below (replacing the previous --input <raw CSV> arg).
Step 3: Run Phase A (scaffold)
Verify the user has Python 3 + openpyxl installed. If not, provide:
pip install openpyxl
Then run:
python3 <plugin-path>/skills/event-lead-enrichment/scripts/build_enriched.py \
--event "<event name>" \
--day <day number> \
--input <working-dir>/<event-slug>/<event-slug>-day-<N>.canonical.csv \
--input-format canonical \
--output <working-dir>/<event-slug>/<event-slug>-day-<N>.xlsx \
--customers <working-dir>/<event-slug>/customers.json \
--rubric <working-dir>/<event-slug>/rubric.md \
--aliases <working-dir>/<event-slug>/aliases.md \
--strip-companies "<host/internal companies>" \
--strip-email-domains "<host/internal domains>" \
--lead-source "<lead-source-from-step-1>" \
--lead-source-detail "<event name>" \
--prior <prior day xlsx files if any> \
--stop-after scaffold
Read <working-dir>/<event-slug>/phase_a_summary.txt and present it to the user. Ask them to confirm the counts look right before proceeding.
Step 4: Run Phase B (enrichment)
Once the user confirms, iterate through domains in <working-dir>/<event-slug>/<event-slug>-day-<N>.domains.json.
For each domain:
-
Apply the alias map from aliases.md to canonicalize the domain (though the script already does this when writing domains.json; use the map here as a safety net).
-
Query CR Organization:
commonroom_list_objects(
objectType: "Organization",
filter: {"type": "and", "clauses": [{"type": "stringFilter", "field": "companyWebsite", "params": {"op": "eq", "value": "<canonical-domain>"}}]},
properties: ["subIndustry", "about", "employees", "revenueRangeMin", "revenueRangeMax", "leadScores", "tags"],
limit: 10
)
- If no hit, fall back to ProspectorCompany:
commonroom_list_objects(
objectType: "ProspectorCompany",
filter: {"type": "and", "clauses": [{"type": "stringFilter", "field": "groupWebsite", "params": {"op": "eq", "value": "<canonical-domain>"}}]},
properties: ["subIndustry", "employees", "revenueRange", "location", "technologies"],
limit: 10
)
- Record the result in
<working-dir>/<event-slug>/cr_enrichment.json with shape:
{
"by_domain": {
"<canonical-domain>": {
"source": "CR" | "Prospector" | "NONE",
"primary_domain": "<domain>",
"name": "<org name>",
"sub_industry": "...",
"about": "...",
"employees": 1200,
"size_bucket": "1000 - 4999",
"revenue_range": "$100M-$500M",
"hq": "City, Country",
"v1_account_pct": 86,
"tech_highlights": ["Prometheus", "Kubernetes"]
}
}
}
Write incrementally. After each domain lookup, re-write cr_enrichment.json (or append and re-serialize). This ensures rate-limit failures mid-loop are recoverable — a retry picks up from the last recorded state.
- On CR rate-limit / error: pause, tell user how many domains completed + which failed. Offer (a) retry failed, (b) skip failed with source "NONE", (c) stop.
Once all domains are processed, run Phase B of the script:
python3 <plugin-path>/skills/event-lead-enrichment/scripts/build_enriched.py \
--event "<event>" \
--day <N> \
--input <working-dir>/<event-slug>/<event-slug>-day-<N>.canonical.csv \
--input-format canonical \
--output <working-dir>/<event-slug>/<event-slug>-day-<N>.xlsx \
--customers <working-dir>/<event-slug>/customers.json \
--rubric <working-dir>/<event-slug>/rubric.md \
--aliases <working-dir>/<event-slug>/aliases.md \
--cr-enrichment <working-dir>/<event-slug>/cr_enrichment.json \
--strip-companies "..." \
--strip-email-domains "..." \
--lead-source "<lead-source-from-step-1>" \
--lead-source-detail "<event name>" \
--prior <prior days if any>
This produces the final xlsx with Summary, All Leads, Removed, and subIndustry Diagnostic sheets.
Step 5: Update Tiger Den event record
Search for the event in Tiger Den:
manage_events(action: "search", query: "<event name>")
If found, update its notes field (append-only) with the day's summary:
manage_events(
action: "update",
id: "<event-uuid>",
notes: "<existing notes>\n\nDay <N> enriched YYYY-MM-DD: <X> leads kept, <Y> current customers, <Z> previous, <A> Conversation, <B> Tier A, <C> Tier B, <D> Tier C, <E> Tier D. File: <event-slug>-day-<N>.xlsx"
)
If not found, offer to create the event record. Ask the user for: event_type (conference/meetup/webinar), start_date, end_date, location, website_url. Then:
manage_events(action: "create", name: "<event>", event_type: "...", start_date: "...", end_date: "...", location: "...")
Then update notes as above.
Step 6: Present results to the user
Present a summary of the run:
- Tier distribution (Conversation / A / B / C / D counts)
- Heat breakdown (Hot / Warm / Mild for Conversation tier)
- Customer matches (Current / Previous)
- SDR status flags (HOT/WARM without notes)
- Path to the xlsx output
- Link to the Tiger Den event record
Remind the user the next step is manual HubSpot CSV import (HubSpot direct push is a v2 feature).
Phase C: Combined rollup (invoked separately)
When the user asks to combine days after the last day of the event:
- List the per-day xlsx files in
<working-dir>/<event-slug>/, confirm which to include
- Re-fetch
rubric.md from Tiger Den (the Summary uses the latest policy text)
- Run:
python3 <plugin-path>/skills/event-lead-enrichment/scripts/build_combined.py \
--event "<event>" \
--inputs <working-dir>/<event-slug>/<event-slug>-day-1.xlsx <working-dir>/<event-slug>/<event-slug>-day-2.xlsx ... \
--rubric <working-dir>/<event-slug>/rubric.md \
--output <working-dir>/<event-slug>/<event-slug>-combined.xlsx \
--day-labels "Day 1,Day 2,Day 3"
Present the combined summary (per-day + Total counts across tier/heat/customers/SDR flags) and the xlsx path to the user.
Known gotchas
- CR filter field name: use
companyWebsite for Organization filtering, groupWebsite for ProspectorCompany. primaryWebsite is NOT a filter field. like on domain returns massive false positives (e.g. *erco.com matched 2091 results in a past run) — always use eq.
- Personal email domains (gmail, gmx, yahoo, etc.) are handled by the script — they never hit CR. For personal-email leads where the Company field is set, the script uses normalized company-name matching against the customer index.
- CR Organization tech stack: no
groupTechStack column exists. Only ProspectorCompany has technologies. Tech_Highlights will be empty for CR-matched rows and populated only for Prospector fallbacks.
- Product-name HOT keywords (
timescale, postgres, tsdb, influx) are noisy at product-adjacent events (GrafanCON, PostgresConf). The rubric keeps them because the false-positive cost is low (SDR reviews Conversation-tier anyway), but flag this in your summary if the user's event is product-adjacent.
- Customer list gap: if the customer pull warns about a low count, see
customer-pull-runbook.md (fetched in Step 0) for expected ranges and remediation steps.