---
name: moltsets-hubspot-event-badge-list-enrichment
description: Use this skill to expand a raw event or badge-scan export of just names and companies into full contact records with MoltSets, before it lands in HubSpot for post-event outreach.
---

# HubSpot Event and Badge List Enrichment

Take a raw event or trade-show attendee export - usually just a name and a company - and expand each row into a full contact record before it goes near HubSpot. Resolve title, business email, LinkedIn, and firmographics through MoltSets, flag rows that cannot be resolved, and hand back an import-ready file so post-event outreach starts with real data rather than a name and a booth number.

## Triggers

"Enrich this event attendee list before I import it into HubSpot", "expand these badge-scan names and companies into full contacts with email and title". Use when the user has a raw attendee/badge export (name + company, sometimes title) destined for HubSpot.

## Requirements

- HubSpot remote MCP (mcp.hubspot.com) and the MoltSets MCP connected in the same client
- Custom objects are not reachable through HubSpot's remote MCP - import to standard contact properties

## Chain

```
Raw attendee export (CSV or sheet: name + company, sometimes title)
  -> normalise company name to domain  (best-effort, or ask for domain if ambiguous)
  -> MoltSets:
       search_business_profile_by_name (name + domain -> title, LinkedIn, seniority)
       linkedin_to_best_email          (LinkedIn URL -> business email)
       search_companies                (domain -> firmographics)
  -> flag unresolved rows              (ambiguous company name, no match found)
  -> hubspot/search-crm-objects        (check for existing contacts, avoid duplicate import)
  -> hubspot/create-object             (import new, enriched contacts)
```

## Inputs

- **Attendee file** - the raw export (CSV or sheet) with at minimum name and company
- **Event context** - event name and date, to tag the imported contacts
- **Company resolution** - guess domains from company name automatically, or confirm ambiguous ones with the user first
- **Import scope** - all resolved rows, or only rows above a confidence threshold
- **Acceptable risk scores** - which A-F deliverability grades to keep (recommended: A, B, C and F; D excluded)

## Risk scores - confirm before enriching

Every email MoltSets returns carries a deliverability risk score - a grade from A to F. Ask which grades to keep before running anything:

> "MoltSets grades email deliverability risk from A (best) to F (no data). Which grades should I keep? I'd recommend **A, B, C and F** - and dropping **D**, since D is a confirmed bounce, complaint or spam trap."

| Grade | Meaning | Best practice |
| --- | --- | --- |
| A | Strongest engagement signal available | Safe to send immediately |
| B | Solid | Fine in regular sends; on a new or warming domain, send a smaller batch before scaling |
| C | Deliverability can't be confirmed | Segment separately from A/B, warm at low volume, watch engagement, suppress non-responders after 1-2 attempts |
| D | Confirmed bounce, complaint or spam trap | Never send - fastest way to trip spam traps and damage sender reputation for the whole list |
| F | No data - unknown risk, not "safe" | Re-verify before sending, or treat like C |

Use whatever set the user confirms. Treat the grade as a filter, not a guarantee - it lowers risk, it doesn't remove it. Pair it with the user's own sending domain reputation and list hygiene.

## `search_companies` filter notes

- **Use `domain` when you have one.** Exact match, always beats a free-text `query`, and full URLs are normalised automatically ("https://www.acme.com/about" → "acme.com") - no need to strip the protocol or path first. `query` searches the company **name only**.
- **Filters are free precision.** Execution cost comes from `query`, not the filters attached to it - a filters-only call runs **~40x cheaper**, and most firmographic lookups need no free text at all. Billing is unchanged: tokens are charged per record returned.
- **Full filter set:** `query`, `domain`, `industry`, `linkedin_industry`, `naics_code`, `employee_range`, `revenue_range`, `country`, `state`, plus `limit`/`offset`.
- **Industry vocabularies overlap.** `industry` carries broad buckets *and* long-tail values (`"Computer Software"`, `"Insurance"`, `"Banks"`, `"Aerospace and Defense"`), so the same company may be filed under either - if a lookup comes back thin, try the neighbouring value. `linkedin_industry` (~150 niche labels) and `naics_code` (any hierarchy level) cover finer granularity.
- **Location is derived from where the team is based.** Accurate for small companies, but **large multinationals resolve to a single one of their offices** - don't lean on `country`/`state` alone for enterprise or international targeting.
- **Range values are exact and skewed.** `"1-10"` dominates `employee_range`; `"$500k - $1M"` dominates `revenue_range`. Prefer numeric bands over legacy `"Small"`/`"Mid-Market"`/`"Enterprise"`/`"Unknown"`, and always use `"$500k - $1M"` over the legacy `"$1 - $1M"` (same range, far fewer records).
- **Read `results.total` before paginating.** Results are `_score`-ranked; 25 max per call, increment `offset` by `limit`.

## Steps

1. Read the raw export and identify what is present per row (name, company, sometimes a title).
2. Normalise the company name to a domain, flagging ambiguous names (common names, multiple possible matches) for confirmation.
3. Run `search_business_profile_by_name` with name plus domain to return title, seniority, and LinkedIn URL.
4. Run `linkedin_to_best_email` where a LinkedIn URL was returned, to get a business email.
5. Run `search_companies` on the domain to attach firmographics to each row.
6. Check resolved contacts against existing HubSpot records to avoid duplicate import, then create the new, enriched contacts tagged with the event name and date.

Before any write-back, every found address is checked against the accepted grade set. Out-of-range grades are reported in the table with their true score and skipped - never written to the CRM.

## Output

| Name (as scanned) | Company | Resolved Domain | Title | Email | Risk | LinkedIn | Confidence | Action |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |

Show the risk score on every row that has an email, including grades outside the accepted set - flag those in `Action` (e.g. "held - grade D") rather than dropping them silently. Only emails inside the accepted set are written back to the CRM.

```
Rows in export:              X
Rows resolved:               X/N (XX%)
Ambiguous / flagged:         X
Already in HubSpot:          X
New contacts imported:       X
MoltSets tokens used:        X

Email risk scores:
  A: X   B: X   C: X   D: X   F: X
  kept (accepted grades):  X
  filtered (excluded):     X
```

## Tips

- Badge scans and business-card OCR are frequently misspelled, so check flagged rows manually rather than force-matching them.
- Tag every imported contact with the event name and date so lifecycle and attribution reporting can isolate this cohort later.
- Run a coverage check on the first 20 to 50 rows before processing a large export, to catch a systematic domain-guessing problem early.
