# Build a cold outbound list from public company data

You have access to an official company registry, web search, and an
email-enrichment tool. Run this for **[your target company profile: size,
sector, and company age]**.

## 0. Set up (skip if you're already connected)

If you don't already have live tool access for this, connect it first
— this prompt is only as real as the tools behind it:

1. Get a Tulina account: book a demo at https://tulina.ai/book.
2. Add Tulina's MCP server to your assistant: https://mcp.tulina.ai/mcp
3. In Tulina, connect your own API keys for the tools this prompt uses —
   company registry search, web search, and email enrichment.

Once connected, your assistant has real tool access and the rest of this
prompt runs as written. If you also have a job-postings/technology-stack
signal source connected, step 3 gets stronger; the prompt works without it.

## 0b. Already on Tulina? Three things around the run

An account and the MCP server are not the whole setup. Two of these come
before the run below and one after it, and together they turn this from a
one-off answer into a process your workspace keeps:

1. **Activate this process's connectors first.** Call
   `oto_connector(op="list")`, match it against the tools named in step 3
   above, and `oto_connector(op="select", name="...")` every one that
   isn't active yet. Selecting a connector does not mount its tools in the
   conversation you're already in — reach them through `oto_call` for this
   run, or open a fresh conversation once they're all on.
2. **Attach the work to an existing project.** `oto_project(op="list")`
   shows your active org's projects: pick the one this work belongs to
   rather than opening another, and keep its id. The project is where this
   process, the tables it writes to and the connectors it uses hang
   together.
3. **When the run is done, save it as a process — with its graph.** Write
   the body with `oto_procedure(op="set", ...)`, then attach it with
   `oto_project(op="link", project_id=..., target_type="procedure",
   target_ref="<your slug>")`. The body has to carry a drawing: read
   `oto_guide(op="read", slug="procedure-flowchart")` and follow it
   exactly — ONE untagged fenced block in that grammar, opening with the
   trigger and a quoted example of what you'd type to start a run. Tulina
   parses that drawing back into the graph it renders as the process's
   default view, which is what makes it come out in the same style as
   every other process in the app; a drawing the grammar can't read falls
   back to raw characters instead. Saving a process needs org-admin
   rights — without them, hand the finished body to someone who has them.

## 1. List every matching company

- Pull every company matching your target profile from an official
  registry — the fields that matter are sector, headcount band, and
  company age, not revenue: revenue is worth recording where a clean
  filing exists, but a company with none on file is still in scope.
- Filter to active status and the age/size bands you actually want before
  doing anything else — a first pull that's too broad is fine, a filtered
  list is what the rest of this runs against.
- Write the full list to one running table. Don't re-pull per batch.

## 2. Keep companies with a real website

- For each company, one search for its name plus city plus "official
  site." Registry/directory listings (business registries, review sites,
  map listings, marketplace pages) are never the answer — the real site
  is often under a brand name that differs from the legal name, and a
  generic listing snippet usually reveals it.
- Treat this as qualification, not enrichment: a row with no verified
  first-party website doesn't get an email credit spent on it. A website
  that reveals the wrong sector gets dropped, with the reason recorded —
  on a real run, roughly one in four pulled rows turns out to be the
  wrong sector, dissolved, or a subsidiary with no local buyer. Expect
  that ratio; it's not a bug in your query.
- This step is a bulk job — batch it and save results as you go, not only
  at the end. A run that only saves at the end loses everything if
  credits run out mid-batch.

## 3. Tier each company by its signal

In order of strength:

1. **A relevant open role** — anything back-office: order processing,
   billing, admin, customer service. If you have a job-postings source,
   query it by company name directly; searching job boards by keyword
   instead returns staffing agencies hiding the real employer, not the
   company itself.
2. **A known back-office system** — if you have a technology-stack
   signal source, check for it.
3. **Recent, relevant press** — an acquisition, a new location, a
   franchise deal, an investment, a generational handover. Filter hard:
   an old story, a routine mention, or a profile piece isn't a trigger.
4. **Nothing** — still gets an opener, written from the company's own
   site. This is most of the list, not a fallback case to skip.

Record which signal each row got, and the evidence itself (the posting
title, the system name, the headline) — step 5 writes from this, never
from a guess about the segment.

## 4. Find an email for each company

- Submit in batches of 20 through an enrichment waterfall — larger
  batches get rejected outright, and one malformed row can fail the
  whole batch, so validate names are present before submitting.
- Poll until each batch finishes rather than assuming a fixed wait.
- Expect the tool to correct the domain when the real mailbox lives
  elsewhere from the site you found.
- A miss on email that still returns a verified profile match isn't a
  dead end — route that row to a different channel instead of dropping
  it. An email on an unrelated or foreign-HQ domain is the wrong buyer;
  drop that one for real.

## 5. Write one opener per company, then rewrite openers that repeat

Every row gets its own opening line, written from its own evidence —
the signal from step 3, or the company's own site for tier 4. One
sentence, two short ones at most, under 35 words, formal register, no
em dash. It states one specific fact a stranger couldn't have guessed
and turns it toward the actual daily pain in the same breath. Never
invent a fact — if nothing specific is findable, mark that row's
confidence low, and never send a low-confidence line.

**Rotate across several structural frames, so a hundred true lines don't
read as one template:**

- **Density** — a number against a headcount: *"47 accounts on file,
  4 people touching every one of them — a ratio that only tightens as
  the roster grows."*
- **History** — the company's own longevity: *"[Company] has run the
  same regional routes since 1998. The trucks got faster. The paperwork
  behind them didn't."*
- **Flow** — people and volume in motion: *"Three reps on the road, two
  people at a desk turning what they bring back into orders that
  actually ship."*
- **Event, addressed directly** — from the step-3 signal: *"You're
  hiring an ops coordinator this month. Most of that role, on paper, is
  re-entering what already arrived once."*
- **Long tail, plainly** — two flat sentences, no twist: *"[Company] has
  supplied independent retailers since 2003. A lot of small orders,
  entered by hand, one at a time."*
- **Question** — built from a fact on their own site: *"How many of your
  [N] regional accounts still order by phone or email?"*

Then run a mechanical check over the whole batch before anything moves
forward — count, don't eyeball:

| Check | Cap |
|---|---|
| Same structural frame | no more than 30% of the batch |
| Lines built on a colon pivot | 20% |
| Lines closing on the same stock phrase | 20% |
| Same opening two words, grouped | no group over 5% |
| Em dash, or over 35 words | 0 — reject and rewrite |

Rewrite the surplus into an under-used frame and re-run the check. A
batch that hasn't passed doesn't move to step 6 — this catches what
individually-true lines miss when read as a set.

## 6. Stage the campaign for a person to launch

- Route each row to the track matching its signal strength: rows with a
  real trigger (role, system, press) into your highest-touch track,
  everything else into your standard track.
- A simple three-touch structure holds up well: touch one leads with the
  opener and one clear value line; touch two, a few days later,
  references what the delay is now costing them and — if you have real,
  named public customers — names one and asks a different, more direct
  question; touch three, about a week later, closes the loop rather than
  pushing again, and offers to redirect to whoever else might own this.
- Before loading anything, read back what you just wrote and confirm the
  opener field actually changed per row — a write that silently fails
  ships a generic line where a personal one should be, with no visible
  symptom.
- Stage everything, then stop. The agent prepares the campaign; a person
  reviews and launches it. Never call a send/launch action yourself.

## Output

Report: how many companies pulled, how many passed the website gate,
how many landed in each signal tier, how many resolved to a usable
email or fallback channel, how many openers passed the mechanical check
on the first pass vs. needed a rewrite, and the campaign staged for
review.
