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

# Linked accounts

> List, read, and remove the accounts of an authorization, and select one with the x-linked-account-id header

A linked account is one OnlyFans account. When an owner authorizes your app, they share specific linked accounts with specific scopes. Every Access call targets exactly one linked account.

All routes on this page take the bearer access token. They only ever return the accounts of that token's authorization. There is no route that lists accounts across authorizations.

## Where linked account ids come from

| Source | When |
| - | - |
| `linked_accounts.data[].id` in the [token response](/v3/tokens#token-response) | At code exchange and on every refresh (first page). |
| `GET /v3/linked-accounts` | Any time you hold a valid access token. |
| `linked_account_id` and `linked_account_ids` in [lifecycle webhooks](/v3/webhooks#lifecycle-events) | When an authorization or account changes. |

The same OnlyFans account in Sandbox, Test, and Live has different ids.

## List linked accounts

```http theme={null}
GET https://api.ofauth.com/v3/linked-accounts
Authorization: Bearer <access_token>
```

| Query parameter | Description |
| - | - |
| `limit` | Page size, `1` to `100`. Defaults to `50`. |
| `cursor` | `next_cursor` from the previous page. |

<CodeGroup>
  ```bash curl theme={null}
  curl "https://api.ofauth.com/v3/linked-accounts?limit=50" \
    -H "Authorization: Bearer $ACCESS_TOKEN"
  ```

  ```javascript Node.js theme={null}
  async function listLinkedAccounts(accessToken) {
    const accounts = []
    let cursor = null
    do {
      const url = new URL("https://api.ofauth.com/v3/linked-accounts")
      url.searchParams.set("limit", "100")
      if (cursor) url.searchParams.set("cursor", cursor)
      const response = await fetch(url, {
        headers: { authorization: `Bearer ${accessToken}` }
      })
      if (!response.ok) throw new Error((await response.json()).error)
      const page = await response.json()
      accounts.push(...page.data)
      cursor = page.next_cursor
    } while (cursor)
    return accounts
  }
  ```
</CodeGroup>

```json theme={null}
{
  "data": [
    {
      "id": "conn_example123",
      "provider": "onlyfans",
      "provider_account_id": "123456789",
      "username": "creator",
      "display_name": "Creator Name",
      "status": "connected",
      "scopes": ["profile:read", "messages:read"],
      "granted_at": "2026-10-01T12:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

Accounts are ordered by `id`. A bad `limit` or `cursor` returns `400` `invalid_request`.

| Field | Description |
| - | - |
| `id` | Send this as `x-linked-account-id`. |
| `provider` | `onlyfans`. |
| `provider_account_id` | The stable OnlyFans user id. |
| `username` | Current OnlyFans username, or `null`. Can change. |
| `display_name` | Current OnlyFans display name, or `null`. Can change. |
| `status` | `connected`, `needs_reconnect`, or `disconnected`. |
| `scopes` | Scopes granted to your app for this account. |
| `granted_at` | When the account was shared with this authorization. |

## Get a linked account

```http theme={null}
GET https://api.ofauth.com/v3/linked-accounts/{id}
Authorization: Bearer <access_token>
```

Returns one account in the shape above. An id that is not part of this authorization returns `404` `linked_account_not_found` with `recovery.action` `list_linked_accounts`.

## Remove a linked account

```http theme={null}
DELETE https://api.ofauth.com/v3/linked-accounts/{id}
Authorization: Bearer <access_token>
```

Removes this authorization's access to one account and returns `204`. The account itself, the rest of the authorization, and other apps' access are unaffected. Repeating the call succeeds. Your webhook destinations receive `linked_account.disconnected` with `reason: "developer_deleted"`.

To end the whole authorization instead, [revoke](/v3/tokens#revoke) its sessions.

## Linked account settings

Per-account settings, such as Vault+, are managed through the API, not the dashboard.

```http theme={null}
GET https://api.ofauth.com/v3/linked-accounts/{id}/settings
PATCH https://api.ofauth.com/v3/linked-accounts/{id}/settings
```

```json theme={null}
{
  "linked_account_id": "conn_example123",
  "vault_plus": { "enabled": true }
}
```

`PATCH` takes exactly `{ "vault_plus": { "enabled": boolean } }` and returns the updated settings.

* `PATCH` needs the `vault:write` scope on that account, or it returns `403` `missing_scope`.
* Vault+ must be on for the environment. Test accounts need a paid Developer plan (`403` `subscription_required`).
* Sandbox accounts always have Vault+ on. `PATCH` in Sandbox returns `409` `linked_account_settings_fixed`.
* A concurrent change returns `409` `linked_account_settings_conflict` with `recovery.action` `retry`.

See [Vault+](/v3/vault-plus).

## Linked account status

| Status | Meaning | What to do |
| - | - | - |
| `connected` | Usable. | Call Access normally. |
| `needs_reconnect` | The OnlyFans session is no longer usable. Your access remains. | Ask the owner to reconnect. Access calls return `linked_account_reconnect_required`. You receive `linked_account.reconnected` when it works again. |
| `disconnected` | The owner disconnected the account. | Stop calling Access for it. |

## Select a linked account

Send the linked account id on every Access and Vault+ call.

```http theme={null}
GET https://api.ofauth.com/v3/access/profile
Authorization: Bearer <access_token>
x-linked-account-id: conn_example123
```

Missing header: `403` `linked_account_required`. An account that is not part of this authorization: `403` `linked_account_not_granted`. See [Errors](/v3/errors).

## Several accounts per authorization

One authorization can cover several linked accounts, for example an agency that manages several creators. Store the account ids per `authorization_id` and let the user choose which account to operate. The access token works for every account in the authorization; only the header changes.

Keep your list current with [lifecycle webhooks](/v3/webhooks#lifecycle-events): `authorization.updated` reports added and removed accounts, and `linked_account.disconnected` reports a single removed account.


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