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

# Working with balances

> How v2_6_balances, v2_6_breakdowns and v2_6_rollovers relate to the balances the API returns, with a worked example.

`v2_6_balances` holds the balances `POST /v1/customers.get` and `POST /v1/entities.get` return, already computed. You don't filter out old plans, pick the current cycle, or add rollovers yourself: every row is a balance the API would show, with the same numbers. This page explains the three ideas you need to read them correctly.

## 1. One row per view

A customer and each of its entities can see a different balance for the same feature. The customer sees grants made at customer level. An entity sees those same customer-level grants, **plus** grants on its own plans. So v2.6 stores one balance per view:

* the **customer view**: `scope = 'customer'`, `internal_entity_id` null. This is `POST /v1/customers.get`.
* one **entity view** per entity: `scope = 'entity'`, `internal_entity_id` set. This is `POST /v1/entities.get`.

`balance_id` names the view: `internal_customer_id:internal_feature_id:internal_entity_id`, with the last part empty for the customer view. `v2_6_breakdowns`, `v2_6_rollovers` and `v2_6_flags` carry the same scope, so each view has its own rows.

> [!WARNING]
>
> **Never add up balances across views.** A customer-level grant appears in the customer view and again in every entity's view, so a sum over all rows counts it many times. For a customer's total, filter `scope = 'customer'`. For one entity, filter its `internal_entity_id`.

## 2. The API's filter is already applied

The tables contain only what the API would return at `as_of`:

* grants on plans whose status counts: `active`, plus `past_due` if your organization keeps past-due plans live. Ended, scheduled and expired plans add nothing.
* grants that have not expired, and loose grants (not tied to any plan) that are still live.
* rollovers that have not expired.

A customer who upgraded from Pro v1 to Pro v2 has one breakdown row from Pro v2, not one per plan they ever held. If you need the history, use `v2_6_subscriptions`, which keeps every status.

## 3. Rollovers first, then breakdowns in order

When usage is tracked, Autumn takes it from rollovers first (the one that expires soonest first), then from the breakdown rows in `position` order: position 1, then 2, and so on. `position` is the order of the API's `breakdown[]` array.

A balance's totals add up from its rows:

* **granted** is the sum of `included_grant + prepaid_grant` over its breakdowns, plus each rollover's `granted`.
* **remaining** is the sum of its breakdowns' `remaining`, plus each rollover's `balance`.
* **usage** is the sum of its breakdowns' `usage`, plus each rollover's `granted − balance`.

## Worked example

Customer `cus_123` has two entities, `user_1` and `user_2`. Each has a Seat plan that grants 100 pooled `credits` and 20 `exports` a month. The two seats' credits add up into one shared pool of 200 credits at customer level, and the customer also has a one-off grant of 30 credits. `user_1` has 10 exports rolled over from last month.

**Credits.** The customer view has two breakdown rows. Each entity's view repeats the same two rows, because both grants are at customer level.

| position | grant | granted | remaining | usage |
| - | - | - | - | - |
| 1 | the shared pool (resets monthly, so it is used first) | 200 | 150 | 50 |
| 2 | the one-off grant | 30 | 30 | 0 |

**Exports.** Only the entity views have rows, one for each entity's own Seat plan, plus `user_1`'s rollover.

| view | row | granted | remaining | usage |
| - | - | - | - | - |
| `user_1` | rollover from last month (used first) | 10 | 4 | 6 |
| `user_1` | breakdown, position 1: Seat plan | 20 | 5 | 15 |
| `user_2` | breakdown, position 1: Seat plan | 20 | 20 | 0 |

The resulting balances:

| view | feature | granted | remaining | usage |
| - | - | - | - | - |
| customer | credits | 230 | 180 | 50 |
| `user_1` | credits | 230 | 180 | 50 |
| `user_2` | credits | 230 | 180 | 50 |
| `user_1` | exports | 30 | 9 | 21 |
| `user_2` | exports | 20 | 20 | 0 |

Three things to notice:

1. There is **no customer row for exports**. Both exports grants are on entity plans, so the customer view has nothing for that feature, just as `POST /v1/customers.get` for `cus_123` shows no exports balance.
2. `user_1`'s exports balance includes the rollover: granted 20 + 10, remaining 5 + 4, usage 15 + 6.
3. Summing every credits row would give 690 granted. The customer actually has 230.

## Reading a customer's balances

These queries return exactly what the API returns. They're written in plain SQL; [Querying](https://docs.useautumn.com/documentation/lakehouse/v2-6/querying.md#rebuild-a-customer) has the ClickHouse and BigQuery versions.

```sql theme={null}
-- getCustomer(customer_id).balances
SELECT b.feature_id, b.granted, b.remaining, b.usage, b.unlimited,
       b.overage_allowed, b.next_reset_at, b.as_of
FROM <namespace>.v2_6_balances b
JOIN <namespace>.v2_6_customers c
  ON c.org_id = b.org_id AND c.env = b.env
 AND c.internal_customer_id = b.internal_customer_id
WHERE b.env = 'live' AND c.customer_id = 'cus_123' AND b.scope = 'customer';

-- getEntity(customer_id, entity_id).balances
SELECT b.feature_id, b.granted, b.remaining, b.usage
FROM <namespace>.v2_6_balances b
JOIN <namespace>.v2_6_entities e
  ON e.org_id = b.org_id AND e.env = b.env
 AND e.internal_entity_id = b.internal_entity_id
WHERE b.env = 'live' AND e.customer_id = 'cus_123' AND e.entity_id = 'user_1'
  AND b.scope = 'entity';

-- One balance's breakdown, in the order it's used, and its rollovers
SELECT position, plan_id, included_grant, prepaid_grant, remaining, usage, reset_resets_at
FROM <namespace>.v2_6_breakdowns
WHERE env = 'live' AND balance_id = '<balance_id>'
ORDER BY position;

SELECT granted, balance, expires_at
FROM <namespace>.v2_6_rollovers
WHERE env = 'live' AND balance_id = '<balance_id>'
ORDER BY expires_at;
```

Join entities through the customer, as above: an `entity_id` is unique within one customer only.

## As of when

Expiry and reset dates are judged once per run, at the moment stored in `v2_6_balances.as_of`. A grant or rollover that expires after `as_of` is still in the tables until the next run. If you need a stricter answer, re-check `expires_at` yourself (null means it never expires):

```sql theme={null}
WHERE expires_at IS NULL OR expires_at > <now in epoch ms>
```

## Pools

With a pooled plan item, grants on several entities add up into one shared customer-level balance. In the tables that pool is **one** row in `v2_6_breakdowns`, with `plan_id` null and `pooled_balance_id` set. Its `included_grant` is the pool's total. The plans that feed it are rows in `v2_6_pooled_balance_contributions`, joined on `pooled_balance_id`; they don't appear as breakdown rows of their own.

```sql theme={null}
SELECT pbc.entity_id, pbc.plan_id, pbc.current_contribution, pbc.next_cycle_contribution
FROM <namespace>.v2_6_breakdowns bd
JOIN <namespace>.v2_6_pooled_balance_contributions pbc
  ON pbc.org_id = bd.org_id AND pbc.env = bd.env
 AND pbc.pooled_balance_id = bd.pooled_balance_id
WHERE bd.env = 'live' AND bd.balance_id = '<balance_id>';
```

## Allocations

A customer can allocate shared credits to its entities (set in `billing_controls.balance_allocations` on `v2_6_customers`). The tables show the result the API shows:

* **Customer view:** breakdown amounts are unchanged, and the balance gains `allocated` (credits reserved for entities) and `unallocated` (credits anyone can use).
* **Entity view:** each shared row shows that entity's share instead of the whole amount, with the allocation in `allocation_amount`. An entity with no allocation sees only the unallocated credits.

`allocated` and `unallocated` are null for features the customer doesn't allocate.

## Overage

`v2_6_breakdowns.overage` is the usage beyond what a grant gave:

```sql theme={null}
SELECT balance_id, plan_id, overage
FROM <namespace>.v2_6_breakdowns
WHERE env = 'live' AND overage > 0
  AND (internal_entity_id IS NULL OR source = 'entity');
```

Shared grants are read from the customer view (`internal_entity_id` null) so each is counted once. Grants an entity owns appear only in that entity's view, so they come from rows with `source = 'entity'`. Overage only builds up on grants with `price_billing_method = 'usage_based'`; `overage_allowed` on the balance tells you whether the feature can go over at all.

## Flags

On/off features, such as `sso`, are in `v2_6_flags`, not in `v2_6_balances`. Each view gets one row per feature it has. If several plans grant the same flag, the row is the one the API shows: an active plan's before a past-due plan's, then a loose grant, then anything else.


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