Skip to Content
Enter
Skip to Menu
Enter
Skip to Footer
Enter
N8N template

Automate HeyReach reports with n8n

Nenad Pavlov
By:
Nenad Pavlov
Submit an N8N template
Copy this template

Creator spotlight

Nenad Pavlov
Nenad Pavlov
Founder & Managing Director @SimplyGrow
A guy who knows a bit about outbound and sales tech

Why this method works

Manual reporting wastes hours every week. This automated workflow runs while you sleep and delivers ready-to-send reports every Monday morning.

The system pulls data directly from HeyReach's API using official n8n integration nodes—no fragile HTTP requests or manual exports. It tracks metrics over time in a spreadsheet and generates professional email drafts automatically.

Version 2 goes further: It identifies exactly which leads replied in the last 7 days, adds their contact details to the report, and creates a dedicated spreadsheet tab for that week's responders.

For agencies, this is essential. Clients expect regular updates. This workflow ensures consistent, data-driven reporting without lifting a finger after initial setup.

Results from this approach

✅ Weekly reports generate automatically every Monday at midnight

✅ Email drafts ready to send when you wake up—just review and click send

✅ Historical data tracked in Google Sheets week after week for trend analysis

✅ Zero manual work pulling stats, copying data, or formatting emails

✅ Client-ready presentations with both aggregate stats and individual lead details

✅ Version 2 bonus: Complete list of leads who replied, with LinkedIn profiles and contact info

Tools required

HeyReach account with active campaigns (Sign up here)

n8n (self-hosted or cloud) for workflow automation

Google Workspace (Gmail + Google Sheets) for drafts and data storage

HeyReach n8n Integration package (install from n8n node library)

Optional: Access to crontab.guru for scheduling help

Execution steps

Initial Setup

Step 1: Install HeyReach n8n Integration

  • Open n8n node library
  • Search "HeyReach"
  • Click Install Package
  • Connect your HeyReach account

Step 2: Choose Your Version

Version 1: Basic stats + email draft + spreadsheet logging (6 main steps)

Version 2: Everything in V1 + replied leads tracking (19 total steps)

Version 1: Basic automated reports

Step 1: Schedule trigger

Node: Schedule Trigger

Configuration:

  • Cron expression: 0 0 * * 1 (Monday at midnight)
  • Use crontab.guru to customize timing

Step 2: Set date variables

Node: Set Variables

Create two variables:

endDate:

{{ $now }}

startDate:

{{ $now.minus({ days: 7 }) }}

Note: Uses Luxon library. Change days: 7 for different reporting periods (14 days, 30 days, etc.)

Step 3: Fetch total campaign stats

Node: HeyReach API → Get Overall Stats

Settings:

  • Select all metrics
  • Date range: Beginning of time to present
  • No additional configuration needed

Retry Settings:

  • Enable: Retry on fail
  • Attempts: 3
  • Delay: 2 seconds

Step 4: Fetch weekly campaign stats

Node: HeyReach API → Get Overall Stats

Settings:

  • Start Date: {{ $('Set Variables').item.json.startDate }}
  • End Date: {{ $('Set Variables').item.json.endDate }}
  • Enable retry: 3 attempts, 2-second delay

Step 5: Create email draft

Node: Gmail → Create Draft

Subject:

Campaign Report - {{ $('Set Variables').item.json.startDate.split('T')[0] }}

Message Body Template:

<aside>👉🏻

Dear Client,

Please find the results for the past 7 days:

WEEKLY STATS:

  • Connection Requests Sent: {{ $('Weekly Stats').item.json.connection_requests_sent }}
  • Acceptance Rate: {{ $('Weekly Stats').item.json.acceptance_rate }}%
  • Messages Sent: {{ $('Weekly Stats').item.json.messages_sent }}
  • Reply Rate: {{ $('Weekly Stats').item.json.reply_rate }}%

TOTAL STATS (All Time):

  • Total Connection Requests: {{ $('Total Stats').item.json.connection_requests_sent }}
  • Total Messages Sent: {{ $('Total Stats').item.json.messages_sent }}
  • Overall Reply Rate: {{ $('Total Stats').item.json.reply_rate }}%

Best regards

</aside>

Options:

  • CC: Add client email addresses (person1@client.com, person2@client.com)
  • BCC: Optional for record-keeping

Technical note: .split('T')[0] extracts date from ISO timestamp

Step 6: Save to spreadsheet

Node: Google Sheets → Append Row

Settings:

  • Spreadsheet: Select by ID or name
  • Sheet: Your stats tracking sheet
  • Mapping mode: Manual column mapping

Columns to map:

  • Start Date: {{ $('Set Variables').item.json.startDate }}
  • End Date: {{ $('Set Variables').item.json.endDate }}
  • Connection Requests Sent
  • Acceptance Rate
  • Messages Sent
  • Reply Rate
  • InMail Sent (if using InMail)
  • Email Reply Rate (if using email)

Optimization: Remove unused columns to keep data clean

Version 2: Advanced reports with lead tracking

Step 1-2: Same as Version 1

Schedule Trigger + Basic Date Variables

Step 3: Extended variables

Node: Set Variables

Add these variables:

  • spreadsheetId: Your Google Sheets ID
  • spreadsheetName: Main stats sheet name
  • leadsSheetName: Leads {{ $now.split('T')[0] }}
  • startDateOnly: {{ $now.minus({ days: 7 }).split('T')[0] }}
  • endDateOnly: {{ $now.split('T')[0] }}

Why: Dynamic expressions in sheet names can fail. Variables ensure reliability.

Step 4: Create new sheet for leads

Node: Google Sheets → Create Sheet

Settings:

  • Spreadsheet ID: {{ $('Set Variables').item.json.spreadsheetId }}
  • Sheet Name: {{ $('Set Variables').item.json.leadsSheetName }}
  • Always Output Data: ✅ MUST be enabled

Behavior:

  • If sheet exists → Returns empty value (no error)
  • If new → Returns sheet data

Step 5: Conditional header logic

Node: IF

Condition: {{ $json.sheetId }} is not empty

True Branch: New sheet created → Add headersFalse Branch: Sheet exists → Skip to merge

Step 6: Define header row

Node: Set Values

Define as strings with empty expressions:

  • firstName: {{}}
  • lastName: {{}}
  • position: {{}}
  • companyName: {{}}
  • profileUrl: {{}}
  • emailAddress: {{}}
  • location: {{}}

Step 7: Write headers to sheet

Node: Google Sheets → Append Row

Settings:

  • Target: New leads sheet
  • Map the values from Step 6

Step 8: Merge branches

Node: Merge

Combines IF branches before continuing workflow

Step 9-10: Fetch campaign stats

Same as Version 1, Steps 3-4

Critical: Enable Retry on Fail to handle API rate limits

Step 11: Get all campaigns

Node: HeyReach API → Get All Campaigns

Output: Collection of all campaigns in your account

Step 12: Split into individual campaigns

Node: Split Out

Mode: Each item separately

Result: One output per campaign

Step 13: Get leads per campaign

Node: HeyReach API → Get Leads from Campaign

Runs in loop for each campaign from Step 12

Settings:

  • Campaign ID: {{ $json.campaignId }}
  • Time Filter: CreationTime
  • Time From: {{ $('Set Variables').item.json.startDate }}
  • Time To: {{ $('Set Variables').item.json.endDate }}

Step 14: Filter for replied Leads

Node: Filter

Condition:

{{ $json.messageStatus }} equals "MESSAGE_REPLY"

Result: Only leads who replied in last 7 days pass through

Note: MESSAGE_REPLY is HeyReach's status value for replied leads

Step 15: Add leads to spreadsheet

Node: Google Sheets → Append Row

Settings:

  • Spreadsheet ID: From variables
  • Sheet Name: {{ $('Set Variables').item.json.leadsSheetName }}

Map these fields:

  • First Name
  • Last Name
  • Position
  • Company Name
  • Profile URL
  • Email Address
  • Location

Step 16: Format leads for Email

Node: Code

JavaScript code:

javascript

`const leads = $input.all();let output = '\n\nLEADS WHO REPLIED THIS WEEK:\n\n';

for (const lead of leads) {const data = lead.json;output += - ${data.firstName} ${data.lastName} (${data.position} at ${data.companyName})\\n;output +=  LinkedIn: ${data.profileUrl}\\n;if (data.emailAddress) {output +=  Email: ${data.emailAddress}\\n;}output += '\n';}

return [{ json: { leadsText: output } }];


**Purpose:** Converts lead data into clean, formatted text block

### Step 17: Create Enhanced Email Draft

**Node:** Gmail → Create Draft

Same configuration as Version 1, Step 5, but **add to end of message body:**

{{ $('Code').item.json.leadsText }}`

Result: Email now includes formatted list of leads who replied

Step 18: Save stats to spreadsheet

Same as Version 1, Step 6

Step 19: Activate workflow

Click Activate toggle in n8n

Workflow now runs automatically every Monday at midnight

Testing before going live

Manual test run:

  1. Click Execute Workflow button
  2. Watch nodes turn green sequentially
  3. Check outputs at each step

Verify these results:

✅ New row appears in main stats spreadsheet

✅ Draft email in Gmail with correct data

✅ (V2 only) New sheet created with leads who replied

✅ (V2 only) Draft email includes formatted lead list

Production Check (Monday morning):

  1. Open Gmail drafts
  2. Review auto-generated report
  3. Verify stats accuracy
  4. Click Send to clients

Troubleshooting common issues

"Sheet already exists" error:

  • Enable "Always Output Data" on Create Sheet node

API rate limit errors:

  • Enable "Retry on Fail" (3 attempts, 2s delay)

Dynamic sheet name not working:

  • Store sheet name in variables first, reference later

Empty lead list in V2:

  • Check filter condition: messageStatus equals MESSAGE_REPLY
  • Verify date range includes recent activity

Missing stats in email:

  • Map correct output fields from HeyReach nodes
  • Test with {{ $json }} to see all available fields

‍