- Home
- /
- Help Center
- /
- ReactIn APIs
- /
- Webhook payloads and how to identify a lead
Webhook payloads and how to identify a lead
The same webhook event can come from a campaign, an automation or a lead magnet, and the payload differs. Learn how to tell the sources apart and which id identifies a person across all of them.
Webhooks push an event to your endpoint every time something happens in your workspace: a lead is added, an invitation is sent, a connection is accepted, a first message goes out, a lead replies. Most of those events can be produced by more than one source, and the payload is not identical in every case. This page documents the differences, and in particular how to identify the lead.
Envelope
Every delivery has the same outer shape:
{
"event": "CONNECTION_ACCEPTED",
"timestamp": "2026-03-11T14:30:00.000Z",
"data": { },
"metadata": {
"organizationId": "org_abc123",
"campaignId": "campaign_xyz789",
"listId": "list_xyz789",
"linkedinAccountId": "li_acc_123",
"automationId": "automation_xyz789"
}
}
metadata only contains the keys that apply to the event. data is where the shape varies.
Identifying the source of an event
CONNECTION_ACCEPTED, INVITATION_SENT, FIRST_MESSAGE_SENT and LEAD_ADDED can each come from several places. Exactly one of these keys is present in data, and it tells you which:
Key in data | Source |
|---|---|
campaign | A campaign you started |
list | A SmartList |
automation | An automation, for example auto-accept |
leadMagnet | A lead magnet |
This matters for reporting. Auto-accept produces CONNECTION_ACCEPTED events for invitations that other people sent to you, not for invitations ReactIn sent on your behalf. If you compute an acceptance rate, filter on data.campaign first, otherwise inbound acceptances inflate your numerator and there is no matching INVITATION_SENT to divide by.
Identifying the lead
The data.lead object always carries two ids:
-
lead.leadId, the organization-wide lead id. It always means the same thing, whatever the source. This is the key to use when you deduplicate people, or match an event against a record you already stored. -
lead.id, the most specific id available in that context. Whendata.campaignordata.listis present, it is the lead's list-scoped id, the sameidreturned by [Get leads from ReactIn via API] and theleadIdaccepted by [Update leads via API]. Whendata.automationordata.leadMagnetis present there is no list involved, solead.idmirrorslead.leadId.
⚠️ The same person added to two SmartLists has two different
lead.idvalues but a singlelead.leadId. Uselead.leadIdto reconcile a person across campaigns, automations and lead magnets.
Both keys are always present, and both can be null when the lead could not be resolved. Automation and lead magnet payloads also expose lead.linkedinUrl, which is a reliable fallback key.
CONNECTION_ACCEPTED, the three shapes
From a campaign. The lead comes from a SmartList, so the full lead record is available.
{
"type": "CONNECTION_ACCEPTED",
"conversation": null,
"lead": {
"id": "list_lead_XXX",
"leadId": "lead_YYY",
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@example.com",
"linkedinUrl": "https://linkedin.com/in/janesmith",
"company": "Acme Inc",
"headline": "Product Manager",
"location": "New York, NY",
"customFields": { "Is Matching My Target": true }
},
"campaign": { "id": "campaign_xyz789", "name": "Q1 Outreach" },
"linkedinAccount": { "id": "li_acc_123", "name": "Sales Rep" }
}
From the auto-accept automation. Someone sent you an invitation and ReactIn accepted it. There is no list, no campaign and no enriched data, so the lead block is reduced to what LinkedIn returns on the invitation. lead.linkedinIdentifier is the LinkedIn member id.
{
"type": "CONNECTION_ACCEPTED",
"lead": {
"id": "lead_YYY",
"leadId": "lead_YYY",
"linkedinIdentifier": "ACoAAABCDEFGHIJ",
"firstName": "Jane",
"lastName": "Smith",
"linkedinUrl": "https://www.linkedin.com/in/janesmith",
"headline": "Product Manager"
},
"automation": {
"id": "automation_xyz789",
"name": "[Automation - Auto accept] Sales Rep"
},
"linkedinAccount": { "id": "li_acc_123", "name": "Sales Rep" }
}
From a lead magnet. Same reduced lead block, with a leadMagnet block instead of automation.
{
"type": "CONNECTION_ACCEPTED",
"lead": {
"id": "lead_YYY",
"leadId": "lead_YYY",
"firstName": "Jane",
"lastName": "Smith",
"linkedinUrl": "https://linkedin.com/in/janesmith",
"headline": "Product Manager"
},
"leadMagnet": { "id": "lead_magnet_xyz789", "name": "LinkedIn Playbook" },
"linkedinAccount": { "id": "li_acc_123", "name": "Sales Rep" }
}
INVITATION_SENT, FIRST_MESSAGE_SENT and LEAD_ADDED follow the same pattern: rich lead block from a campaign or a list, reduced lead block from an automation or a lead magnet.
Seeing the payloads in the app
Go to Settings → Webhooks, create or edit a webhook, and click View payload under any event. Events emitted by several sources show one tab per source.
Good practices
-
Branch on
data.campaign/data.list/data.automation/data.leadMagnetrather than on the presence of a field insidelead. New optional fields may be added to payloads over time, so ignore keys you do not recognize instead of rejecting the delivery. -
Deduplicate on
lead.leadId, falling back tolead.linkedinUrl. -
Webhooks only deliver events that occur after the webhook is created. Existing data does not trigger deliveries.