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

# Multi-Tenant Accounts

> Serve many customers from one StackOne setup — isolate their accounts and manage them by owner.

You serve your end-users, and each of them connects their own provider account — which becomes a [linked account](/gateway/concepts/linked-accounts) you call against. One StackOne setup can serve many customers, and the `origin_owner_id` field is your key to segmenting accounts by owner.

## How customers stay isolated

* **Linked accounts** keep each customer's credentials separate. You target a specific account when making a request, so one customer's data never crosses into another's.
* **Connector profiles** define the connectors and auth available to your customers. You can run multiple profiles per connector — for example, your own OAuth app for some customers and API-key auth for others.
* **Projects** isolate at a higher level. Use separate [projects](/gateway/concepts/organizations-and-projects) when customers need different regions, environments, or API keys.

```mermaid theme={null}
graph TD
    Org["Your organization"] --> P1["Project: production (EU)"]
    Org --> P2["Project: dev (US)"]
    P1 --> CP1["HubSpot profile - your OAuth app"]
    P1 --> CP2["HubSpot profile - API key auth"]
    P1 --> CP3["Workday profile"]
    CP1 --> A1["Linked account - Client A"]
    CP1 --> A2["Linked account - Client B"]
    CP2 --> A3["Linked account - Client C"]
    CP3 --> A4["Linked account - Client A"]

    style Org fill:#0f172a,stroke:#334155,color:#fff
    style P1 fill:#10b981,stroke:#059669,color:#fff
    style P2 fill:#10b981,stroke:#059669,color:#fff
    style CP1 fill:#ecfdf5,stroke:#10b981,color:#065f46
    style CP2 fill:#ecfdf5,stroke:#10b981,color:#065f46
    style CP3 fill:#ecfdf5,stroke:#10b981,color:#065f46
```

## Identifying the client on each account

Every linked account carries the **origin owner** fields set when the account is linked — whether through the dashboard's Link Account flow, the Hub, or a [connect session](/embed/connect-session):

* **Origin Owner ID** (`origin_owner_id`) — your identifier for the client's organization. This is how StackOne knows which of your clients an account belongs to.
* **Origin Owner Name** (`origin_owner_name`) — a human-readable name stored against the account, used across the dashboard (for example, the Account filter in [Request Logs](/connect/troubleshooting)).
* **Origin Username** (`origin_username`, optional) — the end-user's username, e.g. an email address.

The `origin_owner_id` + `origin_owner_name` + `provider` combination decides whether linking creates a new account or updates an existing one — see [New or existing account](/embed/connect-session#new-or-existing-account).

## Provisioning at scale

Onboarding a new customer doesn't require manual setup per account. Configure each connector once in the project, and new customers link themselves against it.

<Note>
  For account-region requirements or a setup serving many customers, talk to your StackOne account manager about the right project structure.
</Note>

## List accounts by owner

Use the `GET /accounts` endpoint with the `origin_owner_id` query parameter to filter accounts for a specific customer:

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    curl "https://api.stackone.com/accounts?origin_owner_id=owner_123" \
      -u "$STACKONE_API_KEY:"
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import requests
    import base64
    import os

    api_key = os.environ["STACKONE_API_KEY"]
    headers = {
        "Authorization": f"Basic {base64.b64encode(f'{api_key}:'.encode()).decode()}"
    }

    response = requests.get(
        "https://api.stackone.com/accounts",
        params={"origin_owner_id": "owner_123"},
        headers=headers
    )

    accounts = response.json()["data"]
    ```
  </Tab>

  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    import { StackOne } from "@stackone/stackone-client-ts";

    const stackOne = new StackOne({
      security: {
        username: process.env.STACKONE_API_KEY!,
        password: "",
      },
    });

    const { linkedAccounts } = await stackOne.accounts.listLinkedAccounts({
      originOwnerId: "owner_123",
    });

    // linkedAccounts = [
    //   { id: "acct_abc", provider: "bamboohr", status: "active", ... },
    //   { id: "acct_def", provider: "salesforce", status: "active", ... }
    // ]
    ```
  </Tab>
</Tabs>

Each returned account carries its `origin_owner_id`, `origin_owner_name`, `provider`, and `status`. See the [List Accounts API reference](/platform/api-reference/accounts/list-accounts) for the full response schema.

### Available filters

Combine filters to narrow down results:

```bash theme={null}
# Active accounts for an owner with a specific provider
curl "https://api.stackone.com/accounts?origin_owner_id=owner_123&provider=bamboohr&status=active" \
  -u "$STACKONE_API_KEY:"
```

| Parameter         | Description                                                                                                             |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `origin_owner_id` | The owner's unique identifier                                                                                           |
| `provider`        | Filter by integration provider (e.g., `bamboohr`, `salesforce`)                                                         |
| `status`          | Filter by status: `active`, `inactive`, `error`. See [account status](/gateway/concepts/linked-accounts#account-status) |

## Build an owner dashboard

Here's how to build a dashboard showing each customer's connected accounts:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from collections import defaultdict
    import requests
    import base64
    import os

    def get_owner_integrations(owner_id: str) -> list[dict]:
        api_key = os.environ["STACKONE_API_KEY"]
        headers = {
            "Authorization": f"Basic {base64.b64encode(f'{api_key}:'.encode()).decode()}"
        }

        response = requests.get(
            "https://api.stackone.com/accounts",
            params={"origin_owner_id": owner_id},
            headers=headers
        )
        return response.json()["data"]

    def group_by_provider(accounts: list[dict]) -> dict[str, list[dict]]:
        grouped = defaultdict(list)
        for account in accounts:
            grouped[account.get("provider", "unknown")].append(account)
        return dict(grouped)
    ```
  </Tab>

  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    import { StackOne } from "@stackone/stackone-client-ts";

    const stackOne = new StackOne({
      security: {
        username: process.env.STACKONE_API_KEY!,
        password: "",
      },
    });

    async function getOwnerIntegrations(ownerId: string) {
      const { linkedAccounts } = await stackOne.accounts.listLinkedAccounts({
        originOwnerId: ownerId,
      });
      return linkedAccounts ?? [];
    }

    // Group by provider for display
    function groupByProvider(accounts: typeof linkedAccounts) {
      const grouped: Record<string, typeof accounts> = {};
      for (const account of accounts ?? []) {
        const provider = account.provider ?? "unknown";
        grouped[provider] = grouped[provider] || [];
        grouped[provider].push(account);
      }
      return grouped;
    }
    ```
  </Tab>
</Tabs>

## Monitor account health

Track the health of your customers' connected accounts, using `getOwnerIntegrations` from the previous section:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    def get_account_health_summary(owner_id: str) -> dict:
        accounts = get_owner_integrations(owner_id)

        return {
            "total": len(accounts),
            "active": sum(1 for a in accounts if a.get("status") == "active"),
            "needs_attention": sum(
                1 for a in accounts if a.get("status") in ("error", "inactive")
            ),
            "by_provider": {
                a.get("provider", "unknown"): a.get("status", "unknown")
                for a in accounts
            },
        }
    ```
  </Tab>

  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    async function getAccountHealthSummary(ownerId: string) {
      const accounts = await getOwnerIntegrations(ownerId);

      return {
        total: accounts.length,
        active: accounts.filter(a => a.status === "active").length,
        needsAttention: accounts.filter(a =>
          ["error", "inactive"].includes(a.status ?? "")
        ).length,
        byProvider: accounts.reduce((acc, a) => {
          acc[a.provider ?? "unknown"] = a.status ?? "unknown";
          return acc;
        }, {} as Record<string, string>)
      };
    }
    ```
  </Tab>
</Tabs>

## Webhooks for account changes

Instead of polling, subscribe to the account lifecycle events — `account.created`, `account.updated`, and `account.deleted` — to keep your system in sync as customers connect and disconnect tools. See [Platform Events](/platform-api/platform-events) for the payloads and [Webhooks](/connect/webhooks) for setup.

## Related

<CardGroup cols={2}>
  <Card title="Connect Session" icon="link" href="/embed/connect-session">
    Set the origin owner fields when your customers link their accounts.
  </Card>

  <Card title="Handle Account Events" icon="bell" href="/embed/handle-account-events">
    React in your backend as customers connect, re-authenticate, or disconnect.
  </Card>

  <Card title="Organizations & Projects" icon="sitemap" href="/gateway/concepts/organizations-and-projects">
    Isolate customers at the project level — regions, environments, API keys.
  </Card>

  <Card title="List Accounts API" icon="code" href="/platform/api-reference/accounts/list-accounts">
    Full API reference for listing accounts.
  </Card>
</CardGroup>
