Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

Connect AI agents to HeyReach

Scalekit connector
Open markdown

The HeyReach connector lets your AI agent act in each user's HeyReach account. Each user connects their own HeyReach API key once, and Scalekit sends it with every call, so your agent never handles credentials. It comes with 14 tools.

Tools
14
What they doRead · write · destructive
9 · 5 · 09 read5 write0 destructive
Users sign in with

Setup

  1. Install the SDK

    Terminal window
    npm install @scalekit-sdk/node dotenv
  2. Set your credentials

    Add your Scalekit credentials to your .env file. Find values in app.scalekit.com > Developers > API Credentials.

    .env
    SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
    SCALEKIT_CLIENT_ID=<your-client-id>
    SCALEKIT_CLIENT_SECRET=<your-client-secret>
  3. Create the HeyReach connection

    In AgentKit > Connections, create a HeyReach connection. The name you give it is the connection_name your code passes. See Configure connections.

    Console steps with screenshots

    Register your HeyReach API key with Scalekit so it can authenticate and proxy LinkedIn outreach requests on behalf of your users. HeyReach uses API key authentication.

    1. Generate a HeyReach API key

      • Sign in to app.heyreach.io and open Dashboard -> Settings -> Integrations -> Get API Key.

      • Create a new API key, give it a descriptive name (e.g., HeyReach Agent), and confirm.

      • Copy the generated key. It is shown only once — store it somewhere safe before navigating away.

    2. Create a connection in Scalekit

      • In Scalekit dashboard, go to AgentKit > Connections > Create Connection. Find HeyReach and click Create.

      • Note the Connection name — you will use this as connection_name in your code (e.g., heyreach).

    3. Add a connected account

      Connected accounts link a specific user identifier in your system to a HeyReach API key. Add one in the dashboard to test. In production, each user adds their own through the authorization link: they enter their credentials on the page it opens.

      In the dashboard, to test

      • Open the connection you created and click the Connected Accounts tab → Add account.

      • Fill in:

        • Your User’s ID — a unique identifier for this user in your system (e.g., user_123)
        • API Key — the HeyReach API key you copied in step 1
      • Click Save.

      From your backend, if your app already has the credentials

      For example, when users enter them on a settings page in your app:

      import { ConnectorStatus } from '@scalekit-sdk/node'
      const authorizationDetails = {
      details: {
      case: 'staticAuth',
      value: { details: { api_key: 'your-heyreach-api-key' } },
      },
      };
      let { connectedAccount } = await scalekit.actions.upsertConnectedAccount({
      connectionName: 'heyreach',
      identifier: 'user_123',
      authorizationDetails,
      });
      // Make sure the account is ACTIVE before the first tool call.
      if (connectedAccount?.status !== ConnectorStatus.ACTIVE) {
      ({ connectedAccount } = await scalekit.actions.upsertConnectedAccount({
      connectionName: 'heyreach',
      identifier: 'user_123',
      authorizationDetails,
      }));
      }
  4. Authorize a user and make your first call

    quickstart.mts
    import { ScalekitClient } from '@scalekit-sdk/node'
    import 'dotenv/config'
    import { createInterface } from 'node:readline/promises'
    const scalekit = new ScalekitClient(
    process.env.SCALEKIT_ENVIRONMENT_URL,
    process.env.SCALEKIT_CLIENT_ID,
    process.env.SCALEKIT_CLIENT_SECRET,
    )
    const actions = scalekit.actions
    const connector = 'heyreach'
    const identifier = 'user_123'
    // Generate an authorization link for the user
    const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
    console.log('Authorize HeyReach:', link)
    const rl = createInterface({ input: process.stdin, output: process.stdout })
    await rl.question('Press Enter after authorizing...')
    rl.close()
    // Make your first call
    const result = await actions.executeTool({
    connector,
    identifier,
    toolName: 'heyreach_check_api_key',
    toolInput: {},
    })
    console.log(result)
    Terminal window
    npx tsx quickstart.mts

    Each user opens the link once and enters their HeyReach credentials there. If your app already has a user's credentials, add the account from your backend instead, as the console steps above show. See Authorize a user for the full flow and statuses.

Tools

Pass the exact name to execute_tool
Try in PlaygroundRequest a tool
  • heyreach_check_api_keyVerify that your HeyReach API key is valid and the connection is working.Read-only

    Check API Key

    Verify that your HeyReach API key is valid and the connection is working. Returns HTTP 200 with empty body on success. Use this to validate a connection before making other API calls.

    Inputs

    This tool takes no inputs.

  • heyreach_get_all_campaignsList all LinkedIn outreach campaigns in your HeyReach account with pagination.Read-only

    Get All Campaigns

    List all LinkedIn outreach campaigns in your HeyReach account with pagination. Returns campaign metadata including status (DRAFT, IN_PROGRESS, PAUSED, FINISHED, FAILED), progress stats, associated lead list, and campaignAccountIds (LinkedIn sender account IDs needed for heyreach_get_overall_stats). Rate limit: 300 requests/minute.

    Inputs

    limitinteger
    Maximum number of campaigns to return. Defaults to 10.
    offsetinteger
    Number of records to skip for pagination. Defaults to 0.
  • heyreach_get_all_linkedin_accountsList the LinkedIn sender accounts (connected LinkedIn profiles) in your HeyReach workspace with pagination.Read-only

    Get All LinkedIn Accounts

    List the LinkedIn sender accounts (connected LinkedIn profiles) in your HeyReach workspace with pagination. Returns each account's ID, name, profile URL, and status. Use the returned account IDs as linkedInAccountId when calling heyreach_add_leads_to_campaign, or as AccountIds in heyreach_get_overall_stats. Rate limit: 300 requests/minute.

    Inputs

    keywordstring
    Optional search keyword to filter accounts by name.
    limitinteger
    Maximum number of LinkedIn accounts to return. Max 100. Defaults to 100.
    offsetinteger
    Number of records to skip for pagination. Defaults to 0.
  • heyreach_get_all_listsList all lead lists in your HeyReach account with pagination.Read-only

    Get All Lists

    List all lead lists in your HeyReach account with pagination. Returns list metadata including name, total lead count, list type, creation date, and associated campaign IDs. Use list IDs with heyreach_get_leads_from_list to retrieve leads. Rate limit: 300 requests/minute.

    Inputs

    limitinteger
    Maximum number of lists to return. Defaults to 10.
    offsetinteger
    Number of records to skip for pagination. Defaults to 0.
  • heyreach_get_campaign_by_idRetrieve detailed information about a specific HeyReach campaign by its ID.Read-only

    Get Campaign By ID

    Retrieve detailed information about a specific HeyReach campaign by its ID. Returns campaign status, progress stats (total users, in progress, finished, failed), associated lead list, and LinkedIn sender accounts. Use get_all_campaigns first to find campaign IDs.

    Inputs

    campaignIdintegerrequired
    The unique ID of the campaign to retrieve. Get campaign IDs from heyreach_get_all_campaigns.
  • heyreach_get_conversationsList LinkedIn inbox conversations across your HeyReach sender accounts with pagination and filters.Read-only

    Get Conversations

    List LinkedIn inbox conversations across your HeyReach sender accounts with pagination and filters. Returns conversation metadata: participants, last message, seen/unseen status, associated campaign and account. Filter by LinkedIn account IDs, campaign IDs, lead profile URL, tags, search string, or seen status. Useful to monitor replies to outreach sent via heyreach_add_leads_to_campaign. Rate limit: 300 requests/minute.

    Inputs

    campaignIdsarray
    Filter conversations to these campaign IDs. Get campaign IDs from heyreach_get_all_campaigns.
    leadLinkedInIdstring
    Filter to conversations with a specific lead by their LinkedIn internal ID.
    leadProfileUrlstring
    Filter to conversations with a specific lead by their LinkedIn profile URL.
    limitinteger
    Maximum number of conversations to return (1-100). Defaults to 10 — a client-side cap applied in the jsonnet template to protect LLM context, since the HeyReach API's own default (~100) can return 400KB+ payloads. Pass a larger value explicitly if you need more.
    linkedInAccountIdsarray
    Filter conversations to these LinkedIn sender account IDs. Get account IDs from heyreach_get_all_linkedin_accounts.
    offsetinteger
    Number of records to skip for pagination. Defaults to 0.
    searchStringstring
    Free-text search across conversation content and participant names.
    seenboolean
    Filter by seen status. true = only seen conversations, false = only unseen. Omit to return both.
    tagsarray
    Filter conversations by lead tags.
  • heyreach_get_leadRetrieve detailed information about a single HeyReach lead by their LinkedIn profile URL.Read-only

    Get Lead

    Retrieve detailed information about a single HeyReach lead by their LinkedIn profile URL. Returns the lead's profile data (name, headline, location, company, position), email addresses (emailAddress, enrichedEmailAddress, customEmailAddress), tags, and custom fields. Useful to verify a lead exists in HeyReach before or after adding them to a campaign. Rate limit: 300 requests/minute.

    Inputs

    profileUrlstringrequired
    The public LinkedIn profile URL of the lead to look up. Example: https://www.linkedin.com/in/janedoe
  • heyreach_get_leads_from_listRetrieve leads from a specific HeyReach lead list with pagination.Read-only

    Get Leads From List

    Retrieve leads from a specific HeyReach lead list with pagination. Returns detailed lead profiles including LinkedIn URL, name, headline, location, company, position, tags, and email addresses. Use heyreach_get_all_lists to find list IDs. Rate limit: 300 requests/minute.

    Inputs

    listIdintegerrequired
    The unique ID of the lead list to retrieve leads from. Get list IDs from heyreach_get_all_lists.
    limitinteger
    Maximum number of leads to return. Defaults to 10 — a client-side cap applied in the jsonnet template to protect LLM context, since lists can hold thousands of leads (observed: 4,054). Pass a larger value explicitly if you need more.
    offsetinteger
    Number of records to skip for pagination. Defaults to 0.

Workflows

Proxy API call
// Verify the connected API key works — no key needed in your code
const result = await actions.request({
connectionName: 'heyreach',
identifier: 'user_123',
path: '/auth/CheckApiKey',
method: 'GET',
});
console.log('API key valid:', result.status === 200);
Add leads to a campaign

The most common HeyReach workflow: pick an active campaign, choose a LinkedIn sender account to send from, and add up to 100 leads in a single call. Each lead is bound to a specific sender — so a campaign with multiple senders can round-robin or be sharded by your code.

examples/heyreach_add_leads.py
# Step 1: Pick a campaign and one of its sender accounts
campaigns = actions.execute_tool(
connection_name='heyreach',
identifier='user_123',
tool_name="heyreach_get_all_campaigns",
tool_input={"limit": 25}
)
campaign = campaigns.data["items"][0] # or filter by name/status
sender_account_id = campaign["campaignAccountIds"][0]
print(f"Adding leads to campaign {campaign['id']} via sender {sender_account_id}")
# Step 2: Add up to 100 leads bound to that sender account
new_leads = [
{"profileUrl": "https://www.linkedin.com/in/jane-doe"},
{"profileUrl": "https://www.linkedin.com/in/john-smith"},
]
result = actions.execute_tool(
connection_name='heyreach',
identifier='user_123',
tool_name="heyreach_add_leads_to_campaign",
tool_input={
"campaignId": campaign["id"],
"accountLeadPairs": [
{"linkedInAccountId": sender_account_id, "lead": lead}
for lead in new_leads
],
# Auto-resume the campaign if it's paused or already finished
"resumePausedCampaign": True,
"resumeFinishedCampaign": True,
}
)
print(f"Added {len(new_leads)} leads:", result.data)
Look up a lead before reaching out

Verify a lead already exists in HeyReach (and check their tags or enrichment status) before adding them to a campaign — this avoids duplicate outreach and lets you skip leads that are already engaged.

examples/heyreach_get_lead.py
result = actions.execute_tool(
connection_name='heyreach',
identifier='user_123',
tool_name="heyreach_get_lead",
tool_input={
"profileUrl": "https://www.linkedin.com/in/jane-doe"
}
)
lead = result.data
if lead:
print(f"{lead['fullName']} — {lead.get('position') or lead.get('summary')}")
print(f" Company: {lead.get('companyName')}")
print(f" Location: {lead.get('location')}")
print(f" Email: {lead.get('emailAddress') or lead.get('enrichedEmailAddress')}")
else:
print("Lead not found — safe to add to a new campaign.")
Monitor inbox replies

After outreach goes out, poll the unified LinkedIn inbox for unseen replies. Filter by campaign or sender account so you only surface conversations relevant to your agent’s workflow.

examples/heyreach_inbox.py
result = actions.execute_tool(
connection_name='heyreach',
identifier='user_123',
tool_name="heyreach_get_conversations",
tool_input={
"campaignIds": [campaign["id"]], # filter to one campaign
"seen": False, # only unseen conversations
"limit": 25,
}
)
for convo in result.data.get("items", []):
profile = convo.get("correspondentProfile", {})
messages = convo.get("messages", [])
last_msg = messages[-1] if messages else {}
print(f"📬 {profile.get('fullName')} — {profile.get('profileUrl')}")
print(f" {last_msg.get('createdAt')} ({last_msg.get('sender')}): "
f"{(last_msg.get('body') or '')[:120]}")
Track campaign performance

Pull aggregate metrics for one or more campaigns — connection acceptance rate, message reply rate, InMail performance — to power dashboards or trigger follow-up actions when a campaign underperforms.

examples/heyreach_stats.py
# Get all sender accounts associated with the campaign
sender_accounts = campaign["campaignAccountIds"]
stats = actions.execute_tool(
connection_name='heyreach',
identifier='user_123',
tool_name="heyreach_get_overall_stats",
tool_input={
"CampaignIds": [campaign["id"]],
"AccountIds": sender_accounts,
}
)
# Response wraps aggregates under `overallStats` and a per-day breakdown
# under `byDayStats` — use `overallStats` for top-line numbers.
s = stats.data["overallStats"]
print(f"Campaign {campaign['id']} — '{campaign['name']}'")
print(f" Connection requests: {s['connectionsSent']} sent / {s['connectionsAccepted']} accepted")
print(f" Acceptance rate: {s['connectionAcceptanceRate']:.1%}")
print(f" Messages: {s['messagesSent']} sent / {s['totalMessageReplies']} replied")
print(f" Reply rate: {s['messageReplyRate']:.1%}")
LangChain integration

Let an LLM decide which HeyReach tool to call based on natural language. This example builds an agent that can list campaigns, add leads, and surface inbox replies on demand.

examples/heyreach_langchain.py
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.prompts import (
ChatPromptTemplate, SystemMessagePromptTemplate,
HumanMessagePromptTemplate, MessagesPlaceholder, PromptTemplate
)
# Load all HeyReach tools in LangChain format. Use page_size=100 so connector tool lists are not truncated.
tools = actions.langchain.get_tools(
identifier='user_123',
providers=["HEYREACH"],
page_size=100
)
prompt = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate(prompt=PromptTemplate(
input_variables=[],
template=(
"You are a LinkedIn outreach assistant with access to HeyReach tools. "
"Use heyreach_get_all_campaigns to find campaigns, heyreach_add_leads_to_campaign "
"to enroll new prospects, heyreach_get_conversations to monitor replies, and "
"heyreach_get_overall_stats to report on performance. Always confirm the campaign "
"and sender account before adding leads."
)
)),
MessagesPlaceholder(variable_name="chat_history", optional=True),
HumanMessagePromptTemplate(prompt=PromptTemplate(
input_variables=["input"], template="{input}"
)),
MessagesPlaceholder(variable_name="agent_scratchpad")
])
llm = ChatOpenAI(model="gpt-4o")
agent = create_openai_tools_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
result = agent_executor.invoke({
"input": "Show me unread replies from my active LinkedIn campaigns in the last 24 hours, and tell me which campaign has the highest acceptance rate."
})
print(result["output"])