---
title: "End-to-End Billing Setup: From Zero to Working Credits in 10 Steps"
description: "Complete walkthrough building a working billing system with OurVibe — metric, metering rule, plan, variant, customer, subscription, and usage in one linear sequence."
order: 6
---

# End-to-End Billing Setup

Pattern: the full billing pipeline, one step at a time.

**Pattern:** COMPLETE BILLING SETUP · 10 STEPS

> **Mental Model:** The full journey from zero to working billing in ten API calls: create a metric, set pricing, build a plan, subscribe a customer, and watch credits flow. Every step shows the exact API call and its response. Ten calls, zero ambiguity.

## Quick Take

- Every billing setup follows the same sequence: **metric → rule → plan → variant → grant template → customer → subscribe → check → use → verify**
- Metering rules price the metric; credit grants fund the customer; entitlement checks gate access
- Usage events are fire-and-forget (`202 Accepted`); balance updates are eventually consistent within seconds
- Idempotency keys on every mutating call protect against network retries

## The goal

Go from an empty QuotaStack tenant to a working billing system where a customer has a subscription, credit grants fire automatically, usage debits credits, and entitlement checks gate access.

We'll use **OurVibe** (a chat companion app) as the running example. By the end, Alice (a customer) will have a weekly subscription that grants 50 credits, and each chat message will cost 1 credit.

## Phase 1: Define what you charge for

### Step 1: Create a billable metric

A billable metric is a named action you charge for. OurVibe charges per message:

```bash
POST /v1/billable-metrics
Idempotency-Key: metric:chat_message

{ "key": "chat_message", "name": "Chat Message" }
```

The `key` is the stable identifier you'll reference everywhere — metering rules, usage events, entitlement checks. It defaults to `type: metered` (credit-based).

### Step 2: Set the price with a metering rule

```bash
POST /v1/metering-rules
Idempotency-Key: rule:chat_message

{ "billable_metric_key": "chat_message", "cost_type": "per_unit", "unit_cost": 1000 }
```

1,000mc per unit = 1 credit per message. Now QuotaStack knows: when a usage event arrives for `chat_message`, debit 1,000mc from the customer's balance.

## Phase 2: Build the plan

### Step 3: Create a plan and variant

The plan is the product ("OurVibe Chat"). The variant is the specific pricing tier ("Weekly Standard" — prepaid, weekly billing cycle):

```bash
POST /v1/plans
Idempotency-Key: plan:ourvibe-chat

{ "name": "OurVibe Chat", "description": "Companion chat subscription plans" }
```

```bash
POST /v1/plans/{plan_id}/variants
Idempotency-Key: variant:weekly-standard

{ "name": "Weekly Standard", "billing_cycle": "weekly", "billing_mode": "prepaid" }
```

### Step 4: Attach a credit grant template

Tell QuotaStack to automatically grant 50,000mc (50 credits) every week:

```bash
POST /v1/plans/{plan_id}/variants/{variant_id}/credit-grants
Idempotency-Key: grant-template:weekly-50k

{
  "credits": 50000,
  "grant_interval": "billing_cycle",
  "grant_type": "recurring",
  "expires_after_seconds": 604800,
  "rollover_percentage": 0,
  "priority": 0
}
```

`expires_after_seconds: 604800` = 7 days. Each grant expires when the next one arrives — no hoarding. `rollover_percentage: 0` means unused credits don't carry over.

At this point you have a complete plan: metric → pricing → variant → automatic grants. Nothing has happened yet because no customer is subscribed.

## Phase 3: Onboard a customer

### Step 5: Create a customer

```bash
POST /v1/customers
Idempotency-Key: customer:user_alice

{ "external_id": "user_alice", "display_name": "Alice" }
```

### Step 6: Subscribe them

After Alice pays via your payment provider (Stripe, Razorpay, etc.), activate the subscription:

```bash
POST /v1/subscriptions
Idempotency-Key: sub:alice-weekly-001

{ "external_customer_id": "user_alice", "plan_variant_id": "{variant_id}" }
```

On activation, QuotaStack immediately fires the credit grant template — Alice now has 50,000mc (50 credits). The subscription status is `active`, with `current_period_end` set to one week from now.

## Phase 4: Use it

### Step 7: Check entitlement before each message

```bash
GET /v1/customer-by-external-id/user_alice/entitlements/chat_message
```

```json
{
  "allowed": true,
  "balance": 50000,
  "estimated_cost": 1000,
  "balance_after": 49000
}
```

`allowed: true` means Alice can send a message. `balance_after: 49000` is what her balance will be afterward. If `allowed` were false, your UI shows "you've used all your messages this week."

### Step 8: Record usage

Alice sends 5 messages. Your backend fires a usage event for each (or batches them):

```bash
POST /v1/usage
Idempotency-Key: usage:msg_001

{
  "external_customer_id": "user_alice",
  "billable_metric_key": "chat_message",
  "units": 5,
  "idempotency_key": "msg_batch_001"
}
```

Returns `202 Accepted` — usage events process asynchronously. 5 × 1,000mc = 5,000mc will be debited within seconds.

### Step 9: Verify the balance

```bash
GET /v1/customer-by-external-id/user_alice/credits
```

```json
{
  "balance": 45000,
  "lifetime_earned": 50000,
  "lifetime_consumed": 5000
}
```

50,000 - 5,000 = 45,000mc remaining. Alice has 45 messages left this week.

## What you've built

```
chat_message metric     → "what we charge for"
  ↓ priced by
per_unit metering rule  → "1,000mc per message"
  ↓ used by
Weekly Standard variant → "50,000mc/week, expires in 7 days"
  ↓ subscribed by
Alice                   → "active, 45,000mc remaining"
  ↓ gated by
entitlement check       → "allowed: true"
  ↓ consumed by
usage events            → "5 messages = 5,000mc debited"
```

The metric defines WHAT you track. The metering rule defines HOW MUCH it costs. The plan variant defines WHO gets credits and when. The subscription binds a customer to a variant. Entitlements gate access. Usage events consume credits.

## Next steps

- Add [variable companion pricing](/docs/use-cases/ai-chat-app#variable-pricing-per-companion) — charge premium companions more by varying the `units` field
- Set up [webhooks](/docs/concepts/webhooks) to react to `subscription.renewal_due` and `credit.consumed` events
- Add a [wallet](/docs/concepts/topups-and-wallets) for pay-as-you-go overflow when weekly credits run out
- Use [reservations](/docs/concepts/reservations) for long-running AI calls that might fail mid-generation

## Concepts Used

- [Metering](/docs/concepts/metering)
- [Credits](/docs/concepts/credits)
- [Subscriptions](/docs/concepts/subscriptions)
- [Entitlements](/docs/concepts/entitlements)
- [Idempotency](/docs/concepts/idempotency)
