# Heyreach Campaign Launchpad V2

> 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.

- Skill: `umerthegtme/heyreach-campaign-launchpad-v2` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add umerthegtme/heyreach-campaign-launchpad-v2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/umerthegtme/heyreach-campaign-launchpad-v2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- Author: UmertheGTME (https://skillmd.com/u/umerthegtme)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/umerthegtme/heyreach-campaign-launchpad-v2

---


# 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`](references/prospeo-api.md) (the API spec) and
[`references/icp-to-filters.md`](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)
```bash
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`](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.

1. **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.
2. Ask **how many leads** with `AskUserQuestion` (presets + custom): `25` · `100` · `250` · `500` · `Custom`.
3. 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.
4. Compare to `remaining_credits`. If short, offer a smaller N, a lighter depth, or stop.
5. **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`
6. Only on `Yes` do you proceed to step 4. If the user never confirms, **stop** — do nothing.

### 4. Extract
Per [`references/prospeo-api.md`](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_id`s 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_id`s 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`](references/prospeo-api.md) — endpoints, the two-step pipeline, credit model, rate limits, plan-gating.
- [`references/icp-to-filters.md`](references/icp-to-filters.md) — ICP → `search-person` filters, value validation, worked examples.
- **heyreach-sequence-templates** — the sequence JSON + the live-validated node rules.

