# 03 — LinkedIn Publishing (Profile & Company Pages)

---

## 1. Reality check

| Destination | API author URN | Typical scopes | PostSync today |
|-------------|----------------|----------------|----------------|
| **Member profile** (personal) | `urn:li:person:{id}` | `w_member_social` (+ OpenID) | **Implemented** (text + image) |
| **Company Page** (organization) | `urn:li:organization:{id}` | `w_organization_social` (+ org admin) | **Not implemented** |

Global peers (Buffer, Hootsuite, etc.) treat **profile vs Company Page** as separate connected destinations. PostSync must do the same.

---

## 2. Current implementation

| Piece | Path |
|-------|------|
| Scopes | `src/lib/social/linkedin-scopes.ts` — member social + OpenID |
| Member resolve | `linkedin-member.ts`, `linkedin-probe.ts` |
| Publish | `linkedin-publish.ts` → `v2/ugcPosts`, `author: personUrn` |
| Token refresh | `linkedin-token.ts`, `ensure-publish-tokens.ts` |
| OAuth | `api/social/linkedin/*`, connected-accounts callbacks |

**Limitation:** author is always the logged-in member. No organization ACL / page picker.

---

## 3. Target: Company Pages

### 3.1 OAuth / products

1. LinkedIn Developer App with **Share on LinkedIn** + **Marketing / Community Management** products as required by current LinkedIn docs.  
2. Request **`w_organization_social`** (and read org scopes as needed, e.g. `r_organization_social` for verification).  
3. App review / Marketing Developer Platform access may be required for Live company posting.

### 3.2 Connection flow

```
LinkedIn OAuth (member + org scopes)
  → identify member
  → GET organization ACLs (admin orgs)
  → User selects Company Page(s) to connect
  → Store ConnectedAccount {
       platform: linkedin,
       targetType: organization,
       externalId: organizationUrn,
       label: page name
     }
  → Optionally also store member connection (targetType: member)
```

### 3.3 Publish

Reuse UGC / Posts API with:

```json
{ "author": "urn:li:organization:123456" }
```

Image and video: register upload, upload binary, attach to post (extend existing Assets flow in `linkedin-publish.ts`).

---

## 4. Manual vs auto

| Mode | Member profile | Company Page |
|------|----------------|--------------|
| Manual | Select “My LinkedIn” | Select one or more Company Pages |
| Auto | If member connection active | If org connection(s) active |

Allow publishing **to both** in one manual action (two API calls, aggregated result).

---

## 5. Media support roadmap

| Media | Member | Organization |
|-------|--------|--------------|
| Text | Live | Target P0 |
| Image | Live | Target P0 |
| Video | P1 | P1 |

Caption limit today ~3000 chars — keep validation in publisher.

---

## 6. Proposed code layout

```
src/lib/social/
  linkedin-publish.ts           # branch author: person | organization
  linkedin-organizations.ts     # list admin orgs, verify ACL
  linkedin-scopes.ts            # add w_organization_social helpers
```

Migration: `connected_accounts.target_type`, `external_id` as URN string.

Env:

```
LINKEDIN_SCOPES=openid profile email w_member_social w_organization_social
LINKEDIN_CLIENT_ID=
LINKEDIN_CLIENT_SECRET=
LINKEDIN_SOCIAL_REDIRECT_URI=
```

---

## 7. Acceptance criteria

- [ ] Connect LinkedIn member (existing)  
- [ ] List & connect Company Pages user administers  
- [ ] Manual image post as Company Page  
- [ ] Manual image post as member  
- [ ] Auto-publish respects which LinkedIn targets are connected  
- [ ] Missing `w_organization_social` shows actionable setup message  
- [ ] Tokens never written into RAG chunks or client bundles  
