Skip to main content

Lead events

Webhooks fired by the Leads product. See Webhooks overview for setup, envelope format, signature verification, retries, and security.

Each webhook subscribes to one or more event types. Subscriptions are independent: a webhook that only subscribes to lead2.created will never receive activity.detected, and vice versa.

EventWhen it firesVolume
lead2.createdThe first time a person is seen as a lead for your brand. Fires once per person, ever.Low
activity.detectedEvery time a tracked person comments, reacts, or reposts on your tracked content. Fires for the first engagement and every subsequent one. Payload includes an isFirstActivity flag so you can distinguish first-touch.High

The engagements array​

Both events ship an engagements array. Every field below is also available on the public REST API (GET /signal-leads/:id and GET /signal-leads), in the same shape — one parser works for the webhook and the API.

postAuthorDetail and source were webhook-only when they shipped. They are now on the REST API too, but opt-in there: pass includeEngagementDetail=true (alongside includeEngagements=true on the list endpoint). On the webhook they are always sent, no flag involved.

FieldTypeDescription
idstringEngagement id
typestring | nullcomment / reaction / repost
reactionTypestring | nulle.g. LIKE, EMPATHY (only when type is reaction)
commentTextstring | nullSanitized comment text (only when type is comment)
postUrlstring | nullThe post that was engaged with
postContentstring | nullText content of the social post
postPostedAtstring | nullWhen the post was originally published (ISO 8601)
postAuthorstring | nullDisplay name of the post author
postAuthorDetailobject | nullStructured post author — see below
sourceobject | nullWhat the signal was watching when it found this post — see below
signalobject | nullThe signal that triggered detection (id, name, signalType)
emittedAtstring | nullWhen the signal emission was created (ISO 8601)
trackedAtstringWhen Limelight detected the engagement (ISO 8601)

activity.detected always sends a single-item engagements array — the engagement that triggered the event. lead2.created includes up to 20 most-recent engagements known for the lead at delivery time.

source — which competitor a post came from​

For competitor signals (posts-from-their-competitors-pages, posts-that-mention-competitors, posts-from-competitors-employees), source.value is the exact LinkedIn URL you configured for that signal. That is the reliable way to attribute a post to a competitor — signal.name only tells you which signal fired, not which entry in it.

FieldTypeDescription
idstring | nullInternal id of the tracked entry
typestring | nullli-company-url, li-company-mention, li-profile-url, post-url, posts-with-keywords
valuestring | nullThe LinkedIn URL you configured for this signal
liCompanyIdstring | nullLinkedIn numeric company id, on company-mention entries
companyobject | nullResolved company record for that page, when we have one

type tells you how to read value: the two li-company-* types point at a company page (the competitor itself), while li-profile-url points at a person you asked us to track. post-url and posts-with-keywords signals are not competitor-scoped and carry no company.

postAuthorDetail — who wrote the post​

FieldTypeDescription
name / firstName / lastNamestring | nullAuthor name
headlinestring | nullLinkedIn headline, e.g. "VP Sales at Acme"
usernamestring | nullLinkedIn vanity name
linkedinProfileUrlstring | nullAuthor's profile URL
profileImageUrlstring | nullAuthor's avatar
companyobject | nullThe author's employer — see the caveat below
leadIdstring | nullThe author's own lead id in your brand, when they are one

company is only populated when the post author is also a tracked lead in your brand, because that is the only place we hold a structured employer for them. Expect it on roughly a third of engagements. When it is null, headline usually names the employer in free text, and for competitor signals source is the more reliable attribution.

The company object on both source and postAuthorDetail uses the same shape as company on GET /signal-leads/:id: id, name, url, homepageUrl, logoUrl, industry, staffCountRange, staffCount, location, country, city, username, description.

Additive, not breaking

postAuthor keeps its existing string | null shape — it is still the author's display name. postAuthorDetail and source are new sibling keys. Every other v1 field is unchanged, and the envelope is still "version": "1".

lead2.created​

{
"id": "wh_...",
"event": "lead2.created",
"version": "1",
"createdAt": "2026-04-16T10:22:00.000Z",
"brandId": "brand_abc",
"data": {
"lead2": {
"id": 1234,
"extId": "li-foo",
"name": "Alice Example",
"headline": "CTO at Example",
"username": "alice",
"title1": "CTO",
"liProfileUrl": "https://linkedin.com/in/alice",
"profileImgUrl": "https://cdn/img.jpg",
"leadCompany": {
"id": 98,
"name": "ACME Corp",
"domain": "acme.com"
}
},
"engagements": [
{
"id": "9876",
"type": "comment",
"reactionType": null,
"commentText": "Love this!",
"postUrl": "https://www.linkedin.com/feed/update/1",
"postContent": "Excited to share our latest B2B creator partnership results...",
"postPostedAt": "2026-04-15T08:00:00.000Z",
"postAuthor": "Pat Author",
"postAuthorDetail": {
"name": "Pat Author",
"firstName": "Pat",
"lastName": "Author",
"headline": "Account Executive at Clay",
"username": "patauthor",
"linkedinProfileUrl": "https://www.linkedin.com/in/patauthor",
"profileImageUrl": "https://cdn/pat.jpg",
"company": null,
"leadId": null
},
"source": {
"id": "2133",
"type": "li-company-url",
"value": "https://www.linkedin.com/company/fullenrich/",
"liCompanyId": null,
"company": {
"id": "18402",
"name": "FullEnrich",
"homepageUrl": "https://fullenrich.com",
"industry": "Computer Software",
"staffCount": 42,
"username": "fullenrich"
}
},
"signal": { "id": "42", "name": "DevOps influencers", "signalType": "keyword" },
"emittedAt": "2026-04-15T09:30:00.000Z",
"trackedAt": "2026-04-16T10:21:50.000Z"
}
]
}
}

activity.detected​

{
"id": "wh_...",
"event": "activity.detected",
"version": "1",
"createdAt": "2026-04-16T10:22:00.000Z",
"brandId": "brand_abc",
"data": {
"isFirstActivity": true,
"lead2": { /* same shape as lead2.created */ },
"engagements": [
{
"id": "9876",
"type": "comment",
"reactionType": null,
"commentText": "Love this!",
"postUrl": "https://www.linkedin.com/feed/update/1",
"postContent": "Excited to share our latest B2B creator partnership results...",
"postPostedAt": "2026-04-15T08:00:00.000Z",
"postAuthor": "Pat Author",
"postAuthorDetail": {
"name": "Pat Author",
"firstName": "Pat",
"lastName": "Author",
"headline": "Account Executive at Clay",
"username": "patauthor",
"linkedinProfileUrl": "https://www.linkedin.com/in/patauthor",
"profileImageUrl": "https://cdn/pat.jpg",
"company": null,
"leadId": null
},
"source": {
"id": "2133",
"type": "li-company-url",
"value": "https://www.linkedin.com/company/fullenrich/",
"liCompanyId": null,
"company": {
"id": "18402",
"name": "FullEnrich",
"homepageUrl": "https://fullenrich.com",
"industry": "Computer Software",
"staffCount": 42,
"username": "fullenrich"
}
},
"signal": { "id": "42", "name": "DevOps influencers", "signalType": "keyword" },
"emittedAt": "2026-04-15T09:30:00.000Z",
"trackedAt": "2026-04-16T10:21:50.000Z"
}
]
}
}

Manual resend​

From the Lead Feed, open any lead's ⋯ menu and choose Send to webhook to re-push lead2.created or the most recent activity.detected to a webhook of your choice.

See Manual resend in the overview for general rules (retention, rate limits).

Notes​

  • Internal source metadata is never sent in webhook payloads. Only the documented, normalized subset is included (commentText, reactionType, postUrl, postContent, postAuthor, postAuthorDetail, source, signal, emittedAt). In particular, source exposes only the LinkedIn URL you configured for the signal plus public company data — never another brand's configuration, and never the raw scrape output.