Indie Kit DocsIndie Kit Docs

B2B Kit

Launch B2B SaaS with support for multiple organizations and plans, comes with a super admin dashboard to manage organizations and plans.

This documentation is for the B2B Kit, which is a set of utilities for building a B2B SaaS application with Indie Kit.

If you don't have the B2B Kit installed, you can install it by running:

git clone https://github.com/Indie-Kit/b2b-boilerplate
cd b2b-boilerplate

Make sure you have valid Indie Kit license to clone this repo. Buy here Indi B2B Kit

Table of Contents

Database Models

Organizations

The B2B Kit implements a multi-organization system where users can belong to multiple organizations with different roles. Each organization can have its own subscription plan and settings.

Key features:

  1. Organization Structure: Each organization has a unique ID, name, slug, and optional image.
  2. Role-based Access: Members have different roles (owner, admin, user) determining their permissions.
  3. Onboarding Process: Organizations track onboarding status and collect onboarding data.
  4. Billing Integration: Organizations can have subscriptions with Stripe or LemonSqueezy.

Core Organization Model:

The organization model includes:

  • Basic info (name, slug, image)
  • Timestamps for creation and updates
  • Onboarding status and data
  • Billing information (customer IDs and subscription IDs)
  • Plan reference

Authentication & Route Wrappers

The B2B Kit provides several authentication wrappers to secure API routes:

withAuthRequired

Used to secure routes that require user authentication.

// Example usage
import withAuthRequired from "@/lib/auth/withAuthRequired";
 
export const GET = withAuthRequired(async (req, context) => {
  const user = await context.session.user;
  
  // Your authenticated route logic here
  
  return NextResponse.json({ data: "Protected data" });
});

withOrganizationAuthRequired

Used to secure routes that require organization-level authentication with role checks.

// Example usage
import withOrganizationAuthRequired from "@/lib/auth/withOrganizationAuthRequired";
import { OrganizationRole } from "@/db/schema/organization";
 
export const PATCH = withOrganizationAuthRequired(async (req, context) => {
  const currentOrganization = await context.session.organization;
  
  // Your organization-protected route logic here
  
  return NextResponse.json({ success: true });
}, OrganizationRole.enum.admin); // Requires admin role or higher

withSuperAdminAuthRequired

Used to secure routes that should only be accessible to super administrators.

// Example usage
import withSuperAdminAuthRequired from "@/lib/auth/withSuperAdminAuthRequired";
 
export const GET = withSuperAdminAuthRequired(async (req, context) => {
  // Super admin only logic here
  
  return NextResponse.json({ data: "Super admin data" });
});

Organization Roles and Permissions

The B2B Kit implements a role-based permission system with three roles:

export const roleEnum = pgEnum("role", ["admin", "owner", "user"]);
  1. Owner: Highest level of access, can perform all actions within an organization
  2. Admin: Can manage organization settings, members, and most resources
  3. User: Basic access to use the application

Role hierarchy is defined in the hasHigherOrEqualRole function:

const roleHierarchy = {
  user: 0,
  admin: 1,
  owner: 2,
};

This hierarchy is used to check if a user has sufficient permissions to perform actions.

Invitations and Memberships

Organization Memberships

The membership model creates a many-to-many relationship between users and organizations:

  • Links a user to an organization
  • Assigns a role (owner, admin, user)
  • Includes timestamps for creation and updates
  • Uses a composite primary key (organizationId, userId)

Organization Invites

The invite system allows organization members to invite others:

  • Stores the invitee's email
  • References the organization
  • Specifies the intended role
  • Includes an expiration timestamp
  • Contains a unique token for verification

Plans and Subscriptions

Plan Model

The plan model defines different subscription tiers:

  • Basic information (name, codename)
  • Default flag to mark the default plan
  • Pricing options (monthly, yearly, one-time)
  • Integration with payment providers (Stripe, LemonSqueezy)
  • Quotas defining feature limits

Subscription Management

Creating Plans (Admin)

Plans are created through the super admin interface:

  1. Navigate to /super-admin/plans
  2. Click on "Create Plan"
  3. Fill in plan details including:
    • Basic info (name, codename)
    • Pricing options
    • Payment provider IDs
    • Feature quotas

Subscription URL Generation

The getSubscribeUrl function generates URLs for subscription pages:

const getSubscribeUrl = ({
  codename, // Plan codename
  type,     // PlanType: MONTHLY, YEARLY, ONETIME
  provider, // PlanProvider: STRIPE, LEMON_SQUEEZY
  trialPeriodDays, // Optional trial period
}: SubscribeParams) => {
  return `${process.env.NEXT_PUBLIC_APP_URL}/app/subscribe?codename=${codename}&type=${type}&provider=${provider}&trialPeriodDays=${trialPeriodDays}`;
};

Example usage in a Next.js Link component:

import Link from "next/link";
import getSubscribeUrl from "@/lib/plans/getSubscribeUrl";
import { PlanType, PlanProvider } from "@/lib/plans/getSubscribeUrl";
 
<Link href={getSubscribeUrl({
  codename: "pro",
  type: PlanType.MONTHLY,
  provider: PlanProvider.STRIPE,
  trialPeriodDays: 7
})}>
  Subscribe to Pro Plan
</Link>

Special Hooks

useOrganization

A React hook for accessing and managing the current organization:

const {
  organization, // Current organization data with plan
  isLoading,    // Loading state
  error,        // Error state
  mutate,       // Function to refresh data
  switchOrganization, // Function to switch to another organization
} = useOrganization();

Example usage:

import useOrganization from "@/lib/organizations/useOrganization";
 
function OrganizationSettings() {
  const { organization, isLoading } = useOrganization();
  
  if (isLoading) return <div>Loading...</div>;
  
  return (
    <div>
      <h1>{organization.name} Settings</h1>
      {/* Organization settings UI */}
    </div>
  );
}

Organization Utilities

The codebase includes several utility functions for organizations:

  • getUserOrganizations: Fetches all organizations a user belongs to
  • getUserOrganizationById: Fetches a specific organization with its plan
  • createOrganization: Creates a new organization and assigns the creator as owner
  • hasHigherOrEqualRole: Checks if a user's role is sufficient for an action
  • userBelongsToOrganization: Verifies if a user is a member of an organization