---
name: moltsets-find-contact
description: Use this skill when the user wants to find a specific person's contact details by name and company and does NOT already have a LinkedIn URL.
---

# Find Contact

Single-person lookup: name + company → business email.

Prefer `MoltSets:search_business_email_by_name` (name + company → business email and matched LinkedIn URL in ONE call). Fall back to `MoltSets:search_people` (search by name, filter by company) then `MoltSets:linkedin_to_business_email` on the matched URL.

## Triggers

"find contact for [Name] at [Company]", "get the email for [Name]", "who is [Name] at [Company]". Optimised for single-person lookups. Do NOT use if the user already has a LinkedIn URL - use enrich-contact instead.

## Set Expectations

Before firing any calls, give the user a brief, dry heads-up. Deadpan over enthusiastic - no filler, no corporate speak.

Facts to convey:
- Name + company → business email in one call (with a search-and-match fallback)
- ~10 seconds total

## 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_people` filters and best practice

Full request parameter set:

| Purpose | Param | Notes |
|---|---|---|
| Free text | `query` | Searches `full_name`, `first_name`, `last_name`, company name, `title` (~85% filled), `headline` (~65% filled). Names and role keywords only |
| Company name | `company` | Analysed match - every word you pass must appear in the stored name |
| Company domain | `company_domain` | Exact; full URLs are normalised automatically |
| Country | `country` | ~99% filled - the most reliable filter available |
| State / region | `state` | The person's location. Full names - "Texas", not "TX" |
| City | `city` | The exact stored city, **not** its suburbs. Pair with `state`/`country` to disambiguate repeated names |
| Seniority | `seniority` | ~60% filled. Intern, Entry, Senior, Manager, Director, VP, Head, C Suite, Owner, Partner |
| Department | `department` | ~60% filled. Shares its data with `functional_area` - use one |
| Functional area | `functional_area` | Same underlying data as `department` - never send both |
| Industry (broad) | `industry` | ~60% filled. 21 broad buckets |
| Industry (niche) | `linkedin_industry` | ~50% filled. LinkedIn's own ~150 labels - "Staffing and Recruiting", "Wellness and Fitness Services" |
| Industry (code) | `naics_code` | ~50% filled. Any hierarchy level - "54" is broad, "541120" is surgical |
| Employer size | `employee_range` | Filters the person's **current employer** - no company lookup needed first |
| Employer revenue | `revenue_range` | Same - applied to the person's current employer |
| Paging | `limit` / `offset` | 10 default, 25 max (5 on the Free plan) |

**Filters are free precision.** Execution cost is driven by the free-text `query`, not by the filters attached to it.

| Query shape | Relative cost to execute |
|---|---|
| `query` alone | baseline |
| `query` + any number of filters | the same - filters add nothing |
| filters only, no `query` | **~40x cheaper** |

- **Never send a bare `query`.** Attaching `country` costs nothing and removes every other market. A query-only call is the least precise shape available and no cheaper than a filtered one.
- **Drop `query` entirely when filters carry the whole intent.** "VPs of sales at US software companies" is fully expressible as `seniority` + `department` + `industry` + `country`, and runs ~40x cheaper as a pure filter search.
- Billing doesn't change either way - tokens are charged per record returned. This is about speed and search load.
- If all you have is free text, add `country`: highest fill rate of any filter, so it discards almost no legitimate match.

**Don't over-stack sparse filters.** Every non-`query` filter is an exact match and silently discards records where that field is empty. Three ~60%-filled filters can cut a viable audience to nothing. When a search comes back thin, drop the sparsest filter first (`naics_code`, `linkedin_industry`) or shorten the NAICS code - not `country`.

**Exact strings matter.** `"C Suite"` has a space, not a hyphen. `"Marketing & Advertising"` and `"Medical & Health"` use ampersands. `"5001+"` and `"$500k - $1M"` must match character for character. `"UK"` and `"HK"` exist as dirty values - prefer `"United Kingdom"` and `"Hong Kong"`.

**Target by company size without naming a company.** `employee_range` and `revenue_range` apply to the person's current employer, so a `search_companies` pass first is usually unnecessary:

```json
{ "seniority": "VP", "employee_range": "51-200", "country": "United States" }
```

Prefer the numeric bands over the legacy `"Small"` / `"Mid-Market"` / `"Enterprise"` / `"Unknown"` values, which carry far thinner coverage.

**Size the search before you walk it.** `search_linkedin_profile` accepts `count_only: true`, which returns the match count without fetching data or charging tokens. Every search response also carries `results.total` - read it before paginating, then increment `offset` by `limit`. Results are `_score`-ranked, so the first page is the best page.

**Emails come back in the search.** Records already include `business_email` and `business_email_risk_score` - for those rows, no follow-up enrichment call is needed.

## Step 1 - Resolve company domain

User gives company name to infer domain (HubSpot to `hubspot.com`, Salesforce to `salesforce.com`).
Unknown company to ask: "What's their website domain?"

## Step 2 - Look up the business email (preferred)

Call `search_business_email_by_name` with `name` (or `first_name` + `last_name`) plus `company_domain` (preferred) or `company`. It returns the business email and the matched LinkedIn URL in one call. If it returns an email, you're done - skip to Output.

## Step 3 - Fallback: search and match

If the preferred call misses, call `search_people` with the name in `query` plus `company_domain` (preferred) or `company`, `limit: 25`. It searches by name and can filter by company. Scan returned records for the target name in `full_name`:
- **No match on first page** to page with `offset` (25, 50, …); if still nothing with `company_domain`, retry with `company`. Report what you tried.
- **One match** to take it.
- **Multiple plausible matches** to present options, ask user to pick.

If the matched record already has `business_email`, use it directly - no extra call needed. Otherwise call `linkedin_to_business_email` with the matched `linkedin_url`.

Ask after: "Want me to also check personal email or mobile?"

## Output

```
Jane Smith - Head of Marketing, Acme Corp
LinkedIn: https://www.linkedin.com/in/janesmith
Business email: jane@acmecorp.com  risk score: A (validated 2024-11-03)
```

**Found, but outside the accepted set:**
```
Sam Lee - Operations Director, Oldco
LinkedIn: https://www.linkedin.com/in/samlee
Business email: sam@oldco.com  risk score: D - outside your accepted grades
D means a confirmed bounce, complaint or spam trap. Shown for visibility; do not send.
```

Show `Not found` for any empty field.

Always show the risk score next to each email, including when the grade falls outside the accepted set - flag it rather than hiding the result. Add the one-line best practice for that grade (from Step 0) when the score is C, D or F.


## Edge cases

- On the `search_people` fallback, put the person's name in `query` and the domain in `company_domain` (not `domain`); you can also narrow with `seniority`/`department`/`country`/`state`/`city` - filters are free, so add whatever you know
- No match after paging and retry to tell user, ask for more detail (full name, domain)
- Never guess or fabricate LinkedIn URLs - only surface what the tool returns
