Workspaces & memberships

@hyperyai/sdk workspace APIs — WorkspaceSwitcher, useMemberships, useActiveWorkspace and setActiveWorkspace for users who belong to several teams and workspaces.

A Hypery user can belong to several teams, each with one or more workspaces. The active team + workspace is stored on the user's OAuth session on the server, so it persists across reloads and devices. These APIs list the memberships and switch the active workspace.

The hooks and WorkspaceSwitcher resolve the gateway URL as: explicit gatewayUrl argument/prop > the provider's gatewayUrl > NEXT_PUBLIC_GATEWAY_URL > a relative /api/... path. setActiveWorkspace is a plain function and can't read the provider — see below.

@hyperyai/sdk 1.1.5 ignored the provider's gatewayUrl here and fell back to NEXT_PUBLIC_GATEWAY_URL or a relative path. If you set that env var only as a workaround, it's no longer needed.

WorkspaceSwitcher

A dropdown listing every workspace grouped by team (the personal team is labelled "Personal"). Selecting one calls setActiveWorkspace, reloads memberships and closes. Renders a loading button first and nothing if the user has no memberships.

'use client';
 
import { WorkspaceSwitcher } from '@hyperyai/sdk';
import { useRouter } from 'next/navigation';
 
export function TopBar() {
  const router = useRouter();
  return <WorkspaceSwitcher onSwitched={() => router.refresh()} />;
}
PropTypeDefaultDescription
onSwitched(teamId: string, workspaceId: string) => voidCalled after a successful switch.
gatewayUrlstringprovider gatewayUrlBase URL for both the membership list and the switch request.
classNamestring''Extra classes on the trigger button.
ariaLabelstring'Switch workspace'Trigger's aria-label.

Switch failures are logged to the console; the dropdown stays open.

useMemberships

useMemberships(opts?: { gatewayUrl?: string }) fetches GET /api/auth/list_memberships with the user's token. Returns data: null when signed out. Concurrent identical requests (same URL and token) share one fetch.

import { useMemberships } from '@hyperyai/sdk';
 
function TeamList() {
  const { data, isLoading, error, reload } = useMemberships();
  if (isLoading) return <p>Loading…</p>;
  if (error) return <button onClick={reload}>Retry ({error})</button>;
  return (
    <ul>
      {data?.memberships.map((m) => (
        <li key={m.team.id}>
          {m.team.isPersonal ? 'Personal' : m.team.name} ({m.team.role})
          <ul>{m.workspaces.map((w) => <li key={w.id}>{w.name}{w.isActive && ' ✓'}</li>)}</ul>
        </li>
      ))}
    </ul>
  );
}
ReturnsTypeDescription
dataMembershipsResponse | null{ activeOrganizationId, activeWorkspaceId, memberships: MembershipEntry[] }. Each workspace gets isActive set from activeWorkspaceId.
isLoadingbooleanLoading state.
errorstring | nullError message.
reload() => Promise<void>Re-fetch.

MembershipEntry is { team: MembershipTeam, workspaces: MembershipWorkspace[] }:

MembershipTeamMembershipWorkspace
id, name, slugid, name, slug
isPersonal: booleanisDefault: boolean, icon: string | null, isActive: boolean
role: 'owner' | 'admin' | 'developer' | 'viewer'role: 'owner' | 'admin' | 'developer' | 'viewer'

useActiveWorkspace

Resolves the active team + workspace. Uses activeWorkspaceId, falling back to the personal team's default workspace.

import { useActiveWorkspace } from '@hyperyai/sdk';
 
const { active, isLoading } = useActiveWorkspace();
if (active?.role === 'viewer') return <ReadOnlyNotice />;
ReturnsTypeDescription
activeActiveWorkspace | null{ teamId, teamName, workspaceId, workspaceName, role }; null until loaded or when nothing resolves.
isLoadingbooleanLoading state.

useActiveWorkspace(opts?: { gatewayUrl?: string }) calls useMemberships(opts) internally; components mounting together share the in-flight request.

setActiveWorkspace

A plain async function (not a hook) that sends PATCH /api/oauth/session with { activeOrganizationId: teamId, activeWorkspaceId: workspaceId }.

import { setActiveWorkspace, useAuth } from '@hyperyai/sdk';
 
const { getAccessToken, gatewayUrl } = useAuth();
 
await setActiveWorkspace({ teamId, workspaceId, getAccessToken, gatewayUrl });
OptionTypeDescription
teamIdstringRequired. Team (organization) id.
workspaceIdstringRequired. Workspace id in that team.
getAccessToken() => Promise<string | null>Required. From useAuth().
gatewayUrlstringBase URL. Defaults to NEXT_PUBLIC_GATEWAY_URL, then a relative path — pass useAuth().gatewayUrl as above.

Returns Promise<void>. Throws No access token; user is not signed in without a token, or Failed to switch workspace: <detail> on a non-2xx response. Components using useMemberships don't refresh on their own — call reload() afterwards.

Next steps