---
name: moltsets-attio-campaign-segment-builder
description: Turn a targeting brief like "VP+ Fintech, 200-1000 employees" into an Attio list, matching existing records first and topping up with net-new people from MoltSets.
---

# Attio Campaign Segment Builder

## Triggers

- "Build a campaign list of VP+ Fintech contacts at 200-1000 employee companies, top up from MoltSets if needed"
- "I need 500 Director+ Marketing contacts at mid-market SaaS companies for a campaign"

## What this skill does

Builds a campaign-ready list against a plain-English brief. It translates the brief into title, seniority, industry, and company size filters, matches against your existing Attio person records first, and only pulls net-new people from MoltSets to make up the shortfall to your target list size. Existing records are never duplicated, and the output is a single Attio list sized to the brief.

## Chain

```
Targeting brief (title / seniority / industry / size / target count)
  to attio/query-records                (existing people matching the brief)
  to compare match count against target list size
  to MoltSets:
      search_people                     (title/seniority/department + industry + employee_range, if shortfall)
  to dedupe candidates against existing Attio records
  to linkedin_to_best_email             (fill email gaps on net-new candidates)
  to attio/create-record                (create net-new person records)
  to attio/add-list-entry               (add existing + net-new records to the target list)
```

## Inputs

- **Targeting brief** - title/seniority, department, industry, employee range, and any other firmographic filter
- **Target list size** - how many total records the campaign needs
- **Existing-record priority** - whether to use existing Attio records first, always, or only above a certain match quality
- **List name** - the Attio list the result should be saved to
- **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_people` filter notes

- **Filters are free precision.** Execution cost comes from the free-text `query`, not the filters attached to it. A filters-only call (no `query`) runs **~40x cheaper**; a bare `query` is the least precise shape available and no cheaper than a filtered one. Never send `query` alone - at minimum attach `country` (~99% filled). Billing is unchanged either way: tokens are charged per record returned.
- **Full filter set:** `query`, `company`, `company_domain`, `country`, `state`, `city`, `seniority`, `department` *or* `functional_area` (same underlying data - never both), `industry`, `linkedin_industry`, `naics_code`, `employee_range`, `revenue_range`, plus `limit`/`offset`.
- **`employee_range` and `revenue_range` apply to the person's current employer**, so you can target by company size or revenue without a `search_companies` pass first. Prefer the numeric bands over legacy `"Small"`/`"Mid-Market"`/`"Enterprise"`/`"Unknown"`.
- **Three industry vocabularies:** `industry` (21 broad buckets), `linkedin_industry` (LinkedIn's ~150 niche labels), `naics_code` (any hierarchy level - "54" broad, "541120" surgical). Pick the one matching how specific the targeting actually is.
- **Fill rates:** `country` ~99%, `title` ~85%, `headline` ~65%, `seniority`/`industry`/`department` ~60%, `naics_code`/`linkedin_industry` ~50%. Every filter is exact-match and silently drops records with an empty field, so stacking three sparse ones can zero out a viable audience. Widen by dropping the sparsest filter first, not `country`.
- **Exact strings:** `"C Suite"` (space, not hyphen), `"Marketing & Advertising"`, `"Medical & Health"`, `"Texas"` not `"TX"`, `"United Kingdom"` not `"UK"`.
- **Size before you walk.** `search_linkedin_profile` with `count_only: true` returns a match count free of tokens; every search response carries `results.total`. Results are `_score`-ranked - the first page is the best page, 25 max per call.
- **Emails are already in the results** - records carry `business_email` and `business_email_risk_score`, so those rows need no enrichment call.

## How it works

1. Parses the brief into structured filters: seniority, department, industry (or `linkedin_industry`/`naics_code` for a niche), country/state, employee range, and revenue range. Aim to express the whole brief in filters - a filters-only `search_people` call needs no `query` at all, and runs ~40x cheaper than the same brief phrased as free text
2. Searches existing Attio person records against those filters first
3. If the existing match count falls short of the target list size, runs `search_people` in MoltSets with the same filters to source net-new candidates. `employee_range` and `revenue_range` apply to the person's current employer, so firmographic targeting needs no `search_companies` pass first. Reads `results.total` before paginating to see whether the shortfall is even coverable
4. Dedupes the candidates against existing Attio records by email and domain plus name
5. Runs `linkedin_to_best_email` on any net-new candidate missing an email
6. Creates the net-new person records in Attio and adds both existing and net-new records to the named list

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 | Company | Title | Seniority | Industry | Email | Risk | Source | Existing/Net-New | Added to List |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |

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

```
Target list size:            X
Existing matches used:       X
Net-new candidates sourced:  X
Net-new added to Attio:      X
Final list size:             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

- Requires the Attio MCP (mcp.attio.com) and the MoltSets MCP connected in the same client
- Prioritising existing Attio records first keeps token spend down and avoids working an account you already have a relationship with
- If the brief is vague on seniority, run a small sample first and confirm the mapping before sourcing the full shortfall
- A per-company cap keeps a single large account from dominating the list at the expense of coverage
- Attio lists can hold a mix of standard and custom object records, so this works for non-person target lists too if your brief calls for it
