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
| Event | Emitted when |
|---|---|
lead.created | A lead is captured in Sakneen |
lead.assigned | A lead without an owner gains a salesperson |
lead.reassigned | A lead moves from one salesperson to another |
lead.status_changed | The lead status moves |
lead.updated | A lead changes in a way no more specific lead event covers |
lead.deleted | A lead is deleted |
All use eventVersion: 1, resource.type: "lead", and the lead's integration
version as resource.version.
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
| Field | Type | Description |
|---|---|---|
leadId | string | Canonical Sakneen lead identifier. Matches resource.id. |
leadCode | number | Organization-scoped lead number shown in the Sakneen UI. |
leadId is not the number you see in SakneenleadId 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
| Field | Type | Description |
|---|---|---|
leadId | string | Canonical lead identifier |
leadCode | number | Organization-scoped lead number |
clientId | string | Linked Client, joining this lead to the client.* stream |
parentId | string | Set when this lead is a duplicate of an earlier one |
isActive | boolean | False for a duplicate lead held behind a parent |
isFresh | boolean | Whether the lead is still awaiting first contact |
compoundsIds | string[] | Compounds the lead is interested in |
preferences | object[] | Captured lead preferences |
salesPersonId | string | Current owner |
assignedAt | string | When the current owner was assigned |
assignedById | string | Who assigned the current owner |
createdById | string | Who captured the lead |
isReassigned | boolean | Whether the lead has changed owner at least once |
reassignedAt | string | When the lead last changed owner |
lastActivityDate | string | Last recorded sales activity |
provider | string | Capture channel, when the lead came from an ad platform |
createdAt / updatedAt | string | Timestamps |
| Organization-configured fields | JSON values | The 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.
| Field | Type | Description |
|---|---|---|
changedFields | string[] | Every field this change touched, sorted |
changed | object | The new value of each changed field |
previous | object | The 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:
| Field | Type | Description |
|---|---|---|
assignmentSource | "auto" | "manual" | Whether Sakneen's auto-assignment or a person assigned the lead |
Status fields
lead.status_changed adds:
| Field | Type | Description |
|---|---|---|
statusSlug | string | Normalized form of the new status |
previousStatusSlug | string | Normalized 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.
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.