This guide walks you through installing Revstack, defining a simple billing config, and checking your first entitlement. By the end, you’ll have a working example that validates feature access.
Prerequisites
Node.js 18 or later
A Revstack account (sign up at app.revstack.dev )
Your Revstack Secret Key (found in your dashboard)
Step 1: Install the SDK
npm install @revstackhq/node @revstackhq/core
We’re using the Node SDK (@revstackhq/node) for this example. If you’re building a React or Next.js app, see the Installation guide for framework-specific instructions.
Step 2: Define Your Billing Config
Create a revstack.config.ts file in your project root:
import { defineConfig , defineFeature , definePlan } from "@revstackhq/core" ;
// Define the features you want to gate
const features = {
"api-calls" : defineFeature ({
name: "API Calls" ,
type: "metered" ,
unit_type: "requests" ,
}),
"premium-support" : defineFeature ({
name: "Premium Support" ,
type: "boolean" ,
unit_type: "count" ,
}),
};
// Define your billing plans
export default defineConfig ({
features ,
plans: {
// Free plan for new users
free: definePlan < typeof features >({
name: "Free" ,
is_default: true ,
is_public: true ,
type: "free" ,
status: "active" ,
features: {
"api-calls" : { value_limit: 1000 , reset_period: "monthly" },
// premium-support not included (defaults to false)
},
}),
// Paid plan with more limits
pro: definePlan < typeof features >({
name: "Pro" ,
is_default: false ,
is_public: true ,
type: "paid" ,
status: "active" ,
prices: [
{
amount: 2900 , // $29.00 in cents
currency: "USD" ,
billing_interval: "monthly" ,
trial_period_days: 14 ,
},
],
features: {
"api-calls" : { value_limit: 100000 , reset_period: "monthly" },
"premium-support" : { value_bool: true },
},
}),
} ,
}) ;
The definePlan<typeof features> pattern gives you autocomplete and compile-time validation. TypeScript will error if you reference a feature that doesn’t exist.
Step 3: Set Your Secret Key
Add your Revstack Secret Key to your environment variables:
REVSTACK_SECRET_KEY = sk_live_...
Never commit your secret key to version control. Always use environment variables or a secrets manager.
Step 4: Initialize the Client
Create a Revstack client instance:
import { Revstack } from "@revstackhq/node" ;
const revstack = new Revstack ({
secretKey: process . env . REVSTACK_SECRET_KEY ! ,
});
Step 5: Check an Entitlement
Use entitlements.check() to validate feature access:
async function handleAPIRequest ( customerId : string ) {
// Check if the customer can make API calls
const { allowed , limit , usage , reason } = await revstack . entitlements . check (
customerId ,
"api-calls"
);
console . log ( `Allowed: ${ allowed } ` );
console . log ( `Limit: ${ limit } ` );
console . log ( `Current Usage: ${ usage } ` );
console . log ( `Reason: ${ reason } ` );
if ( ! allowed ) {
return {
error: "API quota exceeded. Please upgrade your plan." ,
limit ,
usage ,
};
}
// Report usage after successful API call
await revstack . usage . report ({
customer_id: customerId ,
feature_id: "api-calls" ,
delta: 1 ,
});
return { success: true };
}
// Example usage
handleAPIRequest ( "cus_abc123" )
. then ( result => console . log ( result ))
. catch ( error => console . error ( error ));
Step 6: Run Your Code
Execute your script:
You should see output like:
Allowed: true
Limit: 1000
Current Usage: 0
Reason: allowed
{ success: true }
What Just Happened?
Let’s break down what the entitlement check does:
Customer Identification
The SDK identifies the customer by their ID (cus_abc123). In production, this comes from your auth system.
Plan Resolution
Revstack looks up the customer’s active subscription to determine which plan they’re on (e.g., “Free” or “Pro”).
Feature Lookup
The system finds the feature configuration for api-calls in the customer’s plan.
Quota Check
For metered features, Revstack compares current usage against the limit defined in the plan.
Result
Returns allowed: true/false along with context (limit, usage, reason).
Next: Add a Boolean Feature Check
Boolean features work the same way, but return a simple yes/no:
const { allowed } = await revstack . entitlements . check (
customerId ,
"premium-support"
);
if ( ! allowed ) {
return { message: "Upgrade to Pro for 24/7 premium support" };
}
// Show premium support UI
return < PremiumSupportChat />;
Common Patterns
Gate a Feature in an API Route
app . get ( "/api/advanced-analytics" , async ( req , res ) => {
const customerId = req . user . id ;
const { allowed } = await revstack . entitlements . check (
customerId ,
"advanced-analytics"
);
if ( ! allowed ) {
return res . status ( 403 ). json ({
error: "This feature requires a Pro plan" ,
upgrade_url: "https://yourapp.com/pricing" ,
});
}
const analytics = await generateAdvancedAnalytics ( customerId );
res . json ( analytics );
});
Pre-Check Before Expensive Operations
async function generateAIResponse ( customerId : string , prompt : string ) {
// Check quota BEFORE calling the AI API
const { allowed , limit , usage } = await revstack . entitlements . check (
customerId ,
"ai-tokens" ,
{ amount: 1000 } // Estimate token cost
);
if ( ! allowed ) {
throw new Error (
`Insufficient AI tokens. Used ${ usage } / ${ limit } . Upgrade to continue.`
);
}
const response = await openai . chat . completions . create ({
model: "gpt-4" ,
messages: [{ role: "user" , content: prompt }],
});
// Report actual usage
await revstack . usage . report ({
customer_id: customerId ,
feature_id: "ai-tokens" ,
delta: response . usage . total_tokens ,
});
return response ;
}
Enforce Hard Limits
async function addTeamMember ( organizationId : string , email : string ) {
// Check if adding one more seat is allowed
const { allowed , limit , usage } = await revstack . entitlements . check (
organizationId ,
"team-seats" ,
{ amount: 1 }
);
if ( ! allowed ) {
return {
error: `Your plan includes ${ limit } seats and ${ usage } are in use. Upgrade to add more.` ,
};
}
// Add the team member
await db . teamMembers . create ({ organization_id: organizationId , email });
// Increment seat usage
await revstack . usage . report ({
customer_id: organizationId ,
feature_id: "team-seats" ,
delta: 1 ,
});
return { success: true };
}
Error Handling
The SDK throws structured errors for different failure modes:
import { Revstack , RateLimitError , RevstackAPIError } from "@revstackhq/node" ;
try {
await revstack . entitlements . check ( customerId , "api-calls" );
} catch ( error ) {
if ( error instanceof RateLimitError ) {
// Retry after error.retryAfter seconds
console . log ( `Rate limited. Retry after ${ error . retryAfter } s` );
} else if ( error instanceof RevstackAPIError ) {
// API returned a non-2xx status
console . error ( `API error: ${ error . status } - ${ error . message } ` );
} else {
// Network or configuration error
console . error ( "Unexpected error:" , error );
}
}
Full Example
Here’s a complete working example with error handling:
import { Revstack , RateLimitError } from "@revstackhq/node" ;
const revstack = new Revstack ({
secretKey: process . env . REVSTACK_SECRET_KEY ! ,
});
async function processAPIRequest ( customerId : string , operation : string ) {
try {
// 1. Check entitlement
const { allowed , limit , usage , reason } = await revstack . entitlements . check (
customerId ,
"api-calls"
);
if ( ! allowed ) {
return {
success: false ,
error: `Access denied: ${ reason } ` ,
quota: { limit , usage },
};
}
// 2. Perform the operation
const result = await performOperation ( operation );
// 3. Report usage
await revstack . usage . report ({
customer_id: customerId ,
feature_id: "api-calls" ,
delta: 1 ,
});
return {
success: true ,
data: result ,
quota: { limit , usage: usage + 1 },
};
} catch ( error ) {
if ( error instanceof RateLimitError ) {
return {
success: false ,
error: "Service temporarily unavailable" ,
retryAfter: error . retryAfter ,
};
}
throw error ;
}
}
async function performOperation ( operation : string ) {
// Your business logic here
return { result: `Processed: ${ operation } ` };
}
// Run it
processAPIRequest ( "cus_abc123" , "data-export" )
. then ( result => console . log ( JSON . stringify ( result , null , 2 )))
. catch ( error => console . error ( error ));
Next Steps
Installation Guide Set up Revstack for React, Next.js, or other frameworks
Core Concepts Deep dive into entitlements, plans, and usage metering
API Reference Complete documentation for all SDK methods
Billing as Code Learn how to manage your config with the CLI