Three entry points, one package
Import what you need. React SDK for the frontend, server SDK for the backend, data module for reference data.
React SDK
/react
40+ hooks, 20+ conditional components, 13 pre-built settings screens. Add a provider, call hooks, ship. Works with Next.js, Vite, Remix, or any React setup.
- useSaaSAuth() — sign in, sign out, session
- useSaaSWorkspaces() — CRUD, members, roles
- useSubscription() — plans, checkout, trials
- useCreditBalance() — consume, purchase, track
- useQuotaUsageStatus() — record, enforce limits
- usePermissions() — 3-tier permission gates
- useTrialStatus() — days remaining, ending?
- useSeatStatus() — seat limits, invite checks
- useTranslation() — i18n, RTL, native numerals
- usePushNotifications() — subscribe, permission
Server SDK
@buildbase/sdk
Framework-agnostic. Works with Next.js API routes, Express, Hono, Fastify, or any Node.js backend. Factory pattern with automatic session resolution.
- bb.usage.record() — metered billing
- bb.usage.recordBatch() — bulk (up to 100)
- bb.credits.consume() — with idempotency
- bb.subscription.get() — check plan status
- bb.subscription.checkout() — Stripe session
- bb.workspace.list/create/update/delete()
- bb.users.invite() — add workspace member
- bb.notification.send() — email + push
- bb.permissions.check() — verify access
- bb.withSession() — background jobs
Shared
/data + core exports
Reference data, billing utilities, permission constants, and150+ TypeScript types shared across frontend and backend. Zero React dependency.
- countries, currencies, languages, timezones
- formatCents() — locale-aware currency
- getPricingVariant() — multi-currency plans
- calculateTotalSubscriptionCents()
- validateInvite() — seat limit checks
- Permission.* — 12 platform permissions
- resolvePermissions() — 3-tier resolution
- verifyWebhookSignature() — HMAC-SHA256
- parseWebhookEvent() — typed payloads
- eventEmitter — cross-component events
Production code, not pseudocode
Every example runs as-is. Copy, paste, ship.
Add the provider
Add the provider to the root layout. Pass the org ID and auth config. The SDK handles session management, token refresh, workspace loading, and settings resolution automatically.
import { ApiVersion } from '@buildbase/sdk';
import { SaaSOSProvider } from '@buildbase/sdk/react';
export default function Providers({ children }) {
return (
<SaaSOSProvider
serverUrl="https://api.yourapp.com"
version={ApiVersion.V1}
orgId="your-org-id"
auth={{
clientId: 'your-client-id',
redirectUrl: 'http://localhost:3000',
}}
locale="en" // en, es, fr, de, ja, zh, hi, ar
>
{children}
</SaaSOSProvider>
);
}Sign in, sign out
One hook returns the user, auth status, sign in, sign out, and openWorkspaceSettings() to open any settings screen. Conditional components render based on auth state.
import {
useSaaSAuth,
WhenAuthenticated,
WhenUnauthenticated,
} from '@buildbase/sdk/react';
function App() {
const { user, signIn, signOut, openWorkspaceSettings } = useSaaSAuth();
return (
<>
<WhenUnauthenticated>
<button onClick={() => signIn()}>Sign In</button>
</WhenUnauthenticated>
<WhenAuthenticated>
<p>Welcome, {user?.name}</p>
<button onClick={() => openWorkspaceSettings('subscription')}>
Manage Plan
</button>
<button onClick={signOut}>Sign Out</button>
</WhenAuthenticated>
</>
);
}Paywalls as components
useTrialStatus() returns days remaining and whether the trial is ending. Conditional components gate UI by subscription state, specific plans, or trial status. Checkout creates a Stripe session automatically.
import {
useSubscription,
useTrialStatus,
useCreateCheckoutSession,
WhenTrialEnding,
WhenNoSubscription,
WhenSubscriptionToPlans,
} from '@buildbase/sdk/react';
function BillingPage({ workspaceId }) {
const { subscription } = useSubscription(workspaceId);
const { isTrialing, daysRemaining } = useTrialStatus();
const { createCheckoutSession } = useCreateCheckoutSession(workspaceId);
return (
<>
<WhenTrialEnding daysThreshold={3}>
<Banner>{daysRemaining} days left in your trial.</Banner>
</WhenTrialEnding>
<WhenNoSubscription>
<button onClick={() => createCheckoutSession({
planVersionId: 'pro-v1',
billingInterval: 'monthly',
})}>
Subscribe to Pro
</button>
</WhenNoSubscription>
<WhenSubscriptionToPlans plans={['starter']}>
<UpgradePrompt />
</WhenSubscriptionToPlans>
</>
);
}Consume, track, gate
Consume credits with idempotency keys for safe retries. Returns a typed INSUFFICIENT_CREDITS error on 402 with available and requested fields. Track expiring credits with a configurable lookahead window (1-90 days).
import {
useCreditBalance,
useConsumeCredits,
useExpiringCredits,
WhenCreditsExhausted,
WhenCreditsLow,
} from '@buildbase/sdk/react';
function AIFeature({ workspaceId }) {
const { balance } = useCreditBalance(workspaceId);
const { consumeCredits } = useConsumeCredits(workspaceId);
const { expiringCredits } = useExpiringCredits(workspaceId, 7);
const handleGenerate = async () => {
try {
await consumeCredits({ amount: 10, description: 'AI generation' });
// proceed with generation
} catch (err) {
if (err.code === 'INSUFFICIENT_CREDITS') {
// err.available and err.requested are available
}
}
};
return (
<>
<p>Credits: {balance?.available}</p>
{expiringCredits > 0 && <p>{expiringCredits} credits expire soon</p>}
<WhenCreditsLow threshold={10}>
<p>Running low — buy more credits.</p>
</WhenCreditsLow>
<WhenCreditsExhausted>
<p>No credits left.</p>
</WhenCreditsExhausted>
<button onClick={handleGenerate}>Generate (10 credits)</button>
</>
);
}Backend integration
The BuildBase() factory resolves sessions automatically from a callback. For background jobs or non-request contexts, use withSession() to create a scoped client. Supports configurable timeout, retry with exponential backoff, and debug logging.
import BuildBase from '@buildbase/sdk';
const bb = BuildBase({
serverUrl: 'https://api.yourapp.com',
orgId: 'your-org-id',
getSessionId: async () => {
const cookies = await getCookies();
return cookies.get('bb-session')?.value ?? null;
},
});
// API route — record usage and enforce quota
export async function POST(req) {
const usage = await bb.usage.record(workspaceId, {
quotaSlug: 'api-calls',
quantity: 1,
idempotencyKey: req.headers.get('x-request-id'),
});
if (usage.available <= 0) {
return Response.json({ error: 'Quota exceeded' }, { status: 429 });
}
return Response.json({ remaining: usage.available });
}
// Background job — use withSession() for non-request contexts
const api = bb.withSession(serviceAccountSessionId);
await api.credits.consume(workspaceId, { amount: 50 });
await api.notification.send(workspaceId, 'job-completed');Verify and handle
HMAC-SHA256 signature verification with timing-safe comparison and configurable max age (default 5 minutes). Parse events into typed payloads. Handle subscription changes, credit events, workspace actions, and more.
import { parseWebhookEvent } from '@buildbase/sdk';
export async function POST(req) {
const body = await req.text();
// Verifies the signature and parses the payload — returns null on failure
const parsed = parseWebhookEvent({
body,
signature: req.headers.get('x-buildbase-signature'),
timestamp: req.headers.get('x-buildbase-timestamp'),
secret: process.env.WEBHOOK_SECRET,
});
if (!parsed) {
return Response.json({ error: 'Invalid signature' }, { status: 401 });
}
switch (parsed.event) {
case 'subscription.upgraded':
await enablePremiumFeatures(parsed.data.workspaceId);
break;
case 'credit.low_balance':
await notifyTeam(parsed.data.workspaceId);
break;
case 'workspace.member_added':
await syncToExternalCRM(parsed.data);
break;
}
return Response.json({ received: true });
}3-tier permission resolution
Permissions resolve through three layers: workspace overrides, then org settings, then SDK defaults. Owners get all permissions automatically. useSeatStatus() returns member count, included seats, billable seats, max-seat status, and an invite validation result with a human-readable block reason.
import {
usePermissions,
useSaaSAuth,
useSeatStatus,
WhenPermission,
WhenWorkspaceRoles,
Permission,
} from '@buildbase/sdk/react';
function TeamPage({ workspace }) {
const { can } = usePermissions();
const { openWorkspaceSettings } = useSaaSAuth();
const {
memberCount, includedSeats, availableSeats,
isAtMax, canInvite, inviteBlockReason,
} = useSeatStatus(workspace);
return (
<>
<p>{memberCount} of {includedSeats} seats used</p>
<WhenPermission permission={Permission.WORKSPACE_MEMBERS_INVITE}>
<button disabled={!canInvite}>
{canInvite ? 'Invite Member' : inviteBlockReason}
</button>
</WhenPermission>
<WhenWorkspaceRoles roles={['owner', 'admin']}>
<button onClick={() => openWorkspaceSettings('permissions')}>
Manage Permissions
</button>
</WhenWorkspaceRoles>
{can(Permission.WORKSPACE_BILLING_MANAGE) && <BillingLink />}
</>
);
}8 languages, native numerals
600+ translated strings across all settings screens. ICU MessageFormat for pluralization. Native numeral systems: Devanagari for Hindi, Arabic-Indic for Arabic. RTL layout direction for Arabic. English bundled by default; other languages lazy-loaded.
import { useTranslation, SUPPORTED_LOCALES } from '@buildbase/sdk/react';
// Locales: 'en', 'es', 'fr', 'de', 'ja', 'zh', 'hi', 'ar'
function PriceDisplay({ cents, currency }) {
const { t, fmtCents, fmtNum, dir } = useTranslation();
return (
<div dir={dir}> {/* Automatically 'rtl' for Arabic */}
<p>{t('subscription.currentPlan')}</p>
<p>{fmtCents(cents, currency)}</p>
{/* $49.99 (en) | ४९.९९ (hi, Devanagari) | ٤٩٫٩٩ (ar, Arabic-Indic) */}
<p>{fmtNum(1234)}</p>
{/* 1,234 (en) | १,२३४ (hi) | ١٬٢٣٤ (ar) */}
</div>
);
}13 pre-built settings screens
Every SaaS needs workspace settings. The SDK ships a production-ready settings dialog opened with one function call. Translated, permission-gated, and responsive.
openWorkspaceSettings('subscription')Edit name, email, language, country, currency, and timezone
Manage passkeys and review or sign out active sessions
Review signed-in devices, rename them, sign out, or forget unrecognized ones
Review AI agents with account access and disconnect them to revoke access
Set workspace name, icon, and billing currency
Invite members by email, assign roles, track seat usage, remove access
View current plan, switch plans, cancel or resume, access invoices
Track per-quota usage, overage costs, and billing cycle
Check balance, purchase packages, review transaction history
Enable or disable feature flags per workspace
Subscribe to push notifications, toggle per-event email and push delivery
Edit role-permission matrix with granular toggle controls
Permanently delete workspace with confirmation
<PricingPage>, <CreditStorePage>, <WorkspaceSwitcher>, and <BetaForm> — all with render props for full layout control.Declarative UI gating
No if-statements for permissions, subscriptions, or feature flags. Wrap UI in conditional components. The SDK resolves state and renders or hides children.
Auth
Subscriptions
Quotas
Credits
Permissions & Roles
Feature Flags
Also included
8 languages built in
English, Spanish, French, German, Japanese, Chinese, Hindi, Arabic. ICU MessageFormat with pluralization. RTL for Arabic. Lazy-loaded — only English in the initial bundle.
Multi-currency billing
Per-plan pricing variants by currency. formatCents() for locale-aware display. Workspace billing currency lock. Available currencies auto-detected from plan config.
Notifications
Send email and push notifications from the server SDK. Per-event user preferences with toggles. Browser push with VAPID auto-setup. Browser-specific unblock instructions.
Seat-based pricing
useSeatStatus() calculates included seats, billable extras, per-seat cost, invite eligibility, and human-readable block reasons. Syncs with subscription state.
Pricing utilities
calculateTotalSubscriptionCents(), getBasePriceCents(), getQuotaOverageCents(), getBillingIntervalAndCurrencyFromPriceId() — full pricing math without Stripe SDK.
Usage batch recording
bb.usage.recordBatch() sends up to 100 usage events in one call. Idempotency keys per item. Returns per-item success/failure results.
Event system
Global eventEmitter for cross-component communication. Workspace CRUD, user changes, and role updates emit typed events components can subscribe to.
Auth intent preservation
saveAuthIntent() stores the destination before auth redirect. consumeAuthIntent() restores it after sign-in. Survives full page reloads.
40+
React Hooks
20+
Conditional Components
13
Settings Screens
150+
TypeScript Types
8
Languages