Webhook payload reference
Every field in an Embers webhook, from the envelope and version strings to the lead, engagement, contact, report, and test objects, and the key to join on.
Every Embers webhook is a JSON POST with the same envelope around an event-specific data object. This page lists every field, which ones to build on, and which ones are capped or conditional.
The envelope
| Field | Type | What it is |
|---|---|---|
id | UUID string | The event id. Equals the webhook-id header and stays the same on retries and replays |
type | string | One of the event types below |
api_version | string | 2026-07-20. Covers transport: headers, signatures, delivery, and the ids you join on |
schema_version | string | 2026-09-10. Changes when a payload key is added or removed |
created_at | ISO 8601 | When the event was created, not when this attempt was sent |
account | object | id (the workspace UUID) and name |
data | object | Shaped by type |
account lets an agency route events from several client workspaces through one receiver. The 2026-09-10 schema dropped account.type, so route on account.id. Ignore keys you do not recognise.
Event types
| Type | data carries |
|---|---|
lead.created | lead and engagement (recent form) |
lead.engagement.added | lead and engagement (batched form) |
lead.status_changed | lead, engagement (recent form), previous_status, status, changed_at |
report.generated | report, reason, generated_at |
webhook.test | is_test, sample_event_type, and the sample shape of that event |
Send lead events to your own endpoint covers when each one fires.
The lead object
data.lead is the same on every lead event.
| Field | Type | Notes |
|---|---|---|
id | UUID string | The Embers member id. See join on linkedin_urn |
name | string | Full name. There is no first or last name field |
headline | string | The LinkedIn headline |
title | string | Current title, falling back to the current role, then the headline |
company | string | Current company, falling back to the current role |
industry, location | string | As stored on the profile |
linkedin_url | string | Profile URL. Can change |
linkedin_urn | string | LinkedIn’s immutable member id. Can be blank on older records |
score | integer | Lead score, 0 to 100, as shown in the app. See how lead scoring works |
icp_match | boolean or null | null means not yet classified |
icp_confidence | integer | 0 to 100 |
icp_reason | string | One sentence on why the lead matched or did not |
trend | string | rising, stable, or cooling |
top_signals | array of strings | The signals behind the score |
sources | array of strings | Agents that found them: post_engagement, comment_engagement, competitor_engagement, keyword_tracking, qualify_post |
status | string | Pipeline status after the event |
follower_count | integer or null | Divisor for the engagement rate |
first_seen_at | ISO 8601 or null | When Embers first saw this person |
work_history | array | Up to 10 roles: company, title, start_date, end_date, location, industry. No job descriptions |
certifications | array | Up to 10: name, authority, date |
contact | object | Paid plans only. See contact fields |
Status values are active (shown as Pending in the app), contacted, replied, call_booked, not_interested, and dismissed. The leads list describes each one.
Two numbers the app shows are derived rather than sent. Engagement rate is engagement_count divided by follower_count. Grade comes from score: A at 80 or more, B at 60, C at 40, D at 20, F below that.
Join on linkedin_urn
Join people on lead.linkedin_urn. Do not key on linkedin_url: people change their vanity slug, and some are only ever seen in URN form, so one person can appear under two URLs. lead.id is stable per workspace and person, but when two member records are merged the losing id stops appearing. When linkedin_urn is blank on an older record, fall back to lead.id.
Contact fields
lead.contact appears only when the workspace is on a paid Solo plan or above. On Free, and during the free trial, the key is omitted entirely.
| Field | Notes |
|---|---|
email, phone | Stored values only, otherwise null |
included | true only when a stored email or phone exists |
enriched_at, last_verified_at | When the contact was enriched, or null |
source, confidence | Where it came from (harvest or apify) and how sure (safe, verified, or risky), or null |
status | Always ready |
A webhook never triggers a fresh lookup and never invents an address. Reveal a lead’s email and phone explains how contacts get stored. Comment text can still contain details a person published, but that is public post content, not the contact object.
The engagement object
On lead.created, lead.status_changed, and test samples of them, data.engagement is the recent form:
recent: the lead’s 10 most recent engagements, newest first.by_type: counts byengagement_typeacross those 10.first_engaged_at,last_engaged_at: the range they cover.
On lead.engagement.added it is the batched form, one event per lead per scan:
engagement_count: how many engagements Embers first saw betweenwindow_startandwindow_end.engagements: the lead’s most recent engagements, newest first, up to 50. It can include some that an earlier event already carried.truncated:truewhenengagement_countis larger than the array.
Embers sends at most 500 lead.engagement.added events per workspace per scoring pass.
Fields on each engagement item
| Field | Notes |
|---|---|
engagement_type | LinkedIn’s own value, such as comment, comment_reply, repost, praise, or keyword_authored |
engagement_category | Branch on this: comment, reply, reaction, repost, or authored_post |
engaged_at, engaged_at_precision | See below |
source | post (your posts), comment (your comments elsewhere), competitor, or keyword |
comment_text | The comment or reply, up to 500 characters, ending in ... when cut |
post_url, post_snippet | The post, with up to 280 characters of its text |
post_author_name, post_author_type | post_author_type is you, competitor, third_party, or lead |
competitor_name | Set on competitor engagement, otherwise empty |
intent_category | purchase_intent, pain_alignment, advocacy, question, casual, or unknown |
matched_keywords | Keyword items only: the tracked phrases the post matched |
engaged_at_precision is exact for comments and replies, which carry a LinkedIn timestamp. Everything else, including reactions, reposts, and keyword posts, is approximate: Embers substitutes the time it first saw the engagement or the post’s own date. Order a follow-up sequence on exact rows only.
Status, report, and test fields
lead.status_changed adds previous_status, status, and changed_at beside lead. Blocking a company sends one with status set to dismissed.
report.generated carries data.report with id, title, status, date_range, date_start, date_end, share_url (the public link), and view_count, plus reason (created or regenerated) and generated_at. It leaves out the report snapshot. See client reports.
webhook.test carries is_test: true, sample_event_type, and a sample of that event with placeholder data. A sample includes contact when your plan would.
Example lead.status_changed payload
{
"id": "e1b4f6e8-4c4a-4f50-b3c7-2ef2f97d5012",
"type": "lead.status_changed",
"api_version": "2026-07-20",
"schema_version": "2026-09-10",
"created_at": "2026-07-20T10:00:00+00:00",
"account": {
"id": "4d2f0eb9-81e5-4f20-8a60-25cbb9fe9ea5",
"name": "Acme LinkedIn"
},
"data": {
"lead": {
"id": "967caf1c-c0a6-4b98-9f3b-1e0811fb53b4",
"name": "Alex Smith",
"headline": "VP Sales at Acme",
"title": "VP Sales",
"company": "Acme",
"industry": "Insurance",
"location": "Chicago",
"linkedin_url": "https://www.linkedin.com/in/alex-smith/",
"linkedin_urn": "ACoAAABBBCCCDDDEEEFFFGGG",
"score": 82,
"icp_match": true,
"icp_confidence": 90,
"icp_reason": "Insurance principal with clear sales authority.",
"trend": "rising",
"top_signals": ["Commented on multiple posts"],
"sources": ["post_engagement"],
"status": "contacted",
"follower_count": 4200,
"first_seen_at": "2026-06-02T09:15:00+00:00",
"work_history": [
{
"company": "Acme",
"title": "VP Sales",
"start_date": "2021-04",
"end_date": "",
"location": "Chicago",
"industry": "Insurance"
}
],
"certifications": [],
"contact": {
"email": "a***@acme.example",
"phone": null,
"enriched_at": "2026-07-01T00:00:00+00:00",
"last_verified_at": "2026-07-01T00:00:00+00:00",
"included": true,
"source": "harvest",
"confidence": "safe",
"status": "ready"
}
},
"engagement": {
"recent": [
{
"engagement_type": "comment",
"engagement_category": "comment",
"engaged_at": "2026-07-19T14:02:00+00:00",
"engaged_at_precision": "exact",
"source": "post",
"comment_text": "This matches what we see at renewal.",
"post_url": "https://www.linkedin.com/feed/update/7481311828074078208/",
"post_author_name": "Your Name",
"post_author_type": "you",
"post_snippet": "A post about renewal cycles in commercial insurance.",
"competitor_name": "",
"intent_category": "pain_alignment"
}
],
"by_type": { "comment": 1 },
"first_engaged_at": "2026-07-19T14:02:00+00:00",
"last_engaged_at": "2026-07-19T14:02:00+00:00"
},
"previous_status": "active",
"status": "contacted",
"changed_at": "2026-07-20T10:00:00+00:00"
}
}Related
Frequently asked questions
- Why is there no first name or last name in the payload?
- The payload carries one name field. HubSpot contacts and a CSV export with split names give you first and last names; the webhook does not.
- Will a new field break my receiver?
- Not if you ignore keys you do not recognise. Almost every change is additive. The one removal so far, account.type on 2026-09-10, bumped schema_version.
- Why is lead.contact missing from my payloads?
- The workspace is on Free or still in its free trial. Contact details reach the webhook on a paid Solo plan or above.
Was this article helpful?
What went wrong?
Thank you. We read every answer.
Your first 2,000 engagers free
Every day Embers reads the public activity around your market, ranks the people who fit, and hands you a short list with the reason each one surfaced. You decide who to contact.
Start free trial- No card to start
- No LinkedIn login
- Cancel anytime