LinkedIn Apply Connect, OAuth setup, job posting, webhook apply ingestion, and Talent Solutions partner requirements.
LinkedIn integration lets orgs cross-post InsightHire positions to LinkedIn, ingest Easy Apply / Apply Connect applications via webhook, and sync screening results back to LinkedIn. Setup requires LinkedIn Talent Solutions partnership approval — standalone developer apps cannot access Job Posting or Apply Connect APIs.
Prerequisites
- LinkedIn Talent Solutions partner application — apply at LinkedIn ATS Partners.
- LinkedIn Developer App with Client ID and Client Secret.
- Numeric company organization ID (without
urn:li:organization:prefix). - Org Admin role in InsightHire.
Sign In with LinkedIn does not unlock recruitment APIs. If Auth → OAuth 2.0 scopes shows "No permissions added", Member OAuth will fail with invalid_scope_error.
Settings path
| Step | Location |
|---|---|
| Connect integration | /dashboard/settings/integrations/linkedin/setup |
| View connection status | /dashboard/settings/integrations |
| Cross-post a position | Position detail → LinkedIn actions (or API postPosition) |
During setup the UI displays two URLs you must register in the LinkedIn Developer App:
| URL type | API path | Purpose |
|---|---|---|
| OAuth redirect | {API_BASE}/integrations/linkedin/callback | Member OAuth callback (optional) |
| Apply webhook | {API_BASE}/api/webhooks/linkedin/applications | Apply Connect application delivery |
Production example: https://api.insighthire.com/api/webhooks/linkedin/applications
Connection modes
InsightHire supports two connect paths via ats.linkedin.completeOAuth and ats.linkedin.connectWithCredentials:
Client credentials (recommended)
Job Posting and Apply Connect use 2-legged OAuth (client_credentials). No browser redirect required.
- Open LinkedIn setup.
- Choose Client credentials only.
- Enter Client ID, Client Secret, organization URN/ID, and optional poster email.
- Select API type:
apply-connect(default) ortalent-hub. - Submit — InsightHire tests the connection and stores config in
ats_integrations.
Use this mode when LinkedIn Auth shows no member scopes.
Member OAuth (optional)
For Talent Hub admin flows that require member delegation:
- Choose Member OAuth.
- InsightHire generates an authorize URL with scopes:
- Apply Connect:
r_organization_admin,rw_organization_admin,r_recruitment - Talent Hub: above plus
w_recruitment
- Apply Connect:
- User completes LinkedIn consent; callback exchanges code for tokens.
- Tokens stored with refresh support in integration config.
If scopes are not provisioned by LinkedIn, authorization fails — switch to client credentials.
Register webhook in LinkedIn
In your LinkedIn app (Apply Connect configuration):
- Set jobApplicationWebhookUrl to the Apply webhook URL from setup.
- LinkedIn sends a GET challenge to validate ownership:
GET /api/webhooks/linkedin/applications?challengeCode=...InsightHire responds with:
{
"challengeCode": "<code>",
"challengeResponse": "<hex HMAC-SHA256(challengeCode, clientSecret)>"
}- LinkedIn then POSTs
EXPORT_JOB_APPLICATIONevents to the same endpoint.
Webhook apply ingestion
POST bodies are verified with X-LI-Signature:
stringToSign = "hmacsha256=" + rawBody
signature = hex(HMAC-SHA256(stringToSign, clientSecret))On valid signature, InsightHire calls ingestLinkedInApplyConnectApplication, which:
- Resolves the position by
externalJobId(InsightHire position ID or LinkedIn external ID). - Creates or updates the candidate application with
externalSource: LINKEDIN. - Triggers journey invite flow when configured.
Non-apply event types are acknowledged with 200 and ignored.
Posting jobs to LinkedIn
After connection, cross-post from InsightHire via ats.linkedin.postPosition:
| Operation | Effect |
|---|---|
CREATE | Publish new LinkedIn job from position |
UPDATE | Sync title, description, location changes |
CLOSE | Close listing on LinkedIn |
RENEW | Renew expired posting |
Optional posterEmail overrides the default recruiter email on Job Posting API calls.
Manual sync alternatives:
syncJobs— pull LinkedIn jobs into InsightHiresyncApplicants— pull applicants for a job
BullMQ schedules periodic LinkedIn sync after connect.
Status sync-back
When recruiters advance LinkedIn-sourced candidates:
updateCandidateStageupdates local status and maps stage to LinkedIn application status whensyncToLinkedIn: true.postScreeningResultspushes assessment scores (adjusted/raw score, video URL, completion time) to LinkedIn after journey completion.
Partner approval requirements
| Requirement | Detail |
|---|---|
| Product access | Job Posting API and/or Apply Connect must be provisioned by LinkedIn — they do not appear on a standalone Products tab until partnership approval |
| Webhook URL | Must be publicly reachable HTTPS (InsightHire API host) |
| Organization ID | Must match the LinkedIn company page numeric ID |
| Client secret | Used for webhook challenge, signature verify, and token exchange |
| Member scopes | Only after Talent Solutions products are approved; otherwise use client credentials |
Common errors:
| Error | Resolution |
|---|---|
invalid_scope_error | Use client credentials connect; apply for Talent Solutions partnership |
INVALID_SIGNATURE on webhook | Verify client secret matches LinkedIn app; check raw body parsing |
| Position not found on apply | Ensure externalJobId in webhook matches InsightHire position ID used at post time |
| Unauthorized on post | Re-connect integration; refresh tokens or verify partner product access |
API reference (tRPC)
| Procedure | Purpose |
|---|---|
ats.linkedin.getSetupUrls | OAuth redirect + webhook URLs |
ats.linkedin.getAuthUrl | Start member OAuth |
ats.linkedin.connectWithCredentials | 2-legged connect |
ats.linkedin.completeOAuth | Finish member OAuth |
ats.linkedin.testConnection | Verify credentials |
ats.linkedin.postPosition | Cross-post / update / close |
ats.linkedin.syncJobs | Pull jobs from LinkedIn |
ats.linkedin.syncApplicants | Pull applicants |
ats.linkedin.postScreeningResults | Push assessment results |

