Go back to N8N templates
N8N template

Automate HeyReach reports with n8n

By:
Nenad Pavlov
Copy this template
Creator spotlight
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