LinkedIn webhooks and Slack notifications: how replies and accepted invites flow to your team

WarmySender emits real-time webhook events for LinkedIn reply, invite acceptance, message sent, and account disconnect activity. This guide is the canonical reference for how the events flow, where to subscribe, how Slack and Discord messages are formatted, the n8n / generic JSON shape, how to test, how to troubleshoot, and why the webhook layer never affects LinkedIn account safety.

Supported events:

Four `linkedin.*` events are emitted today. All share the same envelope `{ event, id, occurredAt, workspaceId, data }` with HMAC-SHA256 signature on the body and a unique `X-Warmy-Event-Id` header for idempotency.

Example payload (`linkedin.reply_received`):

{
"event": "linkedin.reply_received",
"id": "evt_2026_04_28_aa11bb22cc33",
"occurredAt": "2026-04-28T09:34:18.512Z",
"workspaceId": "ws_abcdef0123456789",
"data": {
"campaignId": "camp_8c2f...",
"enrollmentId": "en_4a7b...",
"prospectId": "prs_4a7b...",
"prospectName": "Zoltan A. Vardy",
"prospectLinkedinUrl": "https://www.linkedin.com/in/zoltanvardy/",
"linkedinAccountId": "lia_9d11...",
"messageText": "Sounds good, send me the deck.",
"threadId": "thr_1f2e...",
"receivedAt": "2026-04-28T09:34:14.000Z"
}
}

Where to subscribe:

  1. Open Settings → Webhooks (`/settings?tab=webhooks`).
  2. Click 'New Webhook'. Paste your endpoint URL — Slack Incoming Webhook URL (https://hooks.slack.com/...), Discord Webhook URL (https://discord.com/api/webhooks/...), n8n / Make / Zapier HTTP Trigger URL, or your own HTTPS endpoint.
  3. Pick events. For sales teams, the typical set is `linkedin.reply_received` + `linkedin.invite_accepted`. For ops, add `linkedin.account_disconnected`. Subscribe to `*` for everything.
  4. Save. WarmySender returns a webhook secret — save it once (shown only on creation). Used for HMAC verification on your endpoint.
  5. Click the new 'Test webhook' button on the webhook row. We fire a sample `linkedin.reply_received` payload to your URL through the real delivery path. Recent Deliveries shows the result.
  6. The row now shows a 'Last fired' timestamp (`last_triggered_at`) updated on every successful delivery — useful for debugging silence (if it stops updating, the upstream signal is missing or the formatter is failing).

How Slack messages are formatted:

WarmySender auto-detects Slack Incoming Webhook URLs (host = `hooks.slack.com`) and formats events as Block Kit messages — no Zapier / Make / n8n middleware needed.

A `linkedin.reply_received` event arrives in Slack as a card with:
- Header: 'New LinkedIn reply' with the WarmySender icon
- Section block: prospect name (linked to LinkedIn profile) + campaign name (linked to campaign detail in WarmySender) + LinkedIn account that received the reply
- Quoted block: the reply body (truncated to ~400 chars for readability; full body is in the JSON payload if you also subscribe via a generic endpoint)
- Action buttons: 'Open in WarmySender' and 'View on LinkedIn' (deep links)
- Context block: receivedAt timestamp in your workspace timezone

A `linkedin.invite_accepted` event arrives as:
- Header: 'New connection accepted'
- Section: prospect name (linked) + campaign name (linked) + 'Accepted at HH:MM YYYY-MM-DD' in workspace TZ
- Action button: 'Send a message'

Formatting is server-side — Slack receives a JSON Block Kit body, NOT raw event JSON. If you point a non-Slack endpoint at a Slack URL by mistake, the formatter still runs because we route on URL host.

Discord support:

As of April 2026, Discord webhook URLs (host = `discord.com` / `discordapp.com`) are auto-detected and formatted as `{ content, embeds }` payloads. Each event becomes an embed with title, description (the reply body or 'X accepted Y's connection request'), color (blue for replies, green for acceptances, red for account_disconnected), and a pair of links in the description.

Before the formatter shipped, Discord webhook URLs received the raw JSON envelope and returned 400 Bad Request ('Cannot send an empty message') because Discord requires `content` or `embeds` at minimum. Old deliveries that hit that path are now in the dead-letter queue and are NOT auto-replayed (replay would fire stale notifications). If you need a backfill, contact support with the affected URL and date range.

Example Discord embed (linkedin.reply_received):

{
"content": null,
"embeds": [{
"title": "New LinkedIn reply: Zoltan A. Vardy",
"description": "\"Sounds good, send me the deck.\"\n\n[View in WarmySender](...) · [View on LinkedIn](...)",
"color": 3447003,
"timestamp": "2026-04-28T09:34:14.000Z",
"footer": {"text": "Campaign: April Outreach · LinkedIn account: [email protected]"}
}]
}

n8n / generic:

Any URL that isn't recognized as Slack or Discord receives the raw signed JSON envelope. This includes n8n HTTP Trigger URLs, Make custom webhook URLs, Zapier Catch Hook URLs, AWS Lambda function URLs, and your own HTTPS endpoints.

Recommended n8n HTTP Trigger config:
- Authentication: None (rely on HMAC verification — see the 'Webhooks — Getting Started' guide for the Node.js verification snippet).
- Method: POST
- Response Mode: Respond with response code 200 immediately, then process asynchronously (don't make WarmySender wait for downstream HubSpot / Slack calls — our retry timeout is 5 seconds).
- Body: keep as JSON. The payload is `{ event, id, occurredAt, workspaceId, data }`.
- Headers to capture: `X-Warmy-Event-Id` (idempotency key, store ~24 h), `X-Warmy-Signature` (parse `t=<unix_ms>,v1=<hmac_hex>`), `X-Warmy-Event-Type` (event name).

For Make / Zapier, the payload shape is identical; switch on `{{$json.event}}` to branch.

Testing your webhook:

  1. On the webhook row in Settings → Webhooks, click the 'Test' button. We POST a sample `linkedin.reply_received` payload to your URL via the real delivery path (same signature, same retries, same dead-letter handling).
  2. Check 'Recent Deliveries' on the same page — you'll see the response code and body within ~10 seconds.
  3. If 2xx: your endpoint accepted the test; you should see the message in Slack / Discord / your n8n flow.
  4. If 4xx: signature verification or formatting on your side rejected the payload; check the response body in Recent Deliveries for the error message.
  5. If 5xx: your endpoint had a server error; we'll retry up to 10 times with exponential backoff (1 min → 72 h).
  6. For real LinkedIn replies (not just the test button), the easiest end-to-end test is to reply to yourself from a second LinkedIn account or have a teammate reply.

Troubleshooting:

Account safety note (CRITICAL):

Webhooks are read-side only. Receiving a webhook never triggers a LinkedIn API call. The emission path is: WarmySender DB row writes (post atomic UPDATE WHERE repliedAt IS NULL / WHERE acceptedAt IS NULL) → emit a webhook event row → deliverer pushes to your URL. No path from a webhook arrival back to Unipile. Subscribing to webhooks does not change polling cadence, daily caps, weekly caps, ramp pacing, or any LinkedIn action volume on your account. Every LinkedIn API call is driven by your campaign sends / invites / profile views, not by webhook activity. Account safety always wins over throughput, and webhooks live entirely on the safe side of that line.

For full HMAC signature verification, idempotency, retry schedule, and the rest of the cross-channel webhook reference (email events too), see the 'Webhooks — Getting Started' guide.

Related guides in LinkedIn

Back to all documentation | Contact support