Documentation Index
Fetch the complete documentation index at: https://docs.billingos.dev/llms.txt
Use this file to discover all available pages before exploring further.
Feature gating is how you turn your free users into paying customers. By restricting access to premium features based on plan, you create natural upgrade moments that drive revenue — without frustrating your users.
Feature types
BillingOS supports three types of features:
| Type | Example | How it works |
|---|
| Boolean | Custom branding | On/off — user either has access or doesn’t |
| Usage quota | 1,000 API calls/month | Counted — resets each billing period |
| Numeric limit | 5 team members | Fixed cap — doesn’t reset |
Client-side gating
Using the FeatureGate component
The simplest approach — wrap content with <FeatureGate>:
import { FeatureGate } from "@billingos/sdk";
<FeatureGate feature="custom_branding">
<BrandingEditor />
</FeatureGate>
Content is only rendered if the user has access. Add a fallback for users who don’t:
import { FeatureGate, UpgradePrompt } from "@billingos/sdk";
<FeatureGate
feature="custom_branding"
fallback={<UpgradePrompt feature="custom_branding" />}
>
<BrandingEditor />
</FeatureGate>
Using the useFeature hook
For more control, check access programmatically:
import { useFeature } from "@billingos/sdk";
function ExportButton() {
const { data } = useFeature("export_pdf");
return (
<button
disabled={!data?.has_access}
onClick={handleExport}
>
Export PDF
{data?.limit && <span>({data.usage}/{data.limit})</span>}
</button>
);
}
Using the useFeatureGate hook
Combines access checking with automatic callbacks:
import { useFeatureGate } from "@billingos/sdk";
function ApiCallButton() {
const { hasAccess, remaining } = useFeatureGate("api_calls", {
onAccessDenied: () => toast.error("Upgrade to access this feature"),
onQuotaExceeded: (usage, limit) =>
toast.warning(`${usage}/${limit} calls used — upgrade for more`),
});
return (
<button disabled={!hasAccess}>
Make API Call ({remaining} left)
</button>
);
}
Server-side gating
For API routes and server actions, use the Node SDK:
import { BillingOS } from "@billingos/node";
const billing = new BillingOS({ secretKey: process.env.BILLINGOS_SECRET_KEY! });
export async function POST(request: Request) {
const userId = getAuthenticatedUser(request);
const entitlement = await billing.checkEntitlement(userId, "api_calls");
if (!entitlement.has_access) {
return Response.json({ error: "Upgrade required" }, { status: 403 });
}
// Process the request...
// Track the usage
await billing.trackUsage({
customerId: userId,
featureKey: "api_calls",
quantity: 1,
});
return Response.json({ result: "success" });
}
Always validate entitlements on the server for security-critical features. Client-side gates improve UX but can be bypassed.