Skip to main content
This event is triggered when a lead’s status changes, whether through campaign automation or manual updates via the API.

When This Event Fires

  • Lead status is updated via the Update Lead Status API
  • Campaign automation changes lead status
  • Lead progresses through the campaign sequence

Payload

{
  "eventId": "evt_1708456789123_abc123def",
  "eventType": "lead.status.changed",
  "timestamp": "2024-02-24T10:30:00.000Z",
  "workspaceId": "ws_abc123xyz",
  "data": {
    "leadId": "lead_abc123",
    "campaignId": "camp_xyz789",
    "linkedinUrl": "https://www.linkedin.com/in/john-doe",
    "previousStatus": "REPLY_RECEIVED",
    "newStatus": "OPPORTUNITY",
    "changedBy": "api"
  }
}

Payload Fields

FieldTypeDescription
eventIdstringUnique event identifier for idempotency
eventTypestringAlways lead.status.changed
timestampstringISO 8601 timestamp when the status changed
workspaceIdstringYour workspace ID
data.leadIdstringThe lead whose status changed
data.campaignIdstringThe campaign this lead belongs to
data.linkedinUrlstringLinkedIn profile URL of the lead
data.previousStatusstringStatus before the change
data.newStatusstringNew status after the change
data.changedBystringHow the change was triggered: api, automation, or user

Status Values

StatusDescription
PENDINGLead added but no action taken yet
CONNECTION_SENTConnection request sent
CONNECTION_ACCEPTEDConnection accepted by lead
MESSAGE_SENTFollow-up message sent
REPLY_RECEIVEDLead has replied
OPPORTUNITYMarked as sales opportunity
NOT_INTERESTEDLead declined or not interested
DONELead journey completed

Use Cases

CRM Sync

Keep your CRM in sync with lead status changes

Pipeline Tracking

Track leads through your sales pipeline

Alerting

Alert team when leads become opportunities

Automation

Trigger external workflows based on status

Example Handler

app.post('/webhooks/sendpilot', async (req, res) => {
  const event = req.body;
  
  if (event.eventType === 'lead.status.changed') {
    const { leadId, previousStatus, newStatus, linkedinUrl } = event.data;
    
    // Sync status to CRM
    await crm.updateLead(leadId, {
      status: mapToCustomStatus(newStatus),
      lastStatusChange: event.timestamp
    });
    
    // Alert on high-value transitions
    if (newStatus === 'OPPORTUNITY') {
      await slack.postMessage({
        channel: '#sales-opportunities',
        text: `🎯 New opportunity!\n` +
              `Lead: ${linkedinUrl}\n` +
              `Previous status: ${previousStatus}`
      });
    }
    
    // Handle negative outcomes
    if (newStatus === 'NOT_INTERESTED') {
      await analytics.track('lead_lost', {
        leadId,
        previousStatus
      });
    }
    
    console.log(`Lead ${leadId}: ${previousStatus}${newStatus}`);
  }
  
  res.status(200).send('OK');
});

function mapToCustomStatus(sendpilotStatus) {
  const mapping = {
    'PENDING': 'new',
    'CONNECTION_SENT': 'contacted',
    'CONNECTION_ACCEPTED': 'connected',
    'MESSAGE_SENT': 'in_conversation',
    'REPLY_RECEIVED': 'engaged',
    'OPPORTUNITY': 'qualified',
    'NOT_INTERESTED': 'closed_lost',
    'DONE': 'closed_won'
  };
  return mapping[sendpilotStatus] || 'unknown';
}
Status changes can be frequent as leads progress through campaign sequences. Consider batching or debouncing if you’re making external API calls on each status change.