Skip to main content

Lead Events

Lead events follow a lead from capture through ownership and funnel changes to deletion.

Unlike Client and Unit events, only lead.created carries a full snapshot. The four mutation events carry only what changed, because a lead's organization-configured fields include contact details that should not be re-sent on every status move.

Event types

EventEmitted when
lead.createdA lead is captured in Sakneen
lead.assignedA lead without an owner gains a salesperson
lead.reassignedA lead moves from one salesperson to another
lead.status_changedThe lead status moves
lead.updatedA lead changes in a way no more specific lead event covers
lead.deletedA lead is deleted

All use eventVersion: 1, resource.type: "lead", and the lead's integration version as resource.version.

Lead conversion

lead.converted is not emitted and is not offered in the subscription catalog. Until it ships, treat lead.status_changed reaching your organization's configured converted status as the conversion signal.

One event per change

A single change to a lead produces exactly one event. When a change could match more than one type, Sakneen picks the most specific one in this order:

deleted > assigned | reassigned > status_changed > updated

The chosen type is a label only — it never limits the content. A bulk edit that reassigns a lead and moves its status arrives as lead.reassigned, and changedFields still lists both changes. If you do not care about the distinction, ignore the event type and read changedFields.

To mirror the full lead state you must subscribe to all six event types.

Identifiers

FieldTypeDescription
leadIdstringCanonical Sakneen lead identifier. Matches resource.id.
leadCodenumberOrganization-scoped lead number shown in the Sakneen UI.
leadId is not the number you see in Sakneen

leadId is the lead's canonical identifier and matches resource.id. The number displayed in the Sakneen interface is published as leadCode. This mirrors clientId and clientCode on Client events.

Both fields appear on every lead event, and both always override organization-configured fields with the same names.

lead.created data contract

FieldTypeDescription
leadIdstringCanonical lead identifier
leadCodenumberOrganization-scoped lead number
clientIdstringLinked Client, joining this lead to the client.* stream
parentIdstringSet when this lead is a duplicate of an earlier one
isActivebooleanFalse for a duplicate lead held behind a parent
isFreshbooleanWhether the lead is still awaiting first contact
compoundsIdsstring[]Compounds the lead is interested in
preferencesobject[]Captured lead preferences
salesPersonIdstringCurrent owner
assignedAtstringWhen the current owner was assigned
assignedByIdstringWho assigned the current owner
createdByIdstringWho captured the lead
isReassignedbooleanWhether the lead has changed owner at least once
reassignedAtstringWhen the lead last changed owner
lastActivityDatestringLast recorded sales activity
providerstringCapture channel, when the lead came from an ad platform
createdAt / updatedAtstringTimestamps
Organization-configured fieldsJSON valuesThe lead fields configured for your organization

Fields the lead does not carry are omitted rather than sent as null.

Sakneen never publishes raw provider or request payloads, internal sales notes, record-system identifiers, or salesperson names, emails, and teams. Resolve people by their identifiers through the External API.

Change contract

lead.assigned, lead.reassigned, lead.status_changed, and lead.updated publish a change rather than a snapshot.

FieldTypeDescription
changedFieldsstring[]Every field this change touched, sorted
changedobjectThe new value of each changed field
previousobjectThe value each changed field held before

Organization-configured fields appear under a dotted name, such as data.status. A field the change added has null in previous; a field it removed has null in changed.

A change that touched nothing you can see publishes no event at all, and does not advance resource.version. You will not see version gaps for silent edits.

Assignment fields

lead.assigned and lead.reassigned add:

FieldTypeDescription
assignmentSource"auto" | "manual"Whether Sakneen's auto-assignment or a person assigned the lead

Status fields

lead.status_changed adds:

FieldTypeDescription
statusSlugstringNormalized form of the new status
previousStatusSlugstringNormalized form of the previous status
changedVia"direct" | "bulk" | "feedback"How the status moved

Lead statuses are configured per organization, so they are free text rather than a fixed set. Sakneen also sends a normalized slug — lowercase with all non-alphanumeric characters removed — so "No Answer", "no-answer", and "noAnswer" all arrive as noanswer. Match on statusSlug when you need stable buckets; use changed["data.status"] when you need the exact configured label.

changedVia: "feedback" means the status moved because a salesperson logged feedback, not because someone set the status directly.

lead.created example

{
"eventId": "019c0f11-2a41-5b8e-9a3c-1d7e4f2b6c80",
"eventType": "lead.created",
"eventVersion": 1,
"occurredAt": "2026-08-06T09:00:00.000Z",
"source": "sakneen",
"organizationId": "64f1a0d6d5d0a14a5c0e8a10",
"correlationId": "019c0f11-2a41-7c25-b41a-6e2f9d3a1b74",
"resource": {
"type": "lead",
"id": "64f1a0d6d5d0a14a5c0e8a30",
"version": 1
},
"data": {
"leadId": "64f1a0d6d5d0a14a5c0e8a30",
"leadCode": 4127,
"clientId": "64f1a0d6d5d0a14a5c0e8a20",
"isActive": true,
"isFresh": true,
"compoundsIds": ["64f1a0d6d5d0a14a5c0e8a40"],
"salesPersonId": "64f1a0d6d5d0a14a5c0e8a50",
"assignedAt": "2026-08-06T09:00:00.000Z",
"createdById": "64f1a0d6d5d0a14a5c0e8a50",
"createdAt": "2026-08-06T09:00:00.000Z",
"updatedAt": "2026-08-06T09:00:00.000Z",
"firstName": "Nour",
"lastName": "Hassan",
"phoneNumber": "+201000000000",
"status": "New"
}
}

lead.assigned example

{
"eventId": "019c0f12-8b03-5d17-84f2-9c1a7e6d3f52",
"eventType": "lead.assigned",
"eventVersion": 1,
"occurredAt": "2026-08-06T10:15:00.000Z",
"source": "sakneen",
"organizationId": "64f1a0d6d5d0a14a5c0e8a10",
"correlationId": "019c0f12-8b03-71c6-a208-4d93b5e0f1aa",
"resource": {
"type": "lead",
"id": "64f1a0d6d5d0a14a5c0e8a30",
"version": 2
},
"data": {
"leadId": "64f1a0d6d5d0a14a5c0e8a30",
"leadCode": 4127,
"changedFields": ["assignedAt", "salesPersonId"],
"changed": {
"assignedAt": "2026-08-06T10:15:00.000Z",
"salesPersonId": "64f1a0d6d5d0a14a5c0e8a50"
},
"previous": {
"assignedAt": null,
"salesPersonId": null
},
"assignmentSource": "auto"
}
}

lead.status_changed example

{
"eventId": "019c0f13-c4d9-5e60-b7a1-2f8c05e94d13",
"eventType": "lead.status_changed",
"eventVersion": 1,
"occurredAt": "2026-08-06T11:40:00.000Z",
"source": "sakneen",
"organizationId": "64f1a0d6d5d0a14a5c0e8a10",
"correlationId": "019c0f13-c4d9-7a3f-9e05-7b1d2c6a8e40",
"resource": {
"type": "lead",
"id": "64f1a0d6d5d0a14a5c0e8a30",
"version": 3
},
"data": {
"leadId": "64f1a0d6d5d0a14a5c0e8a30",
"leadCode": 4127,
"changedFields": ["data.status"],
"changed": { "data.status": "Meeting Scheduled" },
"previous": { "data.status": "No Answer" },
"statusSlug": "meetingscheduled",
"previousStatusSlug": "noanswer",
"changedVia": "feedback"
}
}

lead.deleted example

A deletion carries identifiers and a timestamp only.

{
"eventId": "019c0f14-1f7a-5c22-8d94-6e3b0a7f52c1",
"eventType": "lead.deleted",
"eventVersion": 1,
"occurredAt": "2026-08-06T12:05:00.000Z",
"source": "sakneen",
"organizationId": "64f1a0d6d5d0a14a5c0e8a10",
"correlationId": "019c0f14-1f7a-79b8-a1c3-04e7d29b6f38",
"resource": {
"type": "lead",
"id": "64f1a0d6d5d0a14a5c0e8a30",
"version": 4
},
"data": {
"leadId": "64f1a0d6d5d0a14a5c0e8a30",
"leadCode": 4127,
"deletedAt": "2026-08-06T12:05:00.000Z"
}
}

Recovering from a missed event

Because the mutation events carry changes rather than snapshots, a missed event leaves your copy of a lead permanently behind. This is different from Client and Unit events, where the next event repairs the record on its own.

resource.version increases by exactly one per published change for a given lead, so a gap is detectable. When you see one — or after a destination has been suspended — re-read the lead:

GET /external/apis/v1.0/leads/:recordSystemId
api-key: YOUR_API_KEY

The response wraps the snapshot: its lead field carries the same shape lead.created publishes in data.

{
"ok": true,
"lead": {
"leadId": "64f1a0d6d5d0a14a5c0e8a30",
"leadCode": 4127,
"clientId": "64f1a0d6d5d0a14a5c0e8a20",
"isActive": true,
"isFresh": false,
"salesPersonId": "64f1a0d6d5d0a14a5c0e8a50",
"firstName": "Nour",
"status": "Meeting Scheduled"
}
}

Replace your stored copy with lead and resume applying events from the next one you receive.

Suspended destinations lose events

Events routed to a destination while it is suspended are cancelled and cannot be replayed from the Developer Console. Re-read affected leads after reactivating a destination.

Scope

Version one publishes lead changes made through Sakneen's own lead flows: lead capture across the sales, ecommerce, admin, external API, and AI paths and both bulk imports; assignment and reassignment; status changes, including those derived from sales feedback; other direct lead edits; and deletion.

The following do not emit lead events:

  • Salesforce field synchronization into Sakneen
  • Lead capture from Facebook, LinkedIn, and TikTok ad integrations
  • Lead expiry and inactivity background jobs
  • Migrations, seeders, repairs, and backfills

Assignment performed as part of a CRM synchronization does emit, because the lead genuinely changed owner.

Historical leads are not backfilled. Events begin once an active destination subscribes.