Skip to content

Webhook payload reference

The Embers event envelope, the versions on it, every event type, the lead object, the contact rules, and the key you should join on.

Updated 3 min read Solo and above

Every Embers webhook is a JSON POST with the same envelope. This page describes what is in it and which fields you should build on.

The envelope

Every request carries a stable event id, the event type, two version strings, a created_at, the account that produced it, and a data object shaped by the event.

  • api_version is 2026-07-20. It only changes if the envelope is reshaped in a way a receiver cannot absorb by ignoring unknown keys, which has not happened.
  • schema_version is 2026-09-10. It records field-level changes, so a key that is added or dropped bumps this and leaves api_version alone. 2026-09-10 dropped account.type; route on account.id and account.name.
  • account carries the account id and name, so agencies can route events for several client accounts without looking up a UUID.

Ignore keys you do not recognise.

Event types

TypeWhen it fires
lead.createdEmbers creates a lead score for a person in your account. Carries the engagement that triggered it
lead.engagement.addedA qualified lead you already have engages again. One event per lead per scan, with every new engagement batched in. Not sent alongside lead.created
lead.status_changedSomeone moves a lead to a different pipeline status. Carries previous_status, status, and changed_at
report.generatedA client report finishes generating or regenerating. Carries the report id, title, status, date range, reason, and public share URL, but not the report snapshot
webhook.testAn administrator sent a sample from Developer, then Webhooks. data.is_test is always true and data.sample_event_type names the shape that was chosen

On lead.engagement.added, data.engagement.engagement_count is the true total and data.engagement.engagements is capped at 50 items, with truncated set when the array is shorter than the count.

The lead object

data.lead carries id, name, title, company, linkedin_url, linkedin_urn, score, icp_match, icp_confidence, icp_reason, trend, top_signals, sources, status, follower_count, and first_seen_at, plus work_history and certifications capped at 10 entries each.

score and icp_confidence use the same 0 to 100 scale shown in the app.

There is no first name or last name field. The payload has one name.

Two numbers the app shows are derived rather than sent. Engagement rate is engagement_count divided by follower_count, which is why follower_count is included. 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

lead.linkedin_urn is LinkedIn’s own immutable member id, and it is the key to join on. Do not key on linkedin_url: members change their vanity slug, and some people are only ever seen in URN form, so the same person can appear under two different URLs. lead.id is the Embers UUID and is stable per account and member, but if two member records are merged the losing UUID stops appearing. On older records linkedin_urn may be blank, so fall back to lead.id.

Contact fields

lead.contact is additive and gated by plan.

  • Free: the key is omitted entirely.
  • Solo and above: the key is present, carrying email, phone, enriched_at, last_verified_at, included, source, confidence, and status.

Only values Embers already stored are copied. Missing values are null, and included is true only when a stored value exists. Webhooks never trigger a fresh enrichment and never invent an address or a number. Comment text can still contain contact details the person published themselves, but that is public post content, not the structured contact object.

Engagement timestamps

engaged_at_precision tells you whether engaged_at is a measurement or a substitute. LinkedIn publishes a timestamp for comments and replies, so those are exact. Reactions are approximate, where Embers substitutes the time it first saw the reaction or the parent post’s date. Order a follow-up sequence on exact rows only.

Example

{
  "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",
      "title": "VP Sales",
      "company": "Acme",
      "linkedin_url": "https://www.linkedin.com/in/alex-smith/",
      "linkedin_urn": "ACoAAABBBCCCDDDEEEFFFGGG",
      "score": 82,
      "icp_match": true,
      "icp_confidence": 90,
      "icp_reason": "Strong insurance principal with clear sales authority.",
      "trend": "rising",
      "top_signals": ["Visited pricing"],
      "sources": ["post_engagement"],
      "status": "contacted",
      "follower_count": 4200,
      "first_seen_at": "2026-06-02T09:15:00+00:00",
      "contact": {
        "email": "alex@acme.example",
        "phone": "+13125550100",
        "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're seeing at renewal.",
          "post_url": "https://www.linkedin.com/feed/update/7481311828074078208/",
          "post_author_name": "You",
          "post_author_type": "you",
          "post_snippet": "A post about renewal cycles in commercial insurance.",
          "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"
  }
}

Frequently asked questions

Why is there no first name or last name in the payload?
The payload carries a single name field. Split names appear in the CSV export, in HubSpot, and on the Slack card, not on the webhook.
Will a new field break my receiver?
Most changes are additive. 2026-09-10 removed `account.type`; ignore keys you do not recognise.

Was this article helpful?

Still stuck?

Send us the article you were following and what happened. We answer from the same inbox that writes these pages.

Email support

to move to open esc to close