Relay / LinkedIn capability referencePRIVATE PILOT

LinkedIn capability reference

All 22 capabilities use the same durable operation endpoint. Submit {"capability":"linkedin.identity.get","input":{}} with a unique Idempotency-Key, then poll GET /v1/operations/{operation_id}. The HTTP submission response is an operation envelope; the fields below describe its result when status becomes succeeded. An input validation error may be returned immediately as HTTP 400 or later as a failed operation.

Each input below is the input object inside POST /v1/connections/{connection_id}/operations. Unknown top-level input fields are rejected. All reads still require operations:write to submit and operations:read to inspect. The three sends additionally require linkedin:send. IDs in this section are LinkedIn IDs, never service connection UUIDs: profile_id/public_id are public profile slugs, recipient_actor_id is the LinkedIn profile hash ID, and conversation_id is the ID returned by a conversation read. Provider-derived fields can be null when LinkedIn omits them. Do not build a parser that assumes undocumented fields always exist.

Authentication and Recruiter session (3)

linkedin.login

Input

{} or {"reconnect":true}

Successful result and behavior

reason: "login_succeeded" and state_revision; connection health becomes ready. A browser session and regional proxy are required.

linkedin.health.check

Input

{}

Successful result and behavior

Opens the saved browser session through its pinned proxy, checks the feed and authenticated identity, and returns health: "ready", actor_id, and state_revision on success. Requires an existing ready session. An authentication or identity failure updates connection health; other errors appear as operation failures. This is an explicit provider check, unlike /healthz.

linkedin.recruiter.switch

Input

{} or {"contract":"Recruiter"}; contract text up to 100 characters

Successful result and behavior

contract, recruiter_ready: true, and state_revision. Switches the account's matching Recruiter contract and verifies a read-only Recruiter search before publishing the session. Requires ordinary connection health ready. The connection's separate recruiter.health must become ready before Recruiter reads.

Identity, profile, connections, and organizations (6)

linkedin.identity.get

Input

{}

Successful result and behavior

actor_id, hash_id, public_id, member_id, firstName, lastname, headline, avatar when supplied, plus state_revision. Use actor_id to identify the authenticated account.

linkedin.profile.get

Input

{"profile_id":"jane-example"}

Successful result and behavior

profile with selected basic/contact profile fields, plus state_revision. Public slug: 1–255 letters, digits, _, ., or -.

linkedin.hash_ids.resolve

Input

{"public_ids":["jane-example"]}

Successful result and behavior

hash_ids mapping the supplied public IDs to actor hash IDs, plus state_revision. Supply 1–25 public IDs. Useful before sending to a profile found by public slug.

linkedin.connections.list

Input

{"count":25,"start":0}

Successful result and behavior

connections array; entries include hash_id, public_id, names, headline, avatar, and connected_at when supplied. count 1–50, start 0–10000; defaults 25 and 0. Increase start for the next page.

linkedin.company.get

Input

Exactly one of {"public_id":"example-co"}, {"company_id":"12345"}, or {"company_name":"Example Co"}

Successful result and behavior

company object with mapped company details. Numeric company_id is a string of at most 20 digits.

linkedin.invitations.sent.list

Input

{"start_index":0,"include_total":true}

Successful result and behavior

Returns pending outgoing invitations: entries, count, start_index, has_more, next_start_index, optional total. Pages contain up to ten items. Follow next_start_index until has_more is false; a short nonempty page still requires another read. Dedupe by invitation_id because invitations may be accepted or withdrawn while paging. start_index must be a multiple of ten from 0 to 100000. include_total defaults to false; when requested, total is LinkedIn's advisory count or null if unavailable. Provider errors return a failed operation with upstream_error and safe upstream_status; an unfamiliar page or response returns parsing_error, never a successful empty inventory.

linkedin.profile.get returns a profile object with a stable top-level shape. Identity and summary fields are source, member_id, hash_id, public_id, first_name, last_name, headline, profile_picture, industry, location, and public_profile_url. Contact and network fields are email, address, phone_numbers, network_distance, has_pending_connection_request, and connected_at. Rich fields are about, experience[], and education[]; experience includes title, company, location, dates, and description, while education includes school, degree, major, and dates. The schema also carries optional demographics and Recruiter-only fields such as recruiting; unavailable fields are null or empty lists. The source for this operation is full.

linkedin.company.get returns company with available fields from public_id, company_id, name, tagline, description, url, website, phone, employee_count, employee_count_range, founded_year, industries, industry, specialties, headquarter, locations, logo_url, and cover_image_url. Empty provider fields are omitted.

Discovery and search (5)

linkedin.typeahead

Input

{"kind":"company","keyword":"Example"}

Successful result and behavior

kind, items, state_revision. Kinds: company, job_function, industry, location, people, school, skill, job_title. Keyword: 1–100 printable characters. Use returned IDs when a search filter needs one.

linkedin.people.search

Input

{"keywords":"engineer","filters":{"location_name":"New York"},"start":0,"count":25}

Successful result and behavior

people list and start, plus state_revision. At least one keyword or filter is required. start 0–1000. count 1–50 caps returned rows; the upstream people handler controls its own page size, so a page may contain fewer than count. Advance start by the number of returned rows.

linkedin.jobs.search

Input

{"keywords":"engineer","filters":{"company_name":"Example"},"start":0,"count":25}

Successful result and behavior

jobs list and start, plus state_revision. At least one keyword or filter is required. start 0–1000, count 1–50. Optional decoration_id (up to 255 characters) and job_details_count, job_posting_detail_description_start, job_posting_detail_description_count (each integer 0–1000) control LinkedIn job-detail retrieval.

linkedin.recruiter.typeahead

Input

{"kind":"job_title","keyword":"Engineer"}

Successful result and behavior

kind, items, state_revision. Kinds: company, industry, job_title, location, school, skill, zip. Requires recruiter.health: "ready".

linkedin.recruiter.people.search

Input

{"keywords":"engineer","titles":["Engineer"],"start":0,"count":25} or {"titles":[{"name":"Engineer","title_id":"42"}]}

Successful result and behavior

candidates, paging, total, formatted_total, facets, query_summary, highlight_terms, and state_revision. Supplied title IDs are used directly; name-only titles are resolved by typeahead. Requires verified Recruiter readiness. start 0–1000, count 1–50; at least one keyword, title, or other filter is required. See Recruiter filters below.

Ordinary people[] rows can include member_id, hash_id, public_id, member_distance, name, headline, summary, location, profile_url, insights, and badgeHoverText. jobs[] rows include job_id, job_title, job_url, job_company, job_insight, job_location, easy_apply, and reposted_at; when detail retrieval succeeds they can include company, location, description, posting, and application fields. Provider-derived values may be null. Recruiter candidates[] use the mapped profile shape with Recruiter-specific fields, and a candidate may lack hash_id.

For ordinary people search, filters accepts exactly these keys (strings up to 100 printable characters unless stated otherwise): location_id, current_company_id, past_company_id, industry_id, network, connection_of, follower_of, profile_language, school_id, service_category_id, first_name, last_name, title, company, school, location_name, current_company_name, past_company_name, industry_name, connection_of_name, follower_of_name, school_name, service_category_name, and boolean open_to_volunteer. The *_name filters are resolved through server-side typeahead; resolve ambiguity by using an ID when available. Any name filter selects the free-text handler, so avoid mixing name and ID filters in the same request unless you have checked that combination. keywords is up to 200 printable characters.

For jobs search, filters accepts: location_id, company_id, time_posted_range, job_type, sort_by, commitment, distance, experience_level, function_id, industry_id, populated_place_geo_id, salary_bucket, title_id, workplace_type, location_name, company_name, function_name, industry_name, populated_place_name, title_name (strings up to 100 printable characters), and apply_with_linkedin, early_applicant, in_your_network (booleans). The *_name forms request server-side resolution. LinkedIn determines the meaning of upstream value strings; typeahead and live results are the safest way to select them.

Recruiter filters. titles accepts up to ten strings or objects such as {"name":"Engineer","filter_type":"must_have","time_scope":"current"}. An optional title_id may be supplied in a title object. Supplied IDs are used directly; name-only entries are resolved through Recruiter typeahead. filter_type is can_have (default), must_have, or doesnt_have; title/company time_scope is current_or_past (default), current, past, or past_not_current. The accepted additional top-level fields are:

Field Format
first_names, last_names Lists of name strings.
locations List of location strings or {name,filter_type?,geo_scope?} objects. geo_scope: current (default), preferred_not_current, current_or_preferred.
skills, companies, schools, industries, current_companies, past_companies Lists of strings or {name,filter_type?} objects. companies may also use time_scope. Names are resolved through the relevant Recruiter typeahead.
zip_codes List of nonempty ZIP/postal-code strings.
company_sizes Letter codes A–I: self-employed, 1–10, 11–50, 51–200, 201–500, 501–1000, 1001–5000, 5001–10000, 10000+.
job_functions, seniorities Lists of numeric LinkedIn taxonomy codes. job_functions includes 8 for Engineering; seniorities uses 1–10 (for example 4 Senior, 5 Manager, 6 Director). Query Recruiter facets/results to choose other codes.
networks Codes F first-degree, S second-degree, A group members, O third-degree/others.
profile_languages Language codes such as en, es, zh, de, fr, it, pt, nl, in, ms, ro, ru; accepted codes are service-defined, so test uncommon codes before use.
recently_joined Integer buckets 1 (one day), 2 (2–7 days), 3 (8–14 days), 4 (15–30 days), 5 (1–3 months).
graduation_years, yoe Objects with integer min and/or max; yoe means years of experience.
is_veteran Boolean.
distance Positive integer, used with geographic criteria.

The service validates many Recruiter filter details inside the copied handler, after accepting the operation. Invalid combinations may therefore produce a failed operation with error_code: "validation_error". Start with keywords or a small set of filters, inspect results and error_code, then add filters. A valid empty search result still counts as a working Recruiter session. Recruiter candidate enrichment may leave hash_id absent; do not assume every candidate can immediately be messaged.

Conversations and messages (4)

linkedin.conversations.list

Input

{} or {"category":"PRIMARY_INBOX","count":20,"cursor":"<next-cursor>"}

Successful result and behavior

conversations, next_cursor, state_revision. Categories: PRIMARY_INBOX (default), SECONDARY_INBOX, INMAIL, ARCHIVE, SPAM; count 1–20. Pass the returned cursor for the next page. Alternatively pass last_activity_at as a decimal millisecond timestamp, which is Prox's older coarse cursor; use one cursor form at a time. Conversation entries include conversation_id, participant hash IDs, unread count, and activity time when supplied.

linkedin.conversations.search

Input

{"keywords":"Example","categories":["INBOX"],"count":20}

Successful result and behavior

conversations, next_cursor, state_revision. Keyword 1–100 characters; categories from INBOX, SPAM, ARCHIVE, INMAIL (default: INBOX, SPAM, ARCHIVE); optional first_degree_connections boolean and cursor; count 1–20.

linkedin.conversation.with_recipient

Input

{"recipient_actor_id":"<actor-hash-id>"}

Successful result and behavior

conversation object if a conversation exists, plus state_revision. Useful to find a thread before sending.

linkedin.messages.list

Input

{"conversation_id":"<conversation-id>","count":20}

Successful result and behavior

messages array and state_revision; count 1–20 is sent upstream. Optional cursor maps to Prox's prev_cursor. Alternatively use delivered_at from the oldest returned message as a decimal millisecond timestamp to request earlier messages; dedupe by message_urn because boundary rows can repeat. Use one cursor form at a time. A conversation_urn may replace conversation_id or accompany its matching ID; it must name the authenticated actor. The upstream handler does not expose a next cursor. Message entries can include message_urn, message_body, sender_hash_id, delivered_at, and attachment render content. Treat content as customer data.

For cursor pagination, pass the exact opaque cursor returned by the preceding page, at most 512 characters. A recipient or conversation ID must be 1–255 characters of letters, digits, _, +, =, or -.

Sending and files (4)

The three send capabilities require linkedin:send and an account in ready health. Use a recipient actor ID from linkedin.hash_ids.resolve, a people/connections result, or an existing conversation participant; use a conversation ID returned by conversation reads. The service does not provide a bulk campaign endpoint or guarantee LinkedIn will accept every send.

linkedin.message.send

Input

{"conversation_id":"<conversation-id>","body":"Hello"} or {"recipient_actor_id":"<actor-hash-id>","body":"Hello"}

Successful result and behavior

Exactly one destination. Body up to 2000 characters; either nonblank text or render_content_unions is required. Advanced Prox-style render content accepts up to five nonempty JSON objects totaling 16 KB, for example an already uploaded file union. Use linkedin.attachment.send for normal file uploads. Message text and render content are encrypted in durable input storage. Result contains provider_acknowledged: true, destination, message_id when LinkedIn supplies it, provider result fields, and state_revision.

linkedin.invitation.send

Input

{"recipient_actor_id":"<actor-hash-id>"} with optional "message":"Short note"

Successful result and behavior

Invitation result includes recipient_actor_id, code, message, provider_acknowledged: true, and state_revision. Optional note 1–300 characters. Check code rather than assuming every acknowledgement means SENT.

linkedin.attachment.send

Input

{"conversation_id":"<conversation-id>","filename":"note.pdf","content_type":"application/pdf","data_base64":"<base64>"}; may use recipient_actor_id instead and optional body

Successful result and behavior

Exactly one destination. File must be 1–1,000,000 decoded bytes; filename 1–255 plain characters without slashes; valid media type; optional body up to 2000 characters. Result has send acknowledgement and message ID when supplied. Base64 encode the file bytes before JSON submission.

linkedin.file.download

Input

{"url":"https://media.licdn.com/..."}

Successful result and behavior

content_type, data_base64, size, state_revision. URL must be HTTPS on linkedin.com/licdn.com or a subdomain, with no credentials and no nonstandard port. The current service does not enforce a download-size cap; apply your own response-size limit and decode base64 on your side.