Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/linkedin

README.md

164 lines · 8.1 KB · Markdown

@kody/linkedin

Official LinkedIn “in” mark in white on LinkedIn Blue #0A66C2 (brand guidelines / Simple Icons letter paths; enclosing square omitted so the mark is not inverted). LinkedIn® is a trademark of LinkedIn Corporation. This package is not affiliated with or endorsed by LinkedIn.

Intent

Give any Kody account reusable, headless LinkedIn helpers for identity (/v2/userinfo and the posting personUrn), a generic authenticated request escape hatch, and guarded personal-profile posting. Author identity is always resolved from the connected member — never hard-coded to a specific profile.

This listing is meant to be forked. After you fork, connect your LinkedIn account and run ./smoke-test on your copy. Do not treat the live @kody/linkedin integration as yours.

When To Use

  • Verify the saved linkedin OAuth integration and read the connected member’s identity or person URN
  • Call uncommon LinkedIn REST endpoints with ./request
  • Preview a text, article, image, or video post with dryRun: true, then publish only after confirm: true
  • Upload article thumbnails through the Images API before publishing link-preview cards
  • Delete a post by share or UGC post URN after explicit confirmation
  • Run the native video upload flow: register, upload parts, finalize, then post

Do not use this package as an organization-page poster, a scraping client, or a place to paste access tokens.

OAuth setup

LinkedIn is bring-your-own OAuth. There is no built-in Kody LinkedIn app. Tokens come from the hosted connect page (createAuthenticatedFetch('linkedin')). Never paste a client secret or access token into chat.

  1. Open LinkedIn DevelopersCreate app.
  2. AuthAuthorized redirect URLs → add exactly https://kody.codes/connect/oauth (the connect page shows the same value with a copy button).
  3. Products → request Sign In with LinkedIn using OpenID Connect and Share on LinkedIn.
  4. Note the app’s Client ID and Client Secret (paste them into Kody, not into chat).
  5. Open this prefilled connect URL while signed in to Kody:
https://kody.codes/connect/oauth?provider=linkedin

First-time setup also needs LinkedIn’s authorize and token URLs. Use this complete prefilled URL when the connection does not exist yet:

https://kody.codes/connect/oauth?provider=linkedin&authorizeUrl=https%3A%2F%2Fwww.linkedin.com%2Foauth%2Fv2%2Fauthorization&tokenUrl=https%3A%2F%2Fwww.linkedin.com%2Foauth%2Fv2%2FaccessToken&apiBaseUrl=https%3A%2F%2Fapi.linkedin.com&flow=confidential&tokenExchangeStyle=form&allowedHosts=api.linkedin.com%2Cwww.linkedin.com&dashboardUrl=https%3A%2F%2Fwww.linkedin.com%2Fdevelopers%2Fapps&scopes=openid%20profile%20email%20w_member_social

Decoded:

  • Redirect / callback: https://kody.codes/connect/oauth
  • Authorize: https://www.linkedin.com/oauth/v2/authorization
  • Token: https://www.linkedin.com/oauth/v2/accessToken
  • API base: https://api.linkedin.com
  • Flow: confidential with tokenExchangeStyle=form
  • Hosts: api.linkedin.com, www.linkedin.com
  • Scopes: openid, profile, email, w_member_social
  1. Paste the Client ID and Client Secret into the Kody form, continue to LinkedIn, and approve.
  2. Run Smoke test on the forked package.

Reconnect the same connection with https://kody.codes/connect/oauth?provider=linkedin. Kody reuses the saved authorize/token endpoints.

LinkedIn does not issue a refresh token unless the app has approved partner access. When the access token expires, reconnect — do not invent a refresh flow.

Scopes

ScopeProductUsed by
openidSign In with LinkedIn using OpenID Connect./smoke-test, ./get-user-info, ./get-person-urn
profileSign In with LinkedIn using OpenID Connect./get-user-info (name / picture / locale flags)
emailSign In with LinkedIn using OpenID Connect./get-user-info (email presence flags only)
w_member_socialShare on LinkedInposting, upload, and delete helpers

./request uses whatever scopes the saved token already has. Identity helpers do not return names or emails from ./smoke-test.

Auth / transport

There is no usable public LinkedIn OpenAPI for the full REST surface. This package uses a compact request helper that authenticates with createAuthenticatedFetch('linkedin') and injects LinkedIn protocol headers (LinkedIn-Version, X-Restli-Protocol-Version) for Rest.li endpoints.

Posting helpers resolve author from /v2/userinfo (urn:li:person:{sub}) unless the caller passes an explicit urn:li:person:…. Do not hard-code a member URN in forks.

Smoke test

After connect or reconnect, invoke ./smoke-test in the forked package runtime (not a static import):

import { packages } from 'kody:runtime'

export default async function main() {
	return await packages.invoke({
		kodyId: 'linkedin',
		exportName: './smoke-test',
	})
}

Success looks like { ok: true, integration: 'linkedin', hasSubject: true, personUrnPresent: true } and does not return names, emails, or the raw sub.

Low-level equivalent (execute exploration only):

import { createAuthenticatedFetch } from 'kody:runtime'

export default async function main() {
	const linkedinFetch = await createAuthenticatedFetch('linkedin')
	const response = await linkedinFetch('https://api.linkedin.com/v2/userinfo')
	if (!response.ok) {
		throw new Error('LinkedIn userinfo failed: ' + response.status)
	}
	const data = (await response.json()) as { sub?: string }
	return { ok: true, hasSubject: Boolean(data.sub) }
}

dryRun and posting

Posting and upload helpers never mutate LinkedIn when dryRun: true. A live write requires a fresh confirm: true after the user approved the exact payload.

import createTextPost from 'kody:@kody/linkedin/create-text-post'

export default async function main() {
	return await createTextPost({ text: 'Draft text', dryRun: true })
}

LinkedIn does not scrape Open Graph metadata for API-created article posts. Pass title, description, and either an existing LinkedIn image thumbnail URN or an HTTPS thumbnailUrl.

Exports

  • kody:@kody/linkedin — package overview, connect URL, and scopes
  • kody:@kody/linkedin/smoke-test — verify userinfo access without returning PII
  • kody:@kody/linkedin/get-user-info — LinkedIn OIDC userinfo
  • kody:@kody/linkedin/get-person-urn — author personUrn for posting
  • kody:@kody/linkedin/request — authenticated LinkedIn API request helper
  • kody:@kody/linkedin/create-post — generic Posts API create helper (mutating)
  • kody:@kody/linkedin/create-text-post — text post helper (mutating)
  • kody:@kody/linkedin/create-article-post — article link post helper (mutating)
  • kody:@kody/linkedin/delete-post — delete a post by URN (mutating)
  • kody:@kody/linkedin/register-image-upload — initialize image upload (mutating)
  • kody:@kody/linkedin/create-image-post — image post helper (mutating)
  • kody:@kody/linkedin/register-video-upload — initialize native video upload (mutating)
  • kody:@kody/linkedin/upload-video-part — upload one video part (mutating)
  • kody:@kody/linkedin/finalize-video-upload — finalize native video upload (mutating)
  • kody:@kody/linkedin/create-video-post — video post helper (mutating)

Examples

import getPersonUrn from 'kody:@kody/linkedin/get-person-urn'

export default async function main() {
	return await getPersonUrn()
}
import request from 'kody:@kody/linkedin/request'

export default async function main() {
	return await request({ path: '/v2/userinfo', dryRun: true })
}

Troubleshooting

  • Connect page asks for authorize/token URLs: use the complete first-time URL in OAuth setup.
  • HTTP 401 / expired token: reconnect at https://kody.codes/connect/oauth?provider=linkedin. LinkedIn access tokens do not refresh without partner access.
  • HTTP 403 on posting: the LinkedIn app needs the Share on LinkedIn product and w_member_social. Reconnect after the product is approved.
  • redirect_uri mismatch: the LinkedIn app redirect must be exactly https://kody.codes/connect/oauth.