# Usage-based billing

> Meter events from your system, include free units, and price usage by tier on each subscription invoice.

Source: https://docs.borga.is/billing/usage-based

Usage-based (metered) billing charges customers for what they consume. You define a **Meter** that turns raw events into a quantity, attach a metered **Price** to it, and send **usage events** as things happen. At the end of each billing period Borga aggregates the events, subtracts included units, prices the rest against the tiers and adds a line to the subscription invoice.

**Define a meter**

A meter names the event it counts and how to aggregate it.

```ts Node.js
// Count API requests
const requests = await borga.meters.create({
  name: "API requests",
  event_name: "api_request",
  aggregate_type: "count",
  unit_label: "requests",
});

// Sum tokens, priced per million
const tokens = await borga.meters.create({
  name: "LLM tokens",
  event_name: "llm_completion",
  aggregate_type: "sum",
  aggregate_property: "metadata.total_tokens",
  unit_label: "tokens",
  unit_multiplier: 1_000_000,
});
```

| `aggregate_type` | Result for the period |
| --- | --- |
| `count` | Number of matching events |
| `sum` | Sum of `aggregate_property` across events |
| `max`, `min`, `avg` | Highest, lowest or mean value of `aggregate_property` |
| `unique` | Number of distinct values of `aggregate_property`, for example active users |

`aggregate_property` is a path into the event's metadata such as `metadata.total_tokens`. `filter` restricts which events count, for example `{ "metadata.tier": "pro" }`. `unit_multiplier` divides the aggregate before pricing so you can store raw tokens and price per million.

**Create a metered price**

A metered price references the meter and prices its quantity, optionally with free `included_units` and graduated or volume tiers.

```ts Node.js
const price = await borga.prices.create({
  product: "prod_4Gh6Ij8Kl0Mn2Op4Qr6St8Uv",
  type: "recurring",
  currency: "ISK",
  recurring: { interval: "month", usage_type: "metered" },
  meter: "mtr_1Zx3Cv5Bn7Mq9We1Rt3Yu5Io",
  included_units: 10_000, // first 10k requests are free
  tiers_mode: "graduated",
  tiers: [
    { up_to: 100_000, unit_amount: 2 }, // 2 kr. per request
    { up_to: null, unit_amount: 1 }, // 1 kr. above 100k
  ],
});
```

`graduated` tiers price each band separately (the first 100k at one rate, the rest at another). `volume` tiers price the whole quantity at the rate of the band it lands in. Combine a metered price with a fixed price in one subscription for base-fee-plus-overage plans.

**Send usage events**

Report events as they happen, singly or in batches of up to 1000. Identify the customer by Borga id or by your own `external_customer_id`, which matches `Customer.external_id`.

```ts Node.js
// Single event
await borga.usageEvents.create({
  event_name: "api_request",
  customer: "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz",
  idempotency_key: "req_01J9X2K7M3", // dedupes retries
  metadata: { endpoint: "/v1/chat" },
});

// Batches of up to 1000, identified by your own customer ids
await borga.usageEvents.createBatch([
  {
    event_name: "llm_completion",
    external_customer_id: "user_12345",
    timestamp: "2026-09-07T12:00:00Z",
    idempotency_key: "cmpl_a1",
    metadata: { total_tokens: 640 },
  },
  {
    event_name: "llm_completion",
    external_customer_id: "user_12345",
    timestamp: "2026-09-07T12:00:02Z",
    idempotency_key: "cmpl_a2",
    metadata: { total_tokens: 1024 },
  },
]);
```
```bash curl
curl https://api.borga.is/v1/events \
  -H "Authorization: Bearer sk_test_…" \
  -H "Content-Type: application/json" \
  -d '{
    "event_name": "api_request",
    "customer": "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz",
    "idempotency_key": "req_01J9X2K7M3",
    "metadata": { "endpoint": "/v1/chat" }
  }'
```

Always set `idempotency_key` to something derived from the event, such as your request id. Repeated keys are dropped and counted in the response's `duplicates`, so retrying a batch never double-counts. `timestamp` defaults to now and may not be more than 60 seconds in the future.

**Show customers their usage**

Read a customer's live balance for the current period to build usage dashboards or warnings.

```ts Node.js
const balance = await borga.meters.balance(
  "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz",
  "mtr_1Zx3Cv5Bn7Mq9We1Rt3Yu5Io",
);

console.log(balance.consumed_units, "of", balance.included_units);
console.log("overage:", balance.overage_units);
```

`GET /v1/customer-state/:customerId` returns every active subscription and meter balance for a customer in one call, and `GET /v1/meters/:id/quantities` returns the aggregate for an arbitrary period.

## Included units and rollover

`included_units` on a price (or on a subscription item, which overrides the price) are free each period. Set `credit_rollover: true` on the item to carry unused units into the next period, for data or message bundles.

When a customer has used 80% or more of their included units, Borga emits `usage.high_watermark` once per period so you can warn them before overage starts.

## At period end

The billing cycle reads the meter for the period, applies included and rolled-over units, prices the remainder and adds a `metered` line to the invoice with the unit label. Usage in the new period starts from zero (plus any rollover). Events that arrive after the period closed count towards the period they are timestamped in only if that period is still open, so send events promptly.

## Reference

- [Meters API](/api/meters)
- [Usage events API](/api/usage-events)
- [Prices API](/api/prices) for tiers and included units
