---
name: moltsets-exhibitor-contact-finder
description: Finds senior decision-makers (Director, VP, Head, C Suite, Owner, Partner) and their email addresses for a list of companies using MoltSets — built for event/trade-show exhibitor lists but works for any CSV of company names + domains. Use this whenever the user has a spreadsheet or list of companies (exhibitor lists, prospect lists, account lists) and wants contacts, senior people, or email addresses at each one. Trigger on phrases like "find people at these companies", "get me contacts for this exhibitor list", "senior execs at each of these", or when a CSV with company name/website columns is uploaded alongside a request for contacts. Handles the real-world mess of small-business data like name collisions with unrelated companies, missing domains in company_domain filters, and emails that exist but aren't indexed under the obvious search.
---

# Exhibitor Contact Finder

Turns a raw list of companies (name + domain, typically from an event exhibitor list) into a table of senior contacts with verified email addresses, using MoltSets tools. Built from hard-won trial and error against messy small-business data — most companies on a list like this are NOT well-indexed, and the naive approach (search_people with company_domain filter, stop after 2 hits) misses the majority of real, findable emails.

## Before starting: confirm parameters

Ask (or infer from context) if not already specified:
1. **Target seniorities** — default: Director, VP, Head, C Suite, Owner, Partner (skip Manager/Senior/Entry/Intern)
2. **How many emails per company** — do not silently default this. If the user hasn't stated a number, ask directly, e.g.: "How many people with email addresses do you want per company?" Use whatever number they give as the target (this skill's own examples use 2, but that's just the default example, not a hardcoded rule). If the first several qualifying people have no findable email, keep working down the full qualifying list until the target is met or the list is exhausted.
3. **Fallback to LinkedIn-only when email comes up short** — do not silently include or silently omit LinkedIn-only contacts. If the user hasn't said, ask directly, e.g.: "If I can't find enough people with emails for a company, is it OK to include people with just a LinkedIn URL (no email) to fill out the count?" This question matters most once Step 4 comes up short for a company — if it's still unanswered at that point, ask before deciding whether to pad the results with LinkedIn-only rows or leave the company short. Note the answer in the output (see Output format) so the user can tell which rows are LinkedIn-only fills vs. genuine email finds.
4. **Matching strictness** — default: prefer domain-verified matches, but loose/inferred matches are OK if clearly flagged (see Verification tiers below). Confirm with the user if unsure.
5. **Batch size** — for a large list, do an initial batch of ~20 rows to calibrate before running the rest, since result quality varies a lot by list.

## Known tool gotchas (read before starting)

- **`company_domain` on `search_people` is unreliable** — don't rely on it as the primary filter even though it looks like the obvious param. Some users report it not functioning at all.
- **`company` (name) filter on `search_people` is exact-ish and brittle** — it misses real matches when the stored company name differs from what's on your list (DBA names, "& Associates" vs "and Associates", dropped suffixes like "Enclosures" or "LLC", parent/brand relationships like "AMD Direct Inc., Family of Brands: Summerset Grills"). **Always try a free-text `query` search first** (e.g. `query: "{company name} Director Owner"`, optionally + `country`) — it's far more forgiving and consistently surfaces people the exact filter misses. Use the `company` filter afterward, once you know the real stored name, to pull a fuller roster.
- **`search_linkedin_profile` is not a discovery tool.** It requires a name + company and looks up one specific known person — it will NOT return a roster for a company, and it has a narrower index than `search_people` (a person confirmed via `search_people` can come back 0 results here). Don't use it to find people; only to verify someone you already have a name for, and even then prefer `reverse_linkedin_lookup` (see below).
- **`reverse_linkedin_lookup` doubles as a "genuinely no email exists" check.** If `linkedin_to_best_email` and `search_business_email_by_name` both come back empty for a person, run `reverse_linkedin_lookup` on their profile URL before giving up — if it also shows no email fields, you can trust that the person's email truly isn't in the database (not just missing from that particular search path).

## Step 1: Discover people at each company

1. Run `MoltSets:search_people` with `query: "{company name} Director Owner"` (add `country: "United States"` or similar if the list is geographically scoped), `limit: 15-25`.
2. If that returns nothing useful, try variants: drop legal suffixes (LLC, Inc.), try the product/brand name instead of the legal entity name, try without "Director Owner" appended (sometimes the extra terms dilute a small result set).
3. Once you find a result whose `company.website_url` matches the exhibitor's actual domain, re-run with `MoltSets:search_people` using `company: "{exact stored name from the result}"` and `limit: 25` to pull the fuller roster at that company.

## Step 2: Verification tiers

Every candidate match falls into one of these — track it per person, don't just silently keep the good ones:

- **VERIFIED** — `company.website_url` in the result matches the exhibitor's actual domain exactly.
- **INFERRED** — domain differs, but the record explicitly states the relationship (e.g. headline says "AMD Direct Inc., Family of Brands: Summerset Grills..." when you're researching Summerset Grills). Trust it, but label it inferred.
- **UNVERIFIED** — name matches but nothing confirms it's the same company (generic names like "Bliss Studio," "Jewel Gallery" collide with many unrelated businesses of the same name). Only include if the user has said loose matching is OK, and always flag clearly — these need manual review before outreach.
- **NOT FOUND** — no plausible match after trying query variants. Move to Step 5 (web fallback) or mark as genuinely absent.

## Step 3: Filter to target seniority, then work the full list

Filter results to the target seniorities (default: Director, VP, Head, C Suite, Owner, Partner). Do NOT stop at the first N qualifying names — the goal is N confirmed *emails* (N = whatever the user specified in Step 0), so if the top matches have no email, keep going through every qualifying person in the roster before concluding the company is exhausted.

## Step 4: Chase an email for each qualifying person, in this order

1. Email already present in the `search_people` result (`business_email` or `personal_email` fields)
2. `MoltSets:linkedin_to_best_email` on their LinkedIn URL
3. `MoltSets:search_business_email_by_name` (company domain + name)
4. `MoltSets:reverse_linkedin_lookup` on their profile — confirms whether an email genuinely doesn't exist anywhere vs. just not surfacing in search
5. **Pattern-guessing layer** (do this before giving up — see below)

### Pattern-guessing layer

When a person has a confirmed name + confirmed domain but steps 1-4 found nothing, guess common email conventions and check each via `MoltSets:reverse_email_lookup` (batch multiple guesses in one call using the `emails` array param — up to 100 per call, no token cost on misses). Use this standardized 6-pattern set per person:

1. `firstname@domain`
2. `firstinitiallastname@domain` (e.g. `jmiranda@`)
3. `firstname.lastname@domain`
4. `lastname@domain`
5. `firstnamelastname@domain` (concatenated, no separator)
6. `firstinitial.lastname@domain` (e.g. `j.miranda@`)

If the company has a known parent/DBA relationship (see INFERRED tier above), also run the full 6-pattern set against the parent company's domain.

This genuinely finds real contacts that the forward search tools miss — it directly resolves the guessed address against the reverse index rather than depending on how the person was indexed elsewhere. Example hit: `jesus@artesimports.com` resolved to Jesus Miranda, Owner, when nothing else had found his email.

**Caveat:** a hit here can occasionally surface a *stale* address — check the `current_company` field in the response. If it doesn't match the company you're researching, the person has likely moved on and the address may be dead; flag it rather than treating it as a clean win.

Batch this across every unresolved person in the current sweep in a single `reverse_email_lookup` call rather than one call per person.

### If the target count still isn't met after all of the above

Before padding results with LinkedIn-only contacts, confirm the answer to the "Before starting" question ("OK to include LinkedIn-only people if email comes up short?") if it wasn't already given. If yes: fill remaining slots with qualifying people who have a LinkedIn URL but no confirmed email, up to the target count, and mark them clearly (see Output format). If no, or the user hasn't responded yet, leave the company short rather than guessing, and note how many were found vs. targeted.

## Step 5: If discovery finds nothing at all for a company

Not yet validated end-to-end, but the intended fallback: web-search the company's own site (About/Team/Contact page) for a name and title, then run that name through Step 4's chase order (starting at `search_business_email_by_name`, then the pattern-guessing layer) using the confirmed domain from the exhibitor list.

## Output format

Produce a CSV with these columns:

```
row, company, domain, contact_name, title, seniority, email, email_type, verification_status, linkedin_url, notes
```

- `email_type`: `business`, `personal`, or `none (LinkedIn only)` if this row is a LinkedIn-only fill used to reach the target count (see Step 4)
- `verification_status`: one of `VERIFIED`, `INFERRED - parent company match`, `UNVERIFIED - domain mismatch` (or similar specific reason), `VERIFIED - no email found`, `NOT FOUND`
- For companies with a qualifying person but no email, still include a row with the name/title filled in and email blank — don't just drop them, since the user may want to try outreach another way. If this row is filling the count per the LinkedIn-only fallback (rather than just an incidental "found them, no email" note), say so explicitly in `notes` (e.g. "LinkedIn-only fill — no email found after full chase + pattern-guessing").
- For companies with zero qualifying matches, include one row with company/domain filled in and the rest blank, `verification_status: NOT FOUND`, and a note on what was tried.
- `notes`: free text — why a match is flagged, whether it's a likely duplicate person under two records, whether an email looks stale, etc.

## Efficiency notes

- Batch `reverse_linkedin_lookup` and `reverse_email_lookup` calls using their array params (`linkedin_urls`, `emails`) instead of one call per person — both support up to 100 per call.
- `search_people` result quality varies enormously by company size — large/well-known companies (Simpson Strong-Tie, Geographic Solutions) often resolve cleanly on the first try; small local businesses frequently need every layer above (query variants, pattern-guessing) or genuinely aren't in the database at all. Set expectations with the user accordingly — for a list of small/local exhibitors, expect roughly half to two-thirds to yield usable contacts even with the full process.
- Do an initial small batch (~20 rows) before committing to a full list run, since the failure patterns (name collisions, missing domains) are consistent within a list and worth calibrating for.
