# LinkedIn Outreach Draft Generator — HeyReach Alternative (`khadinakbar/heyreach-alternative`) Actor

Generate LinkedIn outreach drafts from a campaign brief and supplied prospects. Export opening messages, follow-ups, and CRM IDs in a manual-review queue. Every draft remains marked for human review and not sent.

- **URL**: https://apify.com/khadinakbar/heyreach-alternative.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Lead generation, Automation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $100.00 / 1,000 review-ready prospect drafts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## LinkedIn Outreach Draft Generator — HeyReach Alternative

Generate LinkedIn outreach drafts from a campaign brief and supplied prospects. Export opening messages, follow-ups, and CRM IDs in a manual-review queue. Every draft remains marked for human review and not sent. For campaign reviewers, each dataset row is one review-ready lead with message drafts, QA signals, and explicit not-sent status.

### Workflow: put the results to work

Supply the campaign proposition, call to action, and prospect records already approved for preparation. Review opening and follow-up drafts with their CRM IDs, then edit them before any separate sending step. The Actor preserves the not-sent status on every draft.

### What it returns

One dataset item is one review-ready B2B lead record.

| Field | What it gives you |
| --- | --- |
| `sequenceId`, `draftFingerprint` | Stable rerun and CRM join keys. |
| `prospectFullName`, `companyName`, `role`, `linkedInProfileUrl` | Known or public professional identity context; unconfirmed company values remain `null`. |
| `publicProfile` | Selected public headline, location, summary, skills, and short experience highlights—never email addresses, cookies, raw responses, or account state. |
| `leadGeneration` | The public professional search criteria for leads discovered by this Actor. |
| `enrichmentStatus`, `collectionTelemetry`, `source` | Clear collection status and safe provenance. |
| `messageSequence` | Review-only opening and optional follow-up drafts. |
| `draftWarnings`, `approvalChecklist` | Gaps a human must resolve before any manual use. |
| `reviewStatus`, `actionStatus` | Always `requires_human_review` and `not_sent`. |

### Choose a workflow

| Workflow | Starting input | Best use | Does not do |
| --- | --- | --- | --- |
| `review_known_prospects` | CRM or approved-research records | Turn existing leads into governed review records. | Search or collect public profile data unless you opt in. |
| `enrich_public_profiles` | Public `linkedin.com/in/...` URLs | Add selected public professional context to a list you already own. | Access private profiles, emails, cookies, or login-only data. |
| `find_public_leads` | Public professional query or job title, plus optional company/location | Build a bounded public lead-research queue and enrich each returned public profile. | Guarantee a match, contact detail, current employment, or contact permission. |

### Quick start inputs

#### 1. Find and enrich public B2B leads

```json
{
  "workflow": "find_public_leads",
  "campaignName": "US CTO research",
  "campaignGoal": "sales_conversation",
  "senderName": "Example Sender",
  "senderCompany": "Example Studio",
  "reviewOwner": "Example Reviewer",
  "leadSearch": {
    "query": "chief technology officer",
    "company": "Microsoft",
    "location": "United States"
  },
  "valueProposition": "We are researching practical workflow improvements for B2B teams.",
  "callToAction": "Would a brief conversation next week be useful?",
  "followUpCount": 1,
  "maxProspects": 5
}
```

#### 2. Enrich public profile URLs you already have

```json
{
  "workflow": "enrich_public_profiles",
  "campaignName": "Partner profile review",
  "campaignGoal": "partnership",
  "senderName": "Example Sender",
  "profileUrls": [
    "https://www.linkedin.com/in/example-person"
  ],
  "valueProposition": "We are exploring a practical joint workflow for shared B2B customers.",
  "callToAction": "Would it be useful to compare notes next week?",
  "maxProspects": 1
}
```

#### 3. Keep your CRM-only review workflow

```json
{
  "workflow": "review_known_prospects",
  "campaignName": "Event follow-up review",
  "campaignGoal": "event_follow_up",
  "senderName": "Example Sender",
  "reviewOwner": "Example Reviewer",
  "prospects": [{
    "externalId": "event-001",
    "fullName": "Example Contact",
    "companyName": "Example Company",
    "role": "Product Lead",
    "contextNote": "Met at the product leadership roundtable."
  }],
  "valueProposition": "We are continuing the discussion about practical review workflows.",
  "callToAction": "Would it be useful to compare notes next week?",
  "followUpCount": 1,
  "maxProspects": 1
}
```

Set `enrichPublicProfiles: true` in the CRM-only workflow if each supplied prospect already has a public LinkedIn profile URL you want enriched. It defaults to false, preserving the existing CRM-only collection path.

### Representative result

```json
{
  "sequenceId": "seq_c3a8fb37e5b786bb",
  "externalId": null,
  "campaignName": "US CTO research",
  "prospectFullName": "Example Person",
  "companyName": "Example Ltd",
  "role": "Chief Technology Officer",
  "linkedInProfileUrl": "https://www.linkedin.com/in/example-person",
  "publicProfile": {
    "headline": "Chief Technology Officer",
    "location": "United States",
    "summary": "Selected public professional summary.",
    "skills": ["Engineering", "Product strategy"],
    "experienceHighlights": [{ "title": "Chief Technology Officer", "companyName": "Example Ltd" }],
    "collectedAt": "2026-09-02T00:00:00.000Z"
  },
  "enrichmentStatus": "completed",
  "source": "public_lead_search_and_profile_enrichment",
  "reviewStatus": "requires_human_review",
  "actionStatus": "not_sent"
}
```

The sample illustrates shape, not accuracy, contact permission, relevance, or a claim that a person is currently employed by a company.

### How this workflow alternative fits beside HeyReach

This is a focused **workflow alternative**, not a full-suite replacement. It can replace the bounded job of researching public professional leads, enriching a review queue, and preparing a governed record before manual action.

Choose HeyReach when the job needs its broader connected execution suite: sender accounts, active campaigns, automated LinkedIn actions, inbox work, multichannel outreach, workspaces, or its documented integrations. See HeyReach’s current [plans](https://www.heyreach.io/pricing), [campaign workflow](https://help.heyreach.io/en/articles/9877952-how-to-launch-your-first-campaign-in-heyreach), and [integrations](https://www.heyreach.io/integration).

| Same-job dimension | This Actor | HeyReach |
| --- | --- | --- |
| Bounded job | Public lead research, public-profile enrichment, and review records. | Broader connected campaign execution and collaboration. |
| Buyer input | Query/profile URLs/known CRM prospects plus a campaign brief. | Connected sender accounts plus campaign and lead workflow setup. |
| Deliverable | Structured rows with provenance, QA, approval checks, and no-send status. | Campaign operations, actions, inbox, and connected workflows. |
| Billing | Pay per event plus platform usage; see the Pricing tab for current rates. | Current plan and capacity terms apply; check the linked live terms. |
| Effective efficiency | One accepted public or known lead becomes one joinable review record with provenance and QA in the same run. | Broader campaign operations suit jobs that continue beyond review. |
| Trade-off | Best where the work ends at research and human approval. | Best where the work continues into automated outreach and inbox operations. |

### API and agent use

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('khadinakbar/heyreach-alternative').call({
  workflow: 'find_public_leads',
  campaignName: 'Partner research',
  senderName: 'Example Sender',
  leadSearch: { query: 'partnerships lead', location: 'United States' },
  maxProspects: 3
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

For an MCP client, ask for a bounded job and leave every final contact decision with a reviewer:

> Find up to five public CTO profiles in the United States, enrich the public profile facts, return review-only records, show QA warnings, and keep actionStatus as not\_sent. After the run, read OUTPUT.outcome, inspect dataset provenance, and check the capped event cost.

Inspect `OUTPUT.outcome`, `RUN_SUMMARY`, and the dataset before acting on results.

### Outcomes and recovery

| Outcome | Meaning | What to do |
| --- | --- | --- |
| `COMPLETE` | Every in-cap record completed as requested. | Review warnings and complete the checklist. |
| `PARTIAL` | Useful records were written, with invalid, capped, or unavailable enrichment noted. | Read `RUN_SUMMARY`; rerun only the missing scope. |
| `VALID_EMPTY` | A working public search found no eligible public profiles. | Tighten or broaden the query before another run. |
| `INVALID_INPUT` | A required campaign field or workflow input is missing/invalid. | Follow the input example and retry. |
| `CONFIG_ERROR` / `UPSTREAM_FAILED` | The advertised public-data path could not produce useful output. | Retry once later; if repeated, share the run ID with support. |

### Best results

Start with a specific public role or professional query, a clear geography, and a small `maxProspects` cap. Provide factual relationship context for known prospects, confirm public-profile facts in the resulting row, and use `source`, `enrichmentStatus`, and `draftWarnings` as the reviewer’s starting point. The best workflow pairs public data collection with an authorized CRM review process before any manual contact decision.

### Builder's note

I built this workflow around a simple observation: a public profile alone is not a usable lead workflow. I designed the record to preserve provenance, make missing facts visible, keep the data small and reviewable, and leave the contact decision with a person.

### Responsible use

Use only professional data you are authorized to process. Before manual outreach, verify identity, company, factual relevance, legitimate purpose, prior contact, consent, objections, opt-outs, privacy/anti-spam duties, applicable law, platform terms, timing, and channel. This Actor leaves every final contact decision to a human.

HeyReach is a trademark of its owner. This independent Actor is not affiliated with, associated with, or endorsed by HeyReach.

### Pricing and run costs

This Actor uses **Pay per event plus Apify platform usage**. The [Pricing tab](https://apify.com/khadinakbar/heyreach-alternative/pricing) lists the current event rates and billing terms.

| Event | Billing unit | When it applies |
|---|---|---|
| `apify-actor-start` | Actor Start | Charged when the Actor starts running. Number of events charged depends on Actor memory (one event per GB, minimum one event). |
| `review-ready-prospect` | Review-ready prospect draft | One caller-supplied prospect converted into a persisted review-only draft sequence. The Actor never sends or schedules the drafts. |

Run cost combines the charged events and Apify platform usage. Review the run charge limit and requested result count before starting.

### Connect an AI agent

Use the [Apify MCP configurator](https://mcp.apify.com) to choose an available client connection. Inspect this Actor’s current input schema and required credentials before running it.

# Actor input Schema

## `workflow` (type: `string`):

Choose the bounded job for this run. Use review\_known\_prospects for CRM records, enrich\_public\_profiles for public LinkedIn /in/ URLs, or find\_public\_leads for a public professional search followed by profile enrichment. This does not log in to LinkedIn, send messages, create connections, or operate a campaign.

## `campaignName` (type: `string`):

A clear internal label for this review batch, for example 'Q4 fintech partner research'. It is copied into every result so CRM and reviewer workflows can trace drafts to their purpose. This is not an external campaign created on LinkedIn or HeyReach.

## `campaignGoal` (type: `string`):

Choose the real job this review batch supports. The goal changes the opening and follow-up framing while preserving the same human-review and no-send boundary.

## `senderName` (type: `string`):

The human who will review and, if appropriate, manually send a message, for example 'Khadin Akbar'. It is used only to make the draft read naturally and does not authenticate any account. This is not a LinkedIn login, seat, cookie, or automation credential.

## `senderCompany` (type: `string`):

Optional company or team name used to identify the sender naturally in the opening draft. It does not connect or authenticate an account.

## `reviewOwner` (type: `string`):

Optional person responsible for checking identity, context, claims, contact basis, opt-outs, timing, and the final manual-use decision. Defaults to the sender name.

## `tone` (type: `string`):

Choose a consistent writing style for the review queue. A reviewer must still confirm that each draft sounds natural and appropriate for the relationship.

## `prospects` (type: `array`):

People already identified by your CRM, account list, or manual research, for example {"fullName":"Ada Lovelace","companyName":"Example Ltd"}. Optional CRM IDs, public LinkedIn URLs, roles, and context are preserved as caller-provided provenance. Set enrichPublicProfiles only when you want a supplied public profile URL enriched. This is not permission to contact anyone automatically.

## `profileUrls` (type: `array`):

Public LinkedIn /in/ profile URLs to enrich, for example https://www.linkedin.com/in/example-person. Use this with the enrich\_public\_profiles workflow; duplicates and invalid URLs are removed before collection. This accepts only public profile URLs, not company pages, Sales Navigator URLs, cookies, or logins. Collected facts remain review-only and are never used to send outreach.

## `leadSearch` (type: `object`):

Criteria for finding public professional profiles before enrichment. Provide a query or job title, such as chief technology officer, and optionally narrow by company or location. Use this only with find\_public\_leads and keep the request focused; results are capped by maxProspects. This is not a private-contact search, email finder, message sender, or Sales Navigator login.

## `enrichPublicProfiles` (type: `boolean`):

When enabled for review\_known\_prospects, enrich each supplied public LinkedIn /in/ URL before creating its review record. Defaults to false to preserve the existing CRM-only workflow and avoid unnecessary collection. This never uses a LinkedIn login, cookie, email finder, or automation action. Public facts still require human review before any manual use.

## `valueProposition` (type: `string`):

The truthful reason for reaching out, for example 'We are comparing practical AI visibility workflows for B2B teams'. Defaults to a neutral request for a relevant conversation and must be reviewed for accuracy. This is not generated from profile scraping or an instruction to make unsubstantiated claims.

## `callToAction` (type: `string`):

The low-pressure next step placed in the first draft, for example 'Would a 15-minute conversation next week be useful?'. Defaults to a short conversation request. This is a draft phrase only and does not schedule, send, or notify anyone.

## `followUpCount` (type: `integer`):

How many optional review-only follow-up drafts to include after the opening draft. Defaults to 1 and accepts 0 to 2, with a suggested five-day wait between steps. This does not create a send schedule or automate any LinkedIn action.

## `followUpDelayDays` (type: `integer`):

Suggested days between review-only steps. Defaults to 5 and accepts 1 to 14. This is planning metadata only; the Actor does not schedule or send anything.

## `maxProspects` (type: `integer`):

Hard cap on review-ready leads processed in one run. Defaults to 25 and accepts 1 to 25, so callers can bound persisted review-ready record charges and public-data collection. Current event pricing and additional platform usage are shown on the Pricing tab. This does not guarantee a matching public profile or authorize outreach.

## Actor input object example

```json
{
  "workflow": "review_known_prospects",
  "campaignName": "Q4 fintech partner research",
  "campaignGoal": "partnership",
  "senderName": "Khadin Akbar",
  "senderCompany": "Example Studio",
  "reviewOwner": "Example Reviewer",
  "tone": "warm_professional",
  "prospects": [
    {
      "externalId": "crm-001",
      "fullName": "Ada Lovelace",
      "companyName": "Example Ltd",
      "role": "CTO",
      "linkedInProfileUrl": "https://www.linkedin.com/in/example-person",
      "contextNote": "Researching partnerships."
    }
  ],
  "profileUrls": [
    "https://www.linkedin.com/in/example-person"
  ],
  "leadSearch": {
    "query": "chief technology officer",
    "company": "Microsoft",
    "location": "United States"
  },
  "enrichPublicProfiles": false,
  "valueProposition": "We are comparing practical AI visibility workflows for B2B teams.",
  "callToAction": "Would a 15-minute conversation next week be useful?",
  "followUpCount": 1,
  "followUpDelayDays": 5,
  "maxProspects": 25
}
```

# Actor output Schema

## `reviewKit` (type: `string`):

One not-sent, human-review-required campaign record per processed known prospect, public profile, or public lead, including stable IDs, provenance, enrichment state, and QA signals.

## `compactOutput` (type: `string`):

Stable outcome, counts, warnings, and charged draft events for agents and automations.

## `runSummary` (type: `string`):

Detailed terminal diagnostics, input validity, charge counts, and action boundary.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "workflow": "review_known_prospects",
    "campaignName": "Q4 fintech partner research",
    "campaignGoal": "partnership",
    "senderName": "Khadin Akbar",
    "senderCompany": "Example Studio",
    "reviewOwner": "Example Reviewer",
    "tone": "warm_professional",
    "prospects": [
        {
            "externalId": "crm-001",
            "fullName": "Ada Lovelace",
            "companyName": "Example Ltd",
            "role": "CTO",
            "linkedInProfileUrl": "https://www.linkedin.com/in/example-person",
            "contextNote": "Researching partnerships."
        }
    ],
    "profileUrls": [
        "https://www.linkedin.com/in/example-person"
    ],
    "leadSearch": {
        "query": "chief technology officer",
        "company": "Microsoft",
        "location": "United States"
    },
    "enrichPublicProfiles": false,
    "valueProposition": "We are comparing practical AI visibility workflows for B2B teams.",
    "callToAction": "Would a 15-minute conversation next week be useful?",
    "followUpCount": 1,
    "followUpDelayDays": 5,
    "maxProspects": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/heyreach-alternative").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "workflow": "review_known_prospects",
    "campaignName": "Q4 fintech partner research",
    "campaignGoal": "partnership",
    "senderName": "Khadin Akbar",
    "senderCompany": "Example Studio",
    "reviewOwner": "Example Reviewer",
    "tone": "warm_professional",
    "prospects": [{
            "externalId": "crm-001",
            "fullName": "Ada Lovelace",
            "companyName": "Example Ltd",
            "role": "CTO",
            "linkedInProfileUrl": "https://www.linkedin.com/in/example-person",
            "contextNote": "Researching partnerships.",
        }],
    "profileUrls": ["https://www.linkedin.com/in/example-person"],
    "leadSearch": {
        "query": "chief technology officer",
        "company": "Microsoft",
        "location": "United States",
    },
    "enrichPublicProfiles": False,
    "valueProposition": "We are comparing practical AI visibility workflows for B2B teams.",
    "callToAction": "Would a 15-minute conversation next week be useful?",
    "followUpCount": 1,
    "followUpDelayDays": 5,
    "maxProspects": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/heyreach-alternative").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "workflow": "review_known_prospects",
  "campaignName": "Q4 fintech partner research",
  "campaignGoal": "partnership",
  "senderName": "Khadin Akbar",
  "senderCompany": "Example Studio",
  "reviewOwner": "Example Reviewer",
  "tone": "warm_professional",
  "prospects": [
    {
      "externalId": "crm-001",
      "fullName": "Ada Lovelace",
      "companyName": "Example Ltd",
      "role": "CTO",
      "linkedInProfileUrl": "https://www.linkedin.com/in/example-person",
      "contextNote": "Researching partnerships."
    }
  ],
  "profileUrls": [
    "https://www.linkedin.com/in/example-person"
  ],
  "leadSearch": {
    "query": "chief technology officer",
    "company": "Microsoft",
    "location": "United States"
  },
  "enrichPublicProfiles": false,
  "valueProposition": "We are comparing practical AI visibility workflows for B2B teams.",
  "callToAction": "Would a 15-minute conversation next week be useful?",
  "followUpCount": 1,
  "followUpDelayDays": 5,
  "maxProspects": 25
}' |
apify call khadinakbar/heyreach-alternative --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/heyreach-alternative"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/CndFMGxyvxJWcAaUR/builds/vzuhy3obx6doRduG8/openapi.json
