Tutorial: Monitor Job Changes
Learn how to track when your prospects change jobs, get promoted, or move companies - perfect timing for sales outreach.
Why Monitor Job Changes?
- • New budget authority: People in new roles often have budget to spend
- • Perfect timing: Congratulate them and offer to help in their new role
- • Track champions: Follow your product champions to their new companies
- • Update CRM: Keep contact records current automatically
What You'll Build
In this tutorial, you'll build a system that:
- Monitors a list of LinkedIn contacts for job changes
- Receives webhooks when changes are detected
- Triggers automated actions (email, CRM update, Slack notification)
- Tracks the effectiveness of job-change outreach
Step 1: Import Your Contact List
First, prepare a list of LinkedIn profiles you want to monitor. You can get these from:
- Your CRM export
- Sales Navigator lists
- LinkedIn connections export
- Target account contact lists
Create a CSV file with LinkedIn URLs:
linkedin_url,name,current_company
https://linkedin.com/in/jane-smith,Jane Smith,Acme Corp
https://linkedin.com/in/john-doe,John Doe,TechCo
https://linkedin.com/in/sarah-johnson,Sarah Johnson,StartupXYZ
Step 2: Bulk Import Subscriptions
Use the bulk endpoint to import all contacts at once:
const fs = require('fs');
const csv = require('csv-parse/sync');
// Read and parse CSV
const fileContent = fs.readFileSync('contacts.csv', 'utf8');
const contacts = csv.parse(fileContent, {
columns: true,
skip_empty_lines: true
});
// Prepare bulk import
const subscriptions = contacts.map(contact => ({
entity_type: 'contact',
linkedin_url: contact.linkedin_url
}));
// Import in batches of 100
const batchSize = 100;
for (let i = 0; i < subscriptions.length; i += batchSize) {
const batch = subscriptions.slice(i, i + batchSize);
const response = await fetch('https://api.saleswebhooks.com/v1/subscriptions/bulk', {
method: 'POST',
headers: {
'X-API-Key': process.env.SALESWEBHOOKS_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({ subscriptions: batch })
});
const result = await response.json();
console.log(`Imported batch: ${result.created} created, ${result.failed} failed`);
}
Step 3: Set Up Your Webhook Handler
Create an endpoint to receive job change notifications:
const express = require('express');
const crypto = require('crypto');
const app = express();
// Raw body needed for signature verification
app.use(express.raw({ type: 'application/json' }));
// Webhook endpoint
app.post('/webhooks/saleswebhooks', async (req, res) => {
// Verify webhook signature
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
const webhookSecret = process.env.SALESWEBHOOKS_WEBHOOK_SECRET;
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(req.body)
.digest('hex');
if (signature !== expectedSignature) {
return res.status(401).send('Invalid signature');
}
// Parse the webhook
const event = JSON.parse(req.body);
// Handle job change events
if (event.type === 'contact.position_changed' ||
event.type === 'contact.company_changed') {
await handleJobChange(event);
}
res.status(200).send('OK');
});
async function handleJobChange(event) {
const { entity, changes } = event.data;
console.log(`Job change detected for ${entity.linkedin_id}:`);
console.log(` Previous: ${changes.previous_title} at ${changes.previous_company}`);
console.log(` Current: ${changes.current_title} at ${changes.current_company}`);
// Trigger your automation here
await sendCongratulationsEmail(entity, changes);
await updateCRM(entity, changes);
await notifySlack(entity, changes);
}
Step 4: Automate Congratulations Emails
Send personalized congratulation emails when job changes are detected:
async function sendCongratulationsEmail(entity, changes) {
// Fetch contact details from your database
const contact = await getContactByLinkedIn(entity.linkedin_id);
// Determine email template based on change type
let template;
if (changes.current_company !== changes.previous_company) {
template = 'new_company_congrats';
} else {
template = 'promotion_congrats';
}
// Send personalized email
await emailService.send({
to: contact.email,
template: template,
data: {
name: contact.firstName,
previousRole: `${changes.previous_title} at ${changes.previous_company}`,
newRole: `${changes.current_title} at ${changes.current_company}`,
isPromotion: changes.current_company === changes.previous_company
}
});
// Log outreach in CRM
await logActivity(contact.id, 'Sent job change congratulations email');
}
Example email template:
Subject: Congratulations on your new role at {{newCompany}}!
Hi {{name}},
I just saw that you've moved to {{newRole}} - congratulations!
{{#if isPromotion}}
It's great to see you growing within {{currentCompany}}. Your promotion is well-deserved!
{{else}}
Starting at a new company is always exciting. How are you settling in at {{currentCompany}}?
{{/if}}
I'd love to hear about your priorities in the new role. We've helped similar companies
with [specific value prop relevant to their new company/role].
Would you be open to a brief call next week to explore how we might support your success?
Best regards,
[Your name]
Step 5: Update Your CRM Automatically
Keep your CRM data current with job changes:
async function updateCRM(entity, changes) {
// Example with Salesforce
const conn = new jsforce.Connection({
loginUrl: 'https://login.salesforce.com',
accessToken: process.env.SALESFORCE_ACCESS_TOKEN
});
// Find contact by LinkedIn URL
const contacts = await conn.sobject('Contact')
.find({ LinkedIn_URL__c: entity.linkedin_url });
if (contacts.length > 0) {
const contactId = contacts[0].Id;
// Update contact record
await conn.sobject('Contact').update({
Id: contactId,
Title: changes.current_title,
Company: changes.current_company,
Job_Change_Date__c: new Date(),
Previous_Company__c: changes.previous_company,
Previous_Title__c: changes.previous_title
});
// Create task for sales rep
await conn.sobject('Task').create({
WhoId: contactId,
Subject: `Follow up on job change - ${changes.current_title} at ${changes.current_company}`,
Description: `Contact moved from ${changes.previous_title} at ${changes.previous_company}. Send congratulations and explore opportunities.`,
ActivityDate: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000), // 2 days from now
Priority: 'High'
});
}
}
Step 6: Set Up Slack Notifications
Alert your sales team instantly when key prospects change jobs:
async function notifySlack(entity, changes) {
const webhook = process.env.SLACK_WEBHOOK_URL;
// Determine channel based on account tier or territory
const channel = getSlackChannel(entity);
const message = {
channel: channel,
text: `🎯 Job Change Alert: ${entity.linkedin_url}`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*Job Change Detected*\n<${entity.linkedin_url}|View LinkedIn Profile>`
}
},
{
type: 'section',
fields: [
{
type: 'mrkdwn',
text: `*Previous Role:*\n${changes.previous_title}\n${changes.previous_company}`
},
{
type: 'mrkdwn',
text: `*New Role:*\n${changes.current_title}\n${changes.current_company}`
}
]
},
{
type: 'actions',
elements: [
{
type: 'button',
text: { type: 'plain_text', text: '📧 Send Email' },
url: `https://yourcrm.com/contacts/${entity.linkedin_id}/email`
},
{
type: 'button',
text: { type: 'plain_text', text: '📞 Log Call' },
url: `https://yourcrm.com/contacts/${entity.linkedin_id}/call`
}
]
}
]
};
await fetch(webhook, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(message)
});
}
Step 7: Track Job Change Outreach Success
Monitor the effectiveness of your job change outreach:
// Track engagement metrics
const jobChangeMetrics = {
async recordOutreach(contactId, changeType) {
await db.jobChangeOutreach.create({
contact_id: contactId,
change_type: changeType, // 'new_company' or 'promotion'
outreach_date: new Date(),
email_sent: true
});
},
async recordResponse(contactId, responseType) {
await db.jobChangeOutreach.update({
where: { contact_id: contactId },
data: {
response_type: responseType, // 'email_reply', 'meeting_booked', 'no_response'
response_date: new Date()
}
});
},
async getMetrics(timeframe = 30) {
const results = await db.jobChangeOutreach.aggregate({
where: {
outreach_date: {
gte: new Date(Date.now() - timeframe * 24 * 60 * 60 * 1000)
}
},
group_by: ['change_type', 'response_type'],
count: true
});
return {
total_job_changes: results.reduce((sum, r) => sum + r.count, 0),
response_rate: calculateResponseRate(results),
meeting_rate: calculateMeetingRate(results),
by_change_type: groupByChangeType(results)
};
}
};
Best Practices
⏱️ Timing is Everything
- • Reach out within 48 hours of the job change
- • Avoid Mondays (too busy) and Fridays (winding down)
- • Best times: Tuesday-Thursday, 10am-11am or 2pm-3pm
✍️ Personalization Tips
- • Reference specific aspects of their new role
- • Mention mutual connections if any
- • Focus on their success, not your product
- • Keep initial outreach short (under 100 words)
🎯 Prioritization
- • Focus on contacts moving to target accounts
- • Prioritize promotions to decision-maker roles
- • Track champions moving to new companies
- • Flag contacts joining your existing customers
Advanced: Multi-Channel Orchestration
Combine multiple touchpoints for better response rates:
class JobChangeOrchestrator {
async orchestrate(contact, changes) {
const sequence = [
{ day: 0, action: 'linkedin_connection_request' },
{ day: 1, action: 'congratulations_email' },
{ day: 3, action: 'linkedin_message' },
{ day: 7, action: 'follow_up_email' },
{ day: 14, action: 'call_attempt' }
];
for (const step of sequence) {
await this.scheduleAction({
contact_id: contact.id,
action: step.action,
scheduled_for: addDays(new Date(), step.day),
context: { changes }
});
}
}
async executeAction(scheduledAction) {
const { contact_id, action, context } = scheduledAction;
// Check if contact has already responded
if (await this.hasResponded(contact_id)) {
return this.cancelRemainingActions(contact_id);
}
switch (action) {
case 'linkedin_connection_request':
await this.sendConnectionRequest(contact_id, context);
break;
case 'congratulations_email':
await this.sendEmail(contact_id, 'congratulations', context);
break;
case 'linkedin_message':
await this.sendLinkedInMessage(contact_id, context);
break;
// ... more actions
}
}
}
Results You Can Expect
Based on industry benchmarks, job change outreach typically sees:
- 40-60% email open rates (vs. 20-30% for cold outreach)
- 15-25% reply rates (vs. 1-3% for cold outreach)
- 5-10% meeting booking rate (vs. 0.5-1% for cold outreach)
- 2-3x higher conversion rates compared to standard outreach
🚀 Ready to Start?
Job change monitoring is one of the highest-ROI sales activities. Start tracking your key contacts today.
Set Up Job Change Monitoring →