Skip to content
6 changes: 6 additions & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createFunctionsModule } from "./modules/functions.js";
import { createAgentsModule } from "./modules/agents.js";
import { createAppLogsModule } from "./modules/app-logs.js";
import { createUsersModule } from "./modules/users.js";
import { createAccountsModule } from "./modules/accounts.js";
import { RoomsSocket, RoomsSocketConfig } from "./utils/socket-utils.js";
import type {
Base44Client,
Expand Down Expand Up @@ -111,13 +112,15 @@ export function createClient(config: CreateClientConfig): Base44Client {
baseURL: `${serverUrl}/api`,
headers,
token,
appId: String(appId),
onError: options?.onError,
});

const functionsAxiosClient = createAxiosClient({
baseURL: `${serverUrl}/api`,
headers: functionHeaders,
token,
appId: String(appId),
interceptResponses: false,
onError: options?.onError,
});
Expand All @@ -131,13 +134,15 @@ export function createClient(config: CreateClientConfig): Base44Client {
baseURL: `${serverUrl}/api`,
headers: serviceRoleHeaders,
token: serviceToken,
appId: String(appId),
onError: options?.onError,
});

const serviceRoleFunctionsAxiosClient = createAxiosClient({
baseURL: `${serverUrl}/api`,
headers: functionHeaders,
token: serviceToken,
appId: String(appId),
interceptResponses: false,
});

Expand Down Expand Up @@ -192,6 +197,7 @@ export function createClient(config: CreateClientConfig): Base44Client {
}),
appLogs: createAppLogsModule(axiosClient, appId),
users: createUsersModule(axiosClient, appId),
accounts: createAccountsModule(axiosClient, appId),
analytics: createAnalyticsModule({
axiosClient,
serverUrl,
Expand Down
3 changes: 3 additions & 0 deletions src/client.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { FunctionsModule } from "./modules/functions.types.js";
import type { AgentsModule } from "./modules/agents.types.js";
import type { AppLogsModule } from "./modules/app-logs.types.js";
import type { AnalyticsModule } from "./modules/analytics.types.js";
import type { AccountsModule } from "./modules/accounts.types.js";

/**
* Options for creating a Base44 client.
Expand Down Expand Up @@ -85,6 +86,8 @@ export interface CreateClientConfig {
* Provides access to all SDK modules for interacting with the app.
*/
export interface Base44Client {
/** {@link AccountsModule | Accounts module} for multi-tenancy (accounts, members, billing). */
accounts: AccountsModule;
/** {@link AgentsModule | Agents module} for managing AI agent conversations. */
agents: AgentsModule;
/** {@link AnalyticsModule | Analytics module} for tracking custom events in your app. */
Expand Down
13 changes: 13 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,19 @@ export type {

export type { AppLogsModule } from "./modules/app-logs.types.js";

export type {
AccountsModule,
Account,
AccountMembership,
AccountPlan,
AccountRole,
AssignableAccountRole,
AccountStatus,
AccountMembershipStatus,
MyAccountsResponse,
CheckoutSession,
} from "./modules/accounts.types.js";

export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";

export type {
Expand Down
164 changes: 164 additions & 0 deletions src/modules/accounts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { AxiosInstance } from "axios";

import {
getStoredActiveAccountId,
setStoredActiveAccountId,
} from "../utils/common.js";
import type {
Account,
AccountMembership,
AccountPlan,
AccountsModule,
AccountSubscription,
AssignableAccountRole,
CheckoutParams,
CheckoutSession,
MyAccountsResponse,
PublicAccount,
} from "./accounts.types.js";

/**
* Creates the accounts module (multi-tenancy) for the Base44 SDK.
*
* @param axios - Axios instance (responses are unwrapped to data).
* @param appId - Application ID.
* @returns The accounts module.
* @internal
*/
export function createAccountsModule(
axios: AxiosInstance,
appId: string
): AccountsModule {
const base = `/apps/${appId}/accounts`;
const enc = encodeURIComponent;

// Resolve the account id to operate on: an explicit id wins, then the
// explicitly-stored client selection, then the server-resolved default
// (the sole-account case). Throws a clear error when none can be found so
// callers never silently send `/accounts/undefined/...` (which 404s as
// "Account not found").
const resolveAccountId = async (provided?: string | null): Promise<string> => {
if (provided) return provided;
const stored = getStoredActiveAccountId(appId);
if (stored) return stored;
const mine: MyAccountsResponse = await axios.get(`${base}/me`);
if (mine.active_account_id) return mine.active_account_id;
throw new Error(
"No active account: pass an accountId, or have the user select or create an account first."
);
};

return {
getActiveAccountId(): string | undefined {
return getStoredActiveAccountId(appId);
},

switchAccount(accountId: string): void {
setStoredActiveAccountId(appId, accountId);
if (typeof window === "undefined") return;
window.location.reload();
},

setActiveAccount(accountId: string): void {
setStoredActiveAccountId(appId, accountId);
},

clearActiveAccount(): void {
setStoredActiveAccountId(appId, null);
},

async listMine(): Promise<MyAccountsResponse> {
return axios.get(`${base}/me`);
},

async getPublicAccount(slug: string): Promise<PublicAccount> {
return axios.get(`${base}/public/by-slug/${enc(slug)}`);
},

async joinAccount(slug: string): Promise<AccountMembership> {
return axios.post(`${base}/by-slug/${enc(slug)}/join`, {});
},

async create(params: {
name: string;
data?: Record<string, unknown>;
slug?: string;
}): Promise<Account> {
return axios.post(base, params);
},

async update(
accountId: string,
params: { name?: string; data?: Record<string, unknown>; slug?: string }
): Promise<Account> {
return axios.patch(`${base}/${accountId}`, params);
},

async listMembers(accountId?: string): Promise<AccountMembership[]> {
const id = await resolveAccountId(accountId);
return axios.get(`${base}/${id}/members`);
},

async invite(
accountId: string,
email: string,
role: AssignableAccountRole = "member"
): Promise<AccountMembership> {
return axios.post(`${base}/${accountId}/invites`, { email, role });
},

async acceptInvite(accountId: string): Promise<AccountMembership> {
return axios.post(`${base}/${accountId}/accept`, {});
},

async changeMemberRole(
accountId: string,
email: string,
role: AssignableAccountRole
): Promise<AccountMembership> {
return axios.patch(`${base}/${accountId}/members/${enc(email)}/role`, {
role,
});
},

async removeMember(
accountId: string,
email: string
): Promise<{ removed: boolean }> {
return axios.delete(`${base}/${accountId}/members/${enc(email)}`);
},

async transferOwnership(
accountId: string,
email: string
): Promise<{ transferred: boolean }> {
return axios.post(`${base}/${accountId}/transfer-ownership`, { email });
},

billing: {
async listPlans(accountId?: string): Promise<AccountPlan[]> {
const id = await resolveAccountId(accountId);
return axios.get(`${base}/${id}/billing/plans`);
},

async getSubscription(accountId?: string): Promise<AccountSubscription> {
const id = await resolveAccountId(accountId);
return axios.get(`${base}/${id}/billing/subscription`);
},

async startCheckout(
accountIdOrParams: string | CheckoutParams,
maybeParams?: CheckoutParams
): Promise<CheckoutSession> {
const explicitId =
typeof accountIdOrParams === "string" ? accountIdOrParams : undefined;
const params =
typeof accountIdOrParams === "string"
? maybeParams
: accountIdOrParams;
const id = await resolveAccountId(explicitId);
return axios.post(`${base}/${id}/billing/checkout`, params);
},
},
};
}
Loading
Loading