---
ref: workflow:hiring-signal-outbound
title: Email companies whose job posts show they need your product
author: thedogwiththedataonit
tools: [tool:exa/search-web, tool:typesafe/answer-typed-questions, tool:hunter/search-domain, tool:lemlist/add-lead-to-campaign]
tags: [capability:classify-signals, capability:enroll-in-sequence, capability:find-work-emails, capability:search-web, channel:email, has:api, motion:outbound]
updated: 2026-09-29
---

# Email companies whose job posts show they need your product

Finds new job posts for roles you support with Exa, checks each for a real need or a competitor with Jev, and emails the hiring team.

Set up the tools below, then run the steps in order for the user, carrying each step's results into the next. The run is done when the user has the outcome below.

## Outcome

- A table of every job post found, with the company, the role, whether it signals a need, the competitor it names and Jev's confidence.
- The buyer at each company with a real need, with their work email.
- An approved opening line for each buyer that points to the job post, queued in your lemlist campaign, or in your switching campaign when the post names a competitor.

## Inputs

Ask the user for these before you start.

- `role_queries`: the job posts that signal a need, as searches, e.g. hiring first revenue operations manager; SDR team lead job
- `category`: what your product does, in a buyer's words, e.g. sales engagement software
- `competitors`: the tools a job post might name that you replace, e.g. Outreach, Salesloft
- `job_boards`: the job board domains to search, e.g. greenhouse.io, lever.co, ashbyhq.com
- `lookback_days`: how recent a post counts, e.g. 14
- `min_confidence`: how sure Jev must be before its answer is used without you, e.g. 0.8; a yes-or-no answer counts as yes at or above it and as no at or below 1 minus it
- `buyer_titles`: who to email at each company, e.g. VP Sales, Head of Revenue Operations
- `max_lookups`: the most company domains to search for a buyer in one run, since each search spends Hunter credits, e.g. 50
- `campaign`: the lemlist campaign that sends the emails, set up once with a `job_post_line` custom variable, e.g. Hiring signal
- `switch_campaign`: the lemlist campaign for buyers whose post names a competitor, with copy about switching and the same `job_post_line` variable, e.g. Hiring signal: switching

## Set up

### Search the web (Exa, tool:exa/search-web)

Use the first option your agent supports.

Note: On MCP, domain, date and category filters need `web_search_advanced_exa`: list it in the server URL's `tools` parameter with every other tool you want, since the list replaces the defaults.

#### MCP (official, remote)

Add this server to your agent's MCP settings, then sign in when asked.

```json
{ "mcpServers": { "exa": { "url": "https://mcp.exa.ai/mcp?login" } } }
```

Call the MCP tool `web_search_exa`.

#### API (official)

- Base URL: https://api.exa.ai
- Endpoint: `POST /search`
- Auth: send the header `Authorization: Bearer $EXA_API_KEY`
- Get a key: https://dashboard.exa.ai/api-keys

### Answer typed questions (TypeSafe AI, tool:typesafe/answer-typed-questions)

Use the API.

- Base URL: https://api.typesafe.ai
- Endpoint: `POST /v1/systemone`
- Auth: send the header `Authorization: Bearer $TYPESAFE_API_KEY`
- Get a key: https://console.typesafe.ai

Note: Put every question about one record in one request. `GET /v1/models` lists the models and is the cheapest check of a key; a request over the rate limit gets a 429 with a Retry-After header.

Note: Send `state`, `model` (`jev-latest`) and named `questions`, each with `type`, `instructions` and `criteria`: a choice maps labels to descriptions, a score lists its levels in order, a noul's is optional. Read a score by its most likely level; send unsure answers to a person.

### Search a domain for emails (Hunter, tool:hunter/search-domain)

Use the first option your agent supports.

Note: Returns 10 emails by default; page through the rest with `limit` and `offset`.

#### MCP (official, remote)

Add this server to your agent's MCP settings, then sign in when asked.

```json
{ "mcpServers": { "hunter": { "url": "https://mcp.hunter.io/mcp" } } }
```

Call the MCP tool `Domain-Search`.

#### API (official)

- Base URL: https://api.hunter.io/v2
- Endpoint: `GET /domain-search`
- Auth: send the header `X-API-Key: $HUNTER_API_KEY`
- Get a key: https://hunter.io/api-keys

### Add a lead to a campaign (lemlist, tool:lemlist/add-lead-to-campaign)

Use the first option your agent supports.

Note: The campaign automates outreach to its leads: confirm it first. `findEmail`, `verifyEmail`, `findPhone` and `linkedinEnrichment` spend credits, and a lead already in the campaign is refused with `400 LEAD_ALREADY_IN_CAMPAIGN`.

#### CLI (official)

Install the command and sign in with it, then confirm it runs.

```sh
npm install -g @lemlist-official/cli
lemlist --version
```

Run `lemlist api POST /campaigns/{campaignId}/leads/`.

#### API (official)

- Base URL: https://api.lemlist.com/api
- Endpoint: `POST /campaigns/{campaignId}/leads/`
- Auth: send the header `Authorization: Basic $LEMLIST_API_KEY`
- Get a key: https://app.lemlist.com/settings/integrations

Note: `LEMLIST_API_KEY` holds the base64 of `:<api key>`, an empty username and the key; 20 requests every 2 seconds per key.

Before step 1, confirm access to each service with its cheapest read-only call, like a list or a search. Never send or change anything to test access.

## Steps

1. **Find job posts** with Search the web (Exa). Search each of `role_queries` on `job_boards`, published in the last `lookback_days`, with each page's text. Keep each post's URL, job title, company name, the company's own website domain when the post shows it, and the text; drop duplicates.
2. **Read each post** with Answer typed questions (TypeSafe AI). Send the job title and text as the state, with a choice `signal` of exact (the role that uses `category` or that `category` would replace), adjacent (a role that works alongside `category`) and generic (a general hiring push unrelated to `category`), a noul `names_category` (the post says the team will buy, build or run `category`), and a choice `competitor` over `competitors` plus none. Keep every answer with its confidence or probability.
3. **Keep the real needs** yourself. Keep the posts whose signal is exact or whose `names_category` is yes, one per company. Show the user the posts whose signal or competitor confidence is below `min_confidence` or whose `names_category` is unsure, and keep what the user decides.
4. **Find the domains** with Search the web (Exa). For each kept company whose post showed no domain, search company pages for its name. Keep the domains you can confirm, and drop the companies you can't.
5. **Find the buyer** with Search a domain for emails (Hunter). Search each kept company's domain, up to `max_lookups`. Keep the person whose position best matches `buyer_titles`, with their name, position and email; list the companies with no match.
6. **Write the opening lines** yourself. Draft one sentence per buyer that names the role they are hiring for and the problem it hints at, with no pitch. Show the lines to the user.
7. **Queue the emails** with Add a lead to a campaign (lemlist). After the user approves, add each buyer whose post named a competitor to `switch_campaign`, and every other buyer to `campaign`, with their approved line in `job_post_line`.

## Notes

The line points at the role the company is hiring for, not at your product; the campaign's own copy makes the pitch. Keep `switch_campaign` about what changes when a team moves off the competitor.

Run it weekly with `lookback_days` set to 7 so each post is seen once.

## Rules

- Use only the services set up above. The read-only calls they need, like listing ids or polling for results, are fine.
- Ask the user before anything that sends messages, costs money, or changes data, and say how many records it touches. One approval covers a batch the user has seen.
- Never print API keys.
