Seat Limits
Seats are a limit, not a meter.
Your app already knows how many active members are in a workspace. Guapocado should tell your app how many are allowed.
Config
Section titled “Config”import { defineBilling } from "@guapocado/sdk";
export default defineBilling({ entitlements: { seats: { type: "limit" }, sso: { type: "feature" }, }, products: [ { key: "team", pricing: { mode: "recurring", type: "flat", amount: 2900, currency: "usd", frequency: "month", }, entitlements: { sso: false, seats: { included: 5, expansion: { allowed: true, unit: 1, amount: 1000, currency: "usd", }, }, }, }, { key: "business", pricing: { mode: "recurring", type: "flat", amount: 9900, currency: "usd", frequency: "month", }, entitlements: { sso: true, seats: { included: 25, expansion: { allowed: true, unit: 1, amount: 800, currency: "usd", }, }, }, }, ],});Invite Flow
Section titled “Invite Flow”import { createGuapocadoClient } from "@guapocado/sdk";
export async function inviteMember({ orgId, email,}: { orgId: string; email: string;}) { const customerId = `org_${orgId}`; const guap = createGuapocadoClient({ apiKey: process.env.GUAPOCADO_API_KEY!, customerId, });
const seats = await guap.limit("seats"); const activeMembers = await db.member.count({ where: { orgId, status: "active" }, }); const pendingInvites = await db.invite.count({ where: { orgId, status: "pending" }, });
if (activeMembers + pendingInvites >= seats.limit) { throw new Error("Seat limit reached"); }
return db.invite.create({ data: { orgId, email }, });}Count the thing your product actually reserves. Some products count only active members. Others count active members plus pending invites.
Buying More Seats
Section titled “Buying More Seats”If the plan allows expansion, store purchased seats:
await guap.limits.configure("seats", { purchased: 3, autoExpansionEnabled: false,});Then limit("seats") returns the included seats plus purchased seats.
const seats = await guap.limit("seats");
seats.included; // 5seats.purchased; // 3seats.limit; // 8Auto Expansion
Section titled “Auto Expansion”If your product lets customers automatically buy seats when inviting members, turn on auto expansion:
await guap.limits.configure("seats", { autoExpansionEnabled: true,});Then your app can implement a rule like:
if (activeMembers >= seats.limit) { if (!seats.expansionAllowed || !seats.autoExpansionEnabled) { throw new Error("Seat limit reached"); }
await guap.limits.configure("seats", { purchased: seats.purchased + 1, autoExpansionEnabled: true, });}Keep the final business rule in your app because only your app knows what an active member, pending invite, or billable member means.