LinkedIn — Outreach

List inbox conversations

Returns the most recent ~20 conversations on the connected account.

Response shape — read this before parsing

The response returns two parallel views of the people in each thread:

  • participants[] — preferred. Already enriched with name, slug, and headline extracted from the LinkedIn payload server- side, with zero extra API calls. Use these for rendering.
  • participantUrns[] — back-compat, URN strings only.

There is no participant.name at the top level, no peer, no otherPerson, no lastMessage.snippet, no last_message field. If you normalise against those, every contact renders as "(unnamed)".

Field map (exact names, camelCase):

  • conversationUrn — urn:li:msg_conversation:(...). Pass this to GET /linkedin/conversations/{conversationUrn}/messages. Always URL-encode when used as a path parameter.
  • participants[] — Enriched: { urn, name, slug, headline }. Any of name/slug/headline may be null (deleted accounts, restricted profiles), but urn is always present.
  • participantUrns[] — Mirror of participants[].urn. Kept for back-compat with clients written before the enrichment landed.
  • title — Group chat title only. Null on 1-on-1 conversations. Don't fall back to this for the contact name; use participants instead.
  • lastMessage.text — Plain text body. Not snippet, not preview, not bodyText.
  • lastMessage.senderName — Display name of the sender of the latest message. Already resolved server-side; null only when LinkedIn omits the inline profile.
  • lastMessage.senderUrn — URN of the same sender.
  • lastActivityAt, lastReadAt, lastMessage.deliveredAt — Unix ms, not seconds. Nullable for system-initiated threads.

Worked example response

{
  "ok": true,
  "conversations": [
    {
      "conversationUrn": "urn:li:msg_conversation:(urn:li:fsd_profile:ACoAAA…,2-MWE…)",
      "backendUrn": "urn:li:fs_conversation:2-MWE…",
      "isGroup": false,
      "title": null,
      "lastActivityAt": 1715990400000,
      "lastReadAt": 1715990405000,
      "unreadCount": 1,
      "read": false,
      "participants": [
        {
          "urn": "urn:li:fsd_profile:ACoAAA…aaaa",
          "name": "Jane Doe",
          "slug": "jane-doe-1a2b3c",
          "headline": "Head of Sales at Acme"
        },
        {
          "urn": "urn:li:fsd_profile:ACoAAA…me",
          "name": "Me",
          "slug": "me-1a2b3c",
          "headline": null
        }
      ],
      "participantUrns": [
        "urn:li:fsd_profile:ACoAAA…aaaa",
        "urn:li:fsd_profile:ACoAAA…me"
      ],
      "lastMessage": {
        "messageUrn": "urn:li:msg_message:(urn:li:fsd_profile:ACoAAA…,2-MWE…)",
        "text": "Sure, happy to chat — Tuesday at 2pm work?",
        "senderUrn": "urn:li:fsd_profile:ACoAAA…aaaa",
        "senderName": "Jane Doe",
        "deliveredAt": 1715990400000
      }
    }
  ]
}

Rendering the contact name (1-on-1 thread)

Filter your own viewer URN out of participants, then read name directly. No profile-lookup round-trip needed:

const { conversations } = await client.linkedinListConversations({ sessionId });
const viewerUrn = (await client.linkedinGetSession({ id: sessionId })).viewerProfileUrn;

for (const c of conversations) {
  const other = c.participants.find(p => p.urn !== viewerUrn);
  const displayName = other?.name
    ?? (c.isGroup && c.title)
    ?? "(unknown)";
  // …render
}

For group chats, prefer title; if absent (rare), join the other participants' names with commas. For the rare case where participants[i].name is null (deleted / restricted), fall back to POST /linkedin/profile/lookup with the URN — but you'll only need that on a handful of edge cases, not every conversation.

get/linkedin/conversations

Request

  • Base URL: https://anjal.app
  • URL: https://anjal.app/linkedin/conversations
  • Auth: API key in header X-API-Key

Query parameters

sessionIdstring required

Response

Array of conversations.

okboolean

Changes

No recorded changes to this operation across all 1 revision of this API.