{
  "openapi": "3.1.0",
  "info": {
    "title": "Relay local API",
    "version": "0.3.0",
    "description": "Local API for one LinkedIn account per installation. Optional serve --log records local JSONL task sessions under the private data directory. It captures sanitized API inputs/results plus owned-page actions and native network metadata, never authentication headers/cookies or raw network bodies. Native action behavior and operation JSON bodies are unchanged. Full logged API content remains private user data. Logging is disabled by default; task tags cannot enable it. Startup LOG_INIT_FAILED stops a requested logger before browser work; later LOG_WRITE_FAILED degrades capture visibly without changing provider outcomes. Standard local file tools inspect/follow logs; no logging API or hosted exporter is added. Known profile/company/post reads, search, network steps, conversation listing, and notification listing have live API/CLI evidence. Initial feed and selected-conversation message windows have live API/CLI evidence. Initial comment windows have live API/CLI evidence. Own, one selected non-own member, and known-company posts.list initial windows passed live API/CLI checks. Coverage remains limited to the inspected layouts. company.people reads an initial recommendation window on a company People page, optionally narrowed by native keywords; live API/CLI source and cleanup proof passed for seven cards, including one missing profile reference and employment/full-workforce coverage is unproved. Other collection/history endings and attachment support remain unproved. Opening Messaging may auto-select a thread and send read/badge acknowledgements; selected-thread navigation can also acknowledge reads. Opening Notifications is not guaranteed to preserve read state or badges. Notification items can expose a derived postUrl while preserving their supplied targetUrl; listing never opens it. post.get also preserves separate loaded textBlocks while single-region rendered text and counts remain null for multiple regions. Live CLI/HTTP proof of this notification-to-post extension passed for a two-region post, with loaded blocks and header author matched to source; the existing single-region path also passed. Local message/post/invitation/comment/reply/repost drafts use the saved member binding without browser or provider work. Local CLI/HTTP proof for the four added kinds passed, including exact targets, reply revision/restart, unchanged existing-kind support, and rejection of a mismatched reply activity without a saved change; no browser connection occurred. For message/post drafts, a bounded local API/CLI journey passed for exact content/targets/media order, revisions, restart, disconnect, retrieval/listing, and deletion, with private permissions and zero browser connections. This used the saved binding, not fresh browser authentication or send/publish proof. Drafting never sends, publishes, reads selected media, or grants permission to submit. message.send submits exact text to one supplied profile through its normal native Message route, for a reply or new chat, and verifies the accepted message by identity. It requires an explicit caller send instruction; local drafts never send. Full history, attachments, group/InMail/thread-URL sending, and other writes remain unavailable. Requests with an Origin header or an unexpected Host are rejected. No browser-page clients or remote hosting are supported."
  },
  "servers": [{ "url": "http://127.0.0.1:3447" }],
  "security": [{ "apiToken": [] }],
  "paths": {
    "/health": {
      "get": {
        "operationId": "health",
        "summary": "Read service identity without browser access",
        "security": [],
        "responses": {
          "200": {
            "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "Service identity only.",
            "content": { "application/json": { "schema": {
              "type": "object",
              "required": ["service", "version"],
              "properties": { "service": { "const": "Relay" }, "version": { "type": "string" } }
            } } }
          },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/session": {
      "parameters": [{ "$ref": "#/components/parameters/LogSessionTag" }, { "$ref": "#/components/parameters/LegacyLogSessionTag" }],
      "get": {
        "operationId": "getSession",
        "summary": "Connect as needed and verify the selected member",
        "description": "The first successful member verification saves the account binding. Later calls fail on a different member. Login and challenge states return a session status; identity and source failures return an error. Owned browser launch failures return HTTP 503 BROWSER_LAUNCH_FAILED with safe recovery steps for the selected browser. Owned mode uses installed Chrome by default, or managed Chromium when serve --browser chromium is selected. New managed setups use a fresh dedicated --profile after npm ci and npx playwright install chromium --no-shell. Owned mode is visible by default. serve --headless hides its window and cannot be combined with --cdp. Headless mode still needs a browser and cannot complete human sign-in or challenges or guarantee LinkedIn acceptance. In owned mode, retain one app-created authentication tab, hidden when headless. In visible mode, complete normal sign-in or challenge handling in the Relay browser tab, then check session again. For headless auth_required or challenge, stop the service and restart with the same browser, profile, data directory, and port without --headless, then check session and complete the visible flow. After session reports ready, stop and restart with --headless. Restart cancels active reads and invalidates cursors; the token, saved account binding, local drafts, and login profile remain on disk. Disconnect alone does not change the service launch options. Repeated session checks leave it untouched while it remains on a known authentication route. After it leaves that route, normal account verification runs on a separate page; only verified success returns ready, then closes the retained tab. If verification still requires authentication, preserve the original tab regardless of its current URL. To restart an abandoned visible flow, close that tab and check the session again. Attached-browser behavior is unchanged.",
        "responses": {
          "200": {
            "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "Current session status.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Session" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "503": { "$ref": "#/components/responses/BrowserLaunchFailed" },
          "default": { "$ref": "#/components/responses/Error" }
        }
      },
      "delete": {
        "operationId": "disconnectSession",
        "summary": "Cancel active LinkedIn requests and disconnect rea-L",
        "description": "Closes active and held Relay operation pages, any retained owned authentication tab, and the connection, and invalidates all network cursors and prepared post references. An attached browser remains open. An owned browser closes while its persistent profile remains. Login state, account binding, and local drafts are retained. Local draft requests continue during disconnect and do not require a browser connection. Later LinkedIn requests can reconnect but cannot resume invalidated cursors or prepared references.",
        "responses": {
          "200": {
            "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "Disconnected session status.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Session" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/cursors/{cursor}": {
      "parameters": [{ "$ref": "#/components/parameters/LogSessionTag" }, { "$ref": "#/components/parameters/LegacyLogSessionTag" }],
      "delete": {
        "operationId": "releaseCursor",
        "summary": "Close one idle retained network or prepared Posts page",
        "description": "Local lifecycle cleanup for the latest connections.list or invitations.list cursor, or a non-null posts.list referenceCursor. A prepared Posts cursor owns temporary selections, not pagination. Release invalidates all selections from that window. Encode the unchanged token as one URL path segment; send no body. This does not navigate LinkedIn, check an account, or connect a browser. Success confirms only the target page is closed and its cursor consumed, including closure during the release. Other cursors, active pages, authentication tab, browser connection, profile, account binding, and drafts remain. Search cursors hold no page and are invalid here. A repeated release is INVALID_CURSOR. A busy claim is not cancelled. Cancellation before the idle claim stops release; after closing starts, cleanup may finish after cancellation or a lost response. Success is not logout or a collection-end observation. Local native-page cleanup and separate HTTP/CLI dispatch have been checked; no new live LinkedIn proof is claimed.",
        "parameters": [{ "name": "cursor", "in": "path", "required": true, "schema": { "$ref": "#/components/schemas/NetworkCursor" } }],
        "responses": {
          "200": {
            "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "The supplied cursor's retained page is closed. No account, source, or completeness observation is made.",
            "content": { "application/json": { "schema": {
              "type": "object",
              "additionalProperties": false,
              "required": ["cursor", "released"],
              "properties": { "cursor": { "type": "string" }, "released": { "const": true } }
            } } }
          },
          "400": {
            "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "INVALID_CURSOR: malformed token or percent encoding, unknown, consumed, already released, closed-page, or search cursor. No other page is changed.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": {
            "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "FORBIDDEN: Origin or unexpected Host header.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "409": {
            "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "CURSOR_IN_USE: continuation, selected post reading or another release holds the cursor. Its work is not cancelled.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "499": {
            "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "CANCELLED before the cursor claim. A disconnected client cannot receive this response; cancellation after the claim cannot promise rollback.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": {
            "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "CURSOR_RELEASE_FAILED: native close failed and the page remains open. Retain the existing cursor, clear this release's busy claim, and allow explicit retry. Lifecycle state removed by concurrent cleanup is not restored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "503": {
            "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "UNAVAILABLE: the session is already disconnecting or the service is closing. Release does not reconnect.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/capabilities": {
      "parameters": [{ "$ref": "#/components/parameters/LogSessionTag" }, { "$ref": "#/components/parameters/LegacyLogSessionTag" }],
      "get": {
        "operationId": "listCapabilities",
        "summary": "List implemented and planned operations",
        "description": "Implemented operations include input references and collection/query/cleanup limits from the operation contract map. Resolve inputSchemaRef against this service URL, including a custom port. Discovery needs no account binding or browser connection. Implementation status does not establish current LinkedIn access or live validation for each operation.",
        "responses": {
          "200": {
            "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "Operation capabilities.",
            "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Capability" } } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/openapi.json": {
      "parameters": [{ "$ref": "#/components/parameters/LogSessionTag" }, { "$ref": "#/components/parameters/LegacyLogSessionTag" }],
      "get": {
        "operationId": "getSchema",
        "summary": "Read the OpenAPI document without account or browser access",
        "description": "Returns this document unchanged. Requires the API bearer token and applies the same Host, Origin, JSON-error, and no-store rules as capabilities. Local schema references remain within this document; resolve public capability references against the running service URL.",
        "responses": {
          "200": {
            "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "The source OpenAPI document.", "content": { "application/json": { "schema": { "type": "object" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/operations/{operation}": {
      "parameters": [{ "$ref": "#/components/parameters/LogSessionTag" }, { "$ref": "#/components/parameters/LegacyLogSessionTag" }, { "$ref": "#/components/parameters/OperationId" }, { "$ref": "#/components/parameters/LegacyOperationId" }],
      "post": {
        "operationId": "executeOperation",
        "x-relay-operation-contracts": {
          "profile.get": { "inputSchemaRef": "#/components/schemas/LookupInput", "querySupport": "none", "collectionScope": "record", "continuation": "none", "release": null },
          "company.get": { "inputSchemaRef": "#/components/schemas/LookupInput", "querySupport": "none", "collectionScope": "record", "continuation": "none", "release": null },
          "company.people": { "inputSchemaRef": "#/components/schemas/CompanyPeopleInput", "querySupport": "keywords", "collectionScope": "initial_window", "continuation": "none", "release": null },
          "post.get": { "inputSchemaRef": "#/components/schemas/PostInput", "querySupport": "none", "collectionScope": "record", "continuation": "none", "release": null },
          "comments.list": { "inputSchemaRef": "#/components/schemas/LookupInput", "querySupport": "none", "collectionScope": "initial_window", "continuation": "none", "release": null },
          "reactions.list": { "inputSchemaRef": "#/components/schemas/LookupInput", "querySupport": "none", "collectionScope": "initial_window", "continuation": "none", "release": null },
          "conversation.get": { "inputSchemaRef": "#/components/schemas/LookupInput", "querySupport": "none", "collectionScope": "initial_window", "continuation": "none", "release": null },
          "message.send": { "inputSchemaRef": "#/components/schemas/MessageSendInput", "querySupport": "none", "collectionScope": "record", "continuation": "none", "release": null },
          "people.search": { "inputSchemaRef": "#/components/schemas/PeopleSearchInput", "querySupport": "keywords", "collectionScope": "search_page", "continuation": "search_cursor", "release": null },
          "companies.search": { "inputSchemaRef": "#/components/schemas/SearchInput", "querySupport": "keywords", "collectionScope": "search_page", "continuation": "search_cursor", "release": null },
          "connections.list": { "inputSchemaRef": "#/components/schemas/ConnectionsInput", "querySupport": "keywords", "collectionScope": "network_window", "continuation": "network_cursor", "release": { "method": "DELETE", "path": "/v1/cursors/{cursor}", "cli": "relay release <cursor>" } },
          "invitations.list": { "inputSchemaRef": "#/components/schemas/InvitationsInput", "querySupport": "none", "collectionScope": "network_window", "continuation": "network_cursor", "release": { "method": "DELETE", "path": "/v1/cursors/{cursor}", "cli": "relay release <cursor>" } },
          "conversations.list": { "inputSchemaRef": "#/components/schemas/ConversationsInput", "querySupport": "keywords", "collectionScope": "initial_window", "continuation": "none", "release": null },
          "notifications.list": { "inputSchemaRef": "#/components/schemas/NotificationsInput", "querySupport": "none", "collectionScope": "initial_window", "continuation": "none", "release": null },
          "feed.list": { "inputSchemaRef": "#/components/schemas/FeedInput", "querySupport": "none", "collectionScope": "initial_window", "continuation": "none", "release": null },
          "posts.list": { "inputSchemaRef": "#/components/schemas/PostsInput", "querySupport": "none", "collectionScope": "initial_window", "continuation": "none", "release": { "method": "DELETE", "path": "/v1/cursors/{cursor}", "cli": "relay release <cursor>" } },
          "draft.create": { "inputSchemaRef": "#/components/schemas/DraftCreateInput", "querySupport": "none", "collectionScope": "record", "continuation": "none", "release": null },
          "draft.get": { "inputSchemaRef": "#/components/schemas/DraftGetInput", "querySupport": "none", "collectionScope": "record", "continuation": "none", "release": null },
          "draft.update": { "inputSchemaRef": "#/components/schemas/DraftUpdateInput", "querySupport": "none", "collectionScope": "record", "continuation": "none", "release": null },
          "draft.delete": { "inputSchemaRef": "#/components/schemas/DraftDeleteInput", "querySupport": "none", "collectionScope": "record", "continuation": "none", "release": null },
          "drafts.list": { "inputSchemaRef": "#/components/schemas/DraftsListInput", "querySupport": "none", "collectionScope": "all_local_drafts", "continuation": "none", "release": null }
        },
        "summary": "Read LinkedIn, send a text message, or manage local drafts",
        "description": "message.send accepts exactly text and target:{profileUrl}; it follows the supplied native Message route for an existing one-to-one reply or new chat. The bound actor, source recipient, normal form and empty payload are checked before filling exact text. Blank text, outer whitespace and C0/DEL controls other than LF/tab are rejected, without trimming or a new size quota. Existing provider drafts and overlapping sends to the same native recipient are rejected. Thread-URL input, groups, InMail, attachments, forwarding and multi-send are unsupported. Native Send is clicked once; LinkedIn native retries are preserved, without a Relay resend. Acceptance requires a new request-correlated message identity and exact actor/recipient/conversation/text read-back through a source-supplied conversation URL. The accepted receipt does not mean recipient-delivered or read. After possible submission, missing confirmation, cancellation or failure returns SEND_OUTCOME_UNKNOWN; do not automatically retry. A call with a new logical operation ID is a new send. Configured billing can replay a saved local receipt for the same ID without executing again; unresolved work requires review. This is not an exactly-once provider guarantee. profile.get, company.get, comments.list, reactions.list, and conversation.get take a url. posts.list accepts a member/company profile url and optional member-only prepareReferences:true, follows its supplied owner-matching activity/Posts link, and verifies the current Posts tab plus company All filter. It returns the initial source-ordered window with collection owner separate from card authors. Own-member postUrl can be derived from the verified analytics footer when the live actor matches the owner; non-own and unsupported references remain null. Company author remains null; valid company card activity URNs can supply derived detail references. Always partial with nextCursor:null. Company All/Top end is false only from verified current owner-bound paging and complete card mapping; otherwise hasMore remains unknown. Ordinary listing has no held page or menu action. Prepared member results add local selections and an optional retained referenceCursor, without per-card actions. No scroll, filter change, full-history, empty-company or member-end claim. Missing/ambiguous links and unrecognized collections return SOURCE_ERROR. Own, one selected non-own member, and known-company initial windows passed live API/CLI checks; other layouts remain unproved. company.people accepts a company profile url and optional nonblank query, trimmed once. It follows the supplied People link with normal navigation and verifies the company/current-tab association and empty search input. Queried reads also require the exact single keywords parameter and matching selected query pill with no extra filters. It returns companyUrl, separate sectionLabel/associatedMembersText labels, source-ordered profileUrl/name/headline cards, and query when supplied. Unavailable identity stays null, including generic member cards. Populated/default windows remain partial with unknown hasMore. A verified queried no-match returns empty items, sectionLabel:null, the raw zero-associated-members label, complete status, no missing fields and false hasMore. This means no provider matches for that query, not an empty workforce or lack of employment. No cursor, profile visit, action, insight filter or carousel selection. These remain recommendations, not verified employees or a complete workforce. post.get requires either an HTTPS LinkedIn activity URL or the exact cursor/reference pair from a prepared member window. It returns separate loaded textBlocks in source order and retains rendered text only for a single region; multiple regions leave text and displayed counts null. Author identity needs the verified header before the first region, never quoted/body references or notification actors. Notification postUrl is derived only from the selected eligible highlighted-activity target and does not replace its supplied targetUrl; listing does not open it or call post.get. Callers may choose that derived reference for a later post.get. The mapping does not establish notification event/actor/read state or all highlighted-feed layouts; live chain proof passed for one two-region post, alongside a single-region regression check. conversation.get requires a supplied strict www.linkedin.com thread URL and returns one correlated, partial message window with exact loaded response text, raw numeric deliveredAt, and null attachments. It may send native read acknowledgements. people.search/companies.search take query and optional cursor. people.search also accepts connectionDegree 1, 2 or 3 (3rd+), verifies the applied scope and includes it in data only when requested. Keep the same degree, including omission, for continuation. Companies reject this field. Explicit unsupported people-search suggestions return SOURCE_UNSUPPORTED with no result rows; they are not a no-match. connections.list takes an optional nonblank query or cursor, never both. A query fills the native Connections name field and waits for its response and replacement results without scrolling. Queried data includes the trimmed query. Populated query results are partial with unknown hasMore; a verified native no-match is complete with empty items and false hasMore. This proves no provider matches for that query, not absence from the network. Query pages close with no cursor. Query-free connections listing and invitations.list (received/sent direction and optional cursor) retain owned pages through single-use network cursors that do not promise more rows. conversations.list accepts {} or an optional nonblank query, trimmed once. Normal navigation to the native Messaging search page applies recipient, message-content and conversation-name keywords across Inbox, Spam and Archive without a first-degree restriction, not just Focused. Queried data includes query and source-supplied conversation records from the exact matched native response. Positive query data keeps its initial source records. An exact directly chained native successor already captured during the initial read, its empty/null response and the current-query applied-end announcement can prove false hasMore; status is complete only when no item fields are missing, otherwise partial. No captured successor returns partial/unknown promptly; no future request is predicted or awaited. Missing optional end proof also preserves partial/unknown. No continuation action or provider request is issued by Relay for that proof. Empty complete/false hasMore requires an error-free matched empty response with explicit null successor and the exact current-query native no-match UI. Invalid initial-response or query-state evidence is an error. No cursor, inbox scan, per-result opening, exact-match or full-history claim. Query pages close after collection. Query-free conversations.list, notifications.list and feed.list require {} and return partial initial windows without cursors. Feed entries retain unknown kinds, separate loaded textBlocks, nullable header-associated references, and per-card More flags without a complete-content claim. Feed and selected-conversation reads have live API/CLI evidence. comments.list takes a post url and returns initially loaded comments with post-bound URNs, header authors, loaded text, and nullable display metadata. Comment permalinks, reply parents, and continuation are unproved; live comment identity, author, text, date, and cleanup checks passed. reactions.list opens the verified post count reader and returns the initial All-reactions rows with explicit source labels; no reaction action, filter change, full coverage, or continuation is claimed. Live reaction-reader source, state, and cleanup checks passed. Messaging may auto-select a thread and acknowledge reads. Notification text may exceed its CSS-visible portion; no notification targets/actions are opened and native read effects remain unproved. No collection quota or network cursor expiry is imposed. Prepared member posts.list optionally retains eligible unresolved cards without opening menus; post.get accepts their exact cursor/reference pair, resolves only that selected native Embed route, and returns the ordinary canonical source-post result. The temporary reference is not a provider ID or repost-event identity. DELETE /v1/cursors/{cursor} releases one idle held page; disconnect releases all held pages. Cancellation, account mismatch, browser closure, and shutdown invalidate affected network cursors and prepared post references. Closing the client connection aborts further work; a disconnected client cannot receive a final cancellation response. Local draft.create/get/update/delete and drafts.list use saved member binding without browser access and return LocalOperationResult. Create accepts kind/content for message, post, invitation, comment, reply, or repost; exact target fields depend on kind. Reply comment/post association is checked structurally without proving existence or access. Local CLI/HTTP proof for the four added kinds passed, including exact targets, reply revision/restart, unchanged existing-kind support, and rejection of a mismatched reply activity without a saved change; no browser connection occurred. Get accepts id; update accepts id/expectedRevision/content; delete accepts id/expectedRevision; list accepts {}. Drafts survive restart/disconnect until explicitly deleted. Browser disconnect cancels only LinkedIn requests. Cancellation before local commit stops a save/delete; cancellation or a lost response after commit cannot promise rollback. Use get/list to establish saved state and do not assume a failed response left storage unchanged.",
        "parameters": [{
          "name": "operation",
          "in": "path",
          "required": true,
          "schema": { "type": "string", "enum": ["profile.get", "company.get", "company.people", "post.get", "comments.list", "reactions.list", "conversation.get", "message.send", "people.search", "companies.search", "connections.list", "invitations.list", "conversations.list", "notifications.list", "feed.list", "posts.list", "draft.create", "draft.get", "draft.update", "draft.delete", "drafts.list"] }
        }],
        "requestBody": {
          "required": true,
          "content": { "application/json": {
            "schema": { "anyOf": [{ "$ref": "#/components/schemas/MessageSendInput" }, { "$ref": "#/components/schemas/LookupInput" }, { "$ref": "#/components/schemas/PostInput" }, { "$ref": "#/components/schemas/PostsInput" }, { "$ref": "#/components/schemas/CompanyPeopleInput" }, { "$ref": "#/components/schemas/SearchInput" }, { "$ref": "#/components/schemas/PeopleSearchInput" }, { "$ref": "#/components/schemas/ConnectionsInput" }, { "$ref": "#/components/schemas/InvitationsInput" }, { "$ref": "#/components/schemas/ConversationsInput" }, { "$ref": "#/components/schemas/NotificationsInput" }, { "$ref": "#/components/schemas/FeedInput" }, { "$ref": "#/components/schemas/DraftCreateInput" }, { "$ref": "#/components/schemas/DraftGetInput" }, { "$ref": "#/components/schemas/DraftUpdateInput" }, { "$ref": "#/components/schemas/DraftDeleteInput" }, { "$ref": "#/components/schemas/DraftsListInput" }], "description": "Select the inputSchemaRef from this operation object's x-relay-operation-contracts entry (legacy x-rea-l-operation-contracts is the same map). This anyOf is a transport union; it cannot correlate the operation path parameter with its body. Runtime validation remains authoritative for semantic URL and account rules. conversations.list accepts {} or an optional nonblank query, with no cursor. notifications.list, feed.list and drafts.list accept only an empty object. conversation.get accepts only url. posts.list accepts a member/company profile url and optional boolean prepareReferences; true is member-only. post.get accepts exactly url or a prepared cursor/reference pair. No Posts pagination or filter arguments are accepted. company.people accepts a company profile url and optional nonblank query, with no cursor, other filter, carousel or action arguments. Draft create/update accept full operation objects; update replaces all content for the existing kind. Draft inputs reject unknown fields. message.send accepts exactly text and target:{profileUrl}; use MessageSendInput rather than a full local draft object." },
            "examples": {
              "profile": { "summary": "profile.get", "value": { "url": "https://www.linkedin.com/in/example-member/" } },
              "post": { "summary": "post.get", "value": { "url": "https://www.linkedin.com/feed/update/urn:li:activity:7515399685923196928/" } },
              "preparedMemberPosts": { "summary": "posts.list prepare unresolved member selections", "value": { "url": "https://www.linkedin.com/in/example-member/", "prepareReferences": true } },
              "selectedPreparedPost": { "summary": "post.get selected prepared card", "value": { "cursor": "<referenceCursor>", "reference": "<selected item reference>" } },
              "memberPosts": { "summary": "posts.list initial member Posts window", "value": { "url": "https://www.linkedin.com/in/example-member/" } },
              "companyPosts": { "summary": "posts.list initial company Posts window", "value": { "url": "https://www.linkedin.com/company/example-company/" } },
              "companyPeople": { "summary": "company.people initial recommendation cards", "value": { "url": "https://www.linkedin.com/company/example-company/" } },
              "companyPeopleQuery": { "summary": "company.people native keyword query", "value": { "url": "https://www.linkedin.com/company/example-company/", "query": "engineer" } },
              "people": { "summary": "people.search first page", "value": { "query": "software engineer Berlin" } },
              "peopleConnections": { "summary": "people.search first-degree connections", "value": { "query": "software engineer", "connectionDegree": 1 } },
              "companies": { "summary": "companies.search first page", "value": { "query": "renewable energy" } },
              "connections": { "summary": "connections.list initial step", "value": {} },
              "connectionQuery": { "summary": "connections.list native name query (no cursor)", "value": { "query": "Alex Example" } },
              "receivedInvitations": { "summary": "invitations.list received initial step", "value": { "direction": "received" } },
              "sentInvitations": { "summary": "invitations.list sent initial step", "value": { "direction": "sent" } },
              "conversations": { "summary": "conversations.list initial window", "value": {} },
              "conversationSearch": { "summary": "conversations.list native keywords", "value": { "query": "example name" } },
              "notifications": { "summary": "notifications.list initial window", "value": {} },
              "feed": { "summary": "feed.list initial window", "value": {} },
              "messageDraft": { "summary": "draft.create message", "value": { "kind": "message", "content": { "text": "Hello.\n", "target": { "profileUrl": "https://www.linkedin.com/in/example-member/" } } } },
              "postDraft": { "summary": "draft.create post with no selected media", "value": { "kind": "post", "content": { "text": "Post text", "mediaPaths": [] } } },
              "draftGet": { "summary": "draft.get; replace the example UUID with a saved ID", "value": { "id": "b4eec82b-909a-416b-8d7b-75f66b86e7ba" } },
              "draftUpdate": { "summary": "draft.update; replace the ID and expected revision with saved values", "value": { "id": "b4eec82b-909a-416b-8d7b-75f66b86e7ba", "expectedRevision": 1, "content": { "text": "Revised post", "mediaPaths": [] } } },
              "draftDelete": { "summary": "draft.delete; replace the ID and expected revision with saved values", "value": { "id": "b4eec82b-909a-416b-8d7b-75f66b86e7ba", "expectedRevision": 2 } },
              "drafts": { "summary": "drafts.list local summaries", "value": {} }
            }
          } }
        },
        "responses": {
          "402": { "description": "INSUFFICIENT_CREDITS before provider execution in a configured installation. submission is none.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "FORBIDDEN, PAIRING_REQUIRED, DEVICE_REVOKED, KEY_REVOKED or ACCOUNT_UNAVAILABLE. Paid admission was denied before provider execution. PAIRING_REQUIRED means a new installation is not paired, or was unpaired.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "200": {
            "headers": { "X-Relay-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Rea-L-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Relay-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Rea-L-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "LinkedIn reads use OperationResult; local drafts use LocalOperationResult without source/completeness. Draft create/get/update return a full draft; delete returns {id,deleted:true}; drafts.list returns a summary array without text/media paths. Populated network, query-free inbox, selected-conversation, notification, comment, reaction, feed, member/company Posts, and company People windows retain partial status for unproved collection or field/content coverage. An initial company Posts All/Top window can report false hasMore after owner-bound native paging, an unexpired token and complete source/card identity mapping prove the current feed end; its field gaps remain partial. This is not complete company history. A positive conversation query may report false hasMore after an exact native successor already captured during the initial read, its empty/null response and applied-end proof; status is complete only with no missing item fields, otherwise partial. Verified native connection-query, conversation-query and company-People-query no-matches are complete with empty items; this proves no provider matches for that query and scope, not absence of network membership, conversation history or employment. A network nextCursor permits another scroll without promising more rows. Other initial windows retain unknown hasMore unless the company Posts proof above establishes false; all retain nextCursor:null. Post textBlocks and feed/notification text are loaded DOM text and may exceed visible content; post.get keeps single-region rendered text separate and leaves text/counts null for multiple regions; conversation.get preserves loaded response text without a rendered-rich-text claim. Partial collection is a successful response, not an HTTP error. message.send uses OperationResult with MessageSendData: an accepted message verified by identity and exact actor/recipient/text, not delivery/read status. Only a missing deliveredAt makes that receipt partial; hasMore is false and nextCursor is null.",
            "content": { "application/json": { "schema": { "oneOf": [{ "$ref": "#/components/schemas/OperationResult" }, { "$ref": "#/components/schemas/LocalOperationResult" }] } } }
          },
          "400": {
            "headers": { "X-Relay-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Rea-L-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Relay-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Rea-L-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "INVALID_ARGUMENT for malformed input, unknown arguments, invalid URL/query/direction, malformed draft UUIDs, invalid kind-specific targets, structurally mismatched reply comment/post pairs, non-absolute media references, or revisions that are not positive safe integers. Invalid draft updates leave saved content, revision, and timestamps unchanged. INVALID_CURSOR for malformed/mismatched search state or an unknown, consumed, wrong-kind, mismatched or closed retained cursor. INVALID_REFERENCE for a selection absent from an otherwise valid prepared Posts window; that window remains intact. Company preparation, mixed post lookup modes and incomplete cursor/reference pairs are INVALID_ARGUMENT before browser work. message.send also rejects unsupported keys, target types, blank/outer-whitespace text and unsupported controls before browser work.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": {
            "headers": { "X-Relay-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Rea-L-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Relay-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Rea-L-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "NOT_IMPLEMENTED or NOT_FOUND, including a missing saved draft.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "409": {
            "headers": { "X-Relay-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Rea-L-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Relay-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Rea-L-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "Login, challenge, member mismatch, or identity/session ambiguity; CURSOR_IN_USE for a simultaneous network or prepared-Posts selection/release claim. Local drafts: ACCOUNT_BINDING_REQUIRED directs the caller to session once; ACCOUNT_MISMATCH rejects a draft belonging to another saved member; DRAFT_CONFLICT rejects a stale revision without changing the draft, or a revision that cannot be incremented safely. Message sending: NATIVE_DRAFT_PRESENT preserves an existing provider draft; MESSAGE_IN_PROGRESS rejects a concurrent active send to the same native recipient.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "415": {
            "headers": { "X-Relay-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Rea-L-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Relay-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Rea-L-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "The request must use application/json.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "422": {
            "headers": { "X-Relay-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Rea-L-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Relay-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Rea-L-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "SEND_UNSUPPORTED: the native target or composer is not a supported one-recipient plain-text form. No native Send click has occurred.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "499": {
            "headers": { "X-Relay-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Rea-L-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Relay-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Rea-L-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "CANCELLED, if the connection remains available to receive the response. Cancellation or a lost response after a local draft save/delete commits cannot promise rollback. Get/list saved drafts to establish state; mutations are not retried automatically. For message.send this means cancellation before possible submission. After native Send may have run, use SEND_OUTCOME_UNKNOWN instead; it cannot promise rollback. If entered text cannot be cleared or checked, details.nativeDraftMayRemain is true; this warning is preserved in the server result/log even when the client disconnected.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "500": {
            "headers": { "X-Relay-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Rea-L-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Relay-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Rea-L-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "Local drafts: CONFIG_ERROR for an invalid/unreadable saved member binding; DRAFT_STORAGE_ERROR for corrupt, unreadable, or unsafe draft storage. These errors do not return raw filesystem details or an empty listing.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "502": {
            "headers": { "X-Relay-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Rea-L-Operation-Id": { "$ref": "#/components/headers/OperationId" }, "X-Relay-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Rea-L-Billing-State": { "$ref": "#/components/headers/BillingState" }, "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
            "description": "SOURCE_UNSUPPORTED when LinkedIn explicitly cannot support a people query or a selected prepared post lacks the native Embed route. Suggested unrelated rows are not returned. SOURCE_ERROR or SOURCE_CHANGED. Malformed/failed conversation responses, unverified requested-thread correlation, and unverified empty inbox/message/notification/comment/reaction/feed/Posts/People windows are source errors, not empty collections. Prepared post reading returns SOURCE_CHANGED for stale captured state before menu action, or SOURCE_ERROR for ambiguous Embed metadata or failed dialog cleanup; the affected window closes. posts.list also returns SOURCE_ERROR when the supplied owner-matching navigation link, current Posts/All state, or collection boundaries cannot be verified. company.people returns SOURCE_ERROR for unverified owner/tab/query association, extra filters, recommendation boundaries or an unproved empty state; this does not prove an empty workforce. CONTINUATION_UNCONFIRMED means a network step did not prove an append or terminal state; its held page is closed and cursor chain invalidated. This is not proof of an empty or complete collection. Message sending: SEND_OUTCOME_UNKNOWN has details.submission=unknown after possible submission, including cancellation or missing read-back; known message/conversation references may be included. Inspect state before retrying. The CLI uses the same code when its message-send HTTP exchange is lost.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "503": { "$ref": "#/components/responses/BrowserLaunchFailed" },
          "default": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "OperationId": {
        "name": "X-Relay-Operation-Id", "in": "header", "required": false,
        "schema": { "type": "string", "format": "uuid", "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" },
        "description": "Logical ID for a LinkedIn operation; the CLI uses RELAY_OPERATION_ID or generates one. At most one lowercase UUID is accepted; the legacy X-Rea-L-Operation-Id may carry the same value, and a different or repeated value is INVALID_ARGUMENT before provider work. Reuse it only for the exact same input on the same paid device. Configured billing stores a local result for safe replay; unresolved work returns OPERATION_RECOVERY_REQUIRED and must not be repeated under a new ID. Legacy unconfigured installations have no billing replay protection. Local drafts ignore this header. This is separate from the per-HTTP-request ID and log tag."
      },
      "LogSessionTag": {
        "name": "X-Relay-Log-Session", "in": "header", "required": false,
        "schema": { "type": "string", "format": "uuid", "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" },
        "description": "Optional agent-task log tag, normalized to lowercase. The CLI uses RELAY_LOG_SESSION. Authenticated callers must supply at most one valid UUID, optionally repeated with the same value in the legacy X-Rea-L-Log-Session; invalid/empty/duplicate values are INVALID_ARGUMENT before body/provider work, including when capture is disabled. The tag does not enable logging or grant access. Untagged enabled calls use the service run UUID. Public health and rejected Host/Origin/authentication requests ignore untrusted tags and never choose their files."
      },
      "LegacyOperationId": {
        "name": "X-Rea-L-Operation-Id", "in": "header", "required": false,
        "schema": { "type": "string", "format": "uuid", "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" },
        "description": "Legacy spelling of X-Relay-Operation-Id for older clients. When both are sent, they must have the same value."
      },
      "LegacyLogSessionTag": {
        "name": "X-Rea-L-Log-Session", "in": "header", "required": false,
        "schema": { "type": "string", "format": "uuid", "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" },
        "description": "Legacy spelling of X-Relay-Log-Session for older clients. When both are sent, they must have the same value."
      }
    },
    "headers": {
      "OperationId": { "schema": { "type": "string", "format": "uuid" }, "description": "Logical operation ID after a valid LinkedIn operation body/header is accepted. Present on applicable errors as well as success. Not proof of provider submission or billing completion." },
      "BillingState": { "schema": { "type": "string" }, "description": "Optional local accounting snapshot. reserved or unknown holds credits; charged or released is terminal. A local phase can appear before remote reconciliation. Inspect relay billing status/sync for current accounting; never repeat provider work to settle it." },
      "RequestId": { "schema": { "type": "string", "format": "uuid" }, "description": "UUID for this HTTP request, including errors, health and disabled logging. Present when response headers can be written; not proof of delivery." },
      "LogSession": { "schema": { "type": "string", "format": "uuid" }, "description": "Effective trusted/default task UUID when the service started with --log. Omitted when logging is disabled. This is not a provider session or authorization credential." },
      "LogStatus": { "schema": { "type": "string", "enum": ["disabled", "active", "degraded"] }, "description": "Capture state at header commit. Active means accepting records, not durable bytes or peer receipt. A later failure cannot change an earlier header; it is reported to service stderr and subsequent headers as degraded without changing operation outcomes." }
    },
    "securitySchemes": {
      "apiToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "opaque",
        "description": "Read the private installation token in the client process. Send it in Authorization only; never in URLs, command arguments, logs, or source. Local token validation is not commercial entitlement enforcement."
      }
    },
    "responses": {
      "Unauthorized": {
        "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
        "description": "UNAUTHORIZED: missing or invalid local bearer token. No browser work has started.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "BrowserLaunchFailed": {
        "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
        "description": "Read error.code to distinguish HTTP 503 causes. PRICING_NOT_CONFIGURED and ACCESS_UNAVAILABLE deny paid admission before provider work; use billing sync for unresolved accounting only. UNAVAILABLE means the service is shutting down or the account session is disconnecting; browser installation does not resolve those states. BROWSER_LAUNCH_FAILED means the owned browser could not start. The message gives fixed recovery steps for managed Chromium, installed Chrome, or the selected executable. Managed Chromium may need relay browser install from the installed CLI; Chrome may need installation or explicit managed Chromium with a fresh dedicated --profile; a custom executable needs --executable checked. A busy profile requires closing only its browser before retrying. No raw launch output, paths, process arguments, or environment are returned. Navigation, LinkedIn authentication, cancellation, and attached CDP errors retain their existing classification. Local draft operations do not launch a browser.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Error": {
        "headers": { "X-Relay-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Rea-L-Request-Id": { "$ref": "#/components/headers/RequestId" }, "X-Relay-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Rea-L-Log-Session": { "$ref": "#/components/headers/LogSession" }, "X-Relay-Log-Status": { "$ref": "#/components/headers/LogStatus" }, "X-Rea-L-Log-Status": { "$ref": "#/components/headers/LogStatus" } },
        "description": "Error envelope. Codes distinguish local authentication, LinkedIn access, invalid input, source failure, cancellation, and service availability. No credentials or raw provider dumps are returned.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "MessageSendInput": {
        "type": "object", "required": ["text", "target"], "additionalProperties": false,
        "description": "Explicit text send to one profile. Local draft creation is optional. Native field validation remains active; no rea-L text-size quota is added.",
        "properties": {
          "text": { "type": "string", "minLength": 1, "pattern": "^(?!\\s)(?![\\s\\S]*\\s$)[^\\u0000-\\u0008\\u000b-\\u001f\\u007f]+$", "description": "Unchanged final text. Reject blank/outer whitespace and C0/DEL controls other than internal LF/tab. Emoji and internal line breaks are preserved." },
          "target": { "type": "object", "required": ["profileUrl"], "additionalProperties": false, "properties": {
            "profileUrl": { "type": "string", "minLength": 1, "description": "LinkedIn member profile URL, validated/canonicalized with the existing profile URL rules. Conversation URLs are not accepted for sending." }
          } }
        }
      },
      "MessageSendData": {
        "type": "object", "required": ["status", "recipient", "conversationUrl", "conversationUrn", "messageUrn", "senderUrn", "actorUrn", "text", "deliveredAt"], "additionalProperties": false,
        "description": "New message verified by native request/response identity and source read-back. Accepted does not mean recipient-delivered/read. Repeating the call is a new send; do not retry an unknown outcome automatically.",
        "properties": {
          "status": { "const": "accepted" },
          "recipient": { "type": "object", "required": ["profileUrl", "profileUrn", "participantUrn"], "additionalProperties": false, "properties": {
            "profileUrl": { "type": "string", "format": "uri" },
            "profileUrn": { "type": "string", "minLength": 1, "description": "Exact native recipient host identity, without namespace conversion." },
            "participantUrn": { "type": "string", "minLength": 1, "description": "Recipient participant identity from the verified conversation." }
          } },
          "conversationUrl": { "type": "string", "format": "uri", "description": "Source-supplied conversation URL used for message read-back." },
          "conversationUrn": { "type": "string", "minLength": 1 },
          "messageUrn": { "type": "string", "minLength": 1, "description": "New response-correlated message identity verified in the conversation source." },
          "senderUrn": { "type": "string", "minLength": 1 },
          "actorUrn": { "type": "string", "minLength": 1 },
          "text": { "type": "string", "minLength": 1, "description": "Exact input text, verified against the accepted source message." },
          "deliveredAt": { "type": ["number", "null"], "description": "Raw finite provider timestamp or null with a missing field. No timestamp-unit, recipient-delivery or read guarantee." }
        }
      },
      "LookupInput": {
        "type": "object",
        "required": ["url"],
        "additionalProperties": false,
        "properties": {
          "url": { "type": "string", "description": "profile.get: LinkedIn /in/<member>/; company.get: /company/<company>/; post.get/comments.list/reactions.list: absolute HTTPS LinkedIn /feed/update/urn:li:activity:<digits>/. Profile/company operations also accept matching relative paths. conversation.get requires a supplied HTTPS www.linkedin.com/messaging/thread/<id>/ URL with no userinfo, non-default port, query, fragment, or extra path segments; its identifier and destination must not be reconstructed from a URN." }
        }
      },
      "PostInput": {
        "oneOf": [
          { "type": "object", "required": ["url"], "additionalProperties": false,
            "properties": { "url": { "type": "string", "description": "Absolute HTTPS LinkedIn /feed/update/urn:li:activity:<digits>/ URL, using the existing canonical activity validator." } } },
          { "type": "object", "required": ["cursor", "reference"], "additionalProperties": false,
            "properties": {
              "cursor": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "Exact referenceCursor from a prepared member Posts window. This opaque local token is not pagination or a provider ID." },
              "reference": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "Exact non-null reference from one item in that cursor's window. Native selection resolves the source post opened by Embed, not the member's repost event. Tokens are not trimmed or decoded." }
            } }
        ],
        "description": "Exactly one lookup mode: url, or cursor plus reference. Unknown fields, mixed modes and incomplete pairs fail before browser work. Selection rechecks the live account and captured native card, opens only its Embed control, requires agreeing canonical/Open Graph metadata from that iframe, closes the dialog and uses the ordinary detail reader. Release the prepared cursor when finished; valid references can be reused and have no time expiry. Use the returned canonical postUrl for other post operations."
      },
      "PostsInput": {
        "type": "object",
        "required": ["url"],
        "additionalProperties": false,
        "properties": {
          "url": { "type": "string", "description": "Member /in/<member>/ or company /company/<company>/ profile URL using the existing LinkedIn URL rules, including matching relative paths. Normalized to the collection owner's canonical profile URL. Use the selected account's profile URL for own posts. An unambiguous supplied activity/Posts link must match this owner; no collection route is used as a fallback." },
          "prepareReferences": { "type": "boolean", "description": "Prepare temporary local references for eligible unresolved member cards without opening menus. False/omitted keeps the existing shape and closes the page." }
        },
        "description": "posts.list accepts url and optional boolean prepareReferences. Only true prepares a member window; a company owner with true is INVALID_ARGUMENT before browser work. Omitted/false keeps ordinary results. No cursor, filter, scroll or expansion arguments."
      },
      "CompanyPeopleInput": {
        "type": "object",
        "required": ["url"],
        "additionalProperties": false,
        "properties": {
          "url": { "type": "string", "description": "Company /company/<company>/ profile URL using the existing company URL validator. Normalized to the canonical company URL. The read follows only a supplied People link whose owner matches this company; no missing route is constructed." },
          "query": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "Optional native People keywords, trimmed once. Normal navigation applies the query to the verified supplied People link. Populated results remain a partial recommendation window; no employment or full workforce claim." }
        },
        "description": "company.people accepts url and optional nonblank query. No cursor, other filter, profile-opening, carousel, or action arguments."
      },
      "CommentsData": {
        "type": "object",
        "required": [
          "postUrl",
          "items"
        ],
        "additionalProperties": false,
        "properties": {
          "postUrl": {
            "type": "string",
            "format": "uri"
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/CommentItem"
            }
          }
        }
      },
      "CommentItem": {
        "type": "object",
        "required": [
          "commentUrn",
          "author",
          "text",
          "dateText",
          "moreAvailable",
          "clippedOnScreen",
          "commentUrl",
          "parentCommentUrn"
        ],
        "additionalProperties": false,
        "properties": {
          "commentUrn": {
            "type": "string",
            "description": "Source comment URN, unique in this window and bound to the requested activity."
          },
          "author": {
            "anyOf": [
              {
                "type": "object",
                "required": [
                  "name",
                  "profileUrl"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "profileUrl": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Owned menu name and one immediate-header member URL; body mentions cannot supply the author."
          },
          "text": {
            "type": "string",
            "description": "Exact loaded DOM text, including line breaks; no complete-content claim."
          },
          "dateText": {
            "type": [
              "string",
              "null"
            ],
            "description": "Observed relative-date text in the immediate header, without date conversion."
          },
          "moreAvailable": {
            "enum": [
              true,
              null
            ],
            "description": "True for a visible owned text expansion control; otherwise unproved."
          },
          "clippedOnScreen": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Measured text-region overflow, or null when not measurable."
          },
          "commentUrl": {
            "type": "null"
          },
          "parentCommentUrn": {
            "type": "null"
          }
        }
      },
      "ReactionsData": {
        "type": "object",
        "required": [
          "postUrl",
          "selectedFilter",
          "selectedFilterLabel",
          "items"
        ],
        "additionalProperties": false,
        "properties": {
          "postUrl": {
            "type": "string",
            "format": "uri"
          },
          "selectedFilter": {
            "const": "All"
          },
          "selectedFilterLabel": {
            "type": "string",
            "description": "Displayed selected All button label; its total is not collection coverage."
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "object",
              "required": [
                "actor",
                "reactionLabel"
              ],
              "additionalProperties": false,
              "properties": {
                "actor": {
                  "description": "Same-row label name and unambiguous member-profile URL. Unavailable fields remain null.",
                  "type": "object",
                  "required": [
                    "name",
                    "url"
                  ],
                  "properties": {
                    "name": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "url": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri"
                    }
                  }
                },
                "reactionLabel": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Explicit same-row source reaction label; no inferred reaction enum."
                }
              }
            }
          }
        }
      },
      "SearchInput": {
        "type": "object",
        "required": ["query"],
        "additionalProperties": false,
        "properties": {
          "query": { "type": "string", "minLength": 1, "description": "Nonblank search text. Use the same query when requesting continuation." },
          "cursor": { "type": "string", "minLength": 1, "description": "Opaque completeness.nextCursor from this operation's previous response. Bound to the query and selected account. Omit on the first call; do not construct or edit it. It is not an authentication credential." }
        }
      },
      "PeopleSearchInput": {
        "type": "object",
        "required": ["query"],
        "additionalProperties": false,
        "properties": {
          "query": { "type": "string", "minLength": 1, "description": "Nonblank search text, trimmed once. Keep the same query for continuation." },
          "connectionDegree": { "type": "integer", "enum": [1, 2, 3], "description": "Optional native connection scope: 1st, 2nd, or 3rd+ for value 3. Omit for global people search. A no-match is limited to the provider query and scope, not proof of network absence." },
          "cursor": { "type": "string", "minLength": 1, "description": "Opaque cursor from this search. Bound to operation, query, selected account and connectionDegree including its omission. Keep the same query and degree; do not construct or edit the cursor." }
        },
        "description": "People-only connection degree; no company/location filters. The exact native unsupported-search notice returns SOURCE_UNSUPPORTED/502 with no rows, for filtered or global people queries. Company search uses SearchInput and rejects connectionDegree."
      },
      "ConnectionsInput": {
        "type": "object",
        "additionalProperties": false,
        "description": "Use query for native Connections name search without scanning. Query is trimmed once and cannot contain embedded CR/LF or be combined with cursor. Outer whitespace, including line breaks, is trimmed. Query results do not return a cursor; continuation and release apply only to query-free listing.",
        "properties": {
          "query": { "type": "string", "minLength": 1, "pattern": "^\\s*[^\\r\\n]*\\S[^\\r\\n]*\\s*$", "description": "Nonblank native name query. Provider matching rules are not exact-name guarantees." },
          "cursor": { "$ref": "#/components/schemas/NetworkCursor" }
        },
        "not": { "required": ["query", "cursor"] }
      },
      "InvitationsInput": {
        "type": "object",
        "required": ["direction"],
        "additionalProperties": false,
        "properties": {
          "direction": { "type": "string", "enum": ["received", "sent"] },
          "cursor": { "$ref": "#/components/schemas/NetworkCursor" }
        }
      },
      "ConversationsInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "query": { "type": "string", "minLength": 1, "pattern": "^\\s*[^\\r\\n]*\\S[^\\r\\n]*\\s*$", "description": "Nonblank native recipient, message-content or conversation-name keywords, trimmed once. Embedded CR/LF is rejected after trimming; outer whitespace, including line breaks, is trimmed. Searches Inbox, Spam and Archive without a first-degree restriction, not just the starting Focused view. Provider matching is not an exact-name guarantee." }
        },
        "description": "Optional query, or {} for the initial inbox window. No cursor, history, attachment, or send arguments. Query results have no continuation. Opening or searching Messaging may auto-select a thread and acknowledge reads."
      },
      "NotificationsInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {},
        "description": "Empty object only. Initial notification listing has no cursor, target-opening, read-state, or dismiss arguments."
      },
      "FeedInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {},
        "description": "Empty object only. Initial feed reading has no cursor, scrolling, menu, or action arguments."
      },
      "DraftId": {
        "type": "string",
        "format": "uuid",
        "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$",
        "description": "Locally generated UUID. IDs are immutable and stored in lowercase. Use the ID returned by create."
      },
      "DraftRevision": {
        "type": "integer",
        "minimum": 1,
        "maximum": 9007199254740991,
        "description": "Positive safe integer. A new draft starts at 1; a successful update increments it. expectedRevision must match the saved revision."
      },
      "DraftTarget": {
        "oneOf": [
          {
            "type": "object", "required": ["profileUrl"], "additionalProperties": false,
            "properties": { "profileUrl": { "type": "string", "description": "Same validator as profile.get, including matching relative paths; saved as a canonical HTTPS LinkedIn profile URL. No navigation or messaging eligibility check occurs." } }
          },
          {
            "type": "object", "required": ["conversationUrl"], "additionalProperties": false,
            "properties": { "conversationUrl": { "type": "string", "format": "uri", "description": "Supplied HTTPS www.linkedin.com/messaging/thread/<id>/ URL, with no query, fragment, userinfo, non-default port, or extra path segments. Same validator as conversation.get. Drafting does not check participants or access." } }
          }
        ],
        "description": "Exactly one explicit target; no name lookup or inferred destination."
      },
      "MessageDraftContent": {
        "type": "object", "required": ["text", "target"], "additionalProperties": false,
        "properties": {
          "text": { "type": "string", "description": "Exact draft text, including whitespace, line breaks, and an empty string. No provider length limit is inferred." },
          "target": { "$ref": "#/components/schemas/DraftTarget" }
        }
      },
      "PostDraftContent": {
        "type": "object", "required": ["text", "mediaPaths"], "additionalProperties": false,
        "properties": {
          "text": { "type": "string", "description": "Exact draft text, including whitespace, line breaks, and an empty string. No provider length limit is inferred." },
          "mediaPaths": { "type": "array", "items": { "type": "string", "description": "Absolute local filesystem path on the service host, without a null character." }, "description": "Ordered references, including an empty array. No file read, existence/format check, copy, upload, or media quota. References and order are preserved; LinkedIn support is unverified." }
        }
      },
      "InvitationDraftTarget": {
        "type": "object", "required": ["profileUrl"], "additionalProperties": false,
        "properties": {
          "profileUrl": { "type": "string", "description": "Same profile validator as profile.get; stored as the normalized canonical member URL. No browser navigation, invitation eligibility, account relationship, or access check." }
        }
      },
      "PostDraftTarget": {
        "type": "object", "required": ["postUrl"], "additionalProperties": false,
        "properties": {
          "postUrl": { "type": "string", "description": "Same canonical activity URL validator as post.get. Requires an absolute HTTPS LinkedIn /feed/update/urn:li:activity:<digits>/ URL and stores its normalized canonical destination. Validation does not open the post or prove existence, access, authorship, or permission to act." }
        }
      },
      "ReplyDraftTarget": {
        "type": "object", "required": ["postUrl", "commentUrn"], "additionalProperties": false,
        "properties": {
          "postUrl": { "$ref": "#/components/schemas/PostDraftTarget/properties/postUrl" },
          "commentUrn": { "type": "string", "pattern": "^urn:li:comment:\\(activity:[0-9]+,[0-9]+\\)$", "not": { "pattern": "\\s" }, "description": "Exact T9 form urn:li:comment:(activity:<postId>,<commentId>), with decimal digits and no extra prefix, suffix, or whitespace. Preserve this supplied value. Its embedded activity ID must equal the normalized postUrl activity ID as strings, without numeric conversion; the service enforces this cross-field check. This proves only structural association, not comment existence, access, author, or parent/reply relationships. It does not populate parentCommentUrn or create a permalink." }
        }
      },
      "InvitationDraftContent": {
        "type": "object", "required": ["text", "target"], "additionalProperties": false,
        "properties": {
          "text": { "type": "string", "description": "Exact invitation-note text, including whitespace, line breaks, and an empty string. No guessed provider length limit." },
          "target": { "$ref": "#/components/schemas/InvitationDraftTarget" }
        },
        "description": "Local invitation preparation only. No media, conversation target, action flag, browser work, or submission permission."
      },
      "DiscussionDraftContent": {
        "type": "object", "required": ["text", "target"], "additionalProperties": false,
        "properties": {
          "text": { "type": "string", "description": "Exact comment or repost-note text, including whitespace, line breaks, and an empty string. No guessed provider length limit." },
          "target": { "$ref": "#/components/schemas/PostDraftTarget" }
        },
        "description": "Local comment/repost preparation only. No media, conversation target, action flag, browser work, or submission permission."
      },
      "ReplyDraftContent": {
        "type": "object", "required": ["text", "target"], "additionalProperties": false,
        "properties": {
          "text": { "type": "string", "description": "Exact reply text, including whitespace, line breaks, and an empty string. No guessed provider length limit." },
          "target": { "$ref": "#/components/schemas/ReplyDraftTarget" }
        },
        "description": "Local reply preparation only, with a structurally matching post/comment pair. No media, conversation target, action flag, browser work, or submission permission."
      },
      "DraftCreateInput": {
        "type": "object", "required": ["kind", "content"], "additionalProperties": false,
        "properties": { "kind": { "enum": ["message", "post", "invitation", "comment", "reply", "repost"] }, "content": {} },
        "oneOf": [
          { "properties": { "kind": { "const": "message" }, "content": { "$ref": "#/components/schemas/MessageDraftContent" } } },
          { "properties": { "kind": { "const": "post" }, "content": { "$ref": "#/components/schemas/PostDraftContent" } } },
          { "properties": { "kind": { "const": "invitation" }, "content": { "$ref": "#/components/schemas/InvitationDraftContent" } } },
          { "properties": { "kind": { "const": "comment" }, "content": { "$ref": "#/components/schemas/DiscussionDraftContent" } } },
          { "properties": { "kind": { "const": "reply" }, "content": { "$ref": "#/components/schemas/ReplyDraftContent" } } },
          { "properties": { "kind": { "const": "repost" }, "content": { "$ref": "#/components/schemas/DiscussionDraftContent" } } }
        ],
        "description": "draft.create supports message, post, invitation, comment, reply, and repost preparation; returns a full draft at revision 1. Require exactly the content and target fields for the chosen kind. It never sends, publishes, opens a composer, or grants submission permission. Existing message/post records need no migration."
      },
      "DraftGetInput": {
        "type": "object", "required": ["id"], "additionalProperties": false,
        "properties": { "id": { "$ref": "#/components/schemas/DraftId" } }
      },
      "DraftUpdateInput": {
        "type": "object", "required": ["id", "expectedRevision", "content"], "additionalProperties": false,
        "properties": {
          "id": { "$ref": "#/components/schemas/DraftId" },
          "expectedRevision": { "$ref": "#/components/schemas/DraftRevision" },
          "content": { "anyOf": [{ "$ref": "#/components/schemas/MessageDraftContent" }, { "$ref": "#/components/schemas/PostDraftContent" }, { "$ref": "#/components/schemas/InvitationDraftContent" }, { "$ref": "#/components/schemas/DiscussionDraftContent" }, { "$ref": "#/components/schemas/ReplyDraftContent" }] }
        },
        "description": "Replace the whole content object for the saved draft's existing kind. ID, kind, account, and createdAt stay unchanged. Malformed or mismatched targets return INVALID_ARGUMENT and leave content, revision, and timestamps unchanged. A stale expectedRevision returns DRAFT_CONFLICT without modifying the draft."
      },
      "DraftDeleteInput": {
        "type": "object", "required": ["id", "expectedRevision"], "additionalProperties": false,
        "properties": {
          "id": { "$ref": "#/components/schemas/DraftId" },
          "expectedRevision": { "$ref": "#/components/schemas/DraftRevision" }
        },
        "description": "Delete only the selected local draft file after a revision check. Never deletes selected media or LinkedIn content."
      },
      "DraftsListInput": {
        "type": "object", "additionalProperties": false, "properties": {},
        "description": "Empty object only. List every saved draft summary for the bound member, ordered by updatedAt descending then ID. No cursor or collection quota. Corrupt/unreadable records return an error, not an empty or shortened list."
      },
      "Draft": {
        "type": "object",
        "required": ["id", "kind", "account", "revision", "createdAt", "updatedAt", "content"],
        "additionalProperties": false,
        "properties": {
          "id": { "$ref": "#/components/schemas/DraftId" },
          "kind": { "enum": ["message", "post", "invitation", "comment", "reply", "repost"] },
          "account": { "$ref": "#/components/schemas/Account", "description": "Immutable actor copied from the saved installation binding at creation. A name change does not replace this stored actor; member matching uses the normalized profile URL." },
          "revision": { "$ref": "#/components/schemas/DraftRevision" },
          "createdAt": { "type": "string", "format": "date-time", "description": "Immutable local creation timestamp in ISO format." },
          "updatedAt": { "type": "string", "format": "date-time", "description": "Local save timestamp in ISO format." },
          "content": {}
        },
        "oneOf": [
          { "properties": { "kind": { "const": "message" }, "content": { "$ref": "#/components/schemas/MessageDraftContent" } } },
          { "properties": { "kind": { "const": "post" }, "content": { "$ref": "#/components/schemas/PostDraftContent" } } },
          { "properties": { "kind": { "const": "invitation" }, "content": { "$ref": "#/components/schemas/InvitationDraftContent" } } },
          { "properties": { "kind": { "const": "comment" }, "content": { "$ref": "#/components/schemas/DiscussionDraftContent" } } },
          { "properties": { "kind": { "const": "reply" }, "content": { "$ref": "#/components/schemas/ReplyDraftContent" } } },
          { "properties": { "kind": { "const": "repost" }, "content": { "$ref": "#/components/schemas/DiscussionDraftContent" } } }
        ],
        "description": "One current local document per UUID under drafts/<uuid>.json in the private data directory. Directory/file permissions are 0700/0600 on supported systems; symlinks are rejected. Atomic saves and same-draft serialization protect revisions. Drafts survive restart, browser disconnect, and login expiry until explicitly deleted; no revision history is kept."
      },
      "DraftSummary": {
        "type": "object",
        "required": ["id", "kind", "account", "revision", "createdAt", "updatedAt"],
        "additionalProperties": false,
        "properties": {
          "id": { "$ref": "#/components/schemas/DraftId" },
          "kind": { "enum": ["message", "post", "invitation", "comment", "reply", "repost"] },
          "account": { "$ref": "#/components/schemas/Account" },
          "revision": { "$ref": "#/components/schemas/DraftRevision" },
          "createdAt": { "type": "string", "format": "date-time" },
          "updatedAt": { "type": "string", "format": "date-time" },
          "target": {}
        },
        "oneOf": [
          { "properties": { "kind": { "const": "message" }, "target": { "$ref": "#/components/schemas/DraftTarget" } }, "required": ["target"] },
          { "properties": { "kind": { "const": "post" } }, "not": { "required": ["target"] } },
          { "properties": { "kind": { "const": "invitation" }, "target": { "$ref": "#/components/schemas/InvitationDraftTarget" } }, "required": ["target"] },
          { "properties": { "kind": { "const": "comment" }, "target": { "$ref": "#/components/schemas/PostDraftTarget" } }, "required": ["target"] },
          { "properties": { "kind": { "const": "reply" }, "target": { "$ref": "#/components/schemas/ReplyDraftTarget" } }, "required": ["target"] },
          { "properties": { "kind": { "const": "repost" }, "target": { "$ref": "#/components/schemas/PostDraftTarget" } }, "required": ["target"] }
        ],
        "description": "Metadata only, plus the kind-specific explicit target for message/invitation/comment/reply/repost drafts. Post drafts have no target. No draft text, content object, or media paths."
      },
      "DraftDeleted": {
        "type": "object", "required": ["id", "deleted"], "additionalProperties": false,
        "properties": { "id": { "$ref": "#/components/schemas/DraftId" }, "deleted": { "const": true } }
      },
      "LocalOperationResult": {
        "type": "object", "required": ["operation", "account", "data"], "additionalProperties": false,
        "properties": {
          "operation": { "type": "string", "enum": ["draft.create", "draft.get", "draft.update", "draft.delete", "drafts.list"] },
          "account": { "$ref": "#/components/schemas/Account", "description": "Previously verified saved installation binding; not evidence of a currently authenticated browser. Required even while disconnected. A stored draft for another member returns ACCOUNT_MISMATCH." },
          "data": {}
        },
        "oneOf": [
          { "properties": { "operation": { "enum": ["draft.create", "draft.get", "draft.update"] }, "data": { "$ref": "#/components/schemas/Draft" } } },
          { "properties": { "operation": { "const": "draft.delete" }, "data": { "$ref": "#/components/schemas/DraftDeleted" } } },
          { "properties": { "operation": { "const": "drafts.list" }, "data": { "type": "array", "items": { "$ref": "#/components/schemas/DraftSummary" }, "description": "drafts.list summary array sorted by updatedAt descending, then ID. An empty array means no saved drafts." } } }
        ],
        "description": "Local-only envelope, with no source or completeness fields. Create/get/update return Draft; delete returns DraftDeleted; list returns a summary array. No operation connects a browser, verifies its current account, or contacts LinkedIn. Browser disconnect does not cancel or block drafts; service shutdown does cancel active work. Cancellation is checked before save/delete commits, but a cancellation or lost response after commit cannot promise rollback. Get/list establish saved state; mutations are not automatically retried."
      },
      "NetworkCursor": {
        "type": "string",
        "minLength": 1,
        "description": "Opaque single-use token for a retained Relay-owned list page, bound to account, operation, and invitation direction. Omit initially; use the successor cursor after each successful response. It permits the next observed scroll, not guaranteed new rows. Concurrent use returns CURSOR_IN_USE; consumed, unknown, mismatched, or closed tokens return INVALID_CURSOR. Cancellation/failure closes the affected chain; disconnect, account mismatch, browser closure, and shutdown invalidate network cursors. Restart cannot restore them. No time expiry or collection quota is imposed. Release one idle token with DELETE /v1/cursors/{cursor} (CLI: relay release <cursor>) when finished, or disconnect to close all held pages. Release consumes the token and leaves other pages and the browser connected. Search cursors cannot be released. A failed close with the page still open returns CURSOR_RELEASE_FAILED and allows explicit retry; cancellation after the claim cannot promise rollback. This token contains no provider URL or browser credential and does not replace API authentication."
      },
      "Account": {
        "type": "object",
        "required": ["profileUrl", "name"],
        "properties": {
          "profileUrl": { "type": "string", "format": "uri" },
          "name": { "type": ["string", "null"], "description": "Displayed member name when available." }
        }
      },
      "Session": {
        "type": "object",
        "required": ["state", "account", "mode"],
        "properties": {
          "state": { "type": "string", "enum": ["disconnected", "auth_required", "challenge", "ready"] },
          "account": { "anyOf": [{ "$ref": "#/components/schemas/Account" }, { "type": "null" }] },
          "mode": { "type": "string", "enum": ["attached", "owned"] },
          "message": { "type": "string" }
        }
      },
      "Capability": {
        "type": "object",
        "required": ["operation", "effect", "status", "description"],
        "properties": {
          "operation": { "type": "string" },
          "effect": { "type": "string", "enum": ["read", "write", "local-write"], "description": "local-write changes only local draft storage (create/update/delete). Draft get/list are read operations. No implemented draft operation contacts LinkedIn." },
          "status": { "type": "string", "enum": ["implemented", "not_implemented"] },
          "description": { "type": "string" },
          "inputSchemaRef": { "type": "string", "format": "uri-reference", "description": "Resolve against the running service URL, including its port: /v1/openapi.json#/components/schemas/<InputName>. Nested schema references stay local to that document. Shared URL structure does not relax operation-specific URL rules." },
          "querySupport": { "enum": ["keywords", "none"], "description": "Keywords are supported by people.search with an optional connectionDegree (1, 2 or 3 for 3rd+), by companies.search without degree filters, and by connections.list through native Connections name search. Connection query and cursor are exclusive. conversations.list supports recipient, message-content and conversation-name keywords across Inbox, Spam and Archive, without a first-degree restriction or cursor. company.people supports native People keywords with no continuation." },
          "collectionScope": { "enum": ["record", "search_page", "network_window", "initial_window", "all_local_drafts"] },
          "continuation": { "enum": ["search_cursor", "network_cursor", "none"], "description": "For connections.list, network_cursor applies only to query-free listing. Queried connection results close their page without a cursor." },
          "release": { "oneOf": [{ "type": "null" }, {
            "type": "object", "required": ["method", "path", "cli"], "additionalProperties": false,
            "properties": { "method": { "const": "DELETE" }, "path": { "const": "/v1/cursors/{cursor}" }, "cli": { "const": "rea-l release <cursor>" } }
          }], "description": "Query-free network cursors and non-null posts.list referenceCursor values retain pages. Release the latest network successor or prepared Posts cursor when finished. Ordinary Posts results and prepared results with referenceCursor:null need no release. Search cursors need no release. Prepared references are not pagination." }
        },
        "if": { "properties": { "status": { "const": "implemented" } }, "required": ["status"] },
        "then": { "required": ["inputSchemaRef", "querySupport", "collectionScope", "continuation", "release"] }
      },
      "OperationResult": {
        "type": "object",
        "required": ["operation", "account", "data", "source", "completeness"],
        "properties": {
          "operation": { "type": "string", "enum": ["profile.get", "company.get", "company.people", "post.get", "comments.list", "reactions.list", "conversation.get", "message.send", "people.search", "companies.search", "connections.list", "invitations.list", "conversations.list", "notifications.list", "feed.list", "posts.list"] },
          "account": { "$ref": "#/components/schemas/Account" },
          "data": {},
          "source": {
            "type": "object",
            "required": ["url", "observedAt", "method"],
            "properties": {
              "url": { "type": "string", "format": "uri", "description": "Source used for extraction. For posts.list this is the actual loaded member/company collection URL, not the owner's profile URL. For company.people this is the actual company People page, including keywords for a query. Keep source query values out of routine logs." },
              "observedAt": { "type": "string", "format": "date-time" },
              "method": { "type": "string", "enum": ["dom", "observed-request"], "description": "dom extracts loaded page fields. observed-request is a passively captured normal-UI response used for inbox and selected-conversation windows; no request replay is implied." }
            }
          },
          "completeness": {
            "type": "object",
            "required": ["status", "missingFields", "hasMore", "nextCursor"],
            "properties": {
              "status": { "type": "string", "enum": ["complete", "partial"], "description": "Coverage of requested fields, not all LinkedIn data. Populated network, query-free inbox, selected-conversation, notification, comment, reaction, feed, member/company Posts, and company People windows retain partial status for unproved collection or field/content coverage. An initial company Posts All/Top window can report false hasMore after owner-bound native paging, an unexpired token and complete source/card identity mapping prove the current feed end; its field gaps remain partial. This is not complete company history. A positive conversation query may report false hasMore after an exact native successor already captured during the initial read, its empty/null response and applied-end proof; status is complete only with no missing item fields, otherwise partial. Verified native connection-query, conversation-query and company-People-query no-matches are complete with empty items; this proves no provider matches for that query and scope, not absence of network membership, conversation history or employment. More-content controls on other lookups can also produce partial status without missing fields." },
              "missingFields": { "type": "array", "items": { "type": "string" }, "description": "Unavailable field paths, including item paths such as items[0].location or an unknown pagination state." },
              "hasMore": { "type": ["boolean", "null"], "description": "Search: true for observed next-page availability, false for observed end, null when unknown. Network reads return null even with nextCursor; a successful append does not prove future rows. Verified connection-query, conversation-query and company-People-query no-matches return false. A positive conversation query can also return false after exact already-captured native successor and applied-end proof, independently of missing fields; other populated queries return null. An initial company Posts All/Top window may return false from verified owner-bound native paging and complete source/card coverage with an unexpired token; unsupported proof retains null. Lookup operations and query-free inbox/message/notification/feed/member-Posts/People windows return null." },
              "nextCursor": { "type": ["string", "null"], "description": "Search: continuation for observed next-page navigation, bound to query/operation/account. Query-free network listing: single-use held-page cursor permitting the next scroll, bound to operation/account/direction, without promising more rows. Connection queries return null and close their page. Use unchanged. Lookup operations and initial inbox/message/notification/feed/Posts/People windows return null; their pages close after collection." }
            }
          }
        },
        "oneOf": [
          { "properties": { "operation": { "const": "profile.get" }, "data": { "$ref": "#/components/schemas/ProfileData" } } },
          { "properties": { "operation": { "const": "company.get" }, "data": { "$ref": "#/components/schemas/CompanyData" } } },
          { "properties": { "operation": { "const": "company.people" }, "data": { "$ref": "#/components/schemas/CompanyPeopleData" } } },
          { "properties": { "operation": { "const": "post.get" }, "data": { "$ref": "#/components/schemas/PostData" } } },
          { "properties": { "operation": { "const": "comments.list" }, "data": { "$ref": "#/components/schemas/CommentsData" } } },
          { "properties": { "operation": { "const": "reactions.list" }, "data": { "$ref": "#/components/schemas/ReactionsData" } } },
          { "properties": { "operation": { "const": "conversation.get" }, "data": { "$ref": "#/components/schemas/ConversationData" } } },
          { "properties": { "operation": { "const": "message.send" }, "data": { "$ref": "#/components/schemas/MessageSendData" }, "completeness": { "properties": { "hasMore": { "const": false }, "nextCursor": { "type": "null" } } } } },
          { "properties": { "operation": { "const": "people.search" }, "data": { "$ref": "#/components/schemas/PeopleSearchData" } } },
          { "properties": { "operation": { "const": "companies.search" }, "data": { "$ref": "#/components/schemas/CompaniesSearchData" } } },
          { "properties": { "operation": { "const": "connections.list" }, "data": { "$ref": "#/components/schemas/ConnectionsData" } } },
          { "properties": { "operation": { "const": "invitations.list" }, "data": { "$ref": "#/components/schemas/InvitationsData" } } },
          { "properties": { "operation": { "const": "conversations.list" }, "data": { "$ref": "#/components/schemas/ConversationsData" } } },
          { "properties": { "operation": { "const": "notifications.list" }, "data": { "$ref": "#/components/schemas/NotificationsData" } } },
          { "properties": { "operation": { "const": "feed.list" }, "data": { "$ref": "#/components/schemas/FeedData" } } },
          { "properties": { "operation": { "const": "posts.list" }, "data": { "$ref": "#/components/schemas/PostsData" } } }
        ]
      },
      "ProfileData": {
        "type": "object",
        "required": ["profileUrl", "name", "headline", "location", "about", "experience", "education"],
        "properties": {
          "profileUrl": { "type": ["string", "null"] },
          "name": { "type": "string" },
          "headline": { "type": ["string", "null"] },
          "location": { "type": ["string", "null"] },
          "about": { "type": ["string", "null"] },
          "experience": { "type": ["array", "null"], "items": { "type": "string" }, "description": "Visible text blocks; no inferred role or date structure." },
          "education": { "type": ["array", "null"], "items": { "type": "string" }, "description": "Visible text blocks; no inferred degree or date structure." }
        }
      },
      "CompanyData": {
        "type": "object",
        "required": ["companyUrl", "name", "about", "website", "industry", "size", "headquarters", "followers"],
        "properties": {
          "companyUrl": { "type": ["string", "null"] },
          "name": { "type": "string" },
          "about": { "type": ["string", "null"] },
          "website": { "type": ["string", "null"] },
          "industry": { "type": ["string", "null"] },
          "size": { "type": ["string", "null"] },
          "headquarters": { "type": ["string", "null"] },
          "followers": { "type": ["string", "null"], "description": "Displayed text, not an inferred exact count." }
        }
      },
      "CompanyPeopleData": {
        "type": "object",
        "required": ["companyUrl", "sectionLabel", "associatedMembersText", "items"],
        "additionalProperties": false,
        "properties": {
          "companyUrl": { "type": "string", "format": "uri", "description": "Normalized requested company profile URL; not an assertion that every returned person works there." },
          "query": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "Trimmed query, present only for a native keyword query." },
          "sectionLabel": { "type": ["string", "null"], "description": "Loaded heading of the verified People you may know recommendation section, separate from the associated-members aggregate. Null only for a verified query no-match, where the section is known not to exist." },
          "associatedMembersText": { "type": ["string", "null"], "description": "Displayed associated-members label, such as 7 associated members, or null when unavailable/ambiguous. Not a parsed workforce count or evidence of returned-list coverage." },
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/CompanyPeopleItem" } }
        },
        "oneOf": [
          { "properties": { "sectionLabel": { "type": "string" }, "items": { "minItems": 1 } } },
          { "required": ["query"], "properties": { "sectionLabel": { "type": "null" }, "associatedMembersText": { "type": "string", "pattern": "^\\s*0\\s+associated\\s+members\\s*$" }, "items": { "maxItems": 0 } } }
        ],
        "description": "Initial source-ordered recommendation cards, optionally narrowed by native keywords, including cards without a profile URL. Verify the company/current People tab and empty search input; queries also require the exact keyword URL and single selected query pill. Populated/default results have a string sectionLabel, partial status, indexed missing fields and hasMore:null. Only a verified queried no-match has empty items, sectionLabel:null, the raw zero-associated-members label, complete status, no missing fields and hasMore:false. This proves no provider matches for that query, not an empty workforce or lack of employment. All results have nextCursor:null and source.method:dom with the actual People URL. No member visits, actions, insight filters, carousel selection, cap or crawl. The owned page closes after collection; native visit effects remain possible."
      },
      "CompanyPeopleItem": {
        "type": "object",
        "required": ["profileUrl", "name", "headline"],
        "additionalProperties": false,
        "properties": {
          "profileUrl": { "type": ["string", "null"], "format": "uri", "description": "One validated canonical /in/<member>/ destination from this card's title link, or null when absent/conflicting/invalid. Avatar/headline links do not replace the title identity. A generic member card with no link is retained." },
          "name": { "type": ["string", "null"], "description": "Loaded card title text, or null. A generic LinkedIn Member label remains display text, not a known person's identity. Relationship badges, captions, and action labels are excluded." },
          "headline": { "type": ["string", "null"], "description": "Loaded card subtitle text, or null. No length cap or inferred employment/position at the requested company. No complete-profile or universal visible-text claim." }
        },
        "description": "Keep each physical source card in order; never join or deduplicate by name, headline, or position."
      },
      "PostData": {
        "type": "object",
        "required": ["postUrl", "author", "text", "textBlocks", "reactionsText", "commentsText", "repostsText"],
        "description": "One selected post with separate loaded DOM textBlocks and single-region rendered text. Missing or ambiguous values remain null. Multiple regions leave text and all count fields null and the result partial; no region roles or count association are invented. An empty textBlocks array is a missing field, not an empty-post claim. A visible More control also marks the result partial. No complete content/media/discussion/history claim.",
        "properties": {
          "postUrl": { "type": ["string", "null"], "description": "Canonical LinkedIn activity URL without tracking parameters." },
          "author": {
            "anyOf": [{
              "type": "object",
              "required": ["name", "url"],
              "properties": {
                "name": { "type": "string" },
                "url": { "type": "string", "format": "uri", "description": "One unambiguous named member/company URL associated with the unique recognized post-menu name in the header before the first text region. Neither the menu nor the link may be inside that region or contain it. Body/quoted references, collection owners, and notification actors cannot supply missing authorship." }
              }
            }, { "type": "null" }]
          },
          "text": { "type": ["string", "null"], "description": "Existing rendered text from exactly one scoped post body. Null when absent, ambiguous, or multiple regions are present; never a guessed concatenation of textBlocks." },
          "textBlocks": { "type": "array", "items": { "type": "string" }, "description": "Trimmed textContent of each loaded expandable-text region, kept separately in DOM source order. No joining, arbitrary length clipping, or inferred repost/quotation/caption roles. Loaded text does not prove full visibility, complete underlying content, or media fidelity. An empty array is reported missing, not an empty post." },
          "reactionsText": { "type": ["string", "null"], "description": "Displayed reaction-count label from the existing single-region boundary, not a parsed total. Null for multiple text regions." },
          "commentsText": { "type": ["string", "null"], "description": "Displayed comment-count label from the existing single-region boundary; no comment bodies are collected. Null for multiple text regions." },
          "repostsText": { "type": ["string", "null"], "description": "Displayed repost-count label from the existing single-region boundary, not a parsed total. Null for multiple text regions." }
        }
      },
      "SearchData": {
        "type": "object",
        "required": ["query", "items"],
        "properties": {
          "query": { "type": "string" },
          "items": {
            "type": "array",
            "description": "Observed provider-page results in source order. Identity is the profile/company URL; repeated display names remain separate results.",
            "items": { "oneOf": [{ "$ref": "#/components/schemas/PersonSearchItem" }, { "$ref": "#/components/schemas/CompanySearchItem" }] }
          }
        }
      },
      "PeopleSearchData": {
        "allOf": [{ "$ref": "#/components/schemas/SearchData" }, { "properties": { "connectionDegree": { "type": "integer", "enum": [1, 2, 3], "description": "Present only when requested; value 3 means native 3rd+ scope." }, "items": { "items": { "$ref": "#/components/schemas/PersonSearchItem" } } } }]
      },
      "CompaniesSearchData": {
        "allOf": [{ "$ref": "#/components/schemas/SearchData" }, { "properties": { "items": { "items": { "$ref": "#/components/schemas/CompanySearchItem" } } } }]
      },
      "PersonSearchItem": {
        "type": "object",
        "required": ["profileUrl", "name", "headline", "location"],
        "properties": {
          "profileUrl": { "type": "string", "format": "uri" },
          "name": { "type": "string" },
          "headline": { "type": ["string", "null"] },
          "location": { "type": ["string", "null"] }
        }
      },
      "CompanySearchItem": {
        "type": "object",
        "required": ["companyUrl", "name", "industry", "location"],
        "properties": {
          "companyUrl": { "type": "string", "format": "uri" },
          "name": { "type": "string" },
          "industry": { "type": ["string", "null"] },
          "location": { "type": ["string", "null"] }
        }
      },
      "ConnectionsData": {
        "type": "object",
        "required": ["items"],
        "additionalProperties": false,
        "description": "Queried data includes query and has no continuation. A populated query result is partial with unknown hasMore; verified native no-match is complete with empty items and false hasMore. Unqueried listing returns a nonempty window with a retained cursor. No complete-network or exact-name matching claim.",
        "properties": {
          "query": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "The normalized native query, present only for queried calls." },
          "items": { "type": "array", "description": "Observed connections in provider order. Empty items require the verified current-query native no-match state; an unverified empty list is an error.", "items": { "$ref": "#/components/schemas/ConnectionItem" } }
        },
        "if": { "required": ["query"] },
        "else": { "properties": { "items": { "minItems": 1 } } }
      },
      "ConnectionItem": {
        "type": "object",
        "required": ["profileUrl", "name", "headline", "connectedDateText"],
        "properties": {
          "profileUrl": { "type": "string", "format": "uri", "description": "Canonical profile URL identifying this connection." },
          "name": { "type": ["string", "null"] },
          "headline": { "type": ["string", "null"] },
          "connectedDateText": { "type": ["string", "null"], "description": "Displayed connection date text; no timestamp or timezone is inferred." }
        }
      },
      "InvitationsData": {
        "type": "object",
        "required": ["direction", "items"],
        "additionalProperties": false,
        "properties": {
          "direction": { "type": "string", "enum": ["received", "sent"] },
          "items": { "type": "array", "description": "Every newly observed invitation card in this step, in provider order. No complete-list claim.", "items": { "$ref": "#/components/schemas/InvitationItem" } }
        }
      },
      "InvitationItem": {
        "type": "object",
        "required": ["direction", "kind", "invitationUrn", "inviterProfileUrl", "recipientProfileUrl", "targetUrl", "name", "headline", "dateText", "noteText"],
        "properties": {
          "direction": { "type": "string", "enum": ["received", "sent"] },
          "kind": { "type": "string", "enum": ["connection", "company_follow", "newsletter", "unknown"], "description": "Observed card type. Unknown types are retained without inferring a connection request." },
          "invitationUrn": { "type": ["string", "null"], "description": "Observed received-invitation URN. Null on current sent cards; never fabricated from a recipient URL or DOM key." },
          "inviterProfileUrl": { "type": ["string", "null"], "description": "Observed inviter profile, distinct from a company/newsletter subject." },
          "recipientProfileUrl": { "type": ["string", "null"], "description": "Observed recipient profile. Identifies a displayed sent recipient, not a provider invitation object." },
          "targetUrl": { "type": ["string", "null"], "description": "Observed invitation subject URL, including a company or newsletter when present." },
          "name": { "type": ["string", "null"] },
          "headline": { "type": ["string", "null"] },
          "dateText": { "type": ["string", "null"], "description": "Displayed source date text; no timestamp or timezone is inferred." },
          "noteText": { "type": "null", "description": "Full note visibility is unproved because observed notes were clipped. No expansion/action control is activated; the indexed field is reported missing." }
        }
      },
      "ConversationsData": {
        "type": "object",
        "required": ["items"],
        "additionalProperties": false,
        "description": "An initial inbox or native query window. Queried data includes query and keeps the initial records. An exact native successor already captured during the initial read, its empty/null response and current-query applied-end proof can establish false hasMore; status is complete only with no missing fields, otherwise partial. Unproved positive windows remain partial with unknown hasMore; absent captured successors do not cause a wait for future requests. A verified no-match is complete with empty items and false hasMore. These endings concern matches in the observed query scope, not all conversation history. No forced loading, result merge, history, message bodies, attachments, sends, or product continuation. Missing optional end proof preserves valid initial partial data; invalid initial source/UI proof remains an error.",
        "properties": {
          "query": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "The normalized native query, present only for queried calls." },
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/ConversationItem" } }
        },
        "if": { "required": ["query"] },
        "else": { "properties": { "items": { "minItems": 1 } } }
      },
      "ConversationItem": {
        "type": "object",
        "required": ["conversationUrl", "conversationUrn", "participants", "read", "unreadCount", "lastActivityAt", "lastReadAt"],
        "properties": {
          "conversationUrl": { "type": "string", "format": "uri", "description": "Validated HTTPS LinkedIn /messaging/thread/<id>/ URL supplied by this conversation record; never reconstructed from its URN." },
          "conversationUrn": { "type": "string", "description": "Source entityUrn of this conversation record." },
          "participants": { "type": ["array", "null"], "description": "Participants from this record's own conversationParticipants; no name-based joining or removal of the signed-in participant.", "items": { "$ref": "#/components/schemas/ConversationParticipant" } },
          "read": { "type": ["boolean", "null"], "description": "Source read flag at capture. Opening Messaging may acknowledge reads; this is not a before-navigation state." },
          "unreadCount": { "type": ["integer", "null"], "minimum": 0, "description": "Source unread count when present; missing values are null, not zero." },
          "lastActivityAt": { "type": ["number", "null"], "description": "Raw numeric source timestamp. Units and display-date conversion are unverified." },
          "lastReadAt": { "type": ["number", "null"], "description": "Raw numeric source timestamp. Units and display-date conversion are unverified." }
        }
      },
      "ConversationParticipant": {
        "type": "object",
        "required": ["participantUrn", "type", "profileUrl", "firstName", "lastName"],
        "properties": {
          "participantUrn": { "type": ["string", "null"], "description": "Participant's own source entityUrn when present." },
          "type": { "type": "string", "enum": ["member", "organization", "agent", "custom", "unknown"], "description": "Explicit source branch. Unknown or ambiguous branches remain unknown." },
          "profileUrl": { "type": ["string", "null"], "description": "Validated source member profile URL. Non-member and ambiguous branches return null." },
          "firstName": { "type": ["string", "null"], "description": "Text from the observed member firstName object when available; other branches return null." },
          "lastName": { "type": ["string", "null"], "description": "Text from the observed member lastName object when available; other branches return null." }
        }
      },
      "ConversationData": {
        "type": "object",
        "required": ["conversationUrl", "conversationUrn", "messages"],
        "additionalProperties": false,
        "description": "One correlated initial message window, always partial. Every message references this source conversation; correlation to the supplied URL uses an explicit source conversation record or matching selected-thread DOM message identity. No name/preview matching, full-history, rich-text, media, or continuation claim.",
        "properties": {
          "conversationUrl": { "type": "string", "format": "uri", "description": "The validated supplied www.linkedin.com thread destination." },
          "conversationUrn": { "type": "string", "description": "Source conversation.entityUrn shared by all returned messages and correlated to the requested URL." },
          "messages": { "type": "array", "minItems": 1, "description": "Source array order. No general chronological ordering is promised; each messageUrn is unique within this window.", "items": { "$ref": "#/components/schemas/ConversationMessage" } }
        }
      },
      "ConversationMessage": {
        "type": "object",
        "required": ["messageUrn", "senderUrn", "actorUrn", "text", "deliveredAt", "attachments"],
        "properties": {
          "messageUrn": { "type": "string", "description": "Required unique source message entityUrn." },
          "senderUrn": { "type": ["string", "null"], "description": "Source sender.entityUrn, or null when unavailable." },
          "actorUrn": { "type": ["string", "null"], "description": "Source actor.entityUrn, preserved separately without substituting the sender." },
          "text": { "type": ["string", "null"], "description": "Exact loaded body.text when it is a string, preserving whitespace, line breaks, and an explicitly empty string. Null otherwise. No exact rendered-rich-text or complete-media claim." },
          "deliveredAt": { "type": ["number", "null"], "description": "Finite raw source number, or null. Timestamp units, timezone, and absolute-date conversion are unverified." },
          "attachments": { "type": "null", "description": "Attachment schemas remain unverified; this field is reported missing for each message." }
        }
      },
      "FeedData": {
        "type": "object",
        "required": ["items"],
        "additionalProperties": false,
        "description": "Initial populated direct feed wrappers in source order. Only proved loading placeholders are ignored; unknown populated entries remain explicit. Always partial, with no scrolling, cursor, full-history, or empty/end-state claim.",
        "properties": {
          "items": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/FeedItem" } }
        }
      },
      "PostsData": {
        "type": "object",
        "required": ["owner", "items"],
        "additionalProperties": false,
        "properties": {
          "referenceCursor": { "type": ["string", "null"], "minLength": 1, "description": "Present only for prepareReferences:true. Opaque local cursor for the retained member window, or null when no eligible references exist. Not a next-page cursor; release it when finished. Failure/cancellation, release, disconnect, account mismatch, browser closure or restart invalidate affected references." },
          "owner": {
            "type": "object", "required": ["kind", "url"], "additionalProperties": false,
            "properties": {
              "kind": { "type": "string", "enum": ["member", "company"] },
              "url": { "type": "string", "format": "uri", "description": "Normalized member/company profile URL for this collection; not the current activity/Posts page and not a substitute for a card's author." }
            }
          },
          "items": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/FeedItem" } }
        },
        "allOf": [
          { "if": { "required": ["referenceCursor"] },
            "then": { "properties": {
              "owner": { "properties": { "kind": { "const": "member" } } },
              "items": { "items": { "required": ["reference"], "properties": { "reference": { "type": ["string", "null"], "minLength": 1, "description": "Opaque selection bound to this exact native card, or null for an unsupported/already resolved row. It is not a durable post ID or a promise that Embed exists." } } } }
            } },
            "else": { "properties": { "items": { "items": { "not": { "required": ["reference"] } } } } } },
          { "if": { "required": ["referenceCursor"], "properties": { "referenceCursor": { "type": "null" } } },
            "then": { "properties": { "items": { "items": { "properties": { "reference": { "type": "null" } } } } } } },
          { "if": { "required": ["referenceCursor"], "properties": { "referenceCursor": { "type": "string" } } },
            "then": { "properties": { "items": { "contains": { "required": ["reference"], "properties": { "reference": { "type": "string", "minLength": 1 } } } } } } }
        ],
        "description": "Every currently loaded entry in the verified initial Posts collection, in source order; separate cards are never joined by name, text, or URL. Own-member postUrl can come from a unique verified analytics footer after live actor/owner equality, with no per-card actions. Non-own and unsupported member references and company author stay null. Valid company card activity URNs yield derived detail references; absent/invalid URNs retain the card with a missing postUrl. Unknown member entries remain explicit; unclassified or nested company content is a source error. Always partial with indexed missing fields, nextCursor:null, source.method:dom and the actual collection URL. hasMore is normally null; an initial company All/Top feed may return false after successful owner-bound bootstrap/request pairing, complete Update.backendUrn/card mapping, safe paging and an unexpired native token prove its end. Missing or unsupported proof keeps null and preserves valid rows. Ordinary listing has no held page. Optional member preparation adds referenceCursor and per-item reference without opening menus or changing source fields/missingFields. A non-null referenceCursor retains one page; resolve only the selected item through post.get and release the window when finished. Prepared results remain partial with unknown end and nextCursor:null. No scrolling, filter change, text expansion, per-card menu loop, collection quota, complete-history, empty-company or member-end claim. Native read effects are not ruled out. Live API/CLI checks covered an own-member window, one selected non-own member, and one known-company window; other layouts remain unproved. The company detail API matched rendered source text, which can differ in whitespace from loaded collection DOM text."
      },
      "FeedItem": {
        "type": "object",
        "required": ["kind", "postUrl", "author", "textBlocks", "moreAvailable"],
        "properties": {
          "kind": { "type": "string", "enum": ["post", "unknown"], "description": "Post requires exactly one observed post-menu signature. Other populated entries remain unknown." },
          "postUrl": { "type": ["string", "null"], "description": "Home feed: one unambiguous pre-body header activity reference, supplied or derived through the proved activity-URN mapping. Member posts.list: a derived canonical activity URL only for the live account owner and a unique verified outer-card View analytics footer; no menu or extra request is used. Non-own or unsupported references remain null. Company posts.list: a derived canonical detail URL from a valid activity URN on that card, supported by the sampled card-to-detail check; missing/invalid URNs return null. It is not a supplied link or proof of authorship. Body/later references, renderer keys, conflicts, and unproved association do not establish identity." },
          "author": { "$ref": "#/components/schemas/PostData/properties/author", "description": "Home feed/member Posts: verified menu name and one canonical named member/company URL in the pre-body header, or null. Company Posts: always null until the primary actor association is proved. The collection owner and repost attribution never replace the author; names never join cards." },
          "textBlocks": { "type": "array", "items": { "type": "string" }, "description": "Separate loaded text regions in source order, without arbitrary clipping. Home feed/member Posts use expandable-text regions; company Posts use the observed legacy description regions. Do not infer repost, quotation, or comment roles. Loaded text does not establish full visibility or complete content." },
          "moreAvailable": { "type": ["boolean", "null"], "description": "True for an observed visible More control; false when none is visible on a recognized rendered post; null when that state is unproved. False or absent controls do not establish complete underlying content." }
        }
      },
      "NotificationsData": {
        "type": "object",
        "required": ["items"],
        "additionalProperties": false,
        "description": "Initial partial notifications window in source order. No deduplication by text, targets, generated keys, or position. Source method is dom and source URL is the query-free notifications page. Eligible highlighted-activity targets can supply a derived postUrl without changing targetUrl or navigating; the caller chooses whether to use post.get. No cursor, full-history, empty-state, or end claim.",
        "properties": {
          "items": { "type": "array", "minItems": 1, "items": { "$ref": "#/components/schemas/NotificationItem" } }
        }
      },
      "NotificationItem": {
        "type": "object",
        "required": ["notificationId", "text", "clippedOnScreen", "dateText", "targetUrl", "postUrl", "read"],
        "properties": {
          "notificationId": { "type": "null", "description": "No stable provider notification identity is proved. Renderer keys and target post URNs are not notification IDs." },
          "text": { "type": ["string", "null"], "description": "Loaded DOM text from the semantic headline paragraph, without arbitrary clipping. It may exceed the CSS-visible portion and does not establish the complete event or underlying post." },
          "clippedOnScreen": { "type": ["boolean", "null"], "description": "Measured rendered-paragraph overflow: true when its loaded content exceeds the visible box, false when it fits, null when not measurable. A line-clamp declaration alone does not establish clipping." },
          "dateText": { "type": ["string", "null"], "description": "Observed relative-date label. No absolute timestamp or timezone is inferred." },
          "targetUrl": { "type": ["string", "null"], "format": "uri", "description": "One distinct eligible anchor destination supplied by this card. Only HTTPS www.linkedin.com, without userinfo, non-default port, or fragment: /feed/ requires exactly one highlightedUpdateUrn with activity-URN syntax and may include highlightedUpdateType, origin, and showCommentBox once each; /analytics/profile-views/ and /me/search-appearances/ permit no query. Preserve eligible supplied parameters; optional values remain opaque. Unknown or duplicate keys make a candidate ineligible. Actor links are excluded; zero or conflicting eligible targets return null. This target can retain permitted query values; keep them out of routine logs and diagnostics. The target is not opened or verified." },
          "postUrl": { "type": ["string", "null"], "format": "uri", "description": "Derived https://www.linkedin.com/feed/update/<activityUrn>/ lookup reference only from this item's already-selected eligible highlighted-feed targetUrl and exact urn:li:activity:<digits>. Original targetUrl stays unchanged. Analytics, absent/conflicting targets, unsupported URNs, and unverified formats return null with an indexed missing field. This is not a supplied card link, notification/event identity, actor/read-state proof, comment permalink, reply relation, or proof of every highlighted-feed layout. No headline/name inference. Listing never opens it or calls post.get automatically; the caller can use the existing post reader." },
          "read": { "type": "null", "description": "Read state is unproved and is not inferred from styling. Opening Notifications is not guaranteed to leave read state or badges unchanged." }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "string" },
              "message": { "type": "string" },
              "details": { "type": "object", "additionalProperties": true }
            }
          }
        }
      }
    }
  }
}
