// Organizations — end-user workspaces nested under a tenant. // Backend: /api/v1/organizations + /api/v1/admin/organizations. // // Tenant admins (arcadia-admin) bypass per-org membership checks via the // `OrganizationContext` plug, so the same per-org routes used by end-users // are used here to mutate any org in the tenant. import type { ArcadiaClient } from "@crema/arcadia-core-client" export type OrgStatus = "active" | "frozen" | "pending_deletion" | string export type OnOwnerRemoval = | "delete" | "require_transfer" | "freeze_until_new_owner" export type OrgRole = "owner" | "admin" | "member" export type MembershipStatus = "active" | "suspended" | "invited" | string export interface Organization { id: string tenant_id: string slug: string name: string status: OrgStatus on_owner_removal: OnOwnerRemoval settings: Record metadata: Record inserted_at: string updated_at: string } export interface OrgMembership { id: string organization_id: string user_id: string role: OrgRole status: MembershipStatus joined_at: string | null } export interface CreateOrgInput { name: string slug: string on_owner_removal?: OnOwnerRemoval settings?: Record metadata?: Record } export interface UpdateOrgInput { name?: string status?: OrgStatus on_owner_removal?: OnOwnerRemoval settings?: Record metadata?: Record } export interface InviteByEmailInput { email: string role?: OrgRole } export interface AddRestrictedUserInput { email: string password: string first_name: string last_name: string role?: OrgRole } const BASE = "/api/v1/organizations" const ADMIN_BASE = "/api/v1/admin/organizations" // Tenant-wide list: every org in the current tenant. Admin-only. export async function listAllOrganizations( arcadia: ArcadiaClient, ): Promise { const res = await arcadia.GET<{ data: Organization[] }>(ADMIN_BASE) return res.data } // End-user list: orgs the current user is a member of. export async function listMyOrganizations( arcadia: ArcadiaClient, ): Promise { const res = await arcadia.GET<{ data: Organization[] }>(BASE) return res.data } export async function createOrganization( arcadia: ArcadiaClient, input: CreateOrgInput, ): Promise { const res = await arcadia.POST<{ data: Organization }>(BASE, { body: input }) return res.data } export async function getOrganization( arcadia: ArcadiaClient, id: string, ): Promise { const res = await arcadia.GET<{ data: Organization }>(`${BASE}/${id}`) return res.data } export async function updateOrganization( arcadia: ArcadiaClient, id: string, input: UpdateOrgInput, ): Promise { const res = await arcadia.PATCH<{ data: Organization }>(`${BASE}/${id}`, { body: input, }) return res.data } export async function listMembers( arcadia: ArcadiaClient, id: string, status?: MembershipStatus, ): Promise { const path = status ? `${BASE}/${id}/members?status=${encodeURIComponent(status)}` : `${BASE}/${id}/members` const res = await arcadia.GET<{ data: OrgMembership[] }>(path) return res.data } export async function inviteMember( arcadia: ArcadiaClient, id: string, input: InviteByEmailInput, ): Promise<{ type: "membership" | "email_invitation"; [k: string]: unknown }> { const res = await arcadia.POST<{ data: { type: "membership" | "email_invitation"; [k: string]: unknown } }>(`${BASE}/${id}/members/invite`, { body: input }) return res.data } export async function addRestrictedMember( arcadia: ArcadiaClient, id: string, input: AddRestrictedUserInput, ): Promise<{ user: { id: string; email: string; account_type: string }; membership: OrgMembership }> { const res = await arcadia.POST<{ data: { user: { id: string; email: string; account_type: string }; membership: OrgMembership } }>(`${BASE}/${id}/members/add_restricted`, { body: input }) return res.data } export async function changeMemberRole( arcadia: ArcadiaClient, id: string, userId: string, role: "admin" | "member", ): Promise { const res = await arcadia.PATCH<{ data: OrgMembership }>( `${BASE}/${id}/members/${userId}/role`, { body: { role } }, ) return res.data } export async function removeMember( arcadia: ArcadiaClient, id: string, userId: string, ): Promise { await arcadia.DELETE(`${BASE}/${id}/members/${userId}`) } export async function transferOwnership( arcadia: ArcadiaClient, id: string, newOwnerUserId: string, ): Promise { const res = await arcadia.POST<{ data: OrgMembership }>( `${BASE}/${id}/transfer_ownership`, { body: { new_owner_user_id: newOwnerUserId } }, ) return res.data }