---
name: getleads-contact-list
description: Finds the people to contact at each company on a list. Exports everyone GetLeads has at each company, builds an org chart per company, and has AI (Jev) pick everyone likely to have an impact on buying, deciders first (or one decider per company), from their title, headline, About and the company's size, then finds their emails. Use after getleads-company-list (https://getleads.io/company-list/), or when someone says "who should I contact at these companies", "find the decision maker at each account", "pick the right person per company", or hands over a list of companies and wants contacts.
---

# Pick the people to contact at every company

Export everyone at each company, then choose. Filtering the export loses the people who matter: seniority is guessed from the job title and is missing or wrong for about 1 in 8 decision makers (roughly 88% accurate), title filters match whole words only, and most people have no email in the database until they are enriched. `lookup_decision_makers` returns only people with a verified email, so it is not used here.

**Keep the user with you.** Before each stage, say in one line what it does; after it, show the result as a table: people pulled and dropped per company, then the picks with their titles, then emails found.

## 1. Brief

Take the company list: `keep.csv` from getleads-company-list (https://getleads.io/company-list/) (it carries `company_id`, which makes step 3 exact), or the user's own file with a website or domain per company.

Ask what they sell and who signs off on it at their customers, then write `brief.json`:

```json
{
  "offer": "One or two sentences: what the user sells.",
  "buyer": "Who signs off on it at their customers, by company size if that changes it.",
  "avoid": ["Optional: people never to contact, one condition per item, e.g. an intern"]
}
```

The titles of the people who actually bought at their existing customers make the best `buyer`.

## 2. Pull everyone

One `export_contacts` per batch of companies, with `domains` set to each company's website host and no seniority, title, email or `max_per_company` filter, and these `columns`:

`contact_id, full_name, current_title, seniority_level, department, linkedin_headline, about_person, linkedin_url, work_email, email_status, role_start_date, company_id, company_name, company_website, company_linkedin_url, linkedin_employee_count_exact`

- About 25 companies per batch, fewer once their headcounts add up past 5,000. Start every batch's export, then check them, so they run together. Save each as `pulls/<batch>.csv`.
- Don't count first: people-at-company counts often time out.
- A company whose website is a page on another company's site (airbus.com/…), a link shortener or a site builder would pull that site's whole staff by domain. Pull those by `company_name` instead; step 3 drops namesakes.
- An export that returns exactly 50,000 rows is full, whatever `cap_reason` says: split the batch.

## 3. Org charts

```bash
python3 scripts/org_charts.py --companies keep.csv --pulls pulls/*.csv --out run
```

It keeps only people who work at each company, matched on `company_id` when the list has it, otherwise the company's website, then its name. People exported under a company's domain who work elsewhere or nowhere ("Retired") are dropped. Companies of 50 or fewer keep everyone. Bigger ones keep the leadership layer, up to 60 people, most senior first: owners, founders, C-level, VPs, heads and directors, plus anyone with no seniority recorded whose title or headline reads senior.

Show the user `summary.json`: rows pulled, dropped as another company, and companies with nobody.

## 4. Pick

It needs `pip install typesafe-sdk` and `TYPESAFE_API_KEY` (typesafe.ai) in the environment, `.env` or `~/.agents/.env`.

```bash
python3 scripts/pick_contacts.py --charts run/org_charts.jsonl --brief brief.json --out run --limit 10
```

Jev rates each person in a company's org chart as having no impact on buying the offer, having an impact, or deciding, reading them against the rest of the chart and the company's size: at a small company more of the team weighs in, at a larger one mainly the team the offer serves. By default everyone likely to have an impact is kept, deciders first. `--mode top` keeps one decider plus up to two backups instead (`--picks` sets how many); use it when the user wants one contact per company.

Read the ten companies' picks with the user against their charts. If the picks lean the wrong way, fix `buyer` and re-run (answers are cached); then drop `--limit`. 25 companies take seconds. Re-run until `failed_requests` and `not_fully_judged` in `summary.json` are 0: a company is never picked from a partly judged chart.

No TypeSafe key: `--no-judge` writes `run/to_judge.jsonl`. Judge each company's chart yourself against the brief, keeping who each line's `keep` says (it follows `--mode`), write `picks.json` as `{"<key>": ["p3", "p0"]}` (deciders first, `[]` when nobody fits), then run the same command with `--agent-picks picks.json` instead of `--no-judge` to write the outputs. `role` and the scores stay blank for your picks.

## 5. Emails and delivery

Most picks have no email in the export. Run `getleads_get_emails_from_linkedin_batch` on the picks' `linkedin_url`s. The answer also says whether each person still holds the role: drop a pick whose `current_role_status` is `past`.

Deliver from `run/`:
- `contacts.csv`: the people picked at each company, deciders first, with their `role` (decides or has an impact) and the emails found.
- `no_pick.csv`: companies with no pick, the `reason` (nobody in the data owns the area, or the best person was ruled out by an `avoid` rule) and the best person found. Offer those as they are or leave them out.
