---
title: "List completed LinkedIn steps"
method: GET
path: "/sender-profiles/{profile_id}/linkedin/history/"
tags: ["linkedin"]
---

# List completed LinkedIn steps

`GET /sender-profiles/{profile_id}/linkedin/history/`

## Request

- Base URL: `https://api.getoneprofile.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.getoneprofile.ai/sender-profiles/{profile_id}/linkedin/history/`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Settled actions, newest first, with lifetime outcome counts and timings.

## Path parameters

- `profile_id` string, uuid, required

## Query parameters

- `kind` 'profile_view' | 'connection_request' | 'send_message' | 'inmail' | 'withdraw_connection_request' | 'follow_contact' | 'like_latest_post' | 'check_sent_invitations' | 'check_connections' | 'check_inbox' | 'read_conversation': What the browser agent is being asked to do. The four ``CHECK_*``/``READ_*`` kinds are housekeeping: they read our own outbound invitations, our own newest connections and our own conversations to detect what the contact did, so they are free and deliberately excluded from the priced kinds below. ``CHECK_CONNECTIONS`` reads the first batch of the sender's connections list, newest first: a person found there has accepted, whenever the invitation went out. It is how an acceptance is noticed at all once an invitation has fallen out of the ten rows the sent list shows a background tab. ``CHECK_INBOX`` scans the conversation list for threads with new inbound activity; ``READ_CONVERSATION`` opens one of them and reads it in full. The split keeps LinkedIn activity proportional to real replies: the cheap scan runs on every sweep, the expensive read only where the scan saw a change. ``LIKE_LATEST_POST`` targets the contact's most recent post at the moment the browser runs it. A contact with no posts is not a failure: the driver reports the action ``SKIPPED``, and the sequence advances either way. Every profile-targeted outcome carries the facts the browser read off the profile, including the three-state Premium "Open Profile" reading; see the section comment above the outcome models.
- `status` 'queued' | 'resuming' | 'claimed' | 'succeeded' | 'failed' | 'rejected' | 'skipped': Where one queued action stands. Governor-deferral is deliberately NOT a status: an action whose kind has hit its daily cap stays ``QUEUED`` and is simply not handed out, with the reason travelling on the hand-out response instead. A ``DEFERRED`` row would make the queue lie about what is still waiting to happen. ``SKIPPED`` is "not applicable" (no LinkedIn URL, already connected); ``REJECTED`` is "a human refused it"; ``FAILED`` is "we tried and could not". ``RESUMING`` is a step put back in line after an attempt that may have acted on LinkedIn (its browser was granted the press, or lost track of the step): it is never "queued", so no path that assumes nothing happened yet (moving it to another sender, a plain copy, "did not run") ever reads it as such, and it is handed only to a browser that checks LinkedIn before acting.
- `limit` integer
- `offset` integer

## Response `200`

Successful Response

- LinkedInHistoryPageSchema
  - `items` LinkedInActionReadSchema[], required
    - `account_id` string, uuid, required
    - `acted_at` string, date-time, nullable: When the step said it acted on LinkedIn, for a result that arrived late. On a failed step it means the step happened after all: it stays failed for a person, and a Retry checks LinkedIn first and never sends it twice.
    - `approved_at` string, date-time, nullable: When the step was let run: the moment it was queued, since a step is queued only once it may run. Kept for older extension builds.
    - `claimed_at` string, date-time, nullable, required
    - `duration_ms` integer, nullable, required
    - `error_code` 'selector_missing' | 'blocked_checkpoint' | 'not_authenticated' | 'account_mismatch' | 'navigation_failed' | 'timed_out' | 'linkedin_error' | 'agent_restarted' | 'tab_lost' | 'not_applicable' | 'not_connected' | 'invalid_params' | 'user_rejected' | 'cancelled' | 'missed_window' | 'sender_changed' | 'claim_expired' | 'interrupted_repeatedly', required: Why one action did not succeed, as a closed vocabulary. Two populations in one enum, on purpose. The first twelve are reported by the browser agent and were previously a free string that only the extension had written down, so nothing stopped a typo storing and rendering exactly like a real code. The last four the backend writes itself: three when an action settles without the browser ever running it, and ``CLAIM_EXPIRED`` when the browser took a step and then went silent, so nobody can say whether it ran. The list is the whole contract: a code outside it is rejected at the API boundary rather than persisted, so the extension and the dashboard can only ever speak about failures both sides can name.
    - `error_message` string, nullable, required
    - `finished_at` string, date-time, nullable, required
    - `held` boolean: Whether the step is held out of every browser's line, because its paused sequence or paused contact holds it. It runs once they are resumed. Only the dashboard's queue lists a held step that is waiting; a browser is never sent one.
    - `hold` 'sequence_paused' | 'contact_paused': Why a sequence's step may not run now, whatever its approval says. A paused sequence or a paused contact sends nothing on either channel; an approval given meanwhile waits for **Resume sequence** (or the contact's own resume).
    - `id` string, uuid, required
    - `kind` 'profile_view' | 'connection_request' | 'send_message' | 'inmail' | 'withdraw_connection_request' | 'follow_contact' | 'like_latest_post' | 'check_sent_invitations' | 'check_connections' | 'check_inbox' | 'read_conversation', required: What the browser agent is being asked to do. The four ``CHECK_*``/``READ_*`` kinds are housekeeping: they read our own outbound invitations, our own newest connections and our own conversations to detect what the contact did, so they are free and deliberately excluded from the priced kinds below. ``CHECK_CONNECTIONS`` reads the first batch of the sender's connections list, newest first: a person found there has accepted, whenever the invitation went out. It is how an acceptance is noticed at all once an invitation has fallen out of the ten rows the sent list shows a background tab. ``CHECK_INBOX`` scans the conversation list for threads with new inbound activity; ``READ_CONVERSATION`` opens one of them and reads it in full. The split keeps LinkedIn activity proportional to real replies: the cheap scan runs on every sweep, the expensive read only where the scan saw a change. ``LIKE_LATEST_POST`` targets the contact's most recent post at the moment the browser runs it. A contact with no posts is not a failure: the driver reports the action ``SKIPPED``, and the sequence advances either way. Every profile-targeted outcome carries the facts the browser read off the profile, including the three-state Premium "Open Profile" reading; see the section comment above the outcome models.
    - `metered` boolean: Whether the step spends the sender's limits and credit. False for a step the Oneprofile team fired to test the sender: it goes first, waits for no gap and counts against no limit.
    - `outcome` union, required: What one action reported back. Consumers read the member via ``.root``. Null on the row while the action is queued or claimed, and on a row the backend settled unrun by rejection or cancellation; a document of the matching member once a browser ran it or expiry settled it.
      - ProfileViewOutcome: What a profile view saw: the common fields and the nine profile facts. The view IS the action: LinkedIn records it server-side the moment the page renders, so ``page_loaded`` is the verification and a view whose facts all read ``None`` is still a completed view. How long the browser lingered and how far it scrolled are the driver's pacing, not facts about the member, and the row's own ``duration_ms`` already says how long the step took.
        - `already_connected` boolean, nullable
        - `already_following` boolean, nullable
        - `can_send_message` boolean, nullable
        - `invitation_pending` boolean, nullable
        - `kind` 'profile_view'
        - `member_urn` string, nullable
        - `member_urns` string[]
        - `open_profile` boolean, nullable
        - `page_loaded` boolean, nullable
        - `profile_resolved` boolean, nullable
        - `reason` 'invitation_already_pending' | 'connect_not_offered' | 'email_required' | 'message_not_offered' | 'no_invitation_pending' | 'already_connected' | 'profile_not_resolved' | 'vanity_changed': The specific reason a step was not applicable, when the driver knows one. Narrower than the error code beside it: ``not_applicable`` says the step could not apply, and this says why in a way a customer can act on. An invitation already pending is their own earlier work, while "connect not offered" is LinkedIn's decision about that profile.
        - `vanity` string, nullable
      - ConnectionRequestOutcome: What sending an invitation produced: the common fields and the nine profile facts. The facts are read twice: before pressing Connect, which is what decides whether there is anything to send, and again after the invitation window closed, which is the verification. An invitation that went out shows as ``invitation_pending`` on re-reading; a re-reading that shows Connect back on offer is a failure, whatever the window did. ``member_urn`` is the load-bearing field: without it the invitation ledger row never opens and acceptance detection has nothing to match. Whether a note went with the invitation is the step's own ``note``: the browser sends exactly what it was handed or fails rather than send less, and how far a failed attempt got is the row's error message. The one exception is ``sent_without_note``: True when LinkedIn sent the invitation the moment Connect was pressed, with no window to write the note in. The invitation exists, so the step succeeded; the note it was handed did not go with it. None whenever the note went (or there was none to send). Not ``note_sent``: that key was retired, and the release task strips it from stored rows.
        - `already_connected` boolean, nullable
        - `already_following` boolean, nullable
        - `can_send_message` boolean, nullable
        - `found_already` 'earlier_attempt': Why a step that changes LinkedIn found its change already there, and settled succeeded. Only for a step whose own earlier attempt may have made it: the server had granted that attempt the press. A change already there with no grant ever was made outside Oneprofile, and settles skipped ("already pending" and so on), uncharged.
        - `invitation_pending` boolean, nullable
        - `kind` 'connection_request'
        - `member_urn` string, nullable
        - `member_urns` string[]
        - `open_profile` boolean, nullable
        - `page_loaded` boolean, nullable
        - `profile_resolved` boolean, nullable
        - `reason` 'invitation_already_pending' | 'connect_not_offered' | 'email_required' | 'message_not_offered' | 'no_invitation_pending' | 'already_connected' | 'profile_not_resolved' | 'vanity_changed': The specific reason a step was not applicable, when the driver knows one. Narrower than the error code beside it: ``not_applicable`` says the step could not apply, and this says why in a way a customer can act on. An invitation already pending is their own earlier work, while "connect not offered" is LinkedIn's decision about that profile.
        - `sent_without_note` boolean, nullable
        - `vanity` string, nullable
      - SendMessageOutcome: What sending a message produced: the common fields, the profile facts and the thread. ``conversation_urn`` is read off the opened composer when LinkedIn exposes it, which is the first moment a brand-new thread has an id at all. A reply into an existing thread happens on the messaging page, where there is no profile to read, so the profile facts stay ``None`` there and ``member_urn`` is the one the step was given; settlement falls back to the params' urn for a reply that already knew its thread.
        - `already_connected` boolean, nullable
        - `already_following` boolean, nullable
        - `can_send_message` boolean, nullable
        - `conversation_urn` string, nullable
        - `found_already` 'earlier_attempt': Why a step that changes LinkedIn found its change already there, and settled succeeded. Only for a step whose own earlier attempt may have made it: the server had granted that attempt the press. A change already there with no grant ever was made outside Oneprofile, and settles skipped ("already pending" and so on), uncharged.
        - `invitation_pending` boolean, nullable
        - `kind` 'send_message'
        - `member_urn` string, nullable
        - `member_urns` string[]
        - `open_profile` boolean, nullable
        - `page_loaded` boolean, nullable
        - `profile_resolved` boolean, nullable
        - `reason` 'invitation_already_pending' | 'connect_not_offered' | 'email_required' | 'message_not_offered' | 'no_invitation_pending' | 'already_connected' | 'profile_not_resolved' | 'vanity_changed': The specific reason a step was not applicable, when the driver knows one. Narrower than the error code beside it: ``not_applicable`` says the step could not apply, and this says why in a way a customer can act on. An invitation already pending is their own earlier work, while "connect not offered" is LinkedIn's decision about that profile.
        - `vanity` string, nullable
      - InMailOutcome: What sending an InMail produced: the common fields, the profile facts and the thread. ``conversation_urn`` links the thread exactly as it does for a direct message: read off the composer for a new thread, and the one the step was given for a reply, where the profile facts stay ``None`` because the messaging page shows no profile.
        - `already_connected` boolean, nullable
        - `already_following` boolean, nullable
        - `can_send_message` boolean, nullable
        - `conversation_urn` string, nullable
        - `found_already` 'earlier_attempt': Why a step that changes LinkedIn found its change already there, and settled succeeded. Only for a step whose own earlier attempt may have made it: the server had granted that attempt the press. A change already there with no grant ever was made outside Oneprofile, and settles skipped ("already pending" and so on), uncharged.
        - `invitation_pending` boolean, nullable
        - `kind` 'inmail'
        - `member_urn` string, nullable
        - `member_urns` string[]
        - `open_profile` boolean, nullable
        - `page_loaded` boolean, nullable
        - `profile_resolved` boolean, nullable
        - `reason` 'invitation_already_pending' | 'connect_not_offered' | 'email_required' | 'message_not_offered' | 'no_invitation_pending' | 'already_connected' | 'profile_not_resolved' | 'vanity_changed': The specific reason a step was not applicable, when the driver knows one. Narrower than the error code beside it: ``not_applicable`` says the step could not apply, and this says why in a way a customer can act on. An invitation already pending is their own earlier work, while "connect not offered" is LinkedIn's decision about that profile.
        - `vanity` string, nullable
      - WithdrawConnectionRequestOutcome: What withdrawing an invitation produced: the common fields and the nine profile facts. ``invitation_pending`` re-read after the click is the verification. The ledger row settles on the params' urn, not on anything read here.
        - `already_connected` boolean, nullable
        - `already_following` boolean, nullable
        - `can_send_message` boolean, nullable
        - `found_already` 'earlier_attempt': Why a step that changes LinkedIn found its change already there, and settled succeeded. Only for a step whose own earlier attempt may have made it: the server had granted that attempt the press. A change already there with no grant ever was made outside Oneprofile, and settles skipped ("already pending" and so on), uncharged.
        - `invitation_pending` boolean, nullable
        - `kind` 'withdraw_connection_request'
        - `member_urn` string, nullable
        - `member_urns` string[]
        - `open_profile` boolean, nullable
        - `page_loaded` boolean, nullable
        - `profile_resolved` boolean, nullable
        - `reason` 'invitation_already_pending' | 'connect_not_offered' | 'email_required' | 'message_not_offered' | 'no_invitation_pending' | 'already_connected' | 'profile_not_resolved' | 'vanity_changed': The specific reason a step was not applicable, when the driver knows one. Narrower than the error code beside it: ``not_applicable`` says the step could not apply, and this says why in a way a customer can act on. An invitation already pending is their own earlier work, while "connect not offered" is LinkedIn's decision about that profile.
        - `vanity` string, nullable
      - FollowContactOutcome: What following a member produced: the common fields and the nine profile facts. ``already_following`` re-read after the click is the verification.
        - `already_connected` boolean, nullable
        - `already_following` boolean, nullable
        - `can_send_message` boolean, nullable
        - `found_already` 'earlier_attempt': Why a step that changes LinkedIn found its change already there, and settled succeeded. Only for a step whose own earlier attempt may have made it: the server had granted that attempt the press. A change already there with no grant ever was made outside Oneprofile, and settles skipped ("already pending" and so on), uncharged.
        - `invitation_pending` boolean, nullable
        - `kind` 'follow_contact'
        - `member_urn` string, nullable
        - `member_urns` string[]
        - `open_profile` boolean, nullable
        - `page_loaded` boolean, nullable
        - `profile_resolved` boolean, nullable
        - `reason` 'invitation_already_pending' | 'connect_not_offered' | 'email_required' | 'message_not_offered' | 'no_invitation_pending' | 'already_connected' | 'profile_not_resolved' | 'vanity_changed': The specific reason a step was not applicable, when the driver knows one. Narrower than the error code beside it: ``not_applicable`` says the step could not apply, and this says why in a way a customer can act on. An invitation already pending is their own earlier work, while "connect not offered" is LinkedIn's decision about that profile.
        - `vanity` string, nullable
      - LikeLatestPostOutcome: What liking the latest post produced: the common fields and the nine profile facts. A member with no posts is skipped, not failed.
        - `already_connected` boolean, nullable
        - `already_following` boolean, nullable
        - `can_send_message` boolean, nullable
        - `found_already` 'earlier_attempt': Why a step that changes LinkedIn found its change already there, and settled succeeded. Only for a step whose own earlier attempt may have made it: the server had granted that attempt the press. A change already there with no grant ever was made outside Oneprofile, and settles skipped ("already pending" and so on), uncharged.
        - `invitation_pending` boolean, nullable
        - `kind` 'like_latest_post'
        - `member_urn` string, nullable
        - `member_urns` string[]
        - `open_profile` boolean, nullable
        - `page_loaded` boolean, nullable
        - `profile_resolved` boolean, nullable
        - `reason` 'invitation_already_pending' | 'connect_not_offered' | 'email_required' | 'message_not_offered' | 'no_invitation_pending' | 'already_connected' | 'profile_not_resolved' | 'vanity_changed': The specific reason a step was not applicable, when the driver knows one. Narrower than the error code beside it: ``not_applicable`` says the step could not apply, and this says why in a way a customer can act on. An invitation already pending is their own earlier work, while "connect not offered" is LinkedIn's decision about that profile.
        - `vanity` string, nullable
      - CheckSentInvitationsOutcome: One reading of the sent-invitations list, as the members still outstanding. A list with no rows is only ``complete`` when LinkedIn's own empty-list surface was seen, because the list loading is the action here and a list that never rendered is an honest failure. Every urn the reading omits is marked disappeared, a state that does not recover on the next reading, so a reading with a row it could not name is reported and never complete. The list LinkedIn renders now names each member only by the ``/in/<slug>`` of their profile link, and draws the newest rows first with the rest loading on scroll, which a background tab never does: ``newest_only`` is such a reading, the top of the list and not all of it, and is judged only as far down as it reaches (see ``sql_repository.apply_sweep``).
        - `complete` boolean
        - `kind` 'check_sent_invitations'
        - `newest_only` boolean
        - `page_loaded` boolean, nullable
        - `reason` 'invitation_already_pending' | 'connect_not_offered' | 'email_required' | 'message_not_offered' | 'no_invitation_pending' | 'already_connected' | 'profile_not_resolved' | 'vanity_changed': The specific reason a step was not applicable, when the driver knows one. Narrower than the error code beside it: ``not_applicable`` says the step could not apply, and this says why in a way a customer can act on. An invitation already pending is their own earlier work, while "connect not offered" is LinkedIn's decision about that profile.
        - `still_pending_urns` string[]
        - `still_pending_vanities` string[]
      - CheckConnectionsOutcome: One reading of our own connections list, newest connection first. ``rows`` are what the browser reported; the server keeps them on the reading's own log row and strips them from here before the action settles, so this document ends up with the counts only (``rows_read``, ``rows_unread``) and nobody's key outlives its retention on a step's row. ``member_urn`` echoes the salt every key was hashed with (``CheckConnectionsParams.member_urn``); a reading salted for any member but the account's proves nothing. ``sort_proven`` says the list was shown "Recently added" by a token that cannot be a position or a word; a reading that cannot prove it is never judged, though the members it lists are still matched. ``stable`` says two snapshots taken apart agreed. ``empty`` is LinkedIn's own empty-list surface. ``pages`` counts the batches read (one, plus each "Show more"), ``more_available`` whether LinkedIn offered another, ``reached_watermark`` whether the reading got back to the last one's rows. ``named_by`` says which spellings rows carried, which decides which ledger rows a reading can match at all.
        - `complete` boolean
        - `empty` boolean
        - `kind` 'check_connections'
        - `member_urn` string, required
        - `more_available` boolean, nullable
        - `named_by` string[]
        - `page_loaded` boolean, nullable
        - `pages` integer
        - `reached_watermark` boolean, nullable
        - `reason` 'invitation_already_pending' | 'connect_not_offered' | 'email_required' | 'message_not_offered' | 'no_invitation_pending' | 'already_connected' | 'profile_not_resolved' | 'vanity_changed': The specific reason a step was not applicable, when the driver knows one. Narrower than the error code beside it: ``not_applicable`` says the step could not apply, and this says why in a way a customer can act on. An invitation already pending is their own earlier work, while "connect not offered" is LinkedIn's decision about that profile.
        - `rows` LinkedInConnectionRow[]
          - `keys` string[]
        - `rows_read` integer, nullable
        - `rows_unread` integer, nullable
        - `sort_proven` boolean, nullable
        - `stable` boolean, nullable
      - CheckInboxOutcome: One reading of the conversation list. Rows the scan could not name are why a reading is not ``complete``.
        - `complete` boolean
        - `conversations` LinkedInConversationRow[]
          - `conversation_urn` string, nullable
          - `last_activity_at` string, date-time, nullable
          - `last_inbound_at` string, date-time, nullable
          - `member_urn` string, required
          - `snippet` string
        - `kind` 'check_inbox'
        - `page_loaded` boolean, nullable
        - `reason` 'invitation_already_pending' | 'connect_not_offered' | 'email_required' | 'message_not_offered' | 'no_invitation_pending' | 'already_connected' | 'profile_not_resolved' | 'vanity_changed': The specific reason a step was not applicable, when the driver knows one. Narrower than the error code beside it: ``not_applicable`` says the step could not apply, and this says why in a way a customer can act on. An invitation already pending is their own earlier work, while "connect not offered" is LinkedIn's decision about that profile.
      - ReadConversationOutcome: A full reading of one conversation, oldest message first. Which thread this was is the step's own ``conversation_id``, on the same row; the outcome does not repeat it, so there is no field a settlement written without a driver (expiry, restart, a navigation that never landed) can forget. The read watermark only advances on a ``complete`` reading.
        - `complete` boolean
        - `kind` 'read_conversation'
        - `messages` LinkedInMessageRead[]
          - `body_text` string
          - `is_inbound` boolean, required
          - `message_urn` string, required
          - `sender_member_urn` string, required
          - `sender_name` string
          - `sent_at` string, date-time, nullable
        - `page_loaded` boolean, nullable
        - `reason` 'invitation_already_pending' | 'connect_not_offered' | 'email_required' | 'message_not_offered' | 'no_invitation_pending' | 'already_connected' | 'profile_not_resolved' | 'vanity_changed': The specific reason a step was not applicable, when the driver knows one. Narrower than the error code beside it: ``not_applicable`` says the step could not apply, and this says why in a way a customer can act on. An invitation already pending is their own earlier work, while "connect not offered" is LinkedIn's decision about that profile.
    - `params` union, required: What one action is asked to do. Consumers read the member via ``.root``.
      - ProfileViewParams: Open a member's profile and read it like a person would. The navigation is the action. ``profile_url`` is where the browser goes first. ``read_relationship`` asks the browser to settle whether the member is connected or has our invitation pending even where the action bar does not show it, by opening the member's own More menu and reading it: the look a connection request queues when its answer cannot be told any other way.
        - `kind` 'profile_view'
        - `profile_url` string, required
        - `read_relationship` boolean
      - ConnectionRequestParams: Send an invitation, with or without a note. ``profile_url`` is where the browser goes first. ``note`` is what the browser types, already clipped to LinkedIn's ceiling on a word boundary. ``note_original_length`` is how long the rendered note was before clipping, the one number nothing can recover once the text is gone, so history can say who lost text and how much. Whether it was cut follows from the two and is not stored beside them.
        - `kind` 'connection_request'
        - `note` string
        - `note_original_length` integer
        - `profile_url` string, required
      - SendMessageParams: Send a direct message, either from a profile or into an existing thread. Two producers disagree about where a message goes, and both are right: a sequence step knows only the contact's profile, while a reply from the Oneprofile inbox knows the thread and not the profile. Rather than make every field optional and hope, the validator requires at least one usable target. ``member_urn`` travels with a thread reply because the messaging page shows no profile to read it off; the browser hands it back unchanged as the outcome's ``member_urn``, which is what the conversation row is keyed on. ``body_original_length`` follows the rule ``note_original_length`` sets on a connection request: the body was cut exactly when it is longer than ``body``.
        - `body` string, required
        - `body_original_length` integer
        - `conversation_urn` string, nullable
        - `kind` 'send_message'
        - `member_urn` string, nullable
        - `profile_url` string, nullable
      - InMailParams: Send an InMail: a message with a subject line, priced against the sender's own credits. Addressed exactly like a direct message: a sequence step names the contact's profile, an inbox reply names the thread, and the validator requires one of the two. ``member_urn`` travels with a thread reply because the messaging page shows no profile to read it off, and comes back unchanged on the outcome. The subject has its own, much lower ceiling. Each text carries its length before clipping under the same rule as every other clipped field.
        - `body` string, required
        - `body_original_length` integer
        - `conversation_urn` string, nullable
        - `kind` 'inmail'
        - `member_urn` string, nullable
        - `profile_url` string, nullable
        - `subject` string
        - `subject_original_length` integer
      - WithdrawConnectionRequestParams: Withdraw an invitation this sender already sent. ``profile_url`` is where the browser goes first. ``member_urn`` is required, unlike everywhere else: the ledger row is keyed on it, the dispatcher read it off that row when it decided to withdraw, and settlement closes the row by it.
        - `kind` 'withdraw_connection_request'
        - `member_urn` string, required
        - `profile_url` string, required
      - FollowContactParams: Follow a member without connecting to them. ``profile_url`` is where the browser goes.
        - `kind` 'follow_contact'
        - `profile_url` string, required
      - LikeLatestPostParams: Like whatever the member posted most recently, if anything. Starts at ``profile_url``.
        - `kind` 'like_latest_post'
        - `profile_url` string, required
      - CheckSentInvitationsParams: Housekeeping: read our own sent-invitations list. Takes nothing.
        - `kind` 'check_sent_invitations'
      - CheckConnectionsParams: Housekeeping: read the newest rows of our own connections list. ``member_urn`` is the account's member as the server holds it: the exact string the browser salts every row key with, and echoes on the outcome. ``watermark_keys`` are the keys of the rows the last reading saw, newest first, so the browser knows how far back "Show more" needs to go; it may press it at most ``max_more`` times. Both are empty and zero on a first reading, which is then judged on its first batch alone, and the keys are sent only while "Show more" may be pressed at all.
        - `kind` 'check_connections'
        - `max_more` integer
        - `member_urn` string, required
        - `watermark_keys` string[]
      - CheckInboxParams: Housekeeping: scan the conversation list for new inbound activity. Takes nothing.
        - `kind` 'check_inbox'
      - ReadConversationParams: Housekeeping: open one thread the scan flagged and read it in full. ``conversation_id`` names our own row, which is where the reading lands. ``conversation_urn`` is LinkedIn's name for the thread and is what the browser navigates to; a scan hit with no urn is never queued, because there is nothing to open. It is still optional for this release only because stored rows could not be checked before the deploy: it becomes required once a pre-deploy check shows no read_conversation row without one, and until then every consumer keeps handling None. The member's own urn is not carried: it already sits on the row this id names, and neither the browser nor the service ever read the copy.
        - `conversation_id` string, uuid, required
        - `conversation_urn` string, nullable
        - `kind` 'read_conversation'
        - `replies_after` string, date-time, nullable: Only on the read right after a message: a reply is an inbound message LinkedIn dates after this moment, the last reading of the whole inbox. The thread's older history is no reply.
    - `queued_at` string, date-time, required
    - `requires_approval` boolean: Always false: a step that asks for a person waits on Oneprofile's Tasks page, never in the line. Kept for older extension builds.
    - `status` 'queued' | 'resuming' | 'claimed' | 'succeeded' | 'failed' | 'rejected' | 'skipped', required: Where one queued action stands. Governor-deferral is deliberately NOT a status: an action whose kind has hit its daily cap stays ``QUEUED`` and is simply not handed out, with the reason travelling on the hand-out response instead. A ``DEFERRED`` row would make the queue lie about what is still waiting to happen. ``SKIPPED`` is "not applicable" (no LinkedIn URL, already connected); ``REJECTED`` is "a human refused it"; ``FAILED`` is "we tried and could not". ``RESUMING`` is a step put back in line after an attempt that may have acted on LinkedIn (its browser was granted the press, or lost track of the step): it is never "queued", so no path that assumes nothing happened yet (moving it to another sender, a plain copy, "did not run") ever reads it as such, and it is handed only to a browser that checks LinkedIn before acting.
  - `stats` LinkedInStatsReadSchema, required
    - `avg_duration_ms` integer, nullable, required
    - `failed` integer, required
    - `rejected` integer, required
    - `skipped` integer, required
    - `succeeded` integer, required
    - `total` integer, required
  - `total` integer, required

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: No LinkedIn account for this sender.
- `422`: Invalid request made to the API.
- `500`: Internal server error.
- `502`: Bad gateway error.
- `503`: Service unavailable.

---

[API](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile.md) · [All operations](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/llms.txt) · [OpenAPI document](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/revisions/4a6bd71e3f02?raw)
