Kaiten

Entitlements

Metering

How a NUMBER entitlement is measured — the six aggregation methods, append versus set, what happens inside a usage report, and the periodic usage windows that reset a counter.

Overview

Metering is the observed side of the contract: how much of a NUMBER (or NUMBER_AI_CREDIT) entitlement an instance has consumed. Kaiten keeps one usage record per entitlement per instance — a running total, the number of reports that produced it, and, for a periodic entitlement, the start of the window the total belongs to. Your product reports; Kaiten folds the report into the record through the entitlement's aggregation method, compares the result with the grant of the instance's License, and either writes it or refuses it.

Your product
  │  POST /instances/demo-restaurant-prod/entitlements/monthly-orders/usage
  │  { "value": { "type": "number", "value": 1 }, "behavior": "append" }
  ▼
Kaiten — one transaction
  │  resolve the grant (value 100, overage allowance 10 %) and the current window
  │  fold the report: SUM → 41 + 1 = 42
  │  compare with the maximum allowed usage: 42 ≤ 110 → accepted
  ▼
{ "value": { "type": "number", "value": 42, "event_count": 42 }, "limit": { "type": "number", "value": 100 },
  "currentPeriodStart": "2026-09-01T00:00:00Z", "currentPeriodEnd": "2026-10-01T00:00:00Z" }

Only the NUMBER family is metered. A BOOLEAN or CONFIG entitlement has nothing to count — reporting against one is refused with 400.

Examples use $KAITEN_API and $KAITEN_TOKEN as set up in the quickstart, on the seeded Kaiten Sushi Shop data, where the seed leaves demo-restaurant-prod at 41 monthly orders. Responses are illustrative — ids depend on your stack, and the window bounds on the month you run them in.


Aggregation methods

The aggregation method is set on the entitlement, once, and is mandatory for the NUMBER family (SUM when omitted). It decides what the stored total means:

MethodThe total becomesKaiten Sushi Shop
SUMThe running sum of every reported value — a countermonthly-orders: report each order as 1, or a batch as its count
COUNTThe number of reports, whatever their valueslogins, sessions
LATESTThe last value reported — a gaugemenu-items, delivery-drivers, locations: report the current count
MAX / MINThe highest or lowest value seenpeak concurrent kitchen screens
AVERAGEThe mean of all reported valuesaverage order preparation time

The method is immutable once set — PUT /entitlements/{slug} accepts it only so a full-replace body can echo the stored value.


Append and set

Every report carries a behavior:

  • append folds the reported value into the stored total through the aggregation method, and increments event_count.
  • set overwrites the total with the reported value, and resets event_count to 1 — a set is one observation of the counter, so the pair {value, event_count} stays consistent. An AVERAGE that follows a set of 60 with an append of 40 therefore reads (60 × 1 + 40) / 2 = 50, the mean of the two observations.

set is how you correct a counter: it writes an absolute value, so correcting 60 down to 25 under a cap of 30 is compared as 25 > 30 — false — and accepted. Only a set that lands above the grant's maximum allowed usage is refused, exactly like an append would be.

Under LATEST, append and set resolve to the same total — "the count is now n" — and differ only in event_count.


Report usage

curl -X POST "$KAITEN_API/instances/demo-restaurant-prod/entitlements/monthly-orders/usage" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "value": { "type": "number", "value": 1 }, "behavior": "append" }'
{
  "entitlementId": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
  "entitlementSlug": "monthly-orders",
  "licenseId": "c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f",
  "licenseSlug": "standard",
  "value": { "type": "number", "value": 42, "event_count": 42 },
  "limit": { "type": "number", "value": 100 },
  "currentPeriodStart": "2026-09-01T00:00:00Z",
  "currentPeriodEnd": "2026-10-01T00:00:00Z"
}
FieldMeaning
valueThe observed side: the stored total after this report, and the number of reports that produced it
limitThe contract side: the value the instance's License grants, -1 for unlimited, omitted when the License grants nothing for this entitlement
currentPeriodStart / currentPeriodEndThe window the total belongs to (start inclusive, end exclusive); omitted for a lifetime counter

The response does not repeat the grant's overage allowance — read it on the grant itself (GET /licenses/standard/entitlements/monthly-orders, field limitCapExceededOveragePercent). Reporting requires write:instances.

What happens inside a report

One transaction, in this order:

  1. Resolve the context. The entitlement's type (anything but the NUMBER family is refused with 400), its aggregation method, the current usage record, and the grant of the instance's License — its value and its limitCapExceededOveragePercent.
  2. Resolve the window — only if the entitlement declares a reset period. Kaiten reads the database clock, computes the current window, and if the stored record belongs to an earlier window it rolls it over: the total is reset to 0 for the new window and written first, one INSTANCE_ENTITLEMENT_USAGE_PERIOD_ROLLED_OVER event is emitted per closed window, and the cap-reached signal re-arms. The rollover is persisted whatever the incoming report's fate — a refused report does not undo a window that legitimately advanced.
  3. Compute the updated total: append through the aggregation method, or set.
  4. Decide. If the updated total exceeds the grant's maximum allowed usage, ENTITLEMENT_USAGE_REPORT_REJECTED is emitted, the usage is not written, and the call returns 409. There is one comparison and no other branch.
  5. Persist the accepted total and emit ENTITLEMENT_USAGE_REPORT_ACCEPTED.
  6. Signal the crossings: exactly on the value → INSTANCE_ENTITLEMENT_USAGE_REACHED; across the value inside the allowance → INSTANCE_ENTITLEMENT_CAP_EXCEEDED; across the warning boundary → INSTANCE_ENTITLEMENT_USAGE_WARNING_THRESHOLD_REACHED. The two crossing signals fire once per crossing, never on later reports that stay above; the cap-reached signal fires on every report whose total lands exactly on the value.

The arithmetic of step 4 and the three signals are the subject of Usage & Thresholds.


Periodic usage windows

By default a usage record is a lifetime counter: it accumulates for as long as the instance exists. An entitlement can instead declare a reset period, and its counter then lives in successive windows.

PropertyValuesMeaning
resetPeriodHOUR, DAY, WEEK, MONTH, YEARThe cadence at which the counter starts over. Unset means a lifetime counter.
resetAnchorCALENDAR, LICENSE_STARTWhere windows begin: aligned on the UTC calendar (start of the hour, day, week, month, or year), or phased off the instance's startLicenseDate. Required exactly when resetPeriod is set; CALENDAR when omitted.

Both are properties of the entitlement — the measurement, identical for every License — and both are immutable once set. They belong to the NUMBER family only, and not to a LATEST gauge: "the most recent value" has no meaningful per-window reset. Kaiten Sushi Shop's monthly-orders is MONTH / CALENDAR; its other quotas are lifetime counters.

The rollover is lazy. No background job closes windows: a read after a window ends already shows the new window at 0 and writes nothing, and the next report detects that the stored record belongs to an earlier window and rolls it over, using the database clock so that every replica agrees on the current window. The counter restarts at 0 in the new window, and INSTANCE_ENTITLEMENT_USAGE_REACHED can fire again once it lands on the value.

Every closed window is recorded. The rollover emits one INSTANCE_ENTITLEMENT_USAGE_PERIOD_ROLLED_OVER per window closed since the last report — the entitlement's own stored bucket, and one synthetic event per intermediate window that was skipped entirely while the instance was silent — so the audit trail has no gaps:

is_syntheticThe event describes
falseThe real stored bucket of the entitlement, with the value and event_count it closed at
trueAn empty intermediate window, materialized for continuity (value 0, event_count 0)

Each event carries closed_period_start, closed_period_end, and new_period_start. These events are the only history of a closed window: there is no separate usage-history table, and the usage record itself only ever holds the current window.

Read back, a periodic entitlement shows currentPeriodStart and currentPeriodEnd on every usage response; a lifetime counter omits both. Existing records of an entitlement that never had a reset period stay lifetime counters.

A dormant HOUR or DAY entitlement can close thousands of windows in one report. Kaiten writes all their events in a single batch inside the same transaction, so the report costs one round trip whatever the gap.


Read usage

curl "$KAITEN_API/instances/demo-restaurant-prod/entitlements/monthly-orders/usage" \
  -H "Authorization: Bearer $KAITEN_TOKEN"

Same shape as the report response. This read is itself recorded in the instance's audit trail as ENTITLEMENT_VALUE_GET.

curl "$KAITEN_API/instances/demo-restaurant-prod/entitlements/usage" \
  -H "Authorization: Bearer $KAITEN_TOKEN"

An array with one entry per entitlement the License grants, each next to its limit and its window bounds.

curl "$KAITEN_API/entitlement-groups/restaurant-operations/usage?instance=demo-restaurant-prod" \
  -H "Authorization: Bearer $KAITEN_TOKEN"

One entry per member of the group, with usageValue (omitted when nothing has been reported yet), licenseValue, and the window bounds.

Reading requires read:instances for the instance routes and read:entitlements for the group route.


Choosing a method

You want to knowMethodReport
How many orders were processed this monthSUM + resetPeriod: MONTH1 per order, or a batch's count
How many dishes are on the menu right nowLATESTThe current count, after each change
The busiest moment of the dayMAX + resetPeriod: DAYThe current concurrency, periodically
How many API sessions were openedCOUNTAny value — only the number of reports counts
The mean preparation timeAVERAGEEach preparation time

Two accounting styles work with a counter: one append per event for exact accounting, or a periodic append of the interval's count when call volume matters more than immediacy. Both are compared with the grant at report time; the second moves the moment of refusal to the batch.

Measurement lives on the entitlement, enforcement on the grant. The method and the window say how monthly-orders is counted, the same for every License; how many orders a License grants, and how far past that number a report may go, is written on each grant — see Usage & Thresholds.

Exact reference — the usage record's columns and CHECK constraint: the entitlement_usage table.

On this page