Skip to content
← Processes

Enrich contacts with email and phone

A batch of partial contacts gets a real email and phone where one exists, a second provider covering what the first missed, every gap flagged.

Enriching a contact list by hand means opening each profile one at a time, copying what's findable, and giving up on the ones a single tool comes up empty for — which is usually a meaningful slice of any real list, not an edge case.

One provider resolves most of a batch in one async call. The rest is a second, cheaper pass through a different provider for exactly the rows that still need one, and an honest table at the end that shows what worked, what needed the fallback, and what's still missing.

What you get

A clean table, with every contact enriched where the data exists and every real gap flagged rather than dropped.

The part worth getting right

Batching the submission instead of calling per contact is what keeps this fast and cheap — an async batch call processes the whole list in one round trip regardless of size, up to the tool's own limit.

Routing only the real gaps to a second provider, rather than running everything through both, is what keeps the fallback affordable. Most contacts resolve on the first pass; spending a second provider's credits only on the ones that didn't is the difference between an enrichment step that scales and one that doesn't.

Flagging a genuine miss instead of dropping it is what keeps the output trustworthy. A silently shortened list looks clean and hides exactly the contacts a human most needs to look at by hand.

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.

# Enrich a contact list with email and phone

You have access to two enrichment tools: a primary one and a
LinkedIn-based fallback. Run this against **[a list of 1–250 contacts,
each with at least an email, a LinkedIn identifier, or a name and
company]**.

## 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
   — your primary enrichment provider and a LinkedIn-based fallback.

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.

## 1. Assemble the batch

Each contact needs at least one identifying combination: an email, a
LinkedIn URL or slug, or a first name + last name + company. Attach a
reference ID to every contact (your own row id) so results can be
mapped back cleanly — a batch call doesn't guarantee it returns rows in
the order you sent them. If a contact might need the LinkedIn fallback
later, keep its LinkedIn identifier on hand from this step; the
fallback tool requires it.

An under-specified contact doesn't fail the whole batch — the
enrichment tool processes what it can and reports an error or warning
for that one row.

## 2. Submit and poll for results

Submit the whole batch in one call, not one request per contact — batch
size and per-item size limits apply, so split into chunks if you're
over them. The call returns immediately with a request id; the
enrichment runs asynchronously. Poll for the result starting around 30
seconds after submission, then every 20–30 seconds until it's done.

When results come back, treat email quality labels literally: a
personal, verified email at the company's own domain is the best
outcome; a generic mailbox is usable but weaker; anything marked
invalid counts as no email at all, not a low-confidence one.

## 3. Backfill missing emails through the fallback

For every contact still without a usable email, and that has a LinkedIn
identifier: call the fallback tool for email only — don't spend extra
credits pulling phone here, that's a separate concern. If it finds an
email, use it and mark its source as the fallback provider so the final
table shows where each field came from. If it finds nothing, or the
contact has no LinkedIn identifier to fall back on, leave it blank and
flag it in the final step — never drop the row.

## 4. Present the results, flagging gaps

Return one clean table: name, best-qualified email with its source,
phone/mobile if found, and any relevant company data the enrichment
returned. Flag every contact where neither provider found a usable
email — don't drop them silently, since a flagged gap tells a human
exactly who still needs manual work.

This is a standalone enrichment step — it doesn't write anything back
to a CRM. If you want the results pushed somewhere, do that as an
explicit next step, not automatically.

## Output

Report: total contacts submitted, how many resolved on the first
provider, how many needed the fallback and how many of those resolved,
and the final list of contacts still missing an email.

Questions about this process

Yes — an agent can submit a batch to an enrichment provider, then route anyone still missing an email through a second, LinkedIn-based fallback, flagging genuine misses instead of dropping them.

Just one identifying combination — an email, a LinkedIn identifier, or a name plus company. An under-specified contact doesn't block the rest of the batch, only that row.

Not by default — this is a standalone enrichment step with no CRM write. Pushing the results somewhere is a separate, explicit step if you want it.

Most contacts resolve on the first pass. Routing only the real misses to a second, cheaper fallback is what keeps enrichment affordable at scale instead of running everything through both.

Related processes

  • Source and qualify leads

    A target company gets searched across LinkedIn, ranked against your ICP, and the qualified shortlist lands in your CRM with email and mobile filled in.

Experience Tulina for 14 days.

By continuing, you agree to our Terms of Use and Privacy policy.