# @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. ## What it does - 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. ## Prerequisites / 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. Agents: see [`AGENTS.md`](./AGENTS.md) for import paths, smoke, and dryRun. 1. Open [LinkedIn Developers](https://www.linkedin.com/developers/apps) → **Create app**. 2. **Auth** → **Authorized 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: ```text 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: ```text 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` 6. Paste the Client ID and Client Secret into the Kody form, continue to LinkedIn, and approve. 7. 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 | Scope | Product | Used by | | --- | --- | --- | | `openid` | Sign In with LinkedIn using OpenID Connect | smoke-test, get-user-info, get-person-urn | | `profile` | Sign In with LinkedIn using OpenID Connect | get-user-info (name / picture / locale flags) | | `email` | Sign In with LinkedIn using OpenID Connect | get-user-info (email presence flags only) | | `w_member_social` | Share on LinkedIn | posting, upload, and delete helpers | ### Auth / transport There is no usable public LinkedIn OpenAPI for the full REST surface. Helpers authenticate with `createAuthenticatedFetch('linkedin')` and inject 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. ## Done when - Forked copy has a working `linkedin` OAuth connection with the scopes above - Smoke-test returns `{ ok: true, hasSubject: true, personUrnPresent: true }` without names or emails - Posting/upload helpers refuse without `confirm: true`; `dryRun: true` never mutates LinkedIn ## Branding Keep `LICENSE` (MIT), `## Intent`, and `community-icon.svg` intact for community listing. ## Troubleshooting - Connect page asks for authorize/token URLs: use the complete first-time URL above. - HTTP 401 / expired token: reconnect at `https://kody.codes/connect/oauth?provider=linkedin`. - HTTP 403 on posting: the LinkedIn app needs **Share on LinkedIn** and `w_member_social`. Reconnect after approval. - `redirect_uri` mismatch: the LinkedIn app redirect must be exactly `https://kody.codes/connect/oauth`.