# Connection Sessions

Connect and reconnect LinkedIn accounts through hosted connection sessions, track their status, and customize what the user sees when the connection completes.

A connection session is a short-lived link that opens the Linked API connection page, where the end user logs into LinkedIn. Use it to connect a new account or to reconnect one whose LinkedIn session has expired. For the accounts themselves, see [Accounts](/docs/admin-accounts).

## accounts.createConnectionSession

Create a new connection session to connect a LinkedIn account. Returns a link that opens the connection page where the user logs into LinkedIn.

```bash
curl -X POST https://api.linkedapi.io/admin/accounts.createConnectionSession \
  -H "Content-Type: application/json" \
  -H "linked-api-token: linked_your_token_here"
```

**Body:**

The body is optional. Send no body at all to get the default behavior, or pass `completion` to control what the user sees after a successful connection – see [Customizing the completion screen](#customizing-the-completion-screen).

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `completion` | `object` | No | Post-connection behavior of the connection page |

**Response:**

```json
{
  "success": true,
  "result": {
    "sessionId": "990eef7a-...",
    "connectionLink": "https://app.linkedapi.io/connection/990eef7a-..."
  }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `sessionId` | `string` | Session UUID for tracking |
| `connectionLink` | `string` | URL to open in a browser to complete the LinkedIn login |

**Errors:**

| Type | Description |
|------|-------------|
| `noAvailableSeats` | No available seats – all seats are occupied by active accounts or pending connection sessions |
| `dailyConnectionAttemptsExceeded` | Too many connection attempts in the last 24 hours |
| `invalidRequestPayload` | The `completion` object is malformed – the message names the offending field |

> **Flow:** Create session → open `connectionLink` → user logs into LinkedIn → poll `accounts.getConnectionSession` until status is `success` → account appears in `accounts.getAll`.

## accounts.createReconnectionSession

Create a new reconnection session for an account whose status is `reconnection_required`. If the account already has an active reconnection session, it is cancelled and replaced with a new one. If an in-progress reconnection session is applying a pending proxy change, Linked API returns that existing session instead so the proxy change state is preserved.

```bash
curl -X POST https://api.linkedapi.io/admin/accounts.createReconnectionSession \
  -H "Content-Type: application/json" \
  -H "linked-api-token: linked_your_token_here" \
  -d '{"accountId": "a91c20f8-..."}'
```

**Body:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `accountId` | `string` | Yes | UUID of the account to reconnect |
| `completion` | `object` | No | Post-connection behavior of the connection page – see [Customizing the completion screen](#customizing-the-completion-screen) below |

**Response:**

```json
{
  "success": true,
  "result": {
    "reconnectionSessionId": "d90ac1f6-...",
    "reconnectionLink": "https://app.linkedapi.io/connection/d90ac1f6-..."
  }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `reconnectionSessionId` | `string` | Session UUID for tracking |
| `reconnectionLink` | `string` | URL to open in a browser to complete LinkedIn reconnection |

**Errors:**

| Type | Description |
|------|-------------|
| `invalidRequestPayload` | The account is not in `reconnection_required` status or cannot be reconnected |

> **Flow:** Create reconnection session → open `reconnectionLink` → user logs into LinkedIn → poll `accounts.getConnectionSession` with `reconnectionSessionId` until status is `success`.

## accounts.getConnectionSession

Check the status of a connection session.

```bash
curl -X POST https://api.linkedapi.io/admin/accounts.getConnectionSession \
  -H "Content-Type: application/json" \
  -H "linked-api-token: linked_your_token_here" \
  -d '{"sessionId": "990eef7a-..."}'
```

**Body:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `sessionId` | `string` | Yes | Session UUID |

**Response:**

```json
{
  "success": true,
  "result": {
    "session": {
      "sessionId": "990eef7a-...",
      "status": "pending",
      "type": "initial"
    }
  }
}
```

**Session statuses:**

| Status | Description |
|--------|-------------|
| `pending` | Session created, waiting for user to open the link |
| `preparing` | Browser is being provisioned |
| `serving` | Browser is ready, waiting for user to connect |
| `streaming` | User is connected and logging in |
| `success` | Login completed, account is being created |
| `expired` | Session timed out |
| `error` | An error occurred |
| `cancelled` | Session was cancelled |

## accounts.cancelConnectionSession

Cancel an active connection session.

```bash
curl -X POST https://api.linkedapi.io/admin/accounts.cancelConnectionSession \
  -H "Content-Type: application/json" \
  -H "linked-api-token: linked_your_token_here" \
  -d '{"sessionId": "990eef7a-..."}'
```

**Body:**

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `sessionId` | `string` | Yes | Session UUID |

**Response:**

```json
{
  "success": true,
  "result": {}
}
```

## Customizing the completion screen

After the user finishes logging into LinkedIn, the connection page shows a completion screen. The optional `completion` object accepted by `accounts.createConnectionSession` and `accounts.createReconnectionSession` controls what happens there – which is what you need when the connection page is embedded into your own product. Every field is optional, and omitting `completion` entirely keeps the default behavior.

```json
{
  "completion": {
    "limits": { "mode": "ask" },
    "button": { "text": "Back to Acme", "url": "https://app.acme.com/accounts" },
    "successUrl": "https://app.acme.com/connected?session={SESSION_ID}",
    "failureUrl": "https://app.acme.com/failed?session={SESSION_ID}"
  }
}
```

| Field | Type | Description |
|-------|------|-------------|
| `limits.mode` | `string` | How account limits are handled: `ask` (default), `defaults`, or `skip`. Ignored on reconnection, which never changes an account's limits |
| `button.text` | `string` | Label of a custom button shown on the completion screen (1–40 characters) |
| `button.url` | `string` | Where that button leads. `https` only, no placeholders |
| `successUrl` | `string` | Redirect the user here after a successful connection, without any click |
| `failureUrl` | `string` | Redirect the user here when the session ends in `error` or `expired` |

**Limits modes:**

| Mode | Behavior |
|------|----------|
| `ask` | Default. The user is shown the limits editor and chooses the values themselves |
| `defaults` | Recommended limits are applied automatically, the editor is not shown |
| `skip` | No limits are configured and the editor is not shown. The account runs without limits until you set them via [`limits.set`](/docs/admin-limits) |

**Default button:** sessions created through this API do not show the "Continue to platform" button, because it leads to the Linked API dashboard, which your users have no access to. Instead, the completion screen tells the user the account is connected and the tab can be closed. To send the user somewhere, pass either `button` (they leave by clicking) or `successUrl` (they leave immediately). Passing both is rejected: with an instant redirect the button would never be visible.

**Placeholders:** `successUrl` and `failureUrl` may contain placeholders that are substituted before the redirect. Values are URL-encoded.

| Placeholder | Available in | Value |
|-------------|--------------|-------|
| `{SESSION_ID}` | `successUrl`, `failureUrl` | UUID of the connection session |
| `{ACCOUNT_ID}` | `successUrl` | UUID of the account that was just connected |
| `{ERROR_TYPE}` | `failureUrl` | Error type of the failed session, empty for `expired` |

Any other placeholder, an unbalanced brace, or a non-`https` URL is rejected with `invalidRequestPayload` when the session is created – not later, when the user is already on the page.

**When the redirect happens:**

- `successUrl` fires after the limits step. With `mode: "ask"` that means after the user saves or skips the limits editor; with `defaults` or `skip` it fires as soon as the connection succeeds.
- `failureUrl` covers the `error` and `expired` statuses. Sessions you cancel yourself through `accounts.cancelConnectionSession` do not redirect, and neither does an invalid or unknown session link, since there is no configuration to read for it.

> **Do not rely on the redirect for fulfillment.** The user may close the tab or lose connectivity before it happens. Treat arriving on `successUrl` as a hint and confirm the real outcome with `accounts.getConnectionSession`.

**Examples:**

Fully embedded – no questions asked, the user is returned to your product in both outcomes:

```bash
curl -X POST https://api.linkedapi.io/admin/accounts.createConnectionSession \
  -H "Content-Type: application/json" \
  -H "linked-api-token: linked_your_token_here" \
  -d '{
    "completion": {
      "limits": { "mode": "defaults" },
      "successUrl": "https://app.acme.com/connected?session={SESSION_ID}&account={ACCOUNT_ID}",
      "failureUrl": "https://app.acme.com/failed?session={SESSION_ID}&error={ERROR_TYPE}"
    }
  }'
```

Keep the limits editor, but finish with your own button instead of an automatic redirect:

```bash
curl -X POST https://api.linkedapi.io/admin/accounts.createConnectionSession \
  -H "Content-Type: application/json" \
  -H "linked-api-token: linked_your_token_here" \
  -d '{
    "completion": {
      "limits": { "mode": "ask" },
      "button": { "text": "Back to Acme", "url": "https://app.acme.com/accounts" }
    }
  }'
```
