Org & Team Kit

The Org & Team Kit standardizes how apps expose team membership and organization context. It covers the team page, organization switcher, invites, roles, allowed email domains, and the active organization that scopes sharing, connections, service tokens, and app switching.

Pieces

Piece Purpose
<TeamPage> Standard team management page for members, invites, roles, and organization preferences.
<OrgSwitcher> Account menu for the sidebar footer: organizations, Settings, Usage, app downloads, and Log out.
<InvitationBanner> One-click invite and domain-match join prompts.
<RequireActiveOrg> Guard for pages that need an active organization before rendering app data.
useOrgRole() Shared role booleans for member, invite, settings, and domain controls.
appRoles prop Adds a per-app role column and picker to <TeamPage> from a defineAppRoles descriptor.
useAppRoles() An app's role vocabulary, its assignments in the active org, and whether the viewer may edit.
useAppRole() The current user's assigned roles in one app, or an empty array when unassigned.
useSetAppMemberRoles() Replace one member's app-role set. Org owner/admin only, enforced server-side.
Organization handlers Create, rename, switch, invite, accept, remove, and update roles/domains.
Roles Owner, admin, and member gates for sharing, settings, service tokens, and provider connections.
Allowed domains Automatic join and domain-scoped access policy.
App links Workspace app-link parsing and visible app-switcher helpers.

API

Render organization context wherever data scope changes. Put OrgSwitcher in the persistent shell. Organization management lives in Settings: the Organization group's General, Members, Usage, Authentication, Apps, Infrastructure, and Audit log pages (the last four for owners and admins). /team and /settings/organization redirect there.

OrgSwitcher is the account menu (also exported as AccountMenu). Its button shows the signed-in person's photo, name, and current organization, and compact renders the photo alone for a collapsed rail. The menu lists the person's email, their organizations, pending invitations, email-domain matches, and Create organization, then Settings, Usage, and Log out. Member invitations and organization settings live in Settings. Pass utilityLinks to add a "Get apps and extensions" list for the app's downloads; links with external: true open in a new tab.

<OrgSwitcher
  compact={collapsed}
  utilityLinks={[
    {
      id: "chrome-extension",
      label: "Chrome extension",
      href: "https://example.com/extension",
      external: true,
    },
    { id: "desktop-app", label: "Get desktop app", href: "/download" },
  ]}
/>
import {
  OrgSwitcher,
  RequireActiveOrg,
  TeamPage,
} from "@agent-native/toolkit/app/org";
import { useOrgRole } from "@agent-native/core/client/org-team";

export function AppShell() {
  return <OrgSwitcher />;
}

export function SettingsTeamTab() {
  return (
    <RequireActiveOrg>
      <TeamPage createOrgDescription="Create a team to share dashboards and provider connections." />
    </RequireActiveOrg>
  );
}

export function AdminOnlyControl() {
  const { canManageOrg } = useOrgRole();
  return canManageOrg ? <button type="button">Invite members</button> : null;
}

The Settings shell uses the Toolkit organization UI from @agent-native/toolkit/app/org: OrgGeneralPage, OrgMembersPage({ appRoles? }), OrgAuthenticationPage, and OrgAppsPage.

Server code imports the same role helpers from @agent-native/core/org-team. The route plugin mounts /_agent-native/org/* for org creation, switching, invites, domains, member roles, app roles, and A2A secret sync.

Per-app roles

When an app needs its own permission vocabulary on top of team membership, declare it with defineAppRoles and pass the descriptor to TeamPage, and to OrgMembersPage by replacing the Members page (see Settings Kit → Registering a page). The descriptor is the serializable half of the declaration — no database imports — so the same object can be handed to defineAppRoles on the server and to the component in the browser bundle:

import {
  RequireActiveOrg,
  TeamPage,
  useAppRole,
  useAppRoles,
  useSetAppMemberRoles,
} from "@agent-native/core/client/org-team";
import { coachRoles } from "../shared/coach-roles";

export function SettingsTeamTab() {
  return (
    <RequireActiveOrg>
      <TeamPage appRoles={coachRoles} />
    </RequireActiveOrg>
  );
}

export function CoachOnlyControl() {
  const { roles } = useAppRole("coach");
  return roles.includes("coach-admin") ? (
    <button type="button">Archive</button>
  ) : null;
}

export function RoleAdmin() {
  const { data } = useAppRoles("coach");
  const setRoles = useSetAppMemberRoles();
  if (!data?.canManage) return null;
  return (
    <button
      type="button"
      onClick={() =>
        setRoles.mutate({ appId: "coach", email: "dana@acme.com", roles: [] })
      }
    >
      Clear role
    </button>
  );
}

roles, myRoles, and canManage are progressive disclosure only. The server re-resolves the caller's app role on every guarded operation, and assignment edits require the org owner/admin role regardless of what the client believes. See Organizations, Teams & Permissions → App roles for the role model and Actions → Authorization for the server-side authorize guard.

UX Standard

  • Users can see which organization they are in before creating or sharing data.
  • Invites and roles live on Settings › Organization › Members, not in per-resource dialogs.
  • App-specific roles appear as another column on that same team surface, never as a separate permissions page or a second organization.
  • Owners and admins change the org domain, and the control explains who can auto-join.
  • App switching lists workspace/default app links. Active-org continuity comes from the target app sharing the same auth and organization state.

What's next

  • Multi-Tenancy — how org scoping isolates data across tenants.
  • Workspace Management — governance for multi-app workspaces that share an organization.
  • Sharing Kit — the access model that roles and org scope feed into.