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.updated",
  "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"
  }
}

Payload Fields

FieldTypeDescription
eventIdstringUnique event identifier for idempotency
eventTypestringAlways lead.updated
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

Campaign Status Values

StatusDescription
PENDINGLead added but no action taken yet
PROCESSINGLead is being processed
CONNECTION_SENTConnection request sent
CONNECTION_ACCEPTEDConnection accepted by lead
MESSAGE_SENTFollow-up message sent
REPLY_RECEIVEDLead has replied
FOLLOWUP_SENTFollow-up message sent
BLOCKEDLead blocked the sender
PROFILE_UNREACHABLEProfile is unreachable
RATE_LIMITEDRate limited by LinkedIn
FAILEDAction failed
SUCCESSCampaign completed successfully
UNSUBSCRIBEDLead unsubscribed
IRRELEVANTLead marked as irrelevant
SKIPPEDLead was skipped
DONELead journey completed
MEETING_BOOKEDMeeting has been booked
OPPORTUNITYMarked as sales opportunity
LIKED_POSTLead liked a post

Custom Lead Status Values

StatusDescription
LEADNew lead (default)
INTERESTEDLead has shown interest
MEETING_BOOKEDMeeting has been scheduled
MEETING_COMPLETE_NOT_CLOSEDMeeting completed but deal not closed
CLOSEDDeal closed/won
WRONG_PERSONWrong contact/person
NOT_INTERESTEDLead is not interested
NO_RESPONSENo response received

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.updated') {
    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.