> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pendpost.com/llms.txt
> Use this file to discover all available pages before exploring further.

# LinkedIn

> Set up LinkedIn posting with an OAuth app - to your company page or your personal profile, chosen per post.

The LinkedIn lane publishes to your organization (company) page by default, and can publish to your personal profile per post. It publishes only once a LinkedIn credential is present in your `.env`; until then the lane is live-but-unauthenticated and waits for you to connect it (set `PENDPOST_MODE=mock` to exercise it through the mock driver).

You own your LinkedIn account and are responsible for LinkedIn's terms of service. pendpost drafts and schedules, but a human approves before anything goes live.

## Setup ceremony

First create an OAuth app in LinkedIn's developer portal to get a client id and secret. Then run the interactive auth ceremony, which mints the tokens and writes them to your `.env`:

```bash theme={null}
node scripts/linkedin-social.mjs auth
```

## Environment variables

| Variable | Purpose |
| - | - |
| `LINKEDIN_CLIENT_ID` | Your OAuth app's client id. |
| `LINKEDIN_CLIENT_SECRET` | Your OAuth app's client secret. |
| `LINKEDIN_ACCESS_TOKEN` | Access token minted by the auth ceremony. |
| `LINKEDIN_REFRESH_TOKEN` | Refresh token minted by the auth ceremony. |
| `LINKEDIN_TOKEN_EXPIRES_AT` | When the access token expires. |
| `LINKEDIN_ORG_URN` | The organization URN you post as, in the form `urn:li:organization:<digits>`. |
| `LINKEDIN_PERSON_URN` | Your personal-profile URN, in the form `urn:li:person:<id>`. Captured automatically by the auth ceremony (you never enter it by hand); it enables the per-post "Personal profile" option. |
| `LINKEDIN_API_VERSION` | The LinkedIn API version, in `YYYYMM` form (for example `202605`). Optional, defaults to a current version. |

## The organization URN

`LINKEDIN_ORG_URN` identifies the organization page you publish to. It must be in the form `urn:li:organization:<digits>`, for example `urn:li:organization:1234567`. This is the default target for the LinkedIn lane.

> [!NOTE]
>
> A lane publishes only once its credential is present. If the LinkedIn variables are blank, the LinkedIn lane is live-but-unauthenticated and waits for you to connect it. Set `PENDPOST_MODE=mock` to exercise it through the mock driver.

## Company page vs personal profile

Each LinkedIn post carries an author target you pick in the composer: **Company page** (the default) or **Personal profile**. Company-page posting uses `LINKEDIN_ORG_URN`; personal-profile posting uses `LINKEDIN_PERSON_URN`, which the auth ceremony captures for you when the app has the "Share on LinkedIn" and "Sign In with LinkedIn" products granted. If a post is set to Personal profile but no person URN has been captured yet, the pre-flight check blocks it and asks you to reconnect - it never silently publishes to the company page instead.

## Validate the lane

A present credential is not the same as a working one. After setup, validate the LinkedIn lane in the **Setup** tab, or run a live probe with the `health_recheck` tool, to confirm the credential is live and not just configured. The lane is ready when it reports live.

> [!NOTE]
>
> **Over MCP.** Setup and rotation are an interactive human ceremony; secrets are never written through MCP. An agent can still check status (`account_status`), run a live liveness probe (`health_recheck`), and read non-secret config (`config_get`) over MCP without touching credentials. LinkedIn additionally exposes `token_refresh(platform: 'linkedin')`, which renews the access token from the stored refresh token, no new secret required.

## Common failures

| Symptom | Cause | Fix |
| - | - | - |
| Publishing returns a 401 even though setup succeeded. | The access token has expired. | Run `token_refresh(platform: 'linkedin')` to renew it from the stored refresh token, or re-run `node scripts/linkedin-social.mjs auth` if the refresh token is also stale. |
| Publishing returns a 403 or "not authorized to act as organization". | The authenticated person is not an admin of the organization page, or the OAuth app lacks the organization posting product. | Confirm you are an admin of the page, enable the organization posting product on the app, then re-run the auth ceremony. |
| A post set to "Personal profile" is blocked before publishing. | No personal-profile URN has been captured (the token predates member posting, or the "Share on LinkedIn" / "Sign In with LinkedIn" products were not granted at auth time). | Re-run `node scripts/linkedin-social.mjs auth` with those products granted; the ceremony captures `LINKEDIN_PERSON_URN` and the block clears. Or switch the post back to the company page. |
| Company-page posts land on a personal profile (or vice versa). | The post's author target does not match where you expected it, or `LINKEDIN_ORG_URN` is missing/malformed. | Check the author toggle in the composer, and set `LINKEDIN_ORG_URN` to `urn:li:organization:<digits>` (the numeric organization id), then re-check the lane. |
| The API rejects the request with a version error. | `LINKEDIN_API_VERSION` is older than LinkedIn now accepts. | Set `LINKEDIN_API_VERSION` to a current `YYYYMM` value, or clear it to use the built-in default. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.