Lead Events
Lead events follow a lead from capture through ownership and funnel changes to deletion. Every event publishes the stored Lead as Sakneen holds it at the moment of the change.
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 six 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. Every event
carries the whole lead, so a bulk edit that reassigns a lead and moves its
status arrives as lead.reassigned with both new values in data.
Subscribe to every type whose transition you care about. Because each event carries the full lead, subscribing to a single type still keeps your copy correct for the changes that type covers.
Envelope values
Lead events use the standard event envelope with these values:
| Field | Lead event value |
|---|---|
eventType | One of the six types above |
eventVersion | 1 |
occurredAt | The lead snapshot's updatedAt timestamp |
source | sakneen |
organizationId | Organization that owns the lead |
resource.type | lead |
resource.id | Same identifier as data._id |
resource.version | Positive, monotonically increasing lead integration version |
data | The stored lead, described below |
eventVersion identifies the external contract described on this page.
resource.version identifies the revision of one lead. It advances only when a
change is publishable and you are subscribed, so you will not see gaps for
silent edits. Compare versions rather than assuming they are contiguous.
Snapshot behavior
The data object is the complete stored lead document at the moment of the
change, not a field-level patch. Replace your stored snapshot only when the
incoming resource.version is newer than the version you have already
processed.
Because delivery is at least once and each delivery retries independently, events for the same lead can arrive out of order. Applying by version rather than arrival order is what keeps your copy correct.
Identifiers
leadId is the organization number, not the canonical identifierdata._id is the lead's canonical Sakneen identifier and matches
resource.id. data.leadId is the organization-scoped lead number shown in
the Sakneen interface.
This differs from Client events, where the canonical identifier is published as
clientId and the organization number as clientCode. Lead events publish
Sakneen's stored field names directly.
Lead data
Version one delivers the lead exactly as Sakneen stores it. data therefore
reflects your organization's own lead configuration: which fields are present
depends on how your organization uses Sakneen, and on the individual lead.
These fields are always present:
| Field | Type | Description |
|---|---|---|
_id | string | Canonical Sakneen lead identifier; matches resource.id |
organizationId | string | Organization that owns the lead; matches the envelope organization |
leadId | number | Organization-scoped lead number shown in the Sakneen interface |
data | object | Your organization's configured lead fields, such as name, phoneNumber, email, and status |
createdAt | ISO 8601 string | UTC time when the lead was captured |
updatedAt | ISO 8601 string | UTC time of this lead snapshot; matches occurredAt |
Every other stored lead field is delivered as stored. Common examples:
| Field | Type | Description |
|---|---|---|
clientId | string | Linked Client, joining this lead to the client.* stream |
salesPersonId | string | Current owner |
assignedAt | ISO 8601 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 | ISO 8601 string | When the lead last changed owner |
isActive | boolean | False for a duplicate lead held behind a parent |
isFresh | boolean | Whether the lead is still awaiting first contact |
isDeleted | boolean | Whether the lead has been soft-deleted |
parentId | string | Set when this lead is a duplicate of an earlier one |
compoundsIds | string[] | Compounds the lead is interested in |
preferences | object[] | Captured lead preferences |
lastActivityDate | ISO 8601 string | Last recorded sales activity |
provider | string | Capture channel, when the lead came from an ad platform |
recordSystemId | string | Your external lead or submission identifier, when one was supplied |
Value conventions:
- Identifiers are strings, and timestamps are ISO 8601 strings.
leadIdis a number.- Fields with no stored value are omitted. An omitted field is not an empty
string, zero,
false, or an empty array. - Numbers, booleans, arrays, and nested objects appear as stored.
The following are never included in data:
integrationVersion, which is published asresource.versioninstead;- Sakneen's internal document version key and identifier alias;
- computed values and related records loaded from other collections, such as the linked salesperson, client, child leads, activities, and feedback.
Because version one sends the lead as stored, data can contain internal
operational content:
notesanddata.leadNotes— free-text sales commentary written by your team about a named person;rowDataandadditionalData— verbatim import rows and provider request bodies, exactly as they arrived, which can include tracking metadata;- provider, CRM, and record-system identifiers.
Send lead events only to destinations entitled to your organization's full lead data, including personal data belonging to your contacts, and do not forward the payload unchanged to less-trusted systems. Confirm this is compatible with your own privacy commitments before subscribing.
A later event version will add organization-level field selection.
Sakneen can add fields to event version 1 at any time. Ignore unknown fields, and do not assume a fixed set of keys. Read the fields your integration understands, and validate them on your side rather than assuming a Sakneen-side shape guarantee.
Lead statuses
Lead statuses are configured per organization, so data.data.status is free
text rather than a fixed set. Two organizations can use different labels for
the same stage, and one organization can rename a status at any time.
Normalize on your side if you need stable buckets — for example by lowercasing
and removing non-alphanumeric characters, so "No Answer", "no-answer", and
"noAnswer" all collapse to the same key.
Representative event
{
"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": {
"_id": "64f1a0d6d5d0a14a5c0e8a30",
"organizationId": "64f1a0d6d5d0a14a5c0e8a10",
"leadId": 4127,
"clientId": "64f1a0d6d5d0a14a5c0e8a20",
"recordSystemId": "website-form-018f9f29-4b8a-7c1a",
"compoundsIds": ["64f1a0d6d5d0a14a5c0e8a40"],
"salesPersonId": "64f1a0d6d5d0a14a5c0e8a50",
"assignedAt": "2026-08-06T10:15:00.000Z",
"assignedById": "64f1a0d6d5d0a14a5c0e8a50",
"createdById": "64f1a0d6d5d0a14a5c0e8a50",
"isActive": true,
"isDeleted": false,
"isFresh": false,
"isReassigned": false,
"lastActivityDate": "2026-08-06T11:40:00.000Z",
"data": {
"email": "[email protected]",
"firstName": "Nour",
"lastName": "Hassan",
"phoneNumber": "+201000000000",
"status": "Meeting Scheduled"
},
"createdAt": "2026-08-06T09:00:00.000Z",
"updatedAt": "2026-08-06T11:40:00.000Z"
}
}
Deletion snapshots
lead.deleted represents a soft deletion. Its data object is the full final
stored lead with isDeleted: true; it is not a tombstone containing only an
identifier. Repeating a delete for an already deleted lead does not produce a
new lifecycle event.
Recovering from a missed event
Every lead event carries the whole lead, so a missed event repairs itself: the next event you receive for that lead replaces your copy with current state.
One case does not repair itself. Events routed to a destination while it is suspended are cancelled and cannot be replayed from the Developer Console, so a lead that changed only during the suspension will not be corrected until it changes again. Re-read those leads after reactivating a destination:
GET /external/apis/v1.0/leads/:recordSystemId
api-key: YOUR_API_KEY
The response wraps the lead: its lead field carries the same shape lead
events publish in data.
{
"ok": true,
"lead": {
"_id": "64f1a0d6d5d0a14a5c0e8a30",
"organizationId": "64f1a0d6d5d0a14a5c0e8a10",
"leadId": 4127,
"clientId": "64f1a0d6d5d0a14a5c0e8a20",
"recordSystemId": "website-form-018f9f29-4b8a-7c1a",
"salesPersonId": "64f1a0d6d5d0a14a5c0e8a50",
"isActive": true,
"isDeleted": false,
"isFresh": false,
"data": {
"firstName": "Nour",
"status": "Meeting Scheduled"
},
"createdAt": "2026-08-06T09:00:00.000Z",
"updatedAt": "2026-08-06T11:40:00.000Z"
}
}
This route requires the lead to have a recordSystemId. Leads captured without
one cannot be re-read by external identifier.
Processing recommendations
- Verify the webhook signature against the raw request body.
- Deduplicate every event by
eventId; delivery is at least once. - Partition processing by
resource.typeandresource.id. - Apply a snapshot only when
resource.versionis newer than the version you have already processed. - Replace your stored snapshot instead of treating
dataas a field-level patch. - Do not require resource versions to be consecutive.
- Configure the webhook endpoint to accept JSON request bodies up to 1 MiB.
- Return
2xxonly after durable acceptance. See Delivery and retries for automatic and manual retry behavior.
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.