> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oneshotagent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Connect a LinkedIn Account

> Create a hosted LinkedIn login link for a human to connect their own account and grant the agent specific actions.

Creates an expiring **connection intent** and returns a hosted LinkedIn login URL. Free. Authenticated with the signed agent proof (`x-agent-proof`), which the SDKs sign for you.

The human opens the URL and signs in (password, SSO, 2FA, whatever their account needs). OneShot is notified when the login completes. Ownership is bound to your agent **only after OneShot verifies the account server-side**. The redirect the human sees proves nothing on its own.

<Warning>
  The `url` is returned exactly once and is never stored or logged. Hand it to the human directly. It expires in 30 minutes. Open it top-level, because the hosted page cannot solve LinkedIn's captcha inside an iframe.
</Warning>

## Request

<ParamField body="requested_actions" type="string[]" required>
  The actions you are asking the human to grant: any of `read`, `reply`, `view_profile`, `react`, `invite` (`comment` is reserved). Ask only for what the agent needs. The human sees this list as the scope of what they are delegating.
</ParamField>

<ParamField body="success_redirect_url" type="string" optional>
  `https://` URL the human is sent to after connecting. No embedded credentials.
</ParamField>

<ParamField body="failure_redirect_url" type="string" optional>
  `https://` URL on failure.
</ParamField>

An agent can have at most 3 connection links open (pending, unexpired) at a time. A fourth returns `409 intent_pending`.

## Response

```json theme={null}
{
  "intent_id": "8f0c…",
  "type": "create",
  "url": "https://account.unipile.com/…",
  "expires_at": "2026-09-15T12:30:00.000Z",
  "status": "pending",
  "requested_actions": ["read", "reply"]
}
```

## Poll the intent

`GET /v1/tools/linkedin/connect/{intent_id}` (free, proof-signed) returns the intent with `status` ∈ `pending` · `verifying` · `completed` · `failed` · `expired` · `cancelled`. `completed` carries the connected `account`. `failed` carries a `failure_reason`:

| `failure_reason` | Meaning |
| - | - |
| `callback_mismatch` | The provider's notification did not match this intent. Nothing was bound. |
| `wrong_provider_type` | The connected account is not a LinkedIn account. |
| `source_not_ok` | LinkedIn reported the session as not usable right after login. Try again. |
| `identity_mismatch` | On reconnect: a different LinkedIn identity signed in. |
| `duplicate_member` | This human is already connected to this agent (the duplicate upstream connection was deleted). |
| `account_not_found` / `verify_exhausted` | The account could not be read back from the provider. |

## Reconnect

Reconnect keeps the account and its history while the human re-authenticates. `POST /v1/tools/linkedin/accounts/{id}/reconnect` issues a hosted link for an existing account, to restore an expired session or to add granted actions while it is still connected. The intent type is `reconnect`.

The human must complete fresh hosted authentication as the same LinkedIn identity. Creating the link or following the browser redirect does not change permissions. Poll the intent until `completed` confirms server-side verification.

`requested_actions` is the **complete desired grant**, not just the actions to add. Include existing actions you want to keep. Omit the field to keep the current grant. Changing the set increments `grant_version` and refreshes the grant timestamp. An unchanged set keeps its version.

The account ID, messages, conversations, coverage, and sync progress stay intact, with no new history enumeration. Adding actions preserves queued writes. Removing any action invalidates older queued writes, even if it is added back later. Revoked or deleted upstream accounts still require a new connection.

For an account whose complete current grant is `read`, `reply`, and `view_profile` (and no other actions), add `invite` like this:

```typescript theme={null}
const intent = await agent.reconnectLinkedInAccount(account.id, {
  requestedActions: ['read', 'reply', 'view_profile', 'invite'],
});
// Show intent.url to the human, then poll getLinkedInConnection(intent.intent_id).
```

```python theme={null}
intent = client.reconnect_linkedin_account(
    account["id"], requested_actions=list(dict.fromkeys([*account["allowed_actions"], "invite"]))
)
# Async: await client.areconnect_linkedin_account(...).
# Show intent["url"] to the human, then poll get_linkedin_connection(intent["intent_id"]).
```

## Example

```typescript theme={null}
const intent = await agent.linkedinConnect({ requestedActions: ['read', 'reply'] });
// show intent.url to the human …
let status = await agent.getLinkedInConnection(intent.intent_id);
while (status.status === 'pending' || status.status === 'verifying') {
  await new Promise((r) => setTimeout(r, 5_000));
  status = await agent.getLinkedInConnection(intent.intent_id);
}
if (status.status === 'completed') console.log(status.account!.id, status.account!.allowed_actions);
```

```python theme={null}
intent = client.linkedin_connect(["read", "reply"])
# show intent["url"] to the human …
status = client.get_linkedin_connection(intent["intent_id"])
```

## Errors

| Status | `error` | Meaning |
| - | - | - |
| 400 | `invalid_request` | Unknown action, non-https redirect, or empty list. |
| 409 | `intent_pending` | Three links are already open. Wait for one to complete or expire. |
| 502 | `linkedin_link_unavailable` | The connection provider could not issue a link. Retry shortly. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.