diff --git a/README.md b/README.md index 4cd371e..f5f5d2a 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ curl -X POST https://api.publora.com/api/v1/create-post \ | `POST` | `/linkedin-reactions` | Add reaction to a LinkedIn post | [View](https://docs.publora.com/endpoints/linkedin-reactions) | | `DELETE` | `/linkedin-reactions` | Remove a LinkedIn reaction | [View](https://docs.publora.com/endpoints/linkedin-reactions) | | `POST` | `/linkedin-reshare` | Reshare an existing LinkedIn post | [View](https://docs.publora.com/endpoints/linkedin-reshare) | +| `GET` | `/linkedin-mentionables` | List mentionable LinkedIn people | [View](https://docs.publora.com/endpoints/linkedin-mentionables) | | `POST` | `/linkedin-followers` | Get LinkedIn follower statistics | [View](https://docs.publora.com/endpoints/linkedin-followers) | | `POST` | `/linkedin-profile-summary` | Get LinkedIn profile summary | [View](https://docs.publora.com/endpoints/linkedin-profile-summary) | @@ -138,6 +139,7 @@ All plans include full API access. Pro/Premium use per-account pricing — add a - [LinkedIn Statistics](https://docs.publora.com/endpoints/linkedin-statistics) — post and account analytics - [LinkedIn Reactions](https://docs.publora.com/endpoints/linkedin-reactions) — add/remove reactions - [LinkedIn Reshare](https://docs.publora.com/endpoints/linkedin-reshare) — repost an existing LinkedIn post +- [LinkedIn Mentionables](https://docs.publora.com/endpoints/linkedin-mentionables) — list the people you can @mention, with ready-to-paste tokens ### Platform Guides - [X / Twitter](https://docs.publora.com/platforms/x-twitter) · [LinkedIn](https://docs.publora.com/platforms/linkedin) · [Instagram](https://docs.publora.com/platforms/instagram) · [Threads](https://docs.publora.com/platforms/threads) · [TikTok](https://docs.publora.com/platforms/tiktok) · [YouTube](https://docs.publora.com/platforms/youtube) · [Facebook](https://docs.publora.com/platforms/facebook) · [Bluesky](https://docs.publora.com/platforms/bluesky) · [Mastodon](https://docs.publora.com/platforms/mastodon) · [Telegram](https://docs.publora.com/platforms/telegram) diff --git a/SKILL.md b/SKILL.md index e7c0fca..823ee1d 100644 --- a/SKILL.md +++ b/SKILL.md @@ -89,6 +89,7 @@ const response = await fetch('https://api.publora.com/api/v1/platform-connection | `/linkedin-comments` | POST | Post a comment | | `/linkedin-comments` | DELETE | Delete a comment | | `/linkedin-reshare` | POST | Reshare an existing post | +| `/linkedin-mentionables` | GET | List mentionable people (paid plans) | ### Webhooks diff --git a/docs/changelog.md b/docs/changelog.md index 9ec2771..0ffe34e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -28,6 +28,15 @@ This page records externally relevant REST and MCP contract changes. Dates are d - MCP `update_post` exposes `content` and `platforms`, and instructs clients to call `list_connections` before changing targets. - **Migration action:** No change is required for existing callers. Integrations that previously deleted and recreated a post to fix its text or targets should switch to `update-post`. Match on the new `code` values rather than message text, send an `Idempotency-Key` with content/platform edits, and re-read the post with `GET /get-post` on a 409 instead of blind-retrying. If you relied on a repeated connection ID in `platforms` being tolerated, de-duplicate the array. +## 2026-07-17 + +### LinkedIn mentionable-people directory — publora.com #218 + +- **Affected surface:** REST and MCP. +- **Tag:** Additive. +- **Changes:** Added `GET /linkedin-mentionables` and the `linkedin_list_mentionables` MCP tool (the 15th active tool). Both list the per-user directory of native LinkedIn member ids captured automatically from engagement on connected company pages, with `q`/`limit` filtering and a ready-to-paste `mention` token per person. Paid plans only — free plans receive `403 UPGRADE_REQUIRED`. +- **Migration action:** None. New integrations that build person mentions should prefer this directory over harvesting actor URNs manually. + ## 2026-07-15 ### API/MCP correctness release — publora.com #198 diff --git a/docs/endpoints/linkedin-mentionables.md b/docs/endpoints/linkedin-mentionables.md new file mode 100644 index 0000000..a3e42c0 --- /dev/null +++ b/docs/endpoints/linkedin-mentionables.md @@ -0,0 +1,122 @@ +# LinkedIn Mentionables + +List the LinkedIn members you can @mention — a per-user directory of people whose **native member ids** Publora has captured from engagement on your connected company pages. Each entry includes a ready-to-paste `mention` token for [create-post](create-post.md) content, [comment](linkedin-comments.md) messages, or [reshare commentary](linkedin-reshare.md). + +Why this exists: person mentions require LinkedIn's native member id (e.g. `Dk968RHxiO`), which cannot be derived from a linkedin.com profile URL. This directory is where Publora surfaces every native id it has seen for you. See the [LinkedIn Mentions Guide](../guides/linkedin-mentions.md) for the full id explanation and name-matching rules. + +> **Paid plans only.** Free plans receive a `403 UPGRADE_REQUIRED` error (see [Errors](#errors)). + +## List Mentionable People + +``` +GET https://api.publora.com/api/v1/linkedin-mentionables +``` + +### Headers + +| Header | Required | Description | +|--------|----------|-------------| +| `x-publora-key` | Yes | Your API key | + +### Query Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `q` | string | No | Case-insensitive substring filter on the person's name (regex characters are treated literally). Omit to list everyone. | +| `limit` | number | No | Maximum number of entries to return, `1`–`100`. Default `25`. | + +Results are sorted by `lastSeenAt` descending — the most recently engaged people first. + +### Response (HTTP 200) + +```json +{ + "success": true, + "people": [ + { + "personId": "Dk968RHxiO", + "name": "Daria Bulaeva", + "profileUrl": "", + "profilePicture": "", + "source": "comment", + "lastSeenAt": "2026-07-16T10:00:00.000Z", + "mention": "@{urn:li:person:Dk968RHxiO|Daria Bulaeva}" + } + ] +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `personId` | string | The person's **native member id** — the only id form that works in mentions. | +| `name` | string | The display name as captured from LinkedIn. | +| `profileUrl` | string | Currently always an empty string (reserved for future use). | +| `profilePicture` | string | Currently always an empty string (reserved for future use). | +| `source` | string | How the person was last captured: `comment` or `reaction`. The most recent engagement wins. | +| `lastSeenAt` | string | ISO 8601 timestamp of the most recent captured engagement. | +| `mention` | string \| null | Ready-to-paste mention token (`@{urn:li:person:ID\|Name}`) for post content or comment messages. `null` when no usable name is stored. Names containing `\|` or `}` are sanitized in the token. | + +> **Note:** The `mention` token uses the person's full captured name. LinkedIn matches display names strictly — if you edit the name part, follow the [name matching rules](../guides/linkedin-mentions.md#critical-name-matching-requirements) (first name, last name, or full name; exact case; nothing extra). + +### Examples + +#### JavaScript (fetch) + +```javascript +const params = new URLSearchParams({ q: 'daria', limit: '10' }); +const response = await fetch( + `https://api.publora.com/api/v1/linkedin-mentionables?${params}`, + { headers: { 'x-publora-key': 'YOUR_API_KEY' } } +); +const data = await response.json(); + +for (const person of data.people) { + if (person.mention) { + console.log(`${person.name}: ${person.mention}`); + } +} +``` + +#### cURL + +```bash +curl "https://api.publora.com/api/v1/linkedin-mentionables?q=daria&limit=10" \ + -H "x-publora-key: YOUR_API_KEY" +``` + +## How the Directory Fills + +The directory is populated **automatically** — there is no endpoint to add people manually: + +- When comments or reactions are read on posts of your connected **company pages** (via the Publora app's engagement views or the [feed-retrieval API](linkedin-feed-retrieval.md)), LinkedIn returns each engaging member's native app-scoped person id, and Publora persists it in your directory. +- No extra LinkedIn API calls are made — the ids are captured as a side effect of reads you already perform. +- Engagement on **personal-profile posts is not capturable** — LinkedIn restricts the required scope. Only company-page engagement feeds the directory. +- Placeholder/unresolved actors (entries LinkedIn returns without a resolvable member) are skipped. + +There is **no way to add an arbitrary person by profile URL** — the `ACoAA…` web-profile ids visible on linkedin.com cannot be converted to native ids. See [Finding LinkedIn URNs](../guides/linkedin-mentions.md#finding-linkedin-urns) for the full explanation of native vs web-profile ids. + +## MCP Tool + +The same directory is available to MCP clients as the `linkedin_list_mentionables` tool, with the same `q`/`limit` parameters and the same paid-plan requirement. See the [MCP Tools Reference](../mcp/tools-reference.md#linkedin_list_mentionables). + +## Errors + +| Status | Error | Cause | +|--------|-------|-------| +| 401 | `"API key is required"` | No `x-publora-key` header provided | +| 401 | `"Invalid API key"` | Bad `x-publora-key` | +| 403 | `"API access is not enabled for this account"` | Account does not have API access enabled | +| 403 | `"The mentionable-people directory is available on paid plans — upgrade to access it"` | Free plan — the response also includes `"code": "UPGRADE_REQUIRED"` | + +> **Note:** API keys already require a paid plan, so API callers normally hit the `403` API-access error before the `UPGRADE_REQUIRED` one; the latter mainly appears in edge cases (e.g. a plan downgrade with a still-active key). + +## Related + +- [LinkedIn Mentions Guide](../guides/linkedin-mentions.md) — mention syntax, native vs web-profile ids, name matching +- [LinkedIn Comments](linkedin-comments.md) — mention people in comments +- [LinkedIn Reshare](linkedin-reshare.md) — mention people in reshare commentary +- [Create Post](create-post.md) — mention people in scheduled posts + +--- + +*[Publora](https://publora.com) is built by [Creative Content Crafts, Inc.](https://cccrafts.ai) Need AI-powered content creation for LinkedIn, Threads, and X? Try [Co.Actor](https://co.actor) — the best AI service for authentic thought leadership at scale.* diff --git a/docs/guides/linkedin-mentions.md b/docs/guides/linkedin-mentions.md index 86ad1fa..6e1622d 100644 --- a/docs/guides/linkedin-mentions.md +++ b/docs/guides/linkedin-mentions.md @@ -140,6 +140,22 @@ const response = await fetch('https://api.publora.com/api/v1/create-post', { }); ``` +## Who Can I Mention? + +In practice, the people you can mention fall into two groups: + +1. **Your own connected accounts** — every LinkedIn connection's native id is in its `platformId` (see [Finding LinkedIn URNs](#finding-linkedin-urns) below). +2. **People who engaged with your connected company pages** — when comments or reactions are read on a company-page post (in the Publora app or via the API), Publora automatically captures each engaging member's native id into a per-user **mentionable-people directory**. No manual step is needed, and no extra LinkedIn calls are made. Engagement on personal-profile posts is not capturable (LinkedIn restricts the required scope). + +The recommended way to discover mentionable people is the [`GET /linkedin-mentionables`](../endpoints/linkedin-mentionables.md) endpoint (or the `linkedin_list_mentionables` MCP tool — both paid-plan only). It supports name search and returns a ready-to-paste `mention` token for each person: + +```bash +curl "https://api.publora.com/api/v1/linkedin-mentionables?q=daria" \ + -H "x-publora-key: YOUR_API_KEY" +``` + +Anyone outside these two groups — e.g. a person you only know from a linkedin.com profile URL — cannot be mentioned, because their native id is not obtainable (see the honest limitation under [Finding LinkedIn URNs](#finding-linkedin-urns)). + ## Finding LinkedIn URNs ### Person URN @@ -157,6 +173,7 @@ Where to get native member ids: - **Your own connected accounts:** the [platform-connections](../endpoints/platform-connections.md) endpoint returns each LinkedIn connection's `platformId` (e.g. `linkedin-Dk968RHxiO`). Strip the `linkedin-` prefix — the remainder is that member's native id. - **Actor ids returned by Publora's LinkedIn APIs:** endpoints such as [comments](../endpoints/linkedin-comments.md), [reactions](../endpoints/linkedin-reactions.md), and [feed retrieval](../endpoints/linkedin-feed-retrieval.md) return `urn:li:person:…` actor URNs in their responses. The id inside those URNs is the native form and can be used directly. +- **The mentionable-people directory:** [`GET /linkedin-mentionables`](../endpoints/linkedin-mentionables.md) lists every native id Publora has captured for you, searchable by name and with a ready-to-paste `mention` token per person (see [Who Can I Mention?](#who-can-i-mention)). > **Honest limitation:** There is **no public API to look up an arbitrary member's native id.** You can reliably mention people whose ids you obtained through Publora or LinkedIn API surfaces (your connections, actors on posts/comments you retrieved) — **not** people you found via a linkedin.com profile URL. The `ACoAA…` id visible in profile URLs and page source cannot be converted to a native id. @@ -228,6 +245,7 @@ After posting, check your LinkedIn post to ensure: ## Related Guides +- [LinkedIn Mentionables](../endpoints/linkedin-mentionables.md) - List the people you can mention - [LinkedIn Comments](linkedin-comments.md) - Comment on posts - [LinkedIn Reshare](../endpoints/linkedin-reshare.md) - Reshare posts with mention-capable commentary - [LinkedIn Reactions](linkedin-reactions.md) - Like and react to posts diff --git a/docs/guides/mcp-server.md b/docs/guides/mcp-server.md index 0f61d81..b4b33ea 100644 --- a/docs/guides/mcp-server.md +++ b/docs/guides/mcp-server.md @@ -158,6 +158,7 @@ MCP servers don't conflict -- Claude loads all servers and merges their tools. E | `linkedin_create_comment` | Post a comment on a LinkedIn post | | `linkedin_delete_comment` | Delete a comment from a LinkedIn post | | `linkedin_create_reshare` | Reshare a LinkedIn post, optionally with commentary and visibility settings | +| `linkedin_list_mentionables` | List mentionable LinkedIn people with ready-to-paste mention tokens (paid plans) | > LinkedIn **analytics/followers/profile-summary** and **workspace** management are **not** MCP tools — use the [REST OpenAPI reference](https://docs.publora.com/openapi.yaml). LinkedIn feed-retrieval tools (`linkedin_posts`, `linkedin_post_comments`, `linkedin_post_reactions`) are implemented but disabled pending LinkedIn's `r_member_social` approval. diff --git a/docs/mcp/client-setup.md b/docs/mcp/client-setup.md index a444fbe..e047f3e 100644 --- a/docs/mcp/client-setup.md +++ b/docs/mcp/client-setup.md @@ -109,7 +109,7 @@ Start a new conversation and ask: "What MCP tools do you have available?" ``` -Claude should list the 14 active Publora tools, including `linkedin_create_reshare` (3 additional LinkedIn feed-retrieval tools — `linkedin_posts`, `linkedin_post_comments`, `linkedin_post_reactions` — are pending LinkedIn approval). +Claude should list the 15 active Publora tools, including `linkedin_create_reshare` and `linkedin_list_mentionables` (3 additional LinkedIn feed-retrieval tools — `linkedin_posts`, `linkedin_post_comments`, `linkedin_post_reactions` — are pending LinkedIn approval). --- @@ -555,6 +555,6 @@ Some clients support environment variable interpolation: ## Next Steps -- [Tools Reference](./tools-reference.md) — All 14 active tools with parameters +- [Tools Reference](./tools-reference.md) — All 15 active tools with parameters - [Examples](./examples.md) — Real-world conversation examples - [Troubleshooting](./troubleshooting.md) — Common issues and solutions diff --git a/docs/mcp/tools-reference.md b/docs/mcp/tools-reference.md index 92b60f0..fa74cd9 100644 --- a/docs/mcp/tools-reference.md +++ b/docs/mcp/tools-reference.md @@ -1,6 +1,6 @@ # MCP Tools Reference -Complete reference for the 14 active Publora MCP tools with parameters, examples, and code snippets. Media can be attached two ways: the fast path (pass public **https** URLs via `mediaUrls` on `create_post`/`update_post`) or the upload dance (`get_upload_url` → HTTP PUT → `complete_media`). Three additional LinkedIn feed-retrieval tools (`linkedin_posts`, `linkedin_post_comments`, `linkedin_post_reactions`) are pending LinkedIn approval of the `r_member_social` permission — see [LinkedIn Feed Retrieval Tools](#linkedin-feed-retrieval-tools-coming-soon-requires-linkedin-approval) below. LinkedIn analytics and workspace-management features are available via the [REST OpenAPI reference](https://docs.publora.com/openapi.yaml), not MCP. +Complete reference for the 15 active Publora MCP tools with parameters, examples, and code snippets. Media can be attached two ways: the fast path (pass public **https** URLs via `mediaUrls` on `create_post`/`update_post`) or the upload dance (`get_upload_url` → HTTP PUT → `complete_media`). Three additional LinkedIn feed-retrieval tools (`linkedin_posts`, `linkedin_post_comments`, `linkedin_post_reactions`) are pending LinkedIn approval of the `r_member_social` permission — see [LinkedIn Feed Retrieval Tools](#linkedin-feed-retrieval-tools-coming-soon-requires-linkedin-approval) below. LinkedIn analytics and workspace-management features are available via the [REST OpenAPI reference](https://docs.publora.com/openapi.yaml), not MCP. > **Note:** Most tools return the backend API object. `list_connections` deliberately wraps the backend list as `{ "connections": [...] }` for MCP structured content. `list_posts` also supports a `concise` mode that truncates content previews and adds response-format metadata. @@ -791,6 +791,69 @@ Reshare an existing LinkedIn post to your feed, optionally with commentary. --- +## LinkedIn Mentionables Tool + +### linkedin_list_mentionables + +List the LinkedIn members you can @mention — Publora's per-user directory of native member ids captured from engagement (comments and reactions) on your connected company pages. Each entry includes a ready-to-paste `mention` token for post content or comment messages. **Paid plans only** — free plans receive a `403 UPGRADE_REQUIRED` error. Backed by [`GET /linkedin-mentionables`](../endpoints/linkedin-mentionables.md); see the [LinkedIn Mentions Guide](../guides/linkedin-mentions.md) for why native ids are required. + +**Parameters:** + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `q` | string | No | Case-insensitive substring filter on the person's name | +| `limit` | number | No | Maximum entries to return, 1–100 (default: 25) | + +Results are sorted by `lastSeenAt` descending (most recently engaged first). + +**Example prompts:** + +```text +"Who can I mention on LinkedIn?" +"Find the mention token for Daria" +"List people who recently engaged with my company page" +``` + +**Python example:** + +```python +async def list_mentionables(): + headers = {"Authorization": "Bearer sk_YOUR_API_KEY"} + + async with streamablehttp_client("https://mcp.publora.com", headers=headers) as (read, write, _): + async with ClientSession(read, write) as session: + await session.initialize() + + result = await session.call_tool("linkedin_list_mentionables", { + "q": "daria", + "limit": 10 + }) + print(result.content[0].text) +``` + +**Response example:** + +```json +{ + "success": true, + "people": [ + { + "personId": "Dk968RHxiO", + "name": "Daria Bulaeva", + "profileUrl": "", + "profilePicture": "", + "source": "comment", + "lastSeenAt": "2026-07-16T10:00:00.000Z", + "mention": "@{urn:li:person:Dk968RHxiO|Daria Bulaeva}" + } + ] +} +``` + +`source` is `comment` or `reaction` (the most recent engagement wins). `mention` is `null` when no usable name is stored. The directory fills automatically when company-page engagement is read — there is no way to add a person manually or by profile URL. + +--- + ## LinkedIn Feed Retrieval Tools (Coming Soon — Requires LinkedIn Approval) > **Status: DISABLED** - These tools are not yet available. They require the `r_member_social` permission, which is **RESTRICTED** and requires LinkedIn approval. The implementation is ready and will be enabled once LinkedIn approves the permission for Publora. diff --git a/schema/openapi.yaml b/schema/openapi.yaml index 54272b8..e385d8c 100644 --- a/schema/openapi.yaml +++ b/schema/openapi.yaml @@ -2602,6 +2602,92 @@ paths: schema: $ref: "#/components/schemas/Error" + /linkedin-mentionables: + parameters: + - $ref: "#/components/parameters/XPubloraClient" + get: + summary: List mentionable LinkedIn people + description: "List the LinkedIn members you can @mention — a per-user directory of native member ids captured automatically from engagement (comments and reactions) on connected company pages. Each entry includes a ready-to-paste mention token. Available on paid plans only." + operationId: listLinkedInMentionables + tags: + - LinkedIn Analytics + parameters: + - name: q + in: query + required: false + description: "Case-insensitive substring filter on the person's name (regex characters treated literally)" + schema: + type: string + - name: limit + in: query + required: false + description: "Maximum number of entries to return" + schema: + type: integer + default: 25 + minimum: 1 + maximum: 100 + responses: + "200": + description: "Mentionable people, sorted by lastSeenAt descending" + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: true + people: + type: array + items: + type: object + properties: + personId: + type: string + description: "Native LinkedIn member id (the only id form usable in mentions)" + example: "Dk968RHxiO" + name: + type: string + example: "Daria Bulaeva" + profileUrl: + type: string + description: "Currently always empty (reserved)" + example: "" + profilePicture: + type: string + description: "Currently always empty (reserved)" + example: "" + source: + type: string + enum: [comment, reaction] + description: "How the person was last captured (most recent engagement wins)" + example: "comment" + lastSeenAt: + type: string + format: date-time + example: "2026-07-16T10:00:00.000Z" + mention: + type: string + nullable: true + description: "Ready-to-paste mention token for post content or comment messages; null when no usable name is stored" + example: "@{urn:li:person:Dk968RHxiO|Daria Bulaeva}" + "401": + description: Invalid API key + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: API access not enabled, or plan does not include the mentionable-people directory + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + upgradeRequired: + value: { "error": "The mentionable-people directory is available on paid plans — upgrade to access it", "code": "UPGRADE_REQUIRED" } + /list-posts: parameters: - $ref: "#/components/parameters/XPubloraClient"