← All frames · Frame (c)

Installing an app: where, and for whom

Every registered app declares a scope levelpersonal, organization, or both. The dialog only ever asks questions the app and the caller's role actually leave open; a single-scope app installs without a scope question at all.

scopeLevel = both, caller is org owner

The “who can use it” group appears only when the organization scope is selected. Selecting the personal scope hides it — a personal install is always just you.

Caller is a plain member — fail-closed

One member must not be able to push an app into every colleague's launcher. The option is visible but disabled and says why, so the user learns the rule rather than hitting a 403 after committing.

scopeLevel = personal — no scope question

A single-scope app asks nothing. The dialog still confirms, because installing is a write the user should acknowledge.

Already installed for everyone

Install is idempotent per target, so re-opening shows the existing installation and offers the reverse action instead of a second install.

What the API accepts

App scopeLevelRequested scopeModeOutcome
bothpersonalselfInstalled for the caller.
bothorganizationselfRequires active membership.
bothorganizationeveryoneRequires owner or admin, else 403.
personalorganization422 — scope not permitted for this app.
organizationpersonal422 — scope not permitted for this app.
anyanyanyApp not visible to the caller → 404, never 403 (id probing).
Test hooks: [data-dialog='install-app'] · [data-scope-level] · [data-scope='personal'|'organization'] · [data-mode='self'|'everyone'] · [data-guard='requires-org-admin'] · [data-guard='already-installed'] · [data-action='install'|'uninstall'|'cancel'].
Frame (c) — install scope & mode Next: notifications →
Review · app-scopes-user-menu approve or reject each flow ↓
user-menupendingApproveReject
accountspendingApproveReject
install-scopependingApproveReject
notificationspendingApproveReject