Owostack

Addon Packs

Let customers purchase prepaid credit top-ups

Addon Packs

Credit packs are one-time purchasable bundles that top up a prepaid balance. Instead of simple counters (1 API call = 1 unit), you abstract usage into credits — customers buy credit packs (e.g. $10 for 1,000 credits) and features consume credits at different rates. This guide shows how to set them up and sell them.

Set up a credit system

Credits are scoped to a credit system. A credit system groups related features that share a balance (e.g. "AI Credits").

Define it in code:

const aiTokens = metered("ai-tokens", { name: "AI Tokens" });

const aiCredits = creditSystem("ai-credits", {
  name: "AI Credits",
  description: "Credits for AI model usage",
  features: [
    aiTokens(1), // 1 token costs 1 credit
  ],
});

plan("pro", {
  name: "Pro",
  price: 2000,
  currency: "USD",
  interval: "monthly",
  features: [aiTokens.limit(5000, { creditCost: 1 })],
});

Each call to track() for ai-tokens deducts 1 credit from the customer's balance in that credit system.

Create a credit pack

Credit packs are one-time purchasable bundles of credits. You can define them programmatically in your catalog using the creditPack() function:

const starterPack = creditPack("starter-credits", {
  name: "Starter Pack",
  description: "500 credits for getting started",
  credits: 500,
  price: 1000, // $10.00 (minor currency units)
  currency: "USD",
  creditSystem: "ai-credits", // Must match the credit system slug
});

export const owo = new Owostack({
  secretKey: process.env.OWOSTACK_SECRET_KEY!,
  catalog: [
    aiCredits,
    starterPack,
    // ... your plans
  ],
});

Then sync to deploy:

owosk sync

The pack will be created and is now available for purchase.

You can also create credit packs in the dashboard under Add-ons → Credit Packs if you prefer a UI approach.

Credit pack options

OptionTypeRequiredDescription
namestringYesHuman-readable name
descriptionstringNoDescription shown to customers
creditsnumberYesNumber of credits in the pack
pricenumberYesPrice in minor currency units (e.g. 1000 = $10.00)
currencyCurrencyYesCurrency code (e.g. "USD", "NGN")
creditSystemstringYesSlug of the credit system this pack is tied to
providerstringNoOverride the default payment provider
metadataobjectNoCustom metadata for your use

Let customers buy credits

Use addon() to initiate a credit pack purchase:

const result = await owo.addon({
  customer: "user_123",
  pack: "starter-credits", // credit pack slug
  quantity: 1,
});

if (result.requiresCheckout) {
  // Redirect to payment
  console.log(result.checkoutUrl);
} else {
  // Credits applied immediately (card on file was charged)
  console.log("New balance:", result.balance);
}

If the customer has a payment method on file, the charge happens immediately and credits are added to their balance. Otherwise, a checkout URL is returned.

Adjust quantity

Customers can buy multiple packs at once:

const result = await owo.addon({
  customer: "user_123",
  pack: "starter-credits",
  quantity: 3, // 1500 credits total
});

Check the balance

Use check() on a feature in the credit system to see the remaining balance:

const access = await owo.check({
  customer: "user_123",
  feature: "ai-tokens",
});

if (access.credits?.source === "credit_system") {
  const planBalance = access.credits.plan.balance ?? 0;
  console.log("Plan credits remaining:", access.credits.plan.balance);
  console.log("Add-on credits remaining:", access.credits.addonBalance);
  console.log(
    "Total spendable credits:",
    planBalance + access.credits.addonBalance,
  );
}

For credit-system features, access.credits is the canonical balance object. Use top-level balance only as the generic remaining balance for the checked feature.

Consume credits

track() deducts credits automatically when you track a feature inside the credit system:

await owo.track({
  customer: "user_123",
  feature: "ai-tokens",
  value: 1, // deducts creditCost (1) from balance
});

Variable credit costs

Different features can consume credits at different rates within the same credit system:

const gpt4 = metered("gpt-4");
const dallE = metered("dall-e");

const aiCredits = creditSystem("ai-credits", {
  name: "AI Credits",
  features: [
    gpt4(20), // 1 GPT-4 call costs 20 credits
    dallE(50), // 1 DALL-E generation costs 50 credits
  ],
});

// Create different pack sizes
const basicPack = creditPack("basic-credits", {
  name: "Basic Pack",
  credits: 500,
  price: 500, // $5.00
  currency: "USD",
  creditSystem: "ai-credits",
});

const proPack = creditPack("pro-credits", {
  name: "Pro Pack",
  credits: 2500,
  price: 2000, // $20.00
  currency: "USD",
  creditSystem: "ai-credits",
});

When you track() a feature in a credit system, Owostack deducts the calculated amount automatically:

await owo.track({
  customer: "user_123",
  feature: "gpt-4",
});
// Deducts 20 credits from the "ai-credits" balance

Complete example

Here's a complete setup with plans, credit systems, and credit packs:

import { Owostack, metered, creditSystem, creditPack, plan } from "owostack";

// Define features
const apiCalls = metered("api-calls", { name: "API Calls" });
const aiTokens = metered("ai-tokens", { name: "AI Tokens" });

// Define credit system
const apiCredits = creditSystem("api-credits", {
  name: "API Credits",
  description: "Credits for API and AI usage",
  features: [
    apiCalls(1), // 1 API call = 1 credit
    aiTokens(10), // 1 AI token = 10 credits
  ],
});

// Define credit packs
const starterPack = creditPack("starter-pack", {
  name: "Starter Pack",
  description: "500 API credits",
  credits: 500,
  price: 1000, // $10.00
  currency: "USD",
  creditSystem: "api-credits",
});

const proPack = creditPack("pro-pack", {
  name: "Pro Pack",
  description: "2500 API credits",
  credits: 2500,
  price: 3500, // $35.00
  currency: "USD",
  creditSystem: "api-credits",
});

// Define plan with some included credits
const owo = new Owostack({
  secretKey: process.env.OWOSTACK_SECRET_KEY!,
  catalog: [
    plan("pro", {
      name: "Pro",
      price: 2900,
      currency: "USD",
      interval: "monthly",
      features: [
        apiCalls.limit(1000, { reset: "monthly" }),
        apiCredits.credits(100, { reset: "monthly" }), // Include 100 credits monthly
      ],
    }),
    apiCredits,
    starterPack,
    proPack,
  ],
});

export { owo };

Sync once to create everything:

owosk sync

Then let customers purchase additional credits:

// Customer buys a pack
const result = await owo.addon({
  customer: "user_123",
  pack: "pro-pack",
  quantity: 2, // 5000 credits total
});

if (result.requiresCheckout) {
  res.redirect(result.checkoutUrl!);
} else {
  console.log(
    `Added ${result.credits} credits. New balance: ${result.balance}`,
  );
}

// Track usage (deducts credits automatically)
await owo.track({ customer: "user_123", feature: "ai-tokens" });
// Deducts 10 credits from balance

On this page

AI Chat

Owostack docs assistant

Start a new chat below.