Claude
Skills
Sign in
Back

credit-based-billing

Included with Lifetime
$97 forever

Guide for implementing credit-based billing with Dodo Payments - credit entitlements, balances, ledger management, rollover, overage, and meter-based deduction.

General

What this skill does


# Dodo Payments Credit-Based Billing

**Reference: [docs.dodopayments.com/features/credit-based-billing](https://docs.dodopayments.com/features/credit-based-billing)**

Grant customers a balance of credits (API calls, tokens, compute units, or any custom metric) and deduct from that balance as they consume your service.

---

## Overview

Credit-based billing lets you:
- **Issue credits** with subscriptions, one-time purchases, or via API
- **Deduct automatically** via usage meters or manually via API
- **Configure rollover** to carry unused credits forward
- **Handle overage** when credits run out mid-cycle
- **Set expiration** rules per credit entitlement
- **Track everything** via a full audit ledger

Credits work across all product types: subscriptions, one-time purchases, and usage-based billing.

---

## Core Concepts

### Credit Types

| Type | Description | Best For |
|------|-------------|----------|
| **Custom Unit** | Your own metric (tokens, API calls, compute hours) with configurable precision (0–3 decimals) | API calls, AI tokens, compute hours, messages |
| **Fiat Credits** | Real currency value (USD, EUR, etc.) that depletes as customers use your service | Prepaid balances, promotional credits, compensation |

### Credit Lifecycle

1. **Credits Issued** — Granted on purchase (subscription cycle or one-time) or via API
2. **Credits Consumed** — Deducted via meter events or manual API calls
3. **Credits Expire or Roll Over** — At cycle end, unused credits expire or carry forward
4. **Overage Handling** — If balance hits zero, overage is forgiven, billed, or carried as deficit

### Grant Sources

| Source | Description |
|--------|-------------|
| **Subscription** | Credits issued each billing cycle |
| **One-Time** | Credits issued with a one-time payment |
| **API** | Credits granted manually via API or dashboard |
| **Rollover** | Credits carried over from a previous billing cycle |

---

## Quick Start

### 1. Create a Credit Entitlement

```typescript
import DodoPayments from 'dodopayments';

const client = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
});

const credit = await client.creditEntitlements.create({
  name: 'API Credits',
  credit_type: 'custom_unit',
  unit_name: 'API Calls',
  precision: 0,
  expiry_duration: 30, // days
  rollover_enabled: false,
  allow_overage: false,
});
```

### 2. Attach Credits to a Product

In Dashboard → Products → Create/Edit Product → Entitlements → Attach Credits:
- Select the credit entitlement
- Set credits issued per billing cycle (subscriptions) or total (one-time)
- Configure trial credits, proration, low balance threshold

### 3. Create Checkout with Credit Product

```typescript
const session = await client.checkoutSessions.create({
  product_cart: [
    {
      product_id: 'prod_ai_pro_plan', // Product with credits attached
      quantity: 1,
    }
  ],
  customer: { email: '[email protected]' },
  return_url: 'https://yourapp.com/success',
});

// Redirect to session.checkout_url
```

### 4. Deduct Credits via Usage Events

```typescript
// Meter linked to credit entitlement deducts automatically
await client.usageEvents.ingest({
  events: [{
    event_id: `gen_${Date.now()}_${crypto.randomUUID()}`,
    customer_id: 'cus_abc123',
    event_name: 'ai.generation',
    timestamp: new Date().toISOString(),
    metadata: { model: 'gpt-4', tokens: '1500' }
  }]
});
```

### 5. Check Balance

```typescript
const balance = await client.creditEntitlements.balances.get(
  'cent_credit_id',
  'cus_abc123'
);

console.log(`Available: ${balance.available_balance}`);
console.log(`Overage: ${balance.overage_balance}`);
```

---

## API Reference

### Credit Entitlement CRUD

| Operation | Method | Endpoint |
|-----------|--------|----------|
| Create | `POST` | `/credit-entitlements` |
| List | `GET` | `/credit-entitlements` |
| Get | `GET` | `/credit-entitlements/{id}` |
| Update | `PATCH` | `/credit-entitlements/{id}` |
| Delete | `DELETE` | `/credit-entitlements/{id}` |
| Undelete | `POST` | `/credit-entitlements/{id}/undelete` |

### Balance & Ledger Operations

| Operation | Method | Endpoint |
|-----------|--------|----------|
| List All Balances | `GET` | `/credit-entitlements/{id}/balances` |
| Get Customer Balance | `GET` | `/credit-entitlements/{id}/balances/{customer_id}` |
| Create Ledger Entry | `POST` | `/credit-entitlements/{id}/balances/{customer_id}/ledger-entries` |
| List Customer Ledger | `GET` | `/credit-entitlements/{id}/balances/{customer_id}/ledger` |
| List Customer Grants | `GET` | `/credit-entitlements/{id}/balances/{customer_id}/grants` |

---

## Implementation Examples

### TypeScript/Node.js

#### Create Credit Entitlement

```typescript
import DodoPayments from 'dodopayments';

const client = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY!,
});

// Custom unit credit (AI tokens)
const tokenCredit = await client.creditEntitlements.create({
  name: 'AI Tokens',
  credit_type: 'custom_unit',
  unit_name: 'tokens',
  precision: 0,
  expiry_duration: 30,
  rollover_enabled: true,
  max_rollover_percentage: 25,
  rollover_timeframe: 'month',
  max_rollover_count: 3,
  allow_overage: true,
  overage_limit: 50000,
  price_per_unit: 0.001,
  overage_behavior: 'bill_overage_at_billing',
});

// Fiat credit (USD balance)
const usdCredit = await client.creditEntitlements.create({
  name: 'Platform Credits',
  credit_type: 'fiat',
  unit_currency: 'USD',
  expiry_duration: 90,
  rollover_enabled: false,
  allow_overage: false,
});
```

#### Manual Credit/Debit via Ledger Entry

```typescript
// Grant credits manually (e.g., promotional bonus)
await client.creditEntitlements.balances.createLedgerEntry(
  'cent_credit_id',
  'cus_abc123',
  {
    type: 'credit',
    amount: '500',
    description: 'Welcome bonus - 500 free API credits',
    idempotency_key: `welcome_bonus_${customerId}`,
  }
);

// Debit credits manually (e.g., service compensation deduction)
await client.creditEntitlements.balances.createLedgerEntry(
  'cent_credit_id',
  'cus_abc123',
  {
    type: 'debit',
    amount: '100',
    description: 'Manual deduction for premium support',
    idempotency_key: `support_deduction_${Date.now()}`,
  }
);
```

#### Query Customer Balance and Ledger

```typescript
// Get current balance
const balance = await client.creditEntitlements.balances.get(
  'cent_credit_id',
  'cus_abc123'
);
console.log(`Balance: ${balance.available_balance}`);

// List all balances for a credit entitlement
const allBalances = await client.creditEntitlements.balances.list(
  'cent_credit_id'
);

// Get full transaction history
const ledger = await client.creditEntitlements.balances.listLedger(
  'cent_credit_id',
  'cus_abc123'
);

for (const entry of ledger.items) {
  console.log(`${entry.type}: ${entry.amount} | Balance: ${entry.balance_after}`);
}

// List credit grants
const grants = await client.creditEntitlements.balances.listGrants(
  'cent_credit_id',
  'cus_abc123'
);
```

#### Update Credit Entitlement Settings

```typescript
await client.creditEntitlements.update('cent_credit_id', {
  rollover_enabled: true,
  max_rollover_percentage: 50,
  allow_overage: true,
  overage_limit: 10000,
  price_per_unit: 0.002,
  overage_behavior: 'bill_overage_at_billing',
});
```

### Python

```python
from dodopayments import DodoPayments
import os
import uuid
from datetime import datetime

client = DodoPayments(bearer_token=os.environ["DODO_PAYMENTS_API_KEY"])

# Create credit entitlement
credit = client.credit_entitlements.create(
    name="AI Tokens",
    credit_type="custom_unit",
    unit_name="tokens",
    precision=0,
    expiry_duration=30,
    rollover_enabled=True,
    max_rollover_percentage=25,
    allow_overage=True,
    overage_limit=50000,
    price_per_unit=0.001,
    overage_behavior="bill_overage_at_billing",
)

# Grant credits manually
client.credit_entitlements.balances.create_ledger_entry(
    credit_entitlement_id="cent_credit_id

Related in General