Catalog Builders
Reference: metered(), boolean(), creditSystem(), plan() — define features and plans as code
Catalog Builders
Builder functions for defining features and plans declaratively. Features become handles you can use directly for .check() and .track().
metered(slug, opts?)
Create a metered feature handle. Metered features have numeric limits that reset on a schedule.
import { metered } from "@owostack/core";
const apiCalls = metered("api-calls", { name: "API Calls" });Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | Unique feature identifier |
opts.name | string | No | Human-readable name (auto-generated from slug if omitted) |
Methods
.limit(value, config?)
Create a plan feature entry with a specific limit.
apiCalls.limit(1000);
apiCalls.limit(50000, { reset: "daily" });
apiCalls.limit(50000, {
overage: "charge",
overagePrice: 100,
maxOverageUnits: 100000,
});| Parameter | Type | Default | Description |
|---|---|---|---|
value | number | — | Usage limit per period |
config.reset | ResetInterval | "monthly" | Reset schedule |
config.overage | "block" | "charge" | "block" | What happens when limit is exceeded |
config.overagePrice | number | — | Price per overage unit (minor currency) |
config.maxOverageUnits | number | — | Hard cap on overage units per period |
config.billingUnits | number | 1 | Package size for billing |
config.creditCost | number | — | Cost in credits per unit |
.unlimited()
Create a plan feature entry with no limit.
apiCalls.unlimited();.config(opts)
Create a plan feature entry with full config object.
apiCalls.config({
limit: 50000,
reset: "monthly",
overage: "charge",
overagePrice: 100,
});.check(customer, opts?)
Check if a customer has access to this feature.
const result = await apiCalls.check("user_123");
const result = await apiCalls.check("user_123", { value: 5 });
const result = await apiCalls.check("user_123", { sendEvent: true });Returns Promise<CheckResult>.
.track(customer, value?, opts?)
Track usage for this feature.
await apiCalls.track("user_123");
await apiCalls.track("user_123", 5);
await apiCalls.track("user_123", 1, { entity: "workspace_123" });Returns Promise<TrackResult>.
boolean(slug, opts?)
Create a boolean feature handle. Boolean features are either enabled or disabled per plan — no usage tracking.
import { boolean } from "@owostack/core";
const analytics = boolean("analytics", { name: "Analytics Dashboard" });Parameters
Same as metered().
Methods
.on()
Feature is included in this plan.
analytics.on();.off()
Feature is NOT included in this plan.
analytics.off();.check(customer, opts?)
Check if a customer has access to this feature.
const { allowed } = await analytics.check("user_123");Returns Promise<CheckResult>.
Boolean features have no .track() method — the type system enforces this.
creditSystem(slug, config)
Create a credit system definition. A credit system abstracts usage across multiple features into a single "credit" balance.
import { creditSystem, metered } from "@owostack/core";
const gpt4 = metered("gpt-4");
const dallE = metered("dall-e");
const aiCredits = creditSystem("ai-credits", {
name: "AI Credits",
features: [
gpt4(20), // GPT-4 costs 20 credits per track()
dallE(50), // Dall-E costs 50 credits per track()
],
});Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | Unique credit system identifier |
config.name | string | No | Human-readable name |
config.description | string | No | Description |
config.features | FeatureHandle[] | Yes | Array of features that consume from this system |
Methods
.credits(amount, config?)
Include a specific amount of credits for this system in a plan.
aiCredits.credits(1000, { reset: "monthly", overage: "charge" });plan(slug, config)
Create a plan definition for the catalog.
import { plan } from "@owostack/core";
plan("pro", {
name: "Pro",
price: 500000,
currency: "NGN",
interval: "monthly",
features: [
apiCalls.limit(50000, { overage: "charge", overagePrice: 100 }),
aiCredits.credits(1000),
analytics.on(),
seats.limit(20, { reset: "never" }),
],
});Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | Unique plan identifier |
config.name | string | Yes | Human-readable plan name |
config.price | number | Yes | Price in minor currency units (e.g. kobo) |
config.currency | Currency | Yes | Currency code ("NGN", "GHS", "ZAR", "KES", "USD") |
config.interval | PlanInterval | Yes | Billing interval ("monthly", "yearly", etc.) |
config.features | PlanFeatureEntry[] | Yes | Array of feature entries from .limit(), .credits(), etc. |
config.description | string | No | Plan description |
config.planGroup | string | No | Group for upgrade/downgrade logic |
config.trialDays | number | No | Trial period in days |
config.metadata | Record<string, unknown> | No | Custom metadata |
owo.sync()
Push the catalog to the API. Creates and updates features, credit systems, and plans.
const result = await owo.sync();Returns Promise<SyncResult>
interface SyncResult {
success: boolean;
features: { created: string[]; updated: string[]; unchanged: string[] };
creditSystems: { created: string[]; updated: string[]; unchanged: string[] };
plans: { created: string[]; updated: string[]; unchanged: string[] };
warnings: string[];
}CLI: owo sync
Push a catalog from the command line.
npx owo sync [options]| Option | Description |
|---|---|
--config <path> | Path to config file (default: ./owo.config.ts) |
--dry-run | Show what would change without applying |
--key <api-key> | API secret key (or set OWOSTACK_SECRET_KEY env var) |
--url <api-url> | API URL override |
Example config file
// owo.config.ts
import { Owostack, metered, boolean, creditSystem, plan } from "@owostack/core";
const apiCalls = metered("api-calls");
const aiCredits = creditSystem("ai-credits", {
features: [apiCalls(1)],
});
export default new Owostack({
secretKey: process.env.OWOSTACK_SECRET_KEY!,
catalog: [
plan("starter", {
name: "Starter",
price: 0,
currency: "NGN",
interval: "monthly",
features: [aiCredits.credits(1000)],
}),
plan("pro", {
name: "Pro",
price: 500000,
currency: "NGN",
interval: "monthly",
features: [aiCredits.credits(50000)],
}),
],
});