More Tutorials

Show prepaid credit balances, grants, and transactions in your customer portal

Usage-based Billing

Overview

If you bill customers with prepaid credits on Chargebee Billing, you often want to give them visibility into those credits inside your own product—how many are left, where they came from, when they expire, and what was recently consumed.

This tutorial explains how to build those credit widgets using three Chargebee Billing read APIs: List Ledger Account Balances, List Grant Blocks, and List Ledger Operations. You don't need a special portal API for this.

Example customer portal widgets showing credit balance, grant breakdown, and recent transactions.

This is about your customer-facing UI, not Chargebee Billing's hosted self-serve portal. All Chargebee API calls must go through your backend.

Scope

This tutorial covers three use cases:

  1. Total credit balance: a headline "credits remaining" view, broken down into available and on-hold.
  2. Grant-level breakdown: per-grant detail: how much was granted, consumed, remaining, and when it expires.
  3. Recent transactions: a chronological ledger of credit activity, with support for your own metadata.

It does not cover aggregated or time-series views (for example, a daily credit-usage chart), server-side filtering by metadata, or a single granted-vs-remaining rollup across all grants.

Architecture overview

The flow splits responsibilities between your backend and Chargebee Billing as follows.

Your application:

  • Authenticates the signed-in customer and resolves their Chargebee subscription_id and credit unit_id
  • Calls Chargebee Billing from your backend (never from the browser)
  • Shapes API responses into UI-ready payloads
  • Renders balance, grant, and transaction widgets in your product

Chargebee Billing:

  • Returns the real-time rolled-up credit balance for a subscription and unit
  • Returns grant blocks with granted, used, remaining, and expiry details
  • Returns ledger operations that power a chronological activity feed

A typical flow:

  1. When the customer opens your portal or credits page, your front end calls your backend.
  2. Your backend fetches the headline balance from List Ledger Account Balances.
  3. Your backend fetches grant blocks for the per-grant breakdown.
  4. Your backend fetches recent ledger operations for the activity feed.
  5. Your backend returns shaped JSON; your front end renders the widgets.

Before you start

For this tutorial to work, you need the following setup:

Set up your development environment

  1. Obtain an API key from your Chargebee Billing test site (subdomain ends in -test).
  2. Have a backend service that can call Chargebee REST APIs securely.
  3. Be familiar with REST/JSON and the front-end framework of your choice. Formatting and layout are yours—this tutorial focuses on the data.

Set up prepaid credits in Chargebee Billing

  1. Configure prepaid credits on your Chargebee Billing site (credit unit, grants on plan/addon, and optional overage).
  2. Have at least one subscription that has already received credit grants.
  3. Note the subscription ID and credit unit ID (for example, ai_credits) you will use in API filters.

APIs at a glance

Use caseAPIWhat it gives you
1. Total balanceGET /ledger_account_balancesOne rolled-up balance (provisioned + overdraft) per credit unit
2. Grant breakdownGET /grant_blocksOne row per grant block, with lifecycle detail
3. Recent transactionsGET /ledger_operationsOne row per credit operation, newest first

Implementation steps

Follow these steps to power credit widgets in your customer portal.

Conventions used in the examples below:

  • Base URL: https://YOUR_CHARGEBEE_SUBDOMAIN.chargebee.com/api/v2/
  • Auth: HTTP Basic, with the API key as the username: -u YOUR_API_KEY:
  • Numbers are decimal strings. Credit amounts (balance, amount, usable_balance, and similar) are returned as strings to preserve precision. Keep them as strings (or use a decimal library) through formatting and arithmetic—don't convert with JavaScript Number.
  • List envelope. List responses use Chargebee's standard shape: { "list": [{ "<resource>": { ... } }], "next_offset": "..." }. Response snippets below show the inner resource object unless noted.

Use case 1: Total credit balance

Goal: a headline number—"You have X credits available"—optionally split into available vs. on-hold.

Call List Ledger Account Balances, filtered to one subscription and one credit unit. With both filters set, you typically get a single real-time balance snapshot (a provisioned/overdraft pair).

curl https://YOUR_CHARGEBEE_SUBDOMAIN.chargebee.com/api/v2/ledger_account_balances -G \
  -u YOUR_API_KEY: \
  --data-urlencode "subscription_id[is]=1mGETgZVF2umUZq" \
  --data-urlencode "unit_id[is]=ai_credits"

Relevant fields from list[0].ledger_account_balance:

{
  "provisioned_balance": {
    "total_balance": "40",
    "usable_balance": "36.4",
    "hold_amount": "3.6"
  },
  "overdraft_balance": {
    "is_unlimited": false,
    "limit": "50",
    "usable_balance": "25"
  }
}

Use the response fields as follows to render the balance widget:

  • Available nowprovisioned_balance.usable_balance
  • On hold (reserved by in-progress authorizations) → provisioned_balance.hold_amount
  • Total remainingprovisioned_balance.total_balance (usable_balance + hold_amount)
  • Overdraft (only if you offer it) → overdraft_balance.usable_balance, or limit for the cap. If is_unlimited is true, treat overdraft as uncapped and expect limit / balance fields to be null.

A minimal backend handler that shapes this for your front end:

// GET /credits/balance?subscriptionId=...&unitId=...
app.get("/credits/balance", async (req, res) => {
  const cb = await fetchChargebee("/ledger_account_balances", {
    "subscription_id[is]": req.query.subscriptionId,
    "unit_id[is]": req.query.unitId,
  });
  const row = cb.list?.[0]?.ledger_account_balance;
  if (!row?.provisioned_balance) {
    return res.json({ available: "0", onHold: "0", total: "0" });
  }
  const b = row.provisioned_balance;
  res.json({
    available: b.usable_balance,
    onHold: b.hold_amount,
    total: b.total_balance,
  });
});

Use case 2: Grant-level breakdown

Goal: for each bucket of credits, show how much was granted, how much is left, and when it expires.

Call List Grant Blocks, filtered to the subscription (and preferably the same unit_id), scoped to account_type[is]=provisioned so overdraft blocks stay out of the prepaid breakdown, and sorted newest-first.

curl https://YOUR_CHARGEBEE_SUBDOMAIN.chargebee.com/api/v2/grant_blocks -G \
  -u YOUR_API_KEY: \
  --data-urlencode "subscription_id[is]=1mGETgZVF2umUZq" \
  --data-urlencode "unit_id[is]=ai_credits" \
  --data-urlencode "account_type[is]=provisioned" \
  --data-urlencode "sort_by[desc]=created_at"

Each list[].grant_block looks like this:

{
  "id": "__dev__KyVkYhVBJgyRF9",
  "granted_amount": "100",
  "balance": "72.5",
  "hold_amount": "5",
  "used_amount": "22.5",
  "expires_at": 1775402925,
  "grant_source": "subscription_created",
  "status": "available"
}

Map each grant block to a row in your UI:

  • Grantedgranted_amount
  • Remainingbalance
  • On holdhold_amount (optional; useful if you show reserved credits)
  • Usedused_amount
  • Expiresexpires_at (Unix seconds; format as a date)
  • Type / sourcegrant_source (see below)
  • Statestatus (available, in_grace_period, exhausted, scheduled)

Because each grant block carries its own granted_amount and used_amount, a "used vs. granted" bar or percentage is meaningful here—unlike at the account level.

Label grant sources

grant_source is the raw API value describing how the credits were issued. Treat it as a contract, not a display string—rename or group it to fit your product. A common mapping:

grant_sourceSuggested label
subscription_createdInitial credits (subscription created)
subscription_renewedSubscription renewed
grant_renewalRecurring / renewal credits
subscription_changedPlan change
top_upTop-up
promotional_grantsBonus credits
rolloverRolled over

Whether you show each grant as its own row or roll several sources into one line is your call.

Use case 3: Recent transactions

Goal: a chronological feed of credit activity—"used 15 credits", "50 credits added", "10 expired".

Call List Ledger Operations, filtered to the subscription, newest-first, with a page size that fits your UI.

curl https://YOUR_CHARGEBEE_SUBDOMAIN.chargebee.com/api/v2/ledger_operations -G \
  -u YOUR_API_KEY: \
  --data-urlencode "subscription_id[is]=1mGETgZVF2umUZq" \
  --data-urlencode "unit_id[is]=ai_credits" \
  --data-urlencode "sort_by[desc]=created_at" \
  --data-urlencode "limit=25"

To restrict to a time window, add a created_at filter (for example, created_at[after]=<unix_seconds>).

Each list[].ledger_operation is one consolidated event—even if it drew credits from several grant blocks, the customer sees a single line.

{
  "id": "9lfj6x1f5",
  "type": "capture",
  "amount": "15",
  "ledger_operation_timestamp": 1774978580,
  "metadata": { "project_id": "proj_42", "user": "alice" }
}

Map each operation to a row in your UI:

  • Whenledger_operation_timestamp (business event time) or created_at (when Chargebee Billing recorded it)
  • What happenedtype, mapped to a friendly label
  • How many creditsamount

Suggested type labels:

typeSuggested labelCustomer-facing?
capture, capture_authorizationUsageUsually yes
allocationCredits addedUsually yes
expiryExpiredUsually yes
rolloverRolled overOptional
voidRemovedOptional
authorizeHold placedOften hide or show as "Reserved"
release_authorizationHold releasedOften hide
adjustmentAdjustmentOptional

For a simple usage feed, filter with type[in]=["capture","capture_authorization","allocation","expiry"] (or filter client-side to those type values) so holds don't clutter the list.

Use metadata for display context

If you attach a metadata object when recording operations, Chargebee Billing stores and returns it verbatim. Use it to enrich each row—for example, the project, workspace, or user a consumption belongs to.

Summary

In this tutorial, you learned how to:

  • Fetch a real-time credit balance with List Ledger Account Balances and map available, on-hold, and overdraft amounts to your UI.
  • List grant blocks for a per-grant breakdown of granted, used, remaining, expiry, and source.
  • List ledger operations for a recent activity feed, including optional metadata for display context.
  • Proxy all Chargebee Billing calls from your backend and shape responses for your front end.

Reference

Was this tutorial helpful ?
Need more help?

We're always happy to help you with any questions you might have! Click here to reach out to us.


In this Page