HeyReach Campaign Launchpad V2 — Prospeo-sourced
V1 started from a CSV or an existing list. V2 sources the leads for you from Prospeo: the user describes
their ICP in plain English, the skill finds and enriches matching people, imports them to HeyReach, and then
builds the campaign exactly like V1. Two reference files do the heavy lifting:
references/prospeo-api.md (the API spec) and
references/icp-to-filters.md (how to turn an ICP into search filters).
Triggers
"find me [role] at [type of company] and launch a campaign", "build a campaign targeting …", "source leads and
start outreach", "I don't have a list — find the people and run a campaign", "prospect + campaign".
Use V1 (heyreach-campaign-launchpad) instead when the user already has the leads (a CSV or an existing
HeyReach list) and just wants to launch.
Prerequisites
- Prospeo API key —
export PROSPEO_API_KEY=... (sent as the X-KEY header).
- HeyReach workspace key —
export HEYREACH_API_KEY=... (verify with heyreach auth check).
- At least one connected, authenticated LinkedIn sender (
heyreach li-accounts list → authIsValid: true).
Set expectations
This skill spends Prospeo credits and launches real outreach. Be calm and explicit:
"I'll ask a few quick questions, show you exactly how many leads and credits before extracting anything,
and nothing is pulled or charged until you confirm."
The golden rule of the question flow
Ask ONE question at a time, as selectable options, using the AskUserQuestion tool — that renders as
clickable choices in the app and numbered (press 1/2/3) choices in the terminal. Wait for each answer before
the next question. Free-text is only for the two things that can't be enumerated: the ICP description and the
offer/copy instructions.
How it works
0. Auth + account check (read-only, no charge)
heyreach auth check
curl -s https://api.prospeo.io/account-information -H "X-KEY: $PROSPEO_API_KEY"
From the Prospeo response, note response.current_plan and response.remaining_credits — you'll need both for
the cost gate and for plan-gating. Confirm a healthy HeyReach sender exists.
1. Ask the ICP (free text)
"Who do you want to target? Describe your ideal customer in your own words — role, type of company, location,
size, anything that matters."
2. Translate ICP → Prospeo filters, then confirm (selectable)
Using references/icp-to-filters.md:
- Pick the sourcing mode — attribute search (default), named-accounts (user gave target companies/domains →
company.websites), or enrichment-only (user already has LinkedIn URLs/emails → skip search).
- Build the
filters object. Validate every location / job-title / industry / technology value against
POST /search-suggestions (free) so you use Prospeo's canonical strings, not raw user text.
- Respect plan-gating: if a needed filter is above
current_plan, either drop it or flag it.
- Play the interpretation back and let the user confirm or adjust with
AskUserQuestion, e.g.:
"Here's how I read that: VPs/Heads of Sales · Series B SaaS · United States · verified emails only. Good to go?"
Options: Looks right — continue · Adjust targeting · Add target companies
- Ask 1–2 selectable clarifiers only if genuinely ambiguous (seniority band, region, "verified emails only?").
3. 🚦 STRICT count gate — nothing is extracted before this
Do not call /search-person or /bulk-enrich-person until the user has explicitly confirmed the data depth,
the count, AND the cost. This is a hard stop.
Data depth — ask with AskUserQuestion:
"What do you need for each lead?"
Options: LinkedIn only (cheapest — all a LinkedIn campaign needs) · LinkedIn + verified email · LinkedIn + email + mobile
A LinkedIn connection-request + message campaign needs only the profile URL, which search already returns —
so LinkedIn-only is the default and ~25× cheaper. Choose email/mobile only for an email/multichannel step
or to export contacts.
Ask how many leads with AskUserQuestion (presets + custom): 25 · 100 · 250 · 500 · Custom.
Estimate credits by depth (search = 1 credit / 25 results; email = 1 credit/found; mobile = 10/found):
- LinkedIn only →
ceil(N / 25) (e.g. 250 leads ≈ 10 credits)
- + verified email →
ceil(N/25) + up to N
- + mobile →
ceil(N/25) + up to 10 × N
Enrichment only charges on found, so actual ≤ estimate.
Compare to remaining_credits. If short, offer a smaller N, a lighter depth, or stop.
Final confirmation with AskUserQuestion:
"Extract up to {N} leads ({depth}) — about {est} credits (you have {remaining_credits}). Proceed?"
Options: Yes, extract now · Change the number · Cancel
Only on Yes do you proceed to step 4. If the user never confirms, stop — do nothing.
4. Extract
Per references/prospeo-api.md:
- Search loop (always):
POST /search-person with {filters, page}, page = 1,2,3… (25/page) until you've
collected N person_ids or pagination.total_page is exhausted. This already returns the LinkedIn URL,
name, title, company, location, and headline — everything a LinkedIn campaign needs. Self-throttle to the
plan's search rate limit.
- Enrich loop (ONLY if the user chose email/mobile in step 3): batch the
person_ids into groups of 50 →
POST /bulk-enrich-person with data:[{identifier, person_id}], only_verified_email: true (and
enrich_mobile: true only if mobiles were chosen). Collect matched[]; drop not_matched / UNAVAILABLE.
Skip this entirely for LinkedIn-only — no enrichment credits spent.
- Report the real result: "Sourced {found} leads ({depth}) · {credits} credits used."
5. Import to HeyReach
- Create the list:
heyreach lists create --name "<ICP> — <date>".
Known CLI quirk: lists create prints an unmarshal error but the list is created — get its id from the
error body or heyreach lists list --keyword "<name>".
- Map each enriched record and
heyreach lists add-leads in batches of ≤100. Field mapping:
Prospeo (matched[].person / .company) |
HeyReach lead field |
linkedin_url |
profileUrl (required) |
first_name / last_name |
firstName / lastName |
company.name |
companyName |
current_job_title |
position |
enriched email.email |
emailAddress |
location.city / country |
location |
headline, company.industry, recent job-change, etc. |
customUserFields: [{ "name": "...", "value": "..." }] → usable as {VARIABLE} |
(Use the {name,value} shape — it's the validated one. {fieldName,fieldValue} is rejected.)
emailAddress is only populated when the user chose an email/mobile depth in step 3 — LinkedIn-only runs leave it empty, which is fine for a connection-request + message campaign.
6. Copy fork (selectable)
Ask with AskUserQuestion:
"How do you want the messages written?"
Options: I'll write my own · Let AI write it from my offer
- Write your own → user pastes their connection note / messages; you place them into the chosen sequence verbatim.
- AI writes it → ask (free text) for their offer + any rules ("what do you sell, to whom, the angle, tone").
Generate short, value-first messages using the personalization tokens available from the imported fields
(
{FIRST_NAME}, {COMPANY}, {POSITION}, plus any custom field). Every MESSAGE/INMAIL must have a
fallbackMessage. Show the drafts and let the user approve/tweak before applying.
7. Build & launch the campaign (identical to V1)
Hand off to the standard flow — use the heyreach-sequence-templates skill for the sequence:
- Pick a sequence template matching connection degree; drop in the copy from step 6.
- Set the schedule (audience timezone), assign senders, set exclusions.
- Assemble
heyreach campaigns create (sequence rules that the live API enforces: CONNECTION_REQUEST needs a
payload, every non-root node incl. END needs actionDelay ≥ 3h, every MESSAGE/INMAIL needs a fallbackMessage).
- Run the pre-flight checklist, then
heyreach campaigns start, verify IN_PROGRESS, and print the launch summary.
Cost & safety
- LinkedIn-only is the default and ~25× cheaper. Search returns the LinkedIn URL + profile for ~1 credit per
25 leads; emails cost ~1 credit each. A connection-request + message campaign needs only the profile URL, so
skip email enrichment unless running an email/multichannel step or exporting contacts.
- The count gate (step 3) is non-negotiable — never search or enrich before explicit confirmation.
- Prefer
only_verified_email: true so credits are only spent on usable emails.
- Mobiles cost 10× — only request them if the user explicitly wants phones.
- Pace requests to the plan's rate limits (search 1–5/s, enrich 5–30/s); read the
x-*-request-left headers.
INSUFFICIENT_CREDITS (HTTP 400) is the safety net if an estimate runs short mid-extraction — stop and report.
Edge cases
PLAN_REQUIRED (400) — a filter needs a higher Prospeo plan; the filter_error names it. Drop the filter or tell the user which plan unlocks it.
NO_RESULTS — filters too narrow; suggest loosening one constraint (per icp-to-filters.md).
- Low enrich match rate — normal; search finds people, but not everyone has a verified email. Report found vs. requested.
- 25,000-lead ceiling per search, and a single large pull can exhaust the daily search quota on lower plans — warn for big N.
- Connection degree, USER_LIST type, 100-leads-per-add-leads — same rules as V1.
References
references/prospeo-api.md — endpoints, the two-step pipeline, credit model, rate limits, plan-gating.
references/icp-to-filters.md — ICP → search-person filters, value validation, worked examples.
- heyreach-sequence-templates — the sequence JSON + the live-validated node rules.
1---2name: heyreach-campaign-launchpad-v23description: Source leads live from Prospeo by describing your ideal customer in plain English, then build and launch a HeyReach LinkedIn campaign — end to end. Use when the user wants to find/source prospects AND run outreach: "find me [role] at [type of company] and launch a campaign", "build a campaign targeting X", "source leads and start outreach", or "I don't have a list yet, find the people". The skill asks guided, selectable questions one at a time, translates the ICP into Prospeo search filters, confirms the lead count and credit cost before extracting anything (strict gate), imports the leads to HeyReach, lets you paste your own copy or have AI write it from your offer, then runs the same sequence / schedule / senders / pre-flight / launch flow as V1. Requires a Prospeo API key and a HeyReach API key. Pairs with heyreach-sequence-templates.4---56# HeyReach Campaign Launchpad V2 — Prospeo-sourced78V1 started from a CSV or an existing list. **V2 sources the leads for you from Prospeo**: the user describes9their ICP in plain English, the skill finds and enriches matching people, imports them to HeyReach, and then10builds the campaign exactly like V1. Two reference files do the heavy lifting:11[`references/prospeo-api.md`](references/prospeo-api.md) (the API spec) and12[`references/icp-to-filters.md`](references/icp-to-filters.md) (how to turn an ICP into search filters).1314## Triggers1516"find me [role] at [type of company] and launch a campaign", "build a campaign targeting …", "source leads and17start outreach", "I don't have a list — find the people and run a campaign", "prospect + campaign".1819Use **V1 (heyreach-campaign-launchpad)** instead when the user already has the leads (a CSV or an existing20HeyReach list) and just wants to launch.2122## Prerequisites2324- **Prospeo API key** — `export PROSPEO_API_KEY=...` (sent as the `X-KEY` header).25- **HeyReach workspace key** — `export HEYREACH_API_KEY=...` (verify with `heyreach auth check`).26- At least one **connected, authenticated LinkedIn sender** (`heyreach li-accounts list` → `authIsValid: true`).2728## Set expectations2930This skill spends Prospeo credits and launches real outreach. Be calm and explicit:31> "I'll ask a few quick questions, show you exactly how many leads and credits before extracting anything,32> and nothing is pulled or charged until you confirm."3334## The golden rule of the question flow3536**Ask ONE question at a time, as selectable options, using the `AskUserQuestion` tool** — that renders as37clickable choices in the app and numbered (press 1/2/3) choices in the terminal. Wait for each answer before38the next question. Free-text is only for the two things that can't be enumerated: the ICP description and the39offer/copy instructions.4041---4243## How it works4445### 0. Auth + account check (read-only, no charge)46```bash47heyreach auth check48curl -s https://api.prospeo.io/account-information -H "X-KEY: $PROSPEO_API_KEY"49```50From the Prospeo response, note `response.current_plan` and `response.remaining_credits` — you'll need both for51the cost gate and for plan-gating. Confirm a healthy HeyReach sender exists.5253### 1. Ask the ICP (free text)54> "Who do you want to target? Describe your ideal customer in your own words — role, type of company, location,55> size, anything that matters."5657### 2. Translate ICP → Prospeo filters, then confirm (selectable)58Using [`references/icp-to-filters.md`](references/icp-to-filters.md):59- Pick the **sourcing mode** — attribute search (default), named-accounts (user gave target companies/domains →60 `company.websites`), or enrichment-only (user already has LinkedIn URLs/emails → skip search).61- Build the `filters` object. **Validate every location / job-title / industry / technology value** against62 `POST /search-suggestions` (free) so you use Prospeo's canonical strings, not raw user text.63- Respect plan-gating: if a needed filter is above `current_plan`, either drop it or flag it.64- **Play the interpretation back** and let the user confirm or adjust with `AskUserQuestion`, e.g.:65 > "Here's how I read that: VPs/Heads of Sales · Series B SaaS · United States · verified emails only. Good to go?"66 > Options: `Looks right — continue` · `Adjust targeting` · `Add target companies`67- Ask 1–2 selectable clarifiers only if genuinely ambiguous (seniority band, region, "verified emails only?").6869### 3. 🚦 STRICT count gate — nothing is extracted before this70**Do not call `/search-person` or `/bulk-enrich-person` until the user has explicitly confirmed the data depth,71the count, AND the cost.** This is a hard stop.72731. **Data depth** — ask with `AskUserQuestion`:74 > "What do you need for each lead?"75 > Options: `LinkedIn only (cheapest — all a LinkedIn campaign needs)` · `LinkedIn + verified email` · `LinkedIn + email + mobile`7677 A LinkedIn connection-request + message campaign needs **only the profile URL**, which search already returns —78 so **LinkedIn-only is the default and ~25× cheaper**. Choose email/mobile only for an email/multichannel step79 or to export contacts.802. Ask **how many leads** with `AskUserQuestion` (presets + custom): `25` · `100` · `250` · `500` · `Custom`.813. Estimate credits by depth (search = 1 credit / 25 results; email = 1 credit/found; mobile = 10/found):82 - **LinkedIn only → `ceil(N / 25)`** (e.g. 250 leads ≈ 10 credits)83 - **+ verified email → `ceil(N/25)` + up to `N`**84 - **+ mobile → `ceil(N/25)` + up to `10 × N`**8586 Enrichment only charges on found, so actual ≤ estimate.874. Compare to `remaining_credits`. If short, offer a smaller N, a lighter depth, or stop.885. **Final confirmation** with `AskUserQuestion`:89 > "Extract up to **{N} leads** ({depth}) — about **{est} credits** (you have {remaining_credits}). Proceed?"90 > Options: `Yes, extract now` · `Change the number` · `Cancel`916. Only on `Yes` do you proceed to step 4. If the user never confirms, **stop** — do nothing.9293### 4. Extract94Per [`references/prospeo-api.md`](references/prospeo-api.md):95- **Search loop (always):** `POST /search-person` with `{filters, page}`, page = 1,2,3… (25/page) until you've96 collected `N` `person_id`s or `pagination.total_page` is exhausted. This already returns the LinkedIn URL,97 name, title, company, location, and headline — everything a LinkedIn campaign needs. Self-throttle to the98 plan's search rate limit.99- **Enrich loop (ONLY if the user chose email/mobile in step 3):** batch the `person_id`s into groups of **50** →100 `POST /bulk-enrich-person` with `data:[{identifier, person_id}]`, `only_verified_email: true` (and101 `enrich_mobile: true` only if mobiles were chosen). Collect `matched[]`; drop `not_matched` / `UNAVAILABLE`.102 **Skip this entirely for LinkedIn-only — no enrichment credits spent.**103- Report the real result: "Sourced {found} leads ({depth}) · {credits} credits used."104105### 5. Import to HeyReach106- Create the list: `heyreach lists create --name "<ICP> — <date>"`.107 > Known CLI quirk: `lists create` prints an unmarshal error but the list **is** created — get its id from the108 > error body or `heyreach lists list --keyword "<name>"`.109- Map each enriched record and `heyreach lists add-leads` in batches of ≤100. Field mapping:110111| Prospeo (`matched[].person` / `.company`) | HeyReach lead field |112|---|---|113| `linkedin_url` | `profileUrl` (required) |114| `first_name` / `last_name` | `firstName` / `lastName` |115| `company.name` | `companyName` |116| `current_job_title` | `position` |117| enriched `email.email` | `emailAddress` |118| `location.city` / `country` | `location` |119| `headline`, `company.industry`, recent job-change, etc. | `customUserFields: [{ "name": "...", "value": "..." }]` → usable as `{VARIABLE}` |120121(Use the `{name,value}` shape — it's the validated one. `{fieldName,fieldValue}` is rejected.)122123`emailAddress` is only populated when the user chose an email/mobile depth in step 3 — LinkedIn-only runs leave it empty, which is fine for a connection-request + message campaign.124125### 6. Copy fork (selectable)126Ask with `AskUserQuestion`:127> "How do you want the messages written?"128> Options: `I'll write my own` · `Let AI write it from my offer`129- **Write your own** → user pastes their connection note / messages; you place them into the chosen sequence verbatim.130- **AI writes it** → ask (free text) for their offer + any rules ("what do you sell, to whom, the angle, tone").131 Generate short, value-first messages using the personalization tokens available from the imported fields132 (`{FIRST_NAME}`, `{COMPANY}`, `{POSITION}`, plus any custom field). Every `MESSAGE`/`INMAIL` **must** have a133 `fallbackMessage`. Show the drafts and let the user approve/tweak before applying.134135### 7. Build & launch the campaign (identical to V1)136Hand off to the standard flow — use the **heyreach-sequence-templates** skill for the sequence:137- Pick a sequence template matching connection degree; drop in the copy from step 6.138- Set the schedule (audience timezone), assign senders, set exclusions.139- Assemble `heyreach campaigns create` (sequence rules that the live API enforces: `CONNECTION_REQUEST` needs a140 `payload`, every non-root node incl. `END` needs `actionDelay` ≥ 3h, every `MESSAGE`/`INMAIL` needs a `fallbackMessage`).141- Run the **pre-flight checklist**, then `heyreach campaigns start`, verify `IN_PROGRESS`, and print the launch summary.142143---144145## Cost & safety146147- **LinkedIn-only is the default and ~25× cheaper.** Search returns the LinkedIn URL + profile for ~1 credit per148 25 leads; emails cost ~1 credit *each*. A connection-request + message campaign needs only the profile URL, so149 skip email enrichment unless running an email/multichannel step or exporting contacts.150- **The count gate (step 3) is non-negotiable** — never search or enrich before explicit confirmation.151- Prefer `only_verified_email: true` so credits are only spent on usable emails.152- Mobiles cost 10× — only request them if the user explicitly wants phones.153- Pace requests to the plan's rate limits (search 1–5/s, enrich 5–30/s); read the `x-*-request-left` headers.154- `INSUFFICIENT_CREDITS` (HTTP 400) is the safety net if an estimate runs short mid-extraction — stop and report.155156## Edge cases157158- **`PLAN_REQUIRED`** (400) — a filter needs a higher Prospeo plan; the `filter_error` names it. Drop the filter or tell the user which plan unlocks it.159- **`NO_RESULTS`** — filters too narrow; suggest loosening one constraint (per `icp-to-filters.md`).160- **Low enrich match rate** — normal; search finds people, but not everyone has a verified email. Report found vs. requested.161- **25,000-lead ceiling** per search, and a single large pull can exhaust the daily search quota on lower plans — warn for big N.162- **Connection degree, USER_LIST type, 100-leads-per-add-leads** — same rules as V1.163164## References165166- [`references/prospeo-api.md`](references/prospeo-api.md) — endpoints, the two-step pipeline, credit model, rate limits, plan-gating.167- [`references/icp-to-filters.md`](references/icp-to-filters.md) — ICP → `search-person` filters, value validation, worked examples.168- **heyreach-sequence-templates** — the sequence JSON + the live-validated node rules.