# Reach MCP — MCP tools (59)

Server: `https://app.reachmcp.com/mcp` (JSON-RPC over HTTP, `tools/list`, `tools/call`, `prompts/list`, `prompts/get`). Each tool carries MCP annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`), summarised after its name. Tools marked **write** change something on LinkedIn or in the account; they accept an optional `idempotency_key` and count against the account's daily quotas where a family applies. Failures come back as `isError` results carrying the [error object](/docs/errors.md).

**Tool profiles.** `tools/list` returns all 59 tools by default. `https://app.reachmcp.com/mcp?tools=core` lists the **core** profile (32 tools, marked *core* below): what the playbooks and everyday use need, lighter on the model's context. Every tool is callable whatever the listing. Former names (`connect`, `follow`, `invitation_status`, `user_posts`, and the `proxy_` prefix) keep working as aliases.


## Start here

### `reach_playbooks` · read-only · Reach only · *core*

START HERE. Return the catalogue of ready-made LinkedIn playbooks — what this server can actually accomplish, as named workflows rather than raw endpoints. Each entry carries its tool sequence, its prerequisites, and the full instructions to run it. Call this first when the user asks what you can do with their LinkedIn account, or when a request is vague. Pass playbook_id to get one playbook's instructions and then follow them. Use first for a vague request or 'what can you do'; not needed when the user names a precise action.

| Parameter | Type | Notes |
|---|---|---|
| `playbook_id` | string | Id of one playbook from the catalogue, to get its full instructions. |
| `include_instructions` | boolean | Also return the full instruction text of every playbook (longer output). |


## Accounts, quotas and logs

### `list_accounts` · read-only · Reach only · *core*

Return LinkedIn accounts accessible with the provided API key. If the key is restricted, only allowed account IDs are returned. Use first in every session to get the account_id the other tools need; ask which account when several are listed.

### `delete_account` · **write** · destructive · idempotent · Reach only

Delete a LinkedIn account by account_id. Also clears invitation references before deletion. Removes the account from Reach only (LinkedIn is untouched); irreversible, requires the user's explicit confirmation. Not for disconnecting temporarily.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |

### `get_account_quotas` · read-only · Reach only · *core*

Get the quotas configuration and usage counters for a LinkedIn account. Use before a batch to know what is left today; use update_account_quotas to change limits or the activity window.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |

### `update_account_quotas` · **write** · additive · idempotent · Reach only

Update one or more quota configuration fields for a LinkedIn account, and its activity window (the hours, days and timezone jobs run in when a job does not set its own). Only provided fields are updated. Changes limits and the activity window for jobs; does not reset today's counters. Raising a limit is the user's decision, ask first.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `daily_invitations_conf` | integer | Daily limit for 'invitations' on this account; 0 means unlimited. |
| `daily_messages_conf` | integer | Daily limit for 'messages' on this account; 0 means unlimited. |
| `daily_visits_conf` | integer | Daily limit for 'visits' on this account; 0 means unlimited. |
| `daily_imports_standard_conf` | integer | Daily limit for 'imports_standard' on this account; 0 means unlimited. |
| `daily_imports_salesnav_conf` | integer | Daily limit for 'imports_salesnav' on this account; 0 means unlimited. |
| `daily_imports_recruiter_conf` | integer | Daily limit for 'imports_recruiter' on this account; 0 means unlimited. |
| `daily_posts_conf` | integer | Daily limit for 'posts' on this account; 0 means unlimited. |
| `daily_comments_conf` | integer | Daily limit for 'comments' on this account; 0 means unlimited. |
| `daily_reactions_conf` | integer | Daily limit for 'reactions' on this account; 0 means unlimited. |
| `daily_linkedin_requests_conf` | integer | Ceiling on all LinkedIn requests (reads included) per UTC day; protects the account from a LinkedIn restriction. 1 to 2000, default 1000. Raising it is the user's decision. |
| `daily_messaging_reads_conf` | integer | Inbox reads (conversations, messages, Sales Navigator threads) per UTC day. Default 300; 0 = not limited. |
| `messaging_min_interval_seconds` | integer | Seconds before the same inbox page or conversation can be read again. Default 60; 0 = off. |
| `window_start` | string | HH:MM, in the account's timezone: jobs on this account start no earlier. Empty string resets to 09:00. |
| `window_end` | string | HH:MM: jobs on this account stop at this time. Empty string resets to 18:00. |
| `window_days` | array | Weekdays jobs may run: mon, tue, wed, thu, fri, sat, sun. Empty list resets to Monday–Friday. |
| `timezone` | string | IANA zone for the window, e.g. Europe/Paris. Empty string resets to the zone of the account's proxy country. |

### `get_account_request_logs` · read-only · Reach only

Get request logs for a LinkedIn account with optional filtering by request_type and pagination using limit/offset. Use to debug a failing account (each LinkedIn call with its status); for totals use get_account_request_logs_stats.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `request_type` | string | Only rows of this request type (for example send_message, list_conversations, connect). |
| `limit` | integer | Maximum number of rows to return. |
| `offset` | integer | Number of rows to skip. |

### `get_account_request_logs_stats` · read-only · Reach only

Get aggregated request-log statistics for a LinkedIn account, grouped by day, week, or month, with optional action and date-range filters. Use for the weekly review or to size an account's activity; for individual calls use get_account_request_logs.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `group_by` | string (day, week, month) | Bucket size for the statistics: day, week or month. |
| `request_type` | string | Only rows of this request type (for example send_message, list_conversations, connect). |
| `start_date` | string | Start of the period, ISO 8601 (default: 30 days ago). |
| `end_date` | string | End of the period, ISO 8601 (default: now). |

### `get_me` · read-only · calls LinkedIn · *core*

Call LinkedIn Voyager /me for an account through the stored proxy/cookies and return the normalized profile (same fields as Kanbox uses from /me). Use to confirm which LinkedIn profile an account is (name, headline, premium); one LinkedIn call. Not needed before every action.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |


## Inbox and messaging

### `list_conversations` · read-only · calls LinkedIn · *core*

List LinkedIn messenger conversations (Voyager Messaging GraphQL), with optional archived, unread, starred filters and next_cursor paging — same as GET /api/linkedin/{account_id}/conversations. Each item returns conversation_id (the thread id — use this as conversation_linkedin_id when fetching messages or replying) and recipient_linkedin_id (the peer's fsd_profile id — use this as recipient_linkedin_id when sending a new message). Do not call this in a loop to wait for replies: the same read is refused if repeated within 60s, inbox reads are capped at 300 per account per day, and polling gets LinkedIn accounts restricted. Subscribe a webhook to message.received instead. Use for the classic LinkedIn inbox (this is the one that matters in 95% of cases); Sales Navigator threads are separate, see salesnav_list_messaging_threads.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `archived` | boolean | Only archived conversations. |
| `unread` | boolean | Only conversations with unread messages. |
| `starred` | boolean | Only starred conversations. |
| `count` | integer | Page size. |
| `next_cursor` | string | Cursor returned by the previous page; omit for the first page. |

### `list_conversation_messages` · read-only · calls LinkedIn · *core*

List messages in a LinkedIn conversation via Voyager ``/messaging/conversations/{id}/events`` (Kanbox ``get_conversation_messages``), with optional created_before paging — same as GET /api/linkedin/{account_id}/conversations/{conversation_linkedin_id}/messages. Do not call this in a loop to wait for replies: the same read is refused if repeated within 60s, inbox reads are capped at 300 per account per day, and polling gets LinkedIn accounts restricted. Subscribe a webhook to message.received instead. Use after list_conversations to read a thread before replying; the conversation_linkedin_id comes from list_conversations.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `conversation_linkedin_id` | string | required; Conversation (thread) id, as returned by list_conversations. |
| `created_before` | string | Only messages created before this Unix timestamp in milliseconds, to page back in time. |

### `send_message` · **write** · additive · not idempotent, use idempotency_key · calls LinkedIn · *core*

Send a LinkedIn DM via Voyager createMessage: use conversation_linkedin_id for an existing thread or recipient_linkedin_id for a new DM. Use for the classic inbox: conversation_linkedin_id to reply in a thread, recipient_linkedin_id for a new one (1st-degree connections, or InMail). Counts against the daily messages quota; for a Sales Navigator thread use salesnav_send_message; for more than ten messages use create_job.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `text` | string | Plain text to send or post. |
| `conversation_linkedin_id` | string | Conversation (thread) id, as returned by list_conversations. |
| `recipient_linkedin_id` | string | Recipient profile id (fsd_profile id) to start a new conversation; from list_conversations or scrape_profile. Omit when replying in an existing conversation. |
| `in_mail_premium` | boolean | Send as InMail to someone outside the network (premium accounts); requires recipient_linkedin_id. |

### `react_message` · **write** · additive · idempotent · calls LinkedIn

Add or remove an emoji reaction on a LinkedIn message. Set react=false to remove the reaction. message_urn is the message_urn returned by list_conversation_messages (e.g. urn:li:msg_message:(urn:li:fsd_profile:…,123456789)). emoji is a Unicode emoji character (e.g. 👍, ❤️). Pass conversation_linkedin_id to also mark the conversation as read. Use for an emoji reaction on one message inside a thread (message_urn from list_conversation_messages); to reply in words use send_message.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `message_urn` | string | required; Full message URN to react to. |
| `emoji` | string | required; Unicode emoji character (e.g. 👍, ❤️). |
| `react` | boolean | default `True`; True to add the reaction, False to remove it. |
| `conversation_linkedin_id` | string | Conversation ID — when provided the conversation is marked as read. |

### `star_conversation` · **write** · additive · idempotent · calls LinkedIn

Star or unstar a LinkedIn conversation. Set star=false to remove the star. conversation_linkedin_id is the conversation_id returned by list_conversations. Housekeeping in the classic inbox; reversible (star=false). No quota.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `conversation_linkedin_id` | string | required; Conversation (thread) id, as returned by list_conversations. |
| `star` | boolean | default `True`; True to star, False to unstar. |

### `archive_conversation` · **write** · additive · idempotent · calls LinkedIn

Archive or unarchive a LinkedIn conversation. Set archive=false to unarchive. conversation_linkedin_id is the conversation_id returned by list_conversations. Housekeeping in the classic inbox; reversible (archive=false). Not a delete, see delete_conversation.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `conversation_linkedin_id` | string | required; Conversation (thread) id, as returned by list_conversations. |
| `archive` | boolean | default `True`; True to archive, False to unarchive. |

### `delete_conversation` · **write** · destructive · idempotent · calls LinkedIn

Permanently delete a LinkedIn conversation. conversation_linkedin_id is the conversation_id returned by list_conversations. Irreversible for this account; confirm with the user. To hide a thread without losing it use archive_conversation.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `conversation_linkedin_id` | string | required; Conversation (thread) id, as returned by list_conversations. |


## Sales Navigator inbox

### `salesnav_list_messaging_threads` · read-only · calls LinkedIn

List Sales Navigator messaging threads (salesApiMessagingThreads). Requires the account to have a Sales Navigator subscription (has_sales_nav). Returns threads with messages and participant profiles. Paginate by passing next_page_starts_at from the previous response as page_starts_at. Use only when the account has Sales Navigator and the user asks about that inbox; the classic inbox is list_conversations.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `filter` | string (INBOX, ARCHIVE, UNREAD, INMAIL_PENDING, INMAIL_ACCEPTED, INMAIL_DECLINED) | default `INBOX`; Conversation folder. |
| `count` | integer | default `20`; Threads per page. |
| `page_starts_at` | integer | Pagination cursor — next_page_starts_at from the previous response. |

### `salesnav_list_thread_messages` · read-only · calls LinkedIn

Fetch a Sales Navigator thread with all its messages and participant profiles (salesApiMessagingThreads/{id}). Requires a Sales Navigator subscription. Use the thread id returned by salesnav_list_messaging_threads. message_count controls how many (most recent) messages are returned; compare with total_message_count in the response to know if more exist. Use to read one Sales Navigator thread (thread id from salesnav_list_messaging_threads); classic inbox threads use list_conversation_messages.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `thread_id` | string | required; Sales Navigator thread ID. |
| `message_count` | integer | default `20`; Number of most-recent messages to return. |

### `salesnav_send_message` · **write** · additive · not idempotent, use idempotency_key · calls LinkedIn

Send a message in an existing Sales Navigator thread (salesApiMessageActions). Requires the account to have a Sales Navigator subscription. Use the thread id returned by salesnav_list_messaging_threads. Use only to reply inside an existing Sales Navigator thread; everything else (new messages, classic inbox) goes through send_message. Counts against the daily messages quota.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `thread_id` | string | required; Sales Navigator thread ID. |
| `text` | string | required; Message body (plain text). |
| `copy_to_crm` | boolean | default `False`; Copy message to CRM. |


## Network and invitations

### `list_connections` · read-only · calls LinkedIn · *core*

List LinkedIn 1st-degree connections for an account (Voyager dash connections), paged with start/count — same payload as GET /api/linkedin/{account_id}/connections. Use to walk the account's own 1st-degree network; for pending invitations use list_sent_invitations or list_received_invitations; for search use scrape_search.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `start` | integer | Pagination offset, 0-based. |
| `count` | integer | Page size. |

### `send_invitation` · **write** · additive · not idempotent, use idempotency_key · calls LinkedIn · *core*

Send a connection request to a LinkedIn member. Optional message capped at 300 chars (premium) or 200 chars. Use to connect with someone not yet a 1st-degree connection; check get_invitation_status first to avoid duplicates. Counts against the daily invitations quota; for more than ten use create_job. To follow without connecting use follow_member.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `linkedin_id_or_url` | string | required; LinkedIn member ID, vanity name, or profile URL. |
| `message` | string | Optional personalised invitation note. |

### `get_invitation_status` · read-only · calls LinkedIn · *core*

Get the connection and invitation status between the connected account and a LinkedIn member. Returns is_connection, invitation_type (SENT/PENDING/WITHDRAWN/REFUSED). Use before send_invitation or send_message to know the relationship (connected, invitation pending, refused). Read-only, cheap.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `linkedin_id_or_url` | string | required; The LinkedIn member: profile URL, vanity name (the part after /in/), Sales Navigator URL, or internal member id. |

### `withdraw_invitation` · **write** · destructive · idempotent · calls LinkedIn

Withdraw a pending sent connection request. Use to cancel a pending sent invitation (from list_sent_invitations), for example one older than three weeks. Not for received invitations, see decline_invitation.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `linkedin_id_or_url` | string | required; The LinkedIn member: profile URL, vanity name (the part after /in/), Sales Navigator URL, or internal member id. |

### `accept_invitation` · **write** · additive · idempotent · calls LinkedIn

Accept a pending received connection request. Use to accept a received invitation (invitation_secret from list_received_invitations). Ask the user which ones.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `linkedin_id_or_url` | string | required; The LinkedIn member: profile URL, vanity name (the part after /in/), Sales Navigator URL, or internal member id. |

### `decline_invitation` · **write** · destructive · idempotent · calls LinkedIn

Decline/ignore a pending received connection request. Use to ignore a received invitation; the sender is not notified. Not for invitations the account sent, see withdraw_invitation.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `linkedin_id_or_url` | string | required; The LinkedIn member: profile URL, vanity name (the part after /in/), Sales Navigator URL, or internal member id. |

### `remove_connection` · **write** · destructive · idempotent · calls LinkedIn

Remove an existing 1st-degree connection. Irreversible on LinkedIn (the person is not notified); confirm with the user. Not for pending invitations, see withdraw_invitation.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `linkedin_id_or_url` | string | required; The LinkedIn member: profile URL, vanity name (the part after /in/), Sales Navigator URL, or internal member id. |

### `follow_member` · **write** · additive · idempotent · calls LinkedIn

Follow or unfollow a LinkedIn member without connecting. Set follow=false to unfollow. Use to follow someone's posts without a connection request (no quota); to connect use send_invitation.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `linkedin_id_or_url` | string | required; The LinkedIn member: profile URL, vanity name (the part after /in/), Sales Navigator URL, or internal member id. |
| `follow` | boolean | default `True`; True to follow, False to unfollow. |

### `list_received_invitations` · read-only · calls LinkedIn · *core*

List pending connection invitations received by the account (with invitation_secret to accept them). Use to triage invitations waiting for the account (then accept_invitation or decline_invitation); for those the account sent use list_sent_invitations.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `start` | integer | default `0`; Pagination offset, 0-based. |
| `count` | integer | default `20`; Page size (max 100). |

### `list_sent_invitations` · read-only · calls LinkedIn · *core*

List pending connection invitations sent by the account that have not yet been accepted. Use to find invitations the account sent that are still pending (then withdraw_invitation if stale); for received ones use list_received_invitations.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `start` | integer | default `0`; Pagination offset, 0-based. |
| `count` | integer | default `20`; Page size (max 100). |


## Profiles and search

### `scrape_profile` · read-only · calls LinkedIn · *core*

Fetch a complete LinkedIn profile (name, headline, company, experience, skills…). Pass a LinkedIn URL, vanity name, internal member ID, or Sales Navigator lead URL. Uses SalesNav API when available for richer data. Use to read one person in depth (experience, skills, company); costly, one LinkedIn call per profile. For a list of people use scrape_search; to just check the relationship use get_invitation_status.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `linkedin_id_or_url` | string | required; LinkedIn profile URL, vanity name, Sales Navigator URL, or internal member ID. |

### `scrape_search` · read-only · calls LinkedIn · *core*

Run one page of a LinkedIn, Sales Navigator, or Recruiter search and return normalized profile rows. Pass the full search URL. Use start+count to paginate (count ignored for standard LinkedIn, fixed ~10/page). Use to run a search URL you have (LinkedIn, Sales Navigator, Recruiter) one page at a time; build the Sales Navigator URL with salesnav_build_search_url first. Counts against the imports quota.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `url` | string | required; Full LinkedIn / SalesNav / Recruiter search URL. |
| `start` | integer | default `0`; Pagination offset. |
| `count` | integer | default `25`; Page size (max 25). Ignored for standard LinkedIn. |

### `visit_profile` · **write** · additive · idempotent · calls LinkedIn · *core*

Simulate a profile view, triggering a 'viewed your profile' notification for the target member. Use to leave a 'viewed your profile' trace as a soft touch before an invitation; counts against the daily visits quota. Not needed to read a profile, use scrape_profile.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `linkedin_id_or_url` | string | required; LinkedIn member ID, vanity name, or profile URL. |

### `profile_viewers` · read-only · calls LinkedIn · *core*

Get the list of members who recently viewed the account's profile (requires LinkedIn Premium). Use for the 'who viewed my profile' list (needs LinkedIn Premium; empty or an error otherwise). Read-only.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |

### `salesnav_resolve_industry` · read-only · Reach only · *core*

Resolve an industry name (any language) to its LinkedIn Industry Code(s) using the bundled Industry Codes V2 taxonomy (offline, no LinkedIn call). Returns ranked candidates with id, label and hierarchy path. Use the returned id(s) in salesnav_build_search_url's `industry` filter. Call once per industry term; if several candidates look plausible, pick by the hierarchy path. Use for industries only (offline taxonomy, any language, no LinkedIn call); for locations, companies, schools and titles use salesnav_typeahead.

| Parameter | Type | Notes |
|---|---|---|
| `query` | string | required; Industry name to look up, e.g. 'software', 'banque', 'real estate'. |
| `limit` | integer | default `8`; Maximum number of rows to return. |

### `salesnav_typeahead` · read-only · calls LinkedIn · *core*

Live Sales Navigator autocomplete for facets whose ids are dynamic. Use type='geo' to resolve a location to its REGION id (the only reliable way — LinkedIn region ids are not enumerable), and type='company' / 'school' / 'title' for those entity facets. Returns [{id, displayValue, headline}]. Feed the chosen id into salesnav_build_search_url (region / current_company / school / current_title). Requires a connected account. Use for facets whose ids are dynamic (geo, company, school, title…); for industries use salesnav_resolve_industry. One LinkedIn call per lookup.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `type` | string (geo, company, school, title, group, industry) | default `geo`; Facet to autocomplete. 'geo' -> region ids. |
| `query` | string | required; Text to autocomplete, e.g. 'Paris', 'Google', 'HEC'. |
| `count` | integer | default `10`; Page size. |

### `salesnav_build_search_url` · read-only · calls LinkedIn · *core*

Build a Sales Navigator people-search URL from structured lead-search filters, using as many filters as the request implies. RESOLUTION: `industry` accepts numeric ids OR names (auto-resolved via the taxonomy); `region` accepts numeric REGION ids OR location names (auto-resolved via geo typeahead — needs account_id). If a name is ambiguous, the tool returns {needs_disambiguation:[...]} with candidates instead of a URL — re-call with the chosen id. BOOLEAN: `keywords` and `current_title` accept LinkedIn boolean syntax (AND/OR/NOT, quotes, parentheses). EXCLUSION: any list value may be an object {id|name, exclude:true}. Set execute=true to also run the first page of results (reuses the live search) and return leads. Use after resolving the ids to get the Sales Navigator search URL, then scrape_search on it. Builds a URL only, no LinkedIn call.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | Required when passing location/industry NAMES or execute=true. |
| `keywords` | string | Global keyword search; supports boolean operators. |
| `current_title` | string | Current job title; free text + boolean (e.g. '(CTO OR "VP Engineering") NOT interim'). |
| `past_title` | string | Boolean search on past job titles. |
| `first_name` | string | First name filter. |
| `last_name` | string | Last name filter. |
| `company_headcount` | array | COMPANY_HEADCOUNT codes: A=Self-employed B=1-10 C=11-50 D=51-200 E=201-500 F=501-1000 G=1001-5000 H=5001-10000 I=10001+. |
| `company_type` | array | COMPANY_TYPE: C=Public P=Privately Held N=Non Profit D=Educational S=Partnership E=Self-Employed O=Self Owned G=Government. |
| `function` | array | FUNCTION 1-26: 1 Accounting 4 Business Dev 8 Engineering 10 Finance 12 HR 13 IT 15 Marketing 18 Operations 19 Product 25 Sales 26 Support (etc.). |
| `seniority_level` | array | SENIORITY: 320 Owner/Partner 310 CXO 300 VP 220 Director 210 Exp. Manager 200 Entry Manager 130 Strategic 120 Senior 110 Entry 100 Trainee. |
| `years_at_current_company` | array | 1=<1y 2=1-2y 3=3-5y 4=6-10y 5=>10y. |
| `years_in_current_position` | array | 1=<1y 2=1-2y 3=3-5y 4=6-10y 5=>10y. |
| `years_of_experience` | array | 1=<1y 2=1-2y 3=3-5y 4=6-10y 5=>10y. |
| `relationship` | array | RELATIONSHIP: F=1st S=2nd A=Group members O=3rd+. |
| `profile_language` | array | ISO 639-1 codes: en fr es de it pt nl ... |
| `industry` | array | Industry ids (use salesnav_resolve_industry) or names to auto-resolve. Items may be {id\|name, exclude}. |
| `region` | array | REGION ids (use salesnav_typeahead type=geo) or location names to auto-resolve. Items may be {id\|name, exclude}. |
| `current_company` | array | Resolved COMPANY ids (salesnav_typeahead type=company). |
| `past_company` | array | Past companies: names or ids; add exclude:true to exclude one. |
| `school` | array | Resolved SCHOOL ids. |
| `group` | array | LinkedIn groups: names or ids. |
| `follows_your_company` | boolean | Only people who follow your company page. |
| `viewed_your_profile` | boolean | Only people who viewed your profile recently. |
| `past_colleague` | boolean | Only past colleagues. |
| `with_shared_experiences` | boolean | Only people with shared experiences (school, company, group). |
| `recently_changed_jobs` | boolean | Only people who changed jobs in the last 90 days. |
| `posted_on_linkedin` | boolean | Only people who posted on LinkedIn in the last 30 days. |
| `execute` | boolean | default `False`; Also run the first results page and return leads. |
| `start` | integer | default `0`; Pagination offset, 0-based. |
| `count` | integer | default `25`; Page size. |


## Posts and engagement

### `list_user_posts` · read-only · calls LinkedIn · *core*

Get recent posts published by a LinkedIn member (via profileUpdatesV2). Paginate with start/count. Use to read what a member published recently (their content, not who engaged); to get likers and commenters of a post use scrape_post. Read-only.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `linkedin_id_or_url` | string | required; LinkedIn member ID, vanity name, or profile URL. |
| `count` | integer | default `10`; Number of posts (max 50). |
| `start` | integer | default `0`; Pagination offset. |

### `scrape_post` · read-only · calls LinkedIn · *core*

Fetch likers and/or commenters for a LinkedIn post. Pass the full post URL or slug. Set liked=true and/or comments=true. Use to get the people who liked or commented on one post you have the URL of; for the account's own recent posts use scrape_my_posts. Counts against the imports quota.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `post_id_or_url` | string | required; LinkedIn post URL or slug. |
| `liked` | boolean | default `False`; Include likers. |
| `comments` | boolean | default `False`; Include commenters. |

### `scrape_my_posts` · read-only · calls LinkedIn · *core*

Fetch likers and/or commenters for the account's own recent posts. since_hours limits to posts published in the last N hours (default 24). Use for engagement on the account's own posts (likers, commenters, comment URNs for reply_comment); for someone else's post use scrape_post.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `liked` | boolean | default `False`; Include the people who liked. |
| `comments` | boolean | default `False`; Include the people who commented, with their comment text. |
| `since_hours` | integer | default `24`; Only posts from the last N hours (max 720). |
| `max_posts` | integer | default `10`; Max posts to process (max 20). |

### `like_post` · **write** · additive · idempotent · calls LinkedIn · *core*

Like a LinkedIn post on behalf of the connected account. Pass the post URL, slug, or URN. Use for a light touch on a post; idempotent on LinkedIn's side. Counts against the daily reactions quota.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `post_url_or_urn` | string | required; LinkedIn post URL, slug, or urn:li:activity:…. |

### `comment_post` · **write** · additive · not idempotent, use idempotency_key · calls LinkedIn · *core*

Post a comment on a LinkedIn post on behalf of the connected account. Use to write a new top-level comment on a post; to answer an existing comment use reply_comment. Counts against the daily comments quota; the text is public, preview it with the user.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `post_url_or_urn` | string | required; LinkedIn post URL, slug, or URN. |
| `text` | string | required; Comment text. |

### `reply_comment` · **write** · additive · not idempotent, use idempotency_key · calls LinkedIn · *core*

Post a reply to an existing LinkedIn comment. Pass the comment_id (URN) returned by scrape_post as comment_urn — it is used directly as threadUrn. Use to answer a specific comment (comment_urn from scrape_my_posts or scrape_post); for a new comment on the post itself use comment_post. Counts against the daily comments quota.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `comment_urn` | string | required; URN of the comment to reply to (comment_id from scrape-post). |
| `text` | string | required; Reply text. |


## Publishing

### `create_post` · **write** · additive · not idempotent, use idempotency_key · calls LinkedIn · *core*

Create or schedule a LinkedIn post, optionally with a media attachment. For a single image pass a urn:li:digitalmediaAsset:… URN; for multiple images pass a urn:li:fsd_multiPhoto:… URN from create_multi_photo. Set scheduled_at (Unix ms) to schedule; omit it to publish immediately. Publishes now or schedules (scheduled_time); counts against the daily posts quota. Show the user the full text before calling.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `text` | string | required; Plain text to send or post. |
| `media_urn` | string | Optional media URN to attach. |
| `scheduled_at` | integer | Scheduled publish time in Unix milliseconds. Omit to publish now. |
| `visibility` | string | default `ANYONE`; 'ANYONE' or 'CONNECTIONS_ONLY'. |
| `allowed_commenters_scope` | string | default `ALL`; 'ALL' or 'CONNECTIONS_ONLY'. |

### `list_scheduled_posts` · read-only · calls LinkedIn

List scheduled LinkedIn posts for an account, paged. Returns post URNs, text content, and scheduled publish times. Use to see what is queued to publish; published posts are not listed here, use list_user_posts for those.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `start` | integer | default `0`; Pagination offset, 0-based. |
| `count` | integer | default `10`; Page size. |

### `update_scheduled_post` · **write** · additive · idempotent · calls LinkedIn

Update the text and/or scheduled time of an existing scheduled LinkedIn post. post_urn is the URN returned by list_scheduled_posts (e.g. urn:li:ugcPost:…). At least one of text or scheduled_at must be provided. Only for posts still scheduled (post_urn from list_scheduled_posts); a published post cannot be edited through Reach.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `post_urn` | string | required; URN of the scheduled post (urn:li:…), from list_scheduled_posts. |
| `text` | string | Plain text to send or post. |
| `scheduled_at` | integer | Unix timestamp in milliseconds. |

### `delete_scheduled_post` · **write** · destructive · idempotent · calls LinkedIn

Delete a scheduled LinkedIn post. post_urn is the URN returned by list_scheduled_posts (e.g. urn:li:ugcPost:…). Only for posts still scheduled; irreversible, confirm with the user. A published post cannot be deleted through Reach.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `post_urn` | string | required; URN of the scheduled post (urn:li:…), from list_scheduled_posts. |

### `upload_media_from_url` · **write** · additive · not idempotent, use idempotency_key · calls LinkedIn

Upload an image to LinkedIn by fetching it from a public URL. Returns a digitalmediaAsset URN (urn:li:digitalmediaAsset:…) that can be passed to create_post (single image) or create_multi_photo (multi-photo). Use this instead of upload-media when working as an agent — no binary file upload needed. Use before create_post when the post needs an image; returns the URN create_post expects. For several images, then create_multi_photo.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `image_url` | string | required; Publicly accessible URL of the image to upload. |
| `media_upload_type` | string | default `IMAGE_SHARING`; LinkedIn media upload type (default: IMAGE_SHARING). |

### `create_multi_photo` · **write** · additive · not idempotent, use idempotency_key · calls LinkedIn

Create a LinkedIn multi-photo object from a list of digitalmediaAsset URNs. Returns an identifierUrn (urn:li:fsd_multiPhoto:…) to pass to create_post. author_urn is the urn:li:fsd_profile:… of the connected account (get it from get_me). Use after uploading two or more images with upload_media_from_url; returns the single URN create_post takes. Not for one image.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `author_urn` | string | required; Only messages from this author URN. |
| `media_urns` | array | required; List of urn:li:digitalmediaAsset:… URNs. |
| `alt_texts` | array | Optional alt text per image (same order as media_urns). |


## Webhooks

### `list_webhook_endpoints` · read-only · Reach only · *core*

List the webhooks registered for this user: the URLs Reach calls when a conversation gets a new message (message.received), the account gains a 1st-degree connection (connection.new), an account changes connection state (account.status_changed) or a daily quota is close to or at its limit (quota.threshold_reached, quota.reached). Read-only. Use to see where Reach already posts events and which events; also returns the event catalogue.

### `create_webhook_endpoint` · **write** · additive · not idempotent, use idempotency_key · Reach only · *core*

Register a URL to be called when something happens on LinkedIn, so an agent or an n8n/Make workflow can react instead of polling. Returns the signing secret ONCE; deliveries carry 'Reach-Signature: t=<unix>,v1=<hex HMAC-SHA256(secret, t + '.' + body)>'. Events: message.received, connection.new, account.status_changed, quota.threshold_reached, quota.reached, or '*' for all. Omit account_ids to subscribe for every account of the user. https only. Use when the user wants to react to a reply, a new connection, a quota alert or a job event from n8n, Make or their own code; needs an https URL they control. Not for the model to call itself.

| Parameter | Type | Notes |
|---|---|---|
| `url` | string | required; https URL that accepts a JSON POST. |
| `events` | array | required; Event types, or ['*']. |
| `account_ids` | array | Optional: only these LinkedIn account ids. |
| `description` | string | Free-text label for your own reference. |

### `update_webhook_endpoint` · **write** · additive · idempotent · Reach only

Change a webhook's url, events, account_ids, description or is_active. Only provided fields change. Re-enabling clears the failure counter. Use to change events, accounts or url, or to re-enable an endpoint disabled after failures; to stop deliveries temporarily set is_active=false rather than deleting.

| Parameter | Type | Notes |
|---|---|---|
| `endpoint_id` | integer | required; Webhook endpoint id, from list_webhook_endpoints. |
| `url` | string | https URL that receives the signed JSON POST (http accepted for localhost only). |
| `events` | array | Event types to subscribe to (message.received, connection.new, account.status_changed, quota.threshold_reached, quota.reached) or ['*']. |
| `account_ids` | array | Only these LinkedIn account ids; omit for every account of the user. |
| `description` | string | Free-text label for your own reference. |
| `is_active` | boolean | Pause (false) or resume (true) deliveries. |

### `delete_webhook_endpoint` · **write** · destructive · idempotent · Reach only

Delete a webhook endpoint and its delivery history. Irreversible (the delivery history goes too); confirm with the user. To pause deliveries use update_webhook_endpoint with is_active=false.

| Parameter | Type | Notes |
|---|---|---|
| `endpoint_id` | integer | required; Webhook endpoint id, from list_webhook_endpoints. |

### `test_webhook_endpoint` · read-only · calls LinkedIn

Send a signed 'ping' event to a webhook endpoint right now and report the HTTP status it answered. Use to check a receiver end to end (signed ping, HTTP status back); does not deliver real events.

| Parameter | Type | Notes |
|---|---|---|
| `endpoint_id` | integer | required; Webhook endpoint id, from list_webhook_endpoints. |


## Jobs (durable batches)

### `create_job` · **write** · additive · not idempotent, use idempotency_key · Reach only · *core*

Queue a batch of LinkedIn actions (messages, invitations, profile visits or comments) that Reach executes on its own over the following hours or days: spread across the account's activity window (set per account with update_account_quotas; default 09:00-18:00, Monday to Friday, in the account's timezone; overridable per job) with human-like gaps, never beyond the daily quota of the action. Returns immediately with a job_id and the planned schedule; the agent does not have to stay alive. The job pauses by itself when a quota is reached or the account disconnects and resumes when it can; webhooks job.started / job.progress / job.paused / job.completed report what happens. Use it for anything above a handful of actions instead of calling send_message or send_invitation in a loop. Always show the user the items before queuing them. Use for any batch above a handful of actions; returns at once with the schedule. Show the items to the user first. Not for a single action, call the direct tool.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `action` | string (send_message, send_invitation, visit_profile, comment_post) | required; What every item does. send_message: text + conversation_linkedin_id (reply) or recipient_linkedin_id (new thread). send_invitation: linkedin_id_or_url + optional message (<200 chars). visit_profile: linkedin_id_or_url. comment_post: post_url_or_urn + text. |
| `items` | array | required; One object per action, in the order to execute them; fields depend on the action (see action). |
| `label` | string | Short free text shown in the dashboard and in webhook events, e.g. 'Follow-ups week 40'. |
| `window_start` | string | HH:MM in the job's timezone; default: the account's window (09:00 unless changed). |
| `window_end` | string | HH:MM in the job's timezone; default: the account's window (18:00 unless changed). |
| `days` | array | Weekdays the job may run: mon, tue, wed, thu, fri, sat, sun. Default: the account's window (Monday to Friday unless changed). |
| `timezone` | string | IANA zone such as Europe/Paris. Default: the account's window setting, else the zone of its proxy country. |

### `get_job` · read-only · Reach only · *core*

One job with its status, totals, next execution time and every item (scheduled time, result or error). Use to report progress or to see why an item failed; for an overview of all jobs use list_jobs.

| Parameter | Type | Notes |
|---|---|---|
| `job_id` | integer | required; Job id, as returned by create_job or list_jobs. |

### `list_jobs` · read-only · Reach only · *core*

Jobs of this user, newest first, without their items. Filter by account or status (scheduled, running, paused, completed, cancelled). Use to find running or paused jobs before creating a new one on the same account; details and items are in get_job.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | Reach id of the LinkedIn account to act on, from list_accounts. |
| `status` | string (scheduled, running, paused, completed, cancelled) | Only jobs in this status. |
| `limit` | integer | Maximum number of jobs; default 50. |

### `cancel_job` · **write** · destructive · idempotent · Reach only

Cancel a job: its pending items will never run. Items already executed are unaffected. Cannot be undone. Irreversible for the pending items (executed ones stay); confirm with the user. To stop temporarily use pause_job.

| Parameter | Type | Notes |
|---|---|---|
| `job_id` | integer | required; Job id, as returned by create_job or list_jobs. |

### `pause_job` · **write** · additive · idempotent · Reach only

Pause a job: nothing more runs until resume_job is called. Reach also pauses a job by itself when a quota is reached or the account disconnects, and resumes those on its own. Use to hold a job the user is unsure about; resume_job re-plans it. Reach pauses on quota or disconnection by itself.

| Parameter | Type | Notes |
|---|---|---|
| `job_id` | integer | required; Job id, as returned by create_job or list_jobs. |

### `resume_job` · **write** · additive · idempotent · Reach only

Resume a paused job. Its pending items are re-planned from now, inside the window and the remaining quota. Use after a manual pause; jobs paused for quota or disconnection resume on their own and need no call.

| Parameter | Type | Notes |
|---|---|---|
| `job_id` | integer | required; Job id, as returned by create_job or list_jobs. |


## Other

### `search_posts` · read-only · calls LinkedIn · *core*

Search LinkedIn posts by keywords, like the Posts tab of LinkedIn search: who is talking about a topic right now. Returns about 10 posts per page with the text, the engagement and the author (name, headline, member id, relationship degree). Keywords take LinkedIn's syntax: "exact phrase", OR, AND, NOT. Filter by recency, author job title, author company or industry, and content type; paginate with start (0, 10, 20…). Use to find who is posting about a topic (buying signals, hiring, pain points, competitors) and reach the authors; then scrape_post on a result for the people who engaged, scrape_profile on an author to qualify, send_invitation with author_linkedin_id. One LinkedIn call per page of ~10 posts; keep to a few pages. Read-only.

| Parameter | Type | Notes |
|---|---|---|
| `account_id` | integer | required; Reach id of the LinkedIn account to act on, from list_accounts. |
| `keywords` | string | required; What the posts talk about, e.g. "hiring SDR" OR "SDR position". |
| `date_posted` | string (past-24h, past-week, past-month) | Only posts from this period. Omit for any time. |
| `sort_by` | string (date_posted, relevance) | date_posted for the newest first (best for signals); relevance is LinkedIn's default. |
| `content_type` | string (videos, photos, documents, jobs, liveVideos, collaborativeArticles) | Only posts of this type. |
| `author_job_title` | string | Only authors whose job title matches, e.g. CEO or Head of Sales. |
| `author_company_ids` | array | Numeric LinkedIn company ids of the authors' current company (from salesnav_typeahead type=company). |
| `author_industry_ids` | array | Numeric LinkedIn industry ids of the authors (from salesnav_resolve_industry). |
| `start` | integer | default `0`; Pagination offset: 0, 10, 20… |
