Gareth Wilson Gareth Wilson

Guide to LinkedIn Webhooks Features and Best Practices

Published


LinkedIn is the world's largest professional networking platform, with over a billion members across 200 countries. Beyond networking, LinkedIn's Marketing, Talent, and Sales APIs enable organizations to automate recruitment, manage company pages, track lead generation, and monitor social engagement at scale. Webhooks are the primary mechanism for receiving real-time notifications about these events without polling.

This guide covers everything you need to know about LinkedIn webhooks: their features, how to configure them, best practices for production deployments, and the common pain points developers face along with solutions to address them.

What are LinkedIn webhooks?

LinkedIn webhooks are HTTP callbacks that deliver real-time notifications to your endpoints when subscribed events occur on the platform. When a member likes a company post, submits a lead gen form, or comments on an article, LinkedIn sends a JSON payload containing event details to URLs you configure. This enables integration with CRM systems, marketing automation platforms, social media management tools, and any service that can receive HTTP requests.

Webhooks in LinkedIn exist across several contexts:

  • Organization Social Action Notifications — Real-time notifications for social actions (likes, comments, shares, mentions) on organization pages
  • Lead Sync Notifications — Notifications for new lead gen form submissions from LinkedIn ad campaigns
  • Apply Connect Webhooks — Notifications for job applications received through LinkedIn's talent solutions

Webhook access is not universally available — it requires an approved use case and a registered developer application. This guide focuses primarily on Organization Social Action Notifications, as they represent the most commonly used webhook integration for marketing and community management use cases.

LinkedIn webhook features

FeatureDetails
Webhook configurationLinkedIn Developer Portal UI or Subscription API
Hashing algorithmHMACSHA256
ValidationChallenge-response using clientSecret (3-second response window)
Periodic re-validationEvery 2 hours; blocked after 3 consecutive failures
Retry logicEvery 5 minutes for up to 8 hours
Notification batchingUp to 10 notifications per batch
Signature headerX-LI-Signature
DeduplicationVia notificationId in payload
Manual retryNot available (pull API for missed notifications)
Missed notification retention60 days (queryable via pull API)
Browsable logNot available
ProtocolHTTPS required (ngrok URIs not supported)

Supported event types

LinkedIn webhooks currently support a limited set of event types compared to many other platforms:

Event TypeDescription
ORGANIZATION_SOCIAL_ACTION_NOTIFICATIONSLikes, comments, shares, mentions, and other social actions on organization posts and articles
Lead Gen Form SubmissionsNew lead form submissions from LinkedIn ad campaigns (via Lead Notification Subscriptions API)
Apply Connect EventsJob application notifications for talent solutions partners

Each notification includes the action type, allowing your endpoint to route and process events accordingly. The supported social action types include: LIKE, COMMENT, SHARE, SHARE_MENTION, ADMIN_COMMENT, COMMENT_EDIT, and COMMENT_DELETE.

Webhook payload structure

When LinkedIn sends a webhook notification, it delivers a JSON payload containing batched notifications. Notifications are batched by event type with a batch size of up to 10.

OrganizationSocialActionNotification schema

FieldTypeDescription
notificationIdlongGlobal unique identifier to ensure notification uniqueness
organizationalEntityURNThe Organization URN for which the notification belongs
actionStringAction type that triggered the notification (e.g., LIKE, COMMENT, SHARE)
sourcePostURNThe activity or article URN that the member acted on
decoratedSourcePostShareThe decorated share the member acted on (may not be populated on error)
lastModifiedAtlongTimestamp of the last modification of the notification
generatedActivityURNAn activity or comment URN as a result of acting on the target
decoratedGeneratedActivity[Share, Comment]The decorated generated activity created as a result of acting on the sourcePost
subscriberURNMember that created the subscription

Share schema (within notifications)

FieldTypeDescription
entityURNThe unique URN of the share
ownerURNURN of the entity which owns the share
textstringText entered by the member for this share
titlestringMember-supplied title of the content
descriptionstringMember-supplied description of the content
landingPageUrlURLThe main landing page URL of the share
mediaCategorystringThe category of media shared: NONE, ARTICLE, IMAGE, RICH, VIDEO, CAROUSEL

Security with HMAC signatures

LinkedIn uses HMAC-SHA256 signatures to verify webhook authenticity. When LinkedIn sends a notification, it includes a signature in the X-LI-Signature header. The value is the HMAC-SHA256 hash of the JSON-encoded request body, prefixed with hmacsha256=, computed using your application's clientSecret.

Your endpoint must compute the same hash and compare it against the header value to verify that the notification genuinely originated from LinkedIn.

Challenge-response validation

Before a webhook can receive notifications, LinkedIn validates ownership of the URL through a challenge-response flow:

  1. LinkedIn generates a random UUID string as the challengeCode
  2. LinkedIn sends an HTTP GET request to your webhook URL with challengeCode as a query parameter
  3. Your endpoint must compute a challengeResponse — the hex-encoded HMAC-SHA256 signature of the challengeCode using your app's clientSecret
  4. Return both values in a JSON response with a 200 OK status within 3 seconds

For integrations using the parent-child application model (such as Apply Connect), the GET request also includes an applicationId parameter identifying the child application being validated.

Re-validation and blocking

Webhook endpoints are periodically re-validated every 2 hours after the initial validation. If the re-validation check fails 3 times in a row, the endpoint moves to a blocked state where events will no longer be sent. Developers receive warning emails for failed validation attempts and a final notification when the webhook is blocked.

After resolving the issue, developers can manually trigger a new validation check from the developer portal or via the DeveloperWebhooks API.

Subscription management

Unlike many webhook platforms where you simply register a URL and select events, LinkedIn requires a per-member subscription model. Each subscription is uniquely identified by four keys: developerApplication, member (user), entity (organization), and eventType.

Subscription lifetime

Subscriptions are only valid for as long as the member's authorization grant is valid for your application. Subscriptions are removed if:

  • The member's OAuth grant expires
  • The member revokes their grant for your application
  • The member is no longer an administrator of the subscribed organization

You must track these subscription changes and resubscribe members when necessary to continue receiving notifications.

Setting up LinkedIn webhooks

Via the LinkedIn Developer Portal

  1. Navigate to the LinkedIn Developer Portal
  2. Select your application
  3. Open the Webhooks tab (only visible for applications with an approved webhook use case)
  4. Register your webhook URL (must be HTTPS)
  5. LinkedIn will initiate the challenge-response validation
  6. If validation succeeds, your webhook is ready for subscriptions

For Lead Syncing use cases, webhook subscriptions must be created via the Lead Notification Subscriptions API and cannot be created via the UI.

Via the Event Subscription API

Create or update subscriptions using a PUT request to the eventSubscriptions endpoint:

PUT https://api.linkedin.com/rest/eventSubscriptions/(developerApplication:{URL_ENCODED_APP_URN},user:{URL_ENCODED_USER_URN},entity:{URL_ENCODED_ORG_URN},eventType:ORGANIZATION_SOCIAL_ACTION_NOTIFICATIONS)
{
  "webhook": "https://www.example.com/callback"
}

The subscription request will only succeed if the subscribing member is an administrator of the organization being subscribed to.

Retrieving subscriptions

Retrieve a specific subscription by its composite key:

GET https://api.linkedin.com/rest/eventSubscriptions/(developerApplication:{URL_ENCODED_APP_URN},user:{URL_ENCODED_USER_URN},entity:{URL_ENCODED_ORG_URN},eventType:ORGANIZATION_SOCIAL_ACTION_NOTIFICATIONS)

Sample response:

{
  "entity": "urn:li:organization:12345",
  "eventType": "ORGANIZATION_SOCIAL_ACTION_NOTIFICATIONS",
  "user": "urn:li:person:abcDef",
  "expiresAt": "1536130694583",
  "webhook": "https://www.example.com/callback"
}

API versioning

All LinkedIn Marketing API calls require a version header. Include LinkedIn-Version: 202601 (or the appropriate version) in your requests. LinkedIn does not default to the latest version — you must specify it explicitly.

Best practices when working with LinkedIn webhooks

Verifying HMAC signatures

When processing webhooks from LinkedIn, always verify the HMAC signature to ensure requests genuinely originated from LinkedIn.

Node.js

const crypto = require('crypto');
const express = require('express');
const app = express();

// Capture raw body for signature verification
app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf.toString('utf8');
  }
}));

function verifyLinkedInSignature(rawBody, signatureHeader, clientSecret) {
  // LinkedIn signature format: hmacsha256={hash}
  const providedSignature = signatureHeader.replace('hmacsha256=', '');

  const computedSignature = crypto
    .createHmac('sha256', clientSecret)
    .update(rawBody, 'utf8')
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(computedSignature),
    Buffer.from(providedSignature)
  );
}

app.post('/webhooks/linkedin', (req, res) => {
  const signatureHeader = req.headers['x-li-signature'];

  if (!signatureHeader || !verifyLinkedInSignature(
    req.rawBody,
    signatureHeader,
    process.env.LINKEDIN_CLIENT_SECRET
  )) {
    return res.status(401).send('Invalid signature');
  }

  // Process webhook
  res.status(200).send('OK');
});

Python

import hmac
import hashlib
import os
from flask import Flask, request, abort

app = Flask(__name__)

def verify_linkedin_signature(payload, signature_header, client_secret):
    # LinkedIn signature format: hmacsha256={hash}
    provided_signature = signature_header.replace('hmacsha256=', '')

    computed_signature = hmac.new(
        client_secret.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(computed_signature, provided_signature)

@app.route('/webhooks/linkedin', methods=['POST'])
def handle_linkedin_webhook():
    signature_header = request.headers.get('X-LI-Signature')

    if not signature_header or not verify_linkedin_signature(
        request.get_data(as_text=True),
        signature_header,
        os.environ['LINKEDIN_CLIENT_SECRET']
    ):
        abort(401)

    # Process webhook
    return 'OK', 200

Implement the challenge-response handler

Your webhook endpoint must handle both GET requests (for validation) and POST requests (for notifications) on the same URL:

Node.js

const crypto = require('crypto');

app.get('/webhooks/linkedin', (req, res) => {
  const { challengeCode } = req.query;

  if (!challengeCode) {
    return res.status(400).send('Missing challengeCode');
  }

  const challengeResponse = crypto
    .createHmac('sha256', process.env.LINKEDIN_CLIENT_SECRET)
    .update(challengeCode)
    .digest('hex');

  // Must respond within 3 seconds
  res.status(200).json({
    challengeCode,
    challengeResponse
  });
});

Python

import hmac
import hashlib

@app.route('/webhooks/linkedin', methods=['GET'])
def handle_linkedin_challenge():
    challenge_code = request.args.get('challengeCode')

    if not challenge_code:
        return 'Missing challengeCode', 400

    challenge_response = hmac.new(
        os.environ['LINKEDIN_CLIENT_SECRET'].encode('utf-8'),
        challenge_code.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

    # Must respond within 3 seconds
    return {
        'challengeCode': challenge_code,
        'challengeResponse': challenge_response
    }, 200

Use the notificationId for idempotency

LinkedIn can occasionally deliver notifications multiple times. Use the notificationId to implement idempotent processing:

async function processLinkedInNotification(notification) {
  const { notificationId, action, organizationalEntity } = notification;

  // Check if already processed
  const exists = await redis.get(`processed:${notificationId}`);
  if (exists) {
    console.log(`Notification ${notificationId} already processed, skipping`);
    return;
  }

  // Process the notification
  await handleNotification(notification);

  // Mark as processed with TTL
  await redis.setex(`processed:${notificationId}`, 604800, '1'); // 7-day TTL
}

Handle batched notifications

LinkedIn batches notifications by event type, with up to 10 notifications per delivery. Process each notification individually while tracking the batch.

Decouple webhook processing to respond quickly

LinkedIn expects a 2xx response promptly. Process notifications asynchronously to avoid delivery failures.

Track subscription health

Since subscriptions are tied to member authorization, build monitoring to detect dropped subscriptions.

LinkedIn webhook limitations and pain points

Aggressive re-validation and endpoint blocking

The Problem: LinkedIn re-validates webhook endpoints every 2 hours via the challenge-response flow. If your endpoint fails this check just 3 times in a row, it enters a blocked state and stops receiving all events. Even brief server maintenance or a transient networking issue can trigger blocking.

Why It Happens: LinkedIn uses periodic re-validation to ensure webhook endpoints remain under the control of the registered application. The 2-hour cycle with a low failure tolerance is more aggressive than most webhook providers, which typically only validate at registration time.

Workarounds:

  • Deploy your webhook endpoint behind a load balancer with high availability
  • Ensure the challenge-response handler is lightweight and responds well within the 3-second window
  • Set up health monitoring that alerts immediately when the endpoint becomes unreachable
  • Use the DeveloperWebhooks API to programmatically monitor for blocked status and trigger revalidation.

How Hookdeck Can Help: Hookdeck provides a stable, always-available ingress URL for your webhooks, shielding your actual endpoint from validation failures. Since Hookdeck's infrastructure maintains high uptime, the challenge-response validation succeeds reliably, preventing your webhook from entering a blocked state due to downstream endpoint issues.

3-second challenge-response timeout

The Problem: LinkedIn requires your endpoint to compute the HMAC-SHA256 challenge response and return it within 3 seconds. This tight window can be problematic for serverless functions with cold starts, endpoints behind VPNs or proxies, or applications with complex initialization.

Why It Happens: LinkedIn enforces a strict timeout on the validation flow to quickly identify unresponsive or misconfigured endpoints.

Workarounds:

  • Deploy the challenge handler on always-warm infrastructure (not cold-starting serverless functions)
  • Keep the challenge endpoint handler as lightweight as possible — it should only compute HMAC and return the response
  • Avoid ngrok or other tunneling services for production webhooks — LinkedIn explicitly blocks ngrok URIs
  • Pre-compute or cache the clientSecret rather than fetching it from a secrets manager during the request

How Hookdeck Can Help: Hookdeck handles the challenge-response validation on your behalf, responding to LinkedIn's validation checks from its globally distributed infrastructure within the required window. Your actual endpoint only needs to handle notification payloads, not validation challenges.

Limited event type coverage

The Problem: LinkedIn's webhook support covers a narrow set of event types compared to the breadth of the platform. There are no webhooks for profile updates, connection changes, messaging events, job posting status changes, or analytics threshold alerts. Most webhook functionality is limited to organization social actions and lead gen form submissions.

Why It Happens: LinkedIn restricts API and webhook access to protect user privacy and maintain platform control. Webhook availability is tied to specific approved use cases, and LinkedIn is selective about which data can be pushed in real-time.

Workarounds:

  • Use LinkedIn's REST APIs to poll for data that isn't available via webhooks
  • Build polling-based integrations for events like analytics updates or content performance
  • For lead syncing, use the dedicated Lead Notification Subscriptions API rather than the general webhook system
  • Consider combining webhooks for available events with scheduled API polling for everything else

How Hookdeck Can Help: Hookdeck's scheduled source feature can supplement LinkedIn's limited webhook coverage by polling LinkedIn's REST APIs on a schedule and converting the responses into webhook-like events, giving you a unified event-driven architecture even for data LinkedIn doesn't push natively.

Complex per-member subscription model

The Problem: LinkedIn requires individual subscriptions per member, per organization, per event type. Subscriptions are tied to OAuth grants and expire when tokens expire or when a member loses admin access to an organization. This creates significant operational overhead for applications managing webhooks across many organizations.

Why It Happens: LinkedIn's subscription model enforces access control at the member level, ensuring that only authorized administrators can subscribe to notifications for an organization. This is a deliberate security design, but it creates complexity for multi-tenant applications.

Workarounds:

  • Build automated subscription management that tracks grant expiry and resubscribes members
  • Monitor for 401 and 403 errors on LinkedIn Organization API calls to detect dropped subscriptions
  • Implement a reconciliation process that periodically compares active subscriptions against expected subscriptions
  • Use the pull API for missed notifications (retained for 60 days) to backfill gaps caused by subscription lapses

How Hookdeck Can Help: Hookdeck's connection management provides a stable webhook destination that persists independently of LinkedIn's subscription lifecycle. While you still need to manage LinkedIn subscriptions, Hookdeck ensures that when notifications do arrive, they're reliably queued and delivered to your processing endpoint regardless of downstream availability.

Duplicate notification delivery

The Problem: LinkedIn explicitly states that notifications can occasionally be delivered multiple times. Your webhook handler must implement deduplication logic using the notificationId, adding development overhead and requiring external state management.

Why It Happens: LinkedIn's at-least-once delivery guarantee means the system prioritizes ensuring notifications are delivered over preventing duplicates.

Workarounds:

  • Implement idempotent processing using the notificationId as a deduplication key
  • Use a cache like Redis with appropriate TTLs to track processed notifications
  • Design downstream processing to be naturally idempotent where possible

How Hookdeck Can Help: Hookdeck's built-in deduplication feature can automatically filter duplicate webhooks based on the notificationId field, ensuring your endpoint only processes each unique notification once without needing to build custom deduplication infrastructure.

No manual retry or delivery log

The Problem: LinkedIn doesn't provide a browsable delivery log or manual retry capability. If a notification fails after the 8-hour retry window (retries every 5 minutes), it's effectively lost from the push channel. While the pull API retains notifications for 60 days, discovering and reconciling missed notifications requires building custom tooling.

Why It Happens: LinkedIn's webhook infrastructure is designed as a lightweight notification system rather than a full event delivery platform. The pull API is intended as the fallback mechanism.

Workarounds:

  • Implement a reconciliation process using the pull API to query missed notifications periodically
  • Build your own delivery logging by recording every webhook received
  • Set up monitoring that detects gaps in expected notification patterns
  • Use the pull API to bootstrap notifications during product launches and for error recovery

How Hookdeck Can Help: Hookdeck's dashboard provides complete visibility into webhook delivery status, including response codes, latency, and errors. Failed webhooks are preserved in a dead letter queue for inspection and manual replay. Hookdeck's configurable retry policies with exponential backoff provide more control over retry behaviour than LinkedIn's fixed 5-minute intervals.

No support for ngrok or local development tools

The Problem: LinkedIn explicitly does not support ngrok URIs for webhook endpoints, and all webhook URLs must use HTTPS. This makes local development and testing significantly more difficult compared to platforms that allow tunneling services.

Why It Happens: LinkedIn restricts webhook URLs to stable, production-grade HTTPS endpoints to maintain security and reliability of its webhook validation system.

Workarounds:

  • Use cloud-hosted serverless functions (AWS Lambda, Google Cloud Functions) for development and testing
  • Deploy to a staging environment with a stable HTTPS URL
  • Use LinkedIn's built-in test functionality in the developer portal for basic validation
  • Consider alternative tunneling services with custom domains (though these may also be blocked)

How Hookdeck Can Help: Hookdeck provides stable HTTPS URLs that work with LinkedIn's validation requirements. During development, you can use Hookdeck's CLI to forward webhooks from your Hookdeck URL to your local development environment, bypassing LinkedIn's ngrok restriction while maintaining a stable webhook endpoint.

Strict data retention and compliance requirements

The Problem: LinkedIn enforces strict limits on how long you can retain data obtained through its APIs. Member-level social activity data can only be stored for 48 hours, organization-level social activity data for six weeks, and organization data tied to an authenticated org for up to six months. Violations can result in API access revocation or legal action.

Why It Happens: LinkedIn's data retention policies are designed to protect user privacy and prevent data hoarding.

Workarounds:

  • Implement automated data purging pipelines that respect LinkedIn's retention windows
  • Process and aggregate webhook data in real-time rather than storing raw payloads
  • Build your integration to re-fetch data from LinkedIn's APIs when needed rather than caching it long-term
  • Maintain detailed compliance logs to demonstrate adherence to retention policies

How Hookdeck Can Help: Hookdeck's configurable data retention policies allow you to set appropriate TTLs on stored webhook data, helping ensure compliance with LinkedIn's requirements. Hookdeck can also transform and redact sensitive fields before forwarding to your endpoint.

Testing LinkedIn webhooks

Use the developer portal test feature

LinkedIn's developer portal includes basic test functionality for webhook validation. However, this only verifies that your endpoint can complete the challenge-response flow — it doesn't send realistic notification payloads.

Use a request inspector

Before building your handler, inspect real LinkedIn payloads using a service like Hookdeck Console:

  1. Create a Hookdeck webhook URL
  2. Register it as your LinkedIn webhook endpoint
  3. Subscribe to notifications for a test organization
  4. Trigger social actions (like, comment, share) on the organization's posts
  5. Inspect the payload structure and headers in Hookdeck's dashboard

Validate subscription lifecycle

Test your integration with realistic scenarios:

  • Member authorization and subscription creation
  • Notification receipt and processing
  • OAuth token expiry and subscription recovery
  • Webhook endpoint downtime and notification recovery via the pull API
  • Member losing admin access to an organization

Conclusion

LinkedIn webhooks provide a real-time bridge between the platform's social and professional activity and your external systems. The organization social action notification system, combined with lead sync webhooks, enables powerful integrations for marketing automation, community management, and CRM synchronization.

However, LinkedIn's webhook implementation comes with significant operational complexity. The aggressive re-validation cycle, strict challenge-response requirements, per-member subscription model, and limited event coverage mean production deployments require careful engineering. The absence of ngrok support further complicates local development, and strict data retention policies add compliance overhead.

For applications with a single organization and stable infrastructure, LinkedIn's built-in webhook system combined with the pull API fallback works well. For multi-tenant applications managing webhooks across many organizations, or mission-critical integrations where delivery reliability matters, webhook infrastructure like Hookdeck can address LinkedIn's limitations by providing stable ingress URLs that pass validation reliably, automatic deduplication, configurable retries, and comprehensive delivery monitoring without modifying your LinkedIn integration.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.