Build a target list from a sentence
Describe the companies you want in one sentence, and get them sourced, scored on dated signals, enriched, deduplicated against your CRM, and staged as a campaign.
- TriggerTriggeredClaudeChatGPT
Natural language input in chat
e.g. “Logistics companies in France, 250 to 1,000 employees, hiring supply chain roles. Source 40 accounts.”
Run - Completed
Apollo.ioSource accounts and ICP personas
- CompletedHubSpot
Deduplicate against the CRM
- Completed
TheirStack
Serper
ApifyBuying signals only for what survives
- Completed
Apollo.io
FullEnrichCascade for email and phone
- CompletedClaudeChatGPT
Ground each opener in a signal
- Completed
LemlistHubSpotStage the campaign and write back
Building a list is a morning of tab-switching. Someone opens a database, guesses at the filters, exports a few hundred rows, pastes them into a sheet, spot-checks a dozen companies by hand, gives up on checking the rest, and hands over a list whose quality nobody can describe.
The sentence in someone's head is the real specification, and it never survives the trip into a filter panel intact. Meanwhile the evidence that would qualify those companies properly is public, dated and sitting behind an API: who they are hiring, what they run, what they announced last quarter, what their own pages say. The join between the two is the whole job, and so is knowing which of those calls to pay for and in what order.
What you get
A list that matches the sentence you wrote, scored on dated evidence with a source URL behind every claim, enriched with verified addresses and real direct numbers, deduplicated against your CRM, and staged as a campaign ready for a person to launch.
The part worth getting right
Print the inferred filters before spending anything. A sentence is genuinely ambiguous and a filter panel is not, so the ambiguity has to be resolved somewhere, and the only cheap place is out loud, before the search runs. "Mid-sized" resolved to 50 to 250 when the reader meant 250 to 1000 is a completely different list, and finding out afterwards means paying for the wrong one twice.
Every step after that is a gate on the next one, and the order is the design. Sourcing is nearly free, scoring on what the search already returned costs nothing at all, finding people costs, signals cost more, and enrichment is billed per lookup. So the CRM check runs before either of the expensive steps rather than after: a hundred and twenty contacts you already have in pipeline are a hundred and twenty lookups you can simply not buy, and the check that finds them is free. Signals are bought only for companies that still have a reachable contact, because research on a company you cannot reach is research you paid for and cannot use.
Enrichment cascades, and the second provider is not a second opinion. The contact database answers first and a waterfall picks up what it missed, which is the only way to get the hit rate up on a real batch. But a waterfall will sometimes return the exact address the first provider already flagged as hard-bounced, with nothing on the record to say it is dead, so the two answers get compared rather than the later one quietly overwriting the earlier. Same story on phone numbers: a search result will claim a direct line for almost everybody and then hand back the company switchboard, identical for every contact at that company, which is worth keeping and worth labelling but is not a direct line.
Two tiers of opener keep the personalisation honest. Where there is a signal, the email names it and carries the URL it came from, so the recipient can go and look. Where there is not, pretending otherwise produces the vague gesture at relevance that makes outbound read as automated, and a plainer email that claims less is the better failure mode.
The prompt
The real prompt behind the steps above — detailed enough that an agent with a people-search tool, email/phone enrichment, and CRM access can run it as written.
# Build a target list from a sentence
You have access to a B2B contact database, a hiring and technology
signal source, a web search API, a hosted scraper, a waterfall
enrichment provider, a CRM, and an outreach tool. Run this for **[one
sentence describing the companies and people you want]**.
## 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. Create a free account at app.tulina.ai.
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: a B2B contact database, a hiring and technology signal source,
a web search API, a hosted scraper, a waterfall enrichment provider,
your CRM, and your outreach tool.
Once connected, your assistant has real tool access and the rest of
this prompt runs as written.
## 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.
## The spend rule, which governs every step below
Each step is a gate on the next. Sourcing is nearly free,
firmographics are cheap, people search costs, signals cost more, and
enrichment is billed per lookup. **Nothing expensive is ever bought
for a row that has already failed a cheaper test.** If you find
yourself enriching before deduplicating, or buying signals for a
company you have no contact at, you have inverted the order and are
spending money to learn something you were about to throw away.
## 1. Turn the sentence into filters, and show your work
Read the sentence and write out the structured filters you inferred
from it before running anything: headcount range, sectors, countries,
seniority, job functions. Print them.
This step exists so a wrong reading is caught in five seconds rather
than after eight hundred credits. "Mid-sized logistics companies in
France" has to become an explicit headcount band, and if your band is
50 to 250 and the reader meant 250 to 1000, they need to see that
before the search runs, not after.
Anything the sentence did not specify stays unset. Do not invent a
country filter because most customers are French.
## 2. Source the companies, then score them before spending
Run the company search with those filters. Ask for the count first,
then decide:
- Under 50 results, the filters are too narrow. Report that and say
which filter to loosen rather than proceeding with a list nobody can
run a campaign on.
- Over 2,000, they are too broad. Report the count and ask before
spending anything.
**A wide net costs the same as a narrow one.** Most databases bill per
page of results, not per filter, so widen the keywords and narrow
afterwards. Splitting a vertical into several single words returns far
more than one compound phrase does.
**The result set will not be the vertical you asked for.** Keyword
tags are applied loosely and a search for wholesale distribution will
return cosmetics manufacturers and EV charging networks alongside the
real matches. Filter on the industry classification code before
writing any rows, and give a reason for anything that survives
outside it.
Then score every company that made it through, using only the data
the search already returned. No paid signal is bought at this stage.
Rate each one on the criteria the sentence implies, band them into
three tiers, and stop working the bottom tier entirely. This is the
cheapest filter you will ever run and it decides what the expensive
steps operate on.
## 3. Find the people worth reaching
For the top two tiers only, find contacts matching the seniority and
function from step 1. Cap it at two or three per company. More than
that and you are emailing an entire department, which gets noticed
internally and reads as a blast.
A company with no reachable contact is not a target. Mark it and stop
spending on it, whatever it scored.
## 4. Deduplicate against the CRM, before you buy anything else
Check every contact against the CRM. Match on email where you have
one, and on company domain where you do not, because a bare name match
will happily return a different person at a different company.
Drop anyone who is already an open opportunity, an existing customer,
or under active sequence by a colleague. Keep the CRM record id
against the ones you keep, so the write in step 8 updates rather than
duplicates.
Do this before signals and before enrichment, not after. Both are
billed per lookup, and paying to research a company you already sell
to is pure waste. A list of eight hundred contacts where a hundred and
twenty are already in pipeline is a hundred and twenty lookups bought
to rediscover what you were already sitting on, and the check that
avoids it is free.
## 5. Buy signals only for what survived
Now, and only for companies that still have a live contact, gather the
evidence the outreach will be built on. Three sources, each answering
something the others cannot:
- **Open roles and the technology behind them.** A company hiring for
the function your product serves is the strongest single signal
available, because it is dated, public and carries a URL. The stack
matters too, in both directions: a tool you integrate with is a
reason to call, and a direct competitor already installed is a
reason not to.
- **Recent events, from web search.** Funding rounds, acquisitions,
new facilities, leadership changes in the function you sell into.
Set the country explicitly if the market is not the search
provider's default, or you will get the wrong region's results.
- **The pages themselves, from a hosted scraper.** Where the search
returns a thin result, run a scraper against the source the result
points at, the careers page, the company profile, the location
listing. This is what gets you the detail a snippet drops.
Three things that will mislead you if you do not expect them. The
signal source may return **duplicate company records for one domain**
with different headcounts, so take the union of what they say rather
than the first row. It will have **no record at all for many mid-size
firms**, which is a normal result and not an error. And it **misses
parts of the stack**: where a layer that sits on top of a system
appears with nothing under it, the system underneath is almost
certainly present but unnamed, and the contact database's own
technology field is a usable second opinion.
**Re-check independence here, because this is the only step that can.**
Search explicitly for the company being acquired, merged or absorbed.
Firmographic data goes stale, and a company that was folded into a
group last quarter can still score at the top of the batch on
yesterday's record. If it has been, stop and say so with the source
URL. No score survives that.
Write every finding with its source, its type, the detail, its URL and
the date it was captured. **The URL is what lets a human check the
claim before repeating it in an email.** A company where you found
nothing keeps its score and gets recorded as having no signal. An
account with no signals is allowed; an invented signal is not.
## 6. Cascade for email and phone
Enrich only what is left. Take the contact database's own match first,
then fall through to the waterfall provider for whatever it missed.
**Read the contact layer, not just the person layer.** A record can
report an address as verified at the top level while its own contact
detail carries a hard-bounce flag and no deliverable address. That
address has already bounced. An address is only usable when both
layers agree, and sending to one that a provider itself has flagged as
dead is how a sending domain gets burned.
**The waterfall is not an independent check.** Run it where the first
provider returned nothing usable and a profile URL is known, then
compare what comes back against the address the first provider already
rejected. A fallback that hands you the same dead address, with no
indication that it is dead, is not a second source. Only treat it as
new information when it differs.
**Phone data promises more than it delivers.** A search result will
report a direct line for almost everyone, and the matched record then
returns the company switchboard, identical for every contact at that
company. Only a mobile or a direct number is a real number. Write the
switchboard if it is all there is, and label it as such so nobody
calls it expecting a desk to ring.
Record the source of each address and each number honestly. "None" is
a valid value and is the right value for a bounced address. Where
nothing answers at all, mark the contact unreachable rather than
dropping the row, and keep it if the name and title are good, because
a name and a title are enough for a call even when a send is not
possible.
## 7. Write the opener on top of a signal
Write in two tiers. Where a company carries a signal, the opener names
it and cites the URL it came from, so the recipient can go and look.
Where it does not, the plain version goes out and claims less.
Pretending otherwise produces the vague gesture at relevance that
makes outbound read as automated, and a plainer email is the better
failure mode.
Rules that hold in both tiers:
- The opening detail comes from a captured finding on **that** company.
No hook is ever invented to fill the slot.
- An observation about their business, never a compliment. Zero
flattery.
- Plain text. No markup, no links, no signature block. Sequence copy
is usually HTML and a variable is dropped into it verbatim, so an
ampersand or an angle bracket in the text will break the markup
around it.
- Some findings are true, relevant and still not openers. A disclosed
control weakness or a restructuring reads as an attack when it opens
a cold approach. Keep it in the notes as second-conversation
material and open on something structural.
## 8. Stage the campaign and write the list back
Two writes, and neither of them sends anything.
**To the outreach tool:** load the campaign as a draft and attach each
contact's own opener as a per-contact variable that the sequence
resolves at send time. Read the campaign's copy first and write down
every variable it references, because a variable with no value on a
contact is a contact the tool holds back rather than sends. Before the
first real add, validate one payload against the campaign without
adding anyone.
**To the CRM:** read the field definitions first, every run, because
field keys are per-workspace and hardcoding them is how a run writes
into nothing. Upsert the company, then the person, passing the record
id you kept in step 4 so an existing record updates instead of
duplicating. Attach the signals and their URLs as a note, and check
whether you already wrote one for this contact: notes usually cannot
be updated, so a second run writes a second note. Tag everything with
this run's name and date.
Then stop. The launch is a person's decision.
## Output
Report: the filters you inferred, companies found, how many survived
scoring, contacts sourced, how many were dropped as already known and
why, how many companies carried a signal and how many did not,
enrichment hit rate split by provider, how many addresses were
rejected as undeliverable, and the campaign's draft status.
Questions about this process
Related processes
- Brief every cold call before it starts
Every account on the call list gets a note before anyone dials, carrying the signals found on it, what the lead has said publicly, and one opener grounded in both.
- Find buyers engaging with competitors
Everyone who engages with a competitor's posts gets found, scored against your ICP, enriched, and staged for outreach with a human approving the list.
- Outreach based on government data
Every candidate's website is verified before a credit is spent, tiered by signal strength, then written one grounded opener that is checked for repetition.