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:

  1. Monitors a list of LinkedIn contacts for job changes
  2. Receives webhooks when changes are detected
  3. Triggers automated actions (email, CRM update, Slack notification)
  4. 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 →