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:
- Click Execute Workflow button
- Watch nodes turn green sequentially
- 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):
- Open Gmail drafts
- Review auto-generated report
- Verify stats accuracy
- 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


.webp)