> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Seat-Based Billing

> Implement per-user pricing for team software, SaaS products, and enterprise licenses with flexible seat management and proration.

<Info>
  Seat-based billing charges customers based on the number of users on their account. Dodo Payments implements it using the **add-on** system: a base subscription product plus a per-seat add-on whose quantity represents the seat count.
</Info>

<CardGroup cols={2}>
  <Card title="Implementation Tutorial" icon="code" href="/developer-resources/seat-based-pricing">
    Step-by-step guide with code examples.
  </Card>

  <Card title="Add-ons Documentation" icon="puzzle" href="/features/addons">
    Learn about the add-on system that powers seat-based billing.
  </Card>

  <Card title="Subscription Management" icon="repeat" href="/features/subscription">
    Manage seat-based subscriptions and plan changes.
  </Card>

  <Card title="Webhooks" icon="bell" href="/developer-resources/webhooks/intents/subscription">
    Track seat changes with subscription webhooks.
  </Card>
</CardGroup>

***

## What is Seat-Based Billing?

Seat-based billing charges customers based on the number of users who access your product. Instead of a flat fee, the price scales with team size.

### Common Use Cases

| Industry | Example | Pricing Model |
| - | - | - |
| Team Collaboration | Slack, Notion, Asana | Per active user/month |
| Developer Tools | GitHub, GitLab, Jira | Per seat/month |
| CRM Software | Salesforce, HubSpot | Per user license |
| Design Tools | Figma, Canva | Per editor seat |
| Security Software | 1Password, Okta | Per user/month |
| Video Conferencing | Zoom, Teams | Per host license |

### Benefits of Seat-Based Pricing

**For Your Business:**

* Revenue scales as customers grow
* Customers can budget predictably
* Clear upgrade path from individual to team to enterprise
* Higher lifetime value as teams expand

**For Your Customers:**

* Pay only for the users they have
* Easy to understand and forecast costs
* Add or remove users as needed
* Fair pricing that matches team size

***

## How It Works

Dodo Payments implements seat-based billing using the **Add-ons** system. A seat-based subscription has two parts:

| Component | What it is | Example |
| - | - | - |
| **Base product** | The subscription plan | "Team Plan" — \$50/month |
| **Seat add-on** | A per-unit charge for each seat | "Extra Seat" — \$10/month per seat |

The customer's monthly total is:

```
Total = Base price + (Seat add-on price × seat quantity)
```

**Example: 8 extra seats on a Team Plan**

```
Base plan:  $50/month
Seats:      8 × $10 = $80/month
────────────────────────────────
Total:      $130/month
```

***

## Pricing Strategies

Choose the seat-based pricing strategy that fits your business:

### Strategy 1: Base + Per-Seat Add-on

Include a set number of seats in the base plan, charge for additional seats.

```
Team Plan: $49/month (includes 5 seats)
Extra seats: $10/month each
8 total seats = $49 + (3 × $10) = $79/month
```

**Best for:** Products where small teams can function with the base offering.

### Strategy 2: Pure Per-Seat Pricing

Charge a flat rate per seat with no base fee.

```
Per Seat: $12/month
5 users  = 5 × $12  = $60/month
50 users = 50 × $12 = $600/month
```

**Implementation:** Set the base plan price to \$0 and use only the seat add-on.

**Best for:** Simple, transparent pricing.

### Strategy 3: Tiered Seat Pricing

Different base plans with different per-seat rates.

```
Starter:     $0/month   + $15/seat
Pro:         $99/month  + $10/seat
Enterprise:  $499/month + $7/seat
```

**Implementation:** Create separate products for each tier with different add-on prices.

**Best for:** Encouraging upgrades to higher tiers; enterprise sales.

### Strategy 4: Seat Bundles

Sell seats in packs rather than individually.

```
5-Seat Pack:  $50/month  ($10/seat)
10-Seat Pack: $80/month  ($8/seat)
25-Seat Pack: $175/month ($7/seat)
```

**Implementation:** Create multiple add-ons for different pack sizes.

**Best for:** Simplifying purchasing decisions; encouraging larger commitments.

***

## Setting Up Seat-Based Billing

### Step 1: Plan Your Pricing

Before implementation, define your pricing structure:

<Steps>
  <Step title="Define Base Plan">
    Decide what's included in the base subscription:

    * Base price (can be \$0 for pure per-seat)
    * Number of included seats
    * Features available at this tier
  </Step>

  <Step title="Set Seat Pricing">
    Determine the per-seat add-on cost:

    * Price per additional seat
    * Any volume discounts (via multiple add-ons)
    * Maximum seats allowed (if applicable)
  </Step>

  <Step title="Consider Billing Frequency">
    Align seat pricing with your billing cycle:

    * Monthly subscriptions → monthly seat charges
    * Annual subscriptions → annual seat charges (often discounted)
  </Step>
</Steps>

### Step 2: Create the Seat Add-on

In your Dodo Payments dashboard:

1. Navigate to **Products** → **Add-Ons**
2. Click **Create Add-On**
3. Configure the add-on:

| Field | Value | Notes |
| - | - | - |
| Name | "Additional Seat" or "Team Member" | Clear, user-friendly name |
| Description | "Add another team member to your workspace" | Explain what customers get |
| Price | Your per-seat price | e.g., \$10.00 |
| Currency | Match your base product | Must be the same currency |
| Tax Category | Same as base product | Ensures consistent tax handling |

<Tip>
  Use descriptive add-on names that make sense on invoices. "Additional Team Seat" is clearer than "Seat Add-on" for customers reviewing their bills.
</Tip>

### Step 3: Create the Base Subscription

Create your subscription product:

1. Navigate to **Products** → **Create Product**
2. Select **Subscription**
3. Configure pricing and details
4. In the **Add-Ons** section, attach your seat add-on

### Step 4: Attach Add-on to Product

Link the seat add-on to your subscription:

1. Edit your subscription product
2. Scroll to **Add-Ons** section
3. Click **Add Add-Ons**
4. Select your seat add-on
5. Save changes

<Check>
  Your subscription product now supports seat-based pricing. Customers can purchase any quantity of additional seats during checkout.
</Check>

***

## Managing Seats

### Adding Seats to New Subscriptions

When creating a checkout session, specify the seat quantity:

```typescript theme={null}
const session = await client.checkoutSessions.create({
  product_cart: [{
    product_id: 'pdt_team_plan',
    quantity: 1,
    addons: [{
      addon_id: 'adn_YOUR_SEAT_ADDON',
      quantity: 8  // 8 additional seats
    }]
  }],
  customer: { email: 'admin@company.com' },
  return_url: 'https://yourapp.com/success'
});
```

### Changing Seat Count on Existing Subscriptions

Use the Change Plan API to adjust seats. The `addons` array sets the **new total** seat count (not the delta).

```typescript theme={null}
// Currently 3 seats → change to 8 seats
await client.subscriptions.changePlan('sub_123', {
  product_id: 'pdt_team_plan',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately',
  addons: [{
    addon_id: 'adn_YOUR_SEAT_ADDON',
    quantity: 8  // new total, not +5
  }]
});
```

### Removing Seats

To reduce seat count, specify the lower quantity:

```typescript theme={null}
// Currently 8 seats → reduce to 3 seats
await client.subscriptions.changePlan('sub_123', {
  product_id: 'pdt_team_plan',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately',
  addons: [{
    addon_id: 'adn_YOUR_SEAT_ADDON',
    quantity: 3  // new total
  }]
});
```

### Removing All Additional Seats

Pass an empty `addons` array to remove all add-ons:

```typescript theme={null}
// Remove all additional seats, keep only base plan seats
await client.subscriptions.changePlan('sub_123', {
  product_id: 'pdt_team_plan',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately',
  addons: []  // removes all add-ons
});
```

***

## Proration for Seat Changes

When a seat change is applied mid-cycle, Dodo Payments calculates the immediate charge in three steps:

```
1. Credit   →  a portion of the OLD plan's price (base + all add-ons)
2. Charge   →  a FULL cycle of the NEW plan's price
3. Net      =  Charge − Credit
```

The credit amount depends on the **proration mode** you choose. The charge is always a full cycle.

<Warning>
  The charge is **always for a full cycle**. Only the credit varies by mode. This is why the amount charged is rarely "new seats × price × days remaining".

  With `prorated_immediately`, the credit shrinks as the cycle progresses, so the same seat change costs more the later it is made. With `difference_immediately` and `full_immediately`, the credit does not depend on timing, so those two cost the same on any day of the cycle.
</Warning>

### How Each Mode Credits

| Mode | Credit (old plan) | Charge (new plan) | Billing cycle |
| - | - | - | - |
| `prorated_immediately` | Remaining time only (time left ÷ cycle length) | Full cycle | Resets to today |
| `difference_immediately` | The whole old plan | Full cycle | Resets to today |
| `full_immediately` | None (0%) | Full cycle | Resets to today |
| `do_not_bill` | Nothing credited, nothing charged | Not charged now — seats active immediately | **Unchanged** |

With `difference_immediately`, the customer pays only the gap between the old plan price and the new plan price. That is where the name comes from, and it is why the amount is the same whenever in the cycle the change is made.

If the credit is larger than the new cycle charge, the difference is held as subscription-scoped credit and applied automatically to future renewals.

<Warning>
  `prorated_immediately`, `difference_immediately`, and `full_immediately` all **reset the billing cycle to the change date**. The next renewal is re-anchored to the day the seat change is applied. Only **`do_not_bill`** keeps the original renewal date (the new seat count is billed in full at the next renewal, with no charge at the time of change).
</Warning>

<Warning>
  **`do_not_bill` applies the seat change immediately, not at renewal.** The new seat count takes effect as soon as the call succeeds, but nothing is charged until the next renewal.

  When **adding** seats, the customer has them free for the rest of the current cycle. Adding 5 seats at \$10 on day 1 of a 30-day cycle gives them 5 free seats for 29 days, and the higher amount is first charged on the original renewal date.

  When **removing** seats, the reverse applies: the seats are withdrawn immediately and no credit is given for the part of the cycle already paid for.

  Use `do_not_bill` when that is what you intend, such as a courtesy upgrade or a sales-agreed trial of extra seats.
</Warning>

### Worked Example: Adding 5 Seats

One scenario run through all four modes, so the numbers are directly comparable.

```
Plan:      $50/month base + $10/seat add-on
Current:   $50 + 3 × $10 = $80/month
New:       $50 + 8 × $10 = $130/month
Timing:    Day 15 of a 30-day cycle (50% remaining)
```

| Mode | Credit | Charge | **Net today** | Next renewal |
| - | - | - | - | - |
| `prorated_immediately` | −\$40.00 (50% × \$80) | +\$130.00 | **\$90.00** | \$130/month from today |
| `difference_immediately` | −\$80.00 (the whole old plan) | +\$130.00 | **\$50.00** | \$130/month from today |
| `full_immediately` | \$0.00 | +\$130.00 | **\$130.00** | \$130/month from today |
| `do_not_bill` | — | — | **\$0.00** | \$130/month on original renewal date |

In all three immediate modes, the customer receives a **full new month at \$130** in exchange for what they pay today.

### Why Timing Matters for `prorated_immediately`

The same change costs more the later in the cycle it is made, because less of the current cycle is left to credit back.

```
Current: $80/month → New: $130/month
```

| When | Remaining | Credit | Charge | **Net today** |
| - | - | - | - | - |
| Day 3 of 30 | 90% | −\$72.00 | +\$130.00 | **\$58.00** |
| Day 15 of 30 | 50% | −\$40.00 | +\$130.00 | **\$90.00** |
| Day 27 of 30 | 10% | −\$8.00 | +\$130.00 | **\$122.00** |

The customer receives a full new month in every row. Only the split between "already paid for" and "paying now" changes.

To make a seat change cost the same regardless of when it happens, use `difference_immediately`.

### Worked Example: The "Surprising Charge"

This is the case that most often surprises merchants. Adding a small seat add-on late in the cycle can produce a charge much larger than the add-on's price.

```
Plan:      $50/month base, no add-ons
Change:    add 1 seat at $10/month
Timing:    Day 27 of a 30-day cycle (10% remaining)
```

| Mode | Credit | Charge | **Net today** |
| - | - | - | - |
| `prorated_immediately` | −\$5.00 (10% × \$50) | +\$60.00 | **\$55.00** |
| `difference_immediately` | −\$50.00 (the whole old plan) | +\$60.00 | **\$10.00** |
| `full_immediately` | \$0.00 | +\$60.00 | **\$60.00** |
| `do_not_bill` | — | — | **\$0.00** |

Adding a \$10/month seat costs **\$55.00** with `prorated_immediately`. The customer is charged for a full new month at \$60 and credited the \$5 that was left on the old month, and their renewal date resets.

To make small mid-cycle additions cost the seat price and nothing more, use `difference_immediately`.

### Worked Example: Removing Seats (Downgrade)

When the new plan costs less than the credit, the excess is held as **subscription credit** and applied automatically to future renewals of this subscription. It is not added to the [Customer Wallet](/features/customer-wallet) and is not a [credit entitlement](/features/credit-based-billing).

```
Current:   $50 base + 8 × $10 seats = $130/month
New:       $50 base + 2 × $10 seats = $70/month
Timing:    Day 6 of a 30-day cycle (80% remaining)
Mode:      prorated_immediately
```

```
Credit for unused time on the current cycle (base plus all add-ons):
  = $130 × 80%
  = $104.00 credit

Charge for a full cycle at the new seat count:
  = $50 + (2 × $10)
  = $70.00

Net = $70.00 − $104.00 = −$34.00

→ $0 charged today
→ $34.00 held as subscription credit (applied to future renewals)
→ Billing cycle resets; next renewal is $70/month
```

<Info>
  The credit covers the **entire** subscription, base plan and all add-ons, not only the seats being removed.
</Info>

### Reading the Preview Response

`previewChangePlan` returns the exact line items that will be billed. Each line item has a `proration_factor`:

| `proration_factor` | Meaning |
| - | - |
| Negative (e.g. `−0.50`) | **Credit** — fraction of the old plan being refunded |
| `1` | **Charge** — a full new billing cycle |
| `0` | **No charge / no credit** for this line |

```json expandable theme={null}
{
  "immediate_charge": {
    "summary": {
      "settlement_amount": 9000,
      "settlement_currency": "USD"
    },
    "line_items": [
      {
        "type": "subscription",
        "unit_price": 5000,
        "quantity": 1,
        "proration_factor": -0.50
      },
      {
        "type": "addon",
        "unit_price": 1000,
        "quantity": 3,
        "proration_factor": -0.50
      },
      {
        "type": "subscription",
        "unit_price": 5000,
        "quantity": 1,
        "proration_factor": 1
      },
      {
        "type": "addon",
        "unit_price": 1000,
        "quantity": 8,
        "proration_factor": 1
      }
    ]
  }
}
```

Reading that: \$50 base and 3 × \$10 addon credited at 50%, a full \$50 base charged, and 8 × \$10 addon charged. Credit = \$40, charge = \$130, net = \$90.

<Info>
  Proration is calculated to the second based on the exact time of the change, not rounded to the nearest day. The worked examples above use round day-boundary numbers for clarity.
</Info>

<Tip>
  **Choosing a proration mode for seat changes**

  * **`difference_immediately`** — the customer pays the price difference, whenever the change is made. Most predictable for teams that adjust seats often, and easiest to explain in your UI.
  * **`prorated_immediately`** — the customer is credited only for the time left on the current cycle. Costs more the later in the cycle the change is made.
  * **`full_immediately`** — the customer pays for a full new cycle with no credit for unused time.
  * **`do_not_bill`** — the seat change takes effect immediately but nothing is charged now. Added seats are free until the next renewal; removed seats are withdrawn with no credit. The renewal date is preserved and the new seat count is billed in full from that renewal onwards. The only mode that does not reset the billing cycle.

  <Note>
    Seats granted through `do_not_bill` are not credited on a later plan change, because they were never billed. If you add 5 seats with `do_not_bill` and then change to 3 seats, the customer is billed for 3 seats in full with no credit for the 5 they were holding.
  </Note>

  Always call `previewChangePlan` and show the returned amount before confirming. See the [Proration Guide](/developer-resources/subscription-upgrade-downgrade#proration-modes) for detailed comparisons.
</Tip>

### Preview Before Changing

Always preview proration before making changes:

```typescript expandable theme={null}
const preview = await client.subscriptions.previewChangePlan('sub_123', {
  product_id: 'pdt_team_plan',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately',
  addons: [{ addon_id: 'adn_YOUR_SEAT_ADDON', quantity: 8 }]
});

console.log('Charge today:', preview.immediate_charge.summary.settlement_amount);
console.log('Currency:', preview.immediate_charge.summary.settlement_currency);
// Show customer: "Adding 5 seats will cost $90.00 today"
```

***

## Tracking Seats with Webhooks

Monitor seat changes by listening to subscription webhooks:

### Relevant Events

| Event | When Triggered | Use Case |
| - | - | - |
| `subscription.active` | New subscription activated | Provision initial seats |
| `subscription.plan_changed` | Seats added/removed | Update seat count in your app |
| `subscription.renewed` | Subscription renewed | Confirm seat count unchanged |
| `subscription.cancelled` | Subscription cancelled | Deprovision all seats |

### Webhook Handler Example

```typescript expandable theme={null}
app.post('/webhooks/dodo', async (req, res) => {
  const event = req.body;

  switch (event.type) {
    case 'subscription.active': {
      // New subscription - provision seats
      const addonSeats = event.data.addons?.reduce(
        (total, addon) => total + addon.quantity, 0
      ) || 0;
      await provisionSeats(event.data.customer.customer_id, addonSeats);
      break;
    }

    case 'subscription.plan_changed': {
      // Seats changed - update access
      const newSeats = event.data.addons?.reduce(
        (total, addon) => total + addon.quantity, 0
      ) || 0;
      await updateSeatCount(event.data.subscription_id, newSeats);
      break;
    }

    case 'subscription.cancelled':
      // Subscription cancelled - deprovision
      await revokeAllSeats(event.data.subscription_id);
      break;
  }

  res.json({ received: true });
});
```

<Tip>
  The `addons` array in the webhook payload contains the current addon quantities. Sum them to get the total seat count. If your base plan includes seats (e.g. 5 included), add that to the addon total in your application logic.
</Tip>

***

## Enforcing Seat Limits

Your application must enforce seat limits. Dodo Payments tracks billing, but you control access.

<Tabs>
  <Tab title="Hard Limit">
    Strictly prevent adding users beyond the seat count.

    ```typescript expandable theme={null}
    async function inviteUser(teamId: string, email: string) {
      const team = await getTeam(teamId);
      const subscription = await getSubscription(team.subscriptionId);
      const totalSeats = calculateTotalSeats(subscription);
      const usedSeats = await countTeamMembers(teamId);

      if (usedSeats >= totalSeats) {
        throw new Error('No seats available. Please upgrade your plan.');
      }

      await sendInvitation(teamId, email);
    }
    ```
  </Tab>

  <Tab title="Soft Limit with Warning">
    Allow exceeding with a warning and grace period.

    ```typescript expandable theme={null}
    async function inviteUser(teamId: string, email: string) {
      const team = await getTeam(teamId);
      const { totalSeats, usedSeats } = await getSeatInfo(team);

      if (usedSeats >= totalSeats) {
        // Allow but flag for billing
        await flagOverage(teamId, usedSeats - totalSeats + 1);
        await notifyAdmin(team.adminEmail, 'You have exceeded your seat limit');
      }

      await sendInvitation(teamId, email);
    }
    ```
  </Tab>

  <Tab title="Auto-Upgrade">
    Automatically add seats when limit is reached.

    ```typescript expandable theme={null}
    async function inviteUser(teamId: string, email: string) {
      const team = await getTeam(teamId);
      // seatAddonQuantity is the current quantity of the seat add-on on the subscription
      const { totalSeats, usedSeats, subscriptionId, seatAddonQuantity } = await getSeatInfo(team);

      if (usedSeats >= totalSeats) {
        // Automatically add a seat
        await client.subscriptions.changePlan(subscriptionId, {
          product_id: team.productId,
          quantity: 1,
          proration_billing_mode: 'prorated_immediately',
          addons: [{ addon_id: 'adn_YOUR_SEAT_ADDON', quantity: seatAddonQuantity + 1 }]
        });

        await notifyAdmin(team.adminEmail, 'A new seat was added to your plan');
      }

      await sendInvitation(teamId, email);
    }
    ```
  </Tab>
</Tabs>

***

## Advanced Patterns

### Different Seat Types

Offer different seat types with different pricing:

```
Full Seats:      $20/month - Full access to all features
View-Only Seats: $5/month  - Read-only access
Guest Seats:     $0/month  - Limited external collaborator access
```

**Implementation:** Create separate add-ons for each seat type.

```typescript theme={null}
const session = await client.checkoutSessions.create({
  product_cart: [{
    product_id: 'pdt_team_plan',
    quantity: 1,
    addons: [
      { addon_id: 'addon_full_seat', quantity: 10 },
      { addon_id: 'addon_viewer_seat', quantity: 25 },
      { addon_id: 'addon_guest_seat', quantity: 50 }
    ]
  }]
});
```

### Annual Seat Discounts

Offer discounted annual seat pricing:

```
Monthly: $15/seat/month
Annual:  $12/seat/month (20% savings)
```

**Implementation:** Create separate products for monthly and annual plans with different add-on prices.

### Minimum Seat Requirements

Require a minimum number of seats for certain plans:

```typescript theme={null}
async function validateSeatCount(planId: string, seatCount: number) {
  const minimums = {
    'pdt_starter': 1,
    'pdt_team': 5,
    'pdt_enterprise': 25
  };

  if (seatCount < minimums[planId]) {
    throw new Error(`${planId} requires at least ${minimums[planId]} seats`);
  }
}
```

***

## Best Practices

### Pricing Best Practices

* **Clear Communication**: Show per-seat pricing prominently on your pricing page
* **Included Seats**: Consider including a few seats in the base price to reduce friction
* **Volume Discounts**: Offer lower per-seat rates for larger teams to win enterprise deals
* **Annual Incentives**: Discount annual plans to improve cash flow and retention

### Technical Best Practices

* **Cache Seat Counts**: Cache subscription seat counts locally to avoid API calls on every request
* **Sync Regularly**: Periodically sync your local seat count with Dodo Payments via API
* **Handle Failures**: If a seat change fails, show clear error messages and retry options
* **Audit Trail**: Log all seat changes for billing disputes and compliance

### User Experience Best Practices

* **Real-Time Feedback**: Show the cost impact immediately when adjusting seats
* **Confirmation Steps**: Require confirmation before billing changes
* **Proration Transparency**: Explain prorated charges clearly before applying
* **Easy Downgrades**: Don't make it difficult to reduce seats (it builds trust)

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Seat count mismatch between app and billing">
    **Symptom**: Your app shows a different seat count than the subscription.

    **Causes**:

    * Webhook not received or processed
    * Race condition during seat change
    * Cached data not updated

    **Solutions**:

    1. Implement webhook handlers for `subscription.plan_changed`
    2. Add a "Sync with billing" button that fetches current subscription
    3. Set cache TTL to ensure regular refresh
  </Accordion>

  <Accordion title="Unexpected mid-cycle charge amount">
    **Symptom**: Customer confused by mid-cycle charge amount.

    **Cause**: Using `prorated_immediately` late in the billing cycle (see [The Surprising Charge](#worked-example-the-surprising-charge) example above).

    **Solutions**:

    1. Always use `previewChangePlan` before making changes
    2. Show clear breakdown: "Adding X seats will cost \$Y today"
    3. Switch to `difference_immediately` if you want the charge to always match the price difference
  </Accordion>

  <Accordion title="Add-on not appearing in checkout">
    **Symptom**: Seat add-on not available during checkout.

    **Causes**:

    * Add-on not attached to product
    * Add-on archived or deleted
    * Currency mismatch between product and add-on

    **Solutions**:

    1. Verify add-on is attached in product settings
    2. Check add-on status in Add-Ons dashboard
    3. Ensure currencies match exactly
  </Accordion>

  <Accordion title="Cannot reduce seats below current usage">
    **Symptom**: Customer wants to reduce seats but has users assigned.

    **Solutions**:

    1. Show which users must be removed before reducing seats
    2. Implement a workflow: Remove users → Reduce seats
    3. Consider a grace period before enforcing seat reduction
  </Accordion>
</AccordionGroup>

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="Seat-Based Pricing Tutorial" icon="code" href="/developer-resources/seat-based-pricing">
    Complete implementation guide with code.
  </Card>

  <Card title="Add-ons" icon="puzzle" href="/features/addons">
    Understand the add-on system in depth.
  </Card>

  <Card title="Plan Changes & Proration" icon="arrows-rotate" href="/developer-resources/subscription-upgrade-downgrade">
    Handle subscription modifications.
  </Card>

  <Card title="Subscription Webhooks" icon="bell" href="/developer-resources/webhooks/intents/subscription">
    Track subscription events.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.