Skip to content

Accounts, subaccounts and CLI profiles

Editions: OSS, Cloud, Enterprise. Unless stated otherwise, everything on this page ships in OSS.

Multi-account, subaccounts, sharing and access rules are served by an extension plugin. The open-source server does not have these endpoints. The console and the CLI show the related views and commands only when GET /api/v1/features reports the matching capability:

Capability Console CLI
multi_account Header account switcher (with "New account…"), account chooser at sign-in accounts works against the server's memberships
account_hierarchy Subaccounts card on Settings > Account, "Subaccount access" on Users and Teams, Settings > Access grants, sharing on resource pages, read-only shared views, usage and attention by subaccount subaccounts, share
abac_rules Tags on resource pages, Policies > Access rules tags, access

All three are false unless a plugin sets them. With all three off, none of the gated routes, nav items or panels is registered and their code is never downloaded. /console/settings/subaccounts still renders the subaccounts page for old links, but no nav item points at it. If an account endpoint answers 404 anyway (for example after the plugin was removed), the Subaccounts and Access grants pages say the feature is not available on this deployment and other gated panels hide, without an error toast. A 404 on one item, such as a sibling's subaccount id, shows "not found".

CLI profiles and accounts

~/.preloop/config.yaml can hold several named profiles, and each profile can hold one token pair per account. A file written before profiles existed keeps working unchanged: its top-level keys are the default profile.

# The default profile, exactly as before profiles existed.
access_token: <token>
refresh_token: <token>
api_url: https://preloop.ai

current_profile: work          # optional; used when no flag or env var picks one
profiles:
  work:
    api_url: https://preloop.example.com
    access_token: <login token>
    refresh_token: <login token>
    current_account: north     # set by `preloop accounts switch`
    accounts:
      north:
        account_id: 6c1f...
        name: North site
        access_token: <token for north>
        refresh_token: <token for north>

Profile and account names are lowercase letters, digits, - and _.

Which session a command uses

  1. Profile: --profile, then PRELOOP_PROFILE, then current_profile, then default.
  2. Account: --account, then PRELOOP_ACCOUNT, then the profile's current_account. With no account, the profile's own token pair is used.
  3. Token: --token and PRELOOP_TOKEN still override everything.

If an account is asked for and the profile holds no tokens for it, the command stops with a hint to run preloop accounts switch <slug>. It never falls back to another account's tokens.

Commands

  • preloop accounts list lists the accounts you belong to and marks the current one.
  • preloop accounts switch <slug|id> asks the server for a token pair for that account, stores both tokens under the profile and makes it current.
  • preloop accounts current prints the profile and account in use; preloop auth status shows them too.
  • preloop subaccounts list|create|rename|detach|delete (account_hierarchy).
  • preloop share list|add|rm (account_hierarchy). share add adds a share and leaves existing shares alone; share rm <share-id> stops one.
  • preloop tags list|set|rm <kind> <id> (abac_rules). Keys the parent account governs are read-only.
  • preloop access rules list, preloop access rules apply -f <file|-> and preloop access explain --subject --action --resource (abac_rules). apply replaces this account's rules with the file, so the file is the source of truth.

The gated groups are hidden from preloop --help unless the server reports their capability. Running one anyway prints which capability is off.

Sign out everywhere

In Settings → Security, choose Sign out everywhere to end your browser and CLI login sessions across all your account memberships. The CLI equivalent is preloop auth logout --all. Existing access and refresh tokens stop working, and you can sign in again with your normal sign-in method.

To sign another member out of the current account, open Settings → Users, use their row's action menu and choose Sign out everywhere. The confirmation names the member and explains the scope. With role-based access control, this requires manage_users, the same permission used to deactivate a membership. In the open-source edition, every active account member has these permissions. The action is audited and leaves the membership active. Sessions in the person's other accounts remain valid.

Live browser connections and CLI session viewers close when the revocation event arrives. If delivery is unavailable, they check again every minute; database outages can delay that check. The socket closes with code 4401, and signing in again creates a new valid session.

API keys, managed-agent credentials and runner tokens are separate: signing someone out does not rotate or revoke them or stop their runners. Manage those credentials separately when needed. Removing a member is a different action that also revokes their account access and personal credentials.

Security settings list individual CLI logins and let you revoke one. Browser sessions are not listed individually; Sign out everywhere ends all of them within the action's scope.

Endpoint contract for the extension

All paths are under /api/v1. A 404 on a collection means "capability off" to both clients. {kind} is one of ai_model, mcp_server, managed_agent, flow, runner_pool or policy. For runner_pool the id is the pool name, and for policy it is the id of the active baseline version.

Method Path Body and notes
GET /me/memberships {items:[{account_id, account_name, slug, parent_account_id, last_used_at}]}
POST /auth/switch-account {account_id}, returns {access_token, refresh_token}
POST /me/accounts {name}; creates a root account owned by the caller and returns {account:{id, name, parent_account_id}, membership}. The console then calls /auth/switch-account. 404 means the server predates it
GET, POST /accounts/{id}/subaccounts POST {name, tags}
GET, PATCH, DELETE /accounts/{id}/subaccounts/{sub_id} PATCH {name, tags}; 404 when sub_id is not a child of id
POST /accounts/{id}/subaccounts/{sub_id}/detach
GET, POST /accounts/{id}/access-grants {subject_type, subject_id, level, target, subaccount_ids}
DELETE /accounts/{id}/access-grants/{grant_id}
GET /accounts/{id}/shares?resource_type=&resource_id= A resource can have several shares
POST /accounts/{id}/shares {resource_type, resource_id, target}; target is {type: all}, {type: selected, subaccount_ids} or {type: tag, key, value}
DELETE /accounts/{id}/shares/{share_id}
GET /accounts/{id}/shared-resources/{kind}/{resource_id} {kind, id, name, provider, identifier, description, price, shared_from}; never credentials
GET /accounts/{id}/usage/rollup?subaccount_id=&start=&end= {rows:[{subaccount_id, subaccount_name, model, day, requests, cost_usd}]}
GET /accounts/{id}/attention/rollup {items:[{subaccount_id, subaccount_name, count}]}
GET, PUT /tags/{kind}/{resource_id} GET {tags, governed_keys, version}; PUT {tags, version}
GET, PUT /access/rules GET {rules, inherited, modes, version}; PUT {rules, version}
GET /access/rules/export {yaml}
POST /access/rules/apply {yaml}
POST /access/explain {subject, action, resource}, returns {effect, reason, rule_ids}
POST /access/modes/preview {action, mode}, returns {losing_access, preview_token}
PUT /access/modes/{action} {mode, preview_token}

Tag and rule writes carry the version the client read. The server should answer 409 when the stored set has a different version, and the clients then reload instead of overwriting. A client that sends version: null has not read a version (for example against a server that does not return one).

Moving an action to require_permit can remove access. The console enables the save only after it has shown the preview, and sends the preview's preview_token so the server can check that the preview was seen.