Guides
Meter Usage and Enforce Limits
Report consumption against NUMBER grants on three seeded instances — a hard cap that refuses the next report, an allowance that accepts a report past the cap, an unlimited grant — then correct a counter, wire the events, and design a quota of your own.
Kaiten Sushi Shop grants the same menu-items entitlement three ways: Starter caps it at 5 with no allowance, Standard grants 10 with a 10 % allowance, Premium 50 with 20 % — and Premium's locations is unlimited. This guide walks three seeded instances through exactly those grants: Ninja Osaka's hard cap refuses the next report, Demo Restaurant's allowance accepts a report past the cap, Sakura Tokyo's unlimited grant never pushes back. Along the way you read usage, correct a counter, wire the events Kaiten emits, and design a quota of your own.
Reporting is an API act by nature: in production, your product's backend reports through one of the SDKs or plain HTTP, and the console is where you watch the effect. You need $KAITEN_API and a token ($KAITEN_TOKEN) carrying write:instances — which includes read:instances — plus read:licenses for one read of a grant, and write:entitlements and write:licenses for the last step, as set up in the quickstart, and a stack seeded with the Kaiten Sushi Shop dataset. Responses are illustrative — ids, timestamps, and period bounds depend on your stack and the day you seed it.
Read the current usage
- Open Customers → Instances, then
ninja-osaka-prod, then its Entitlements & Usage tab. The rows carry the entitlements' names: Menu Items reads5 / 5— at the grant already, with the Limit reached badge; Monthly Orders reads18 / 20inside the current month, whose bounds the row shows as its current window.
The console measures each meter against the most the grant allows — its value plus its allowance — and stops at 100 %. Its Near limit badge appears from 80 % of that maximum: a fixed band of the console, distinct from the entitlement's own warningThresholdPercent, which drives an event.
Hit the hard cap
Ninja Osaka publishes a sixth dish. menu-items is a LATEST gauge — each report states the current count rather than an increment:
curl -i -X POST "$KAITEN_API/instances/ninja-osaka-prod/entitlements/menu-items/usage" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": {"type": "number", "value": 6}, "behavior": "append"}'HTTP/1.1 409 Conflict
Content-Type: application/problem+json{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.8",
"title": "Conflict",
"status": 409,
"code": "ReportEntitlementUsageMetric.ThresholdExceeded",
"detail": "Usage for menu-items exceeds the allowed threshold",
"instance": "/api/instances/ninja-osaka-prod/entitlements/menu-items/usage"
}6 > 5: the report is refused and not written — read the usage back and it is still 5 of 5. The refusal is recorded as ENTITLEMENT_USAGE_REPORT_REJECTED, so a customer who later disputes a blocked dish can be shown the exact report that was refused and the grant it was refused against. The body follows RFC 9457 and carries a stable code alongside these fields.
Reporting 5 again is accepted: the total is unchanged, event_count grows by one, and because the total sits exactly on the grant, INSTANCE_ENTITLEMENT_USAGE_REACHED fires — the signal an upgrade flow listens for.
The counter tells the same story. Two more orders take monthly-orders from 18 to exactly 20 — accepted, INSTANCE_ENTITLEMENT_USAGE_REACHED; the 21st is refused the same way, because that grant is a hard cap too:
curl -X POST "$KAITEN_API/instances/ninja-osaka-prod/entitlements/monthly-orders/usage" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": {"type": "number", "value": 2}, "behavior": "append"}'Then the 21st:
curl -i -X POST "$KAITEN_API/instances/ninja-osaka-prod/entitlements/monthly-orders/usage" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": {"type": "number", "value": 1}, "behavior": "append"}'HTTP/1.1 409 Conflict, with the same code as the refused dish and "detail": "Usage for monthly-orders exceeds the allowed threshold".
What the refusal means for the end user — a disabled checkout, an upgrade prompt — is your product's decision; Kaiten enforces at the report boundary.
Report inside an allowance
Demo Restaurant runs on Standard v1, where menu-items is granted 10 with a 10 % allowance: the maximum allowed usage is 10 + 10 × 10 / 100 = 11. The instance holds 6 dishes. Publish five more:
curl -X POST "$KAITEN_API/instances/demo-restaurant-prod/entitlements/menu-items/usage" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": {"type": "number", "value": 11}, "behavior": "append"}'{
"entitlementId": "3d4e5f6a-7b8c-4d9e-0f1a-2b3c4d5e6f7a",
"entitlementSlug": "menu-items",
"licenseId": "c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f",
"licenseSlug": "standard",
"value": {"type": "number", "value": 11, "event_count": 4},
"limit": {"type": "number", "value": 10}
}Accepted, one past the grant. Two events are written: ENTITLEMENT_USAGE_REPORT_ACCEPTED for the report, and INSTANCE_ENTITLEMENT_CAP_EXCEEDED because the total crossed the granted value inside the allowance — with overage: 1. No early warning fires on the way: the seeded menu-items sets no warning threshold (see the last step to set one). Open demo-restaurant-prod in the console, on its Entitlements & Usage tab: Menu Items reads 11 / 10 (+10% overage), at 100 % and with the Limit reached badge. The meter is measured against the maximum allowed usage — here 11 — so the total sits at the allowance's edge, where the next report is refused; a tick on the meter marks the granted 10.
Publish a twelfth dish:
curl -i -X POST "$KAITEN_API/instances/demo-restaurant-prod/entitlements/menu-items/usage" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": {"type": "number", "value": 12}, "behavior": "append"}'409 Conflict — 12 > 11, the same refusal as on Starter, one step later. The total stays at 11. INSTANCE_ENTITLEMENT_CAP_EXCEEDED fires once per crossing: reporting 11 again does not repeat it; the signal fires again only after the total has dropped back under the grant and crosses it anew.
Report against an unlimited grant
Sakura Tokyo's production instance runs on Premium v2, where locations is granted -1 — the unlimited sentinel — with limitCapExceededOveragePercent: -1. The restaurant group opens a fourth location:
curl -X POST "$KAITEN_API/instances/sakura-tokyo-prod/entitlements/locations/usage" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": {"type": "number", "value": 4}, "behavior": "append"}'{
"entitlementId": "5a6b7c8d-9e0f-4a1b-2c3d-4e5f6a7b8c9d",
"entitlementSlug": "locations",
"licenseId": "b2f3e4d5-c6a7-4b8c-9d0e-1f2a3b4c5d6e",
"licenseSlug": "premium-v2",
"value": {"type": "number", "value": 4, "event_count": 4},
"limit": {"type": "number", "value": -1}
}Accepted, and only ENTITLEMENT_USAGE_REPORT_ACCEPTED is written: an unlimited grant has no cap to reach, no allowance to exceed, and no warning boundary to cross. The same locations entitlement is a hard cap of 1 on Starter — the enforcement belongs to the grant, which is why Kaiten can sell the same quota as unlimited to one tier and capped to another.
Correct a counter
behavior: set writes an absolute value instead of folding one in — the way to correct a total downward, or to undo this guide. Put Demo Restaurant's menu back at 6:
curl -X POST "$KAITEN_API/instances/demo-restaurant-prod/entitlements/menu-items/usage" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": {"type": "number", "value": 6}, "behavior": "set"}'The response shows "value": 6 with "event_count": 1 — a set is one observation of the counter, and the count restarts from it. A set is compared with the grant like any report: 6 > 11 is false, so it is accepted; a set that lands above the maximum allowed usage is refused the same way as an append. Put Sakura Tokyo back at 3 locations and Ninja Osaka's monthly-orders back at 18 the same way.
React to the events
Every step above produced events, and each one also landed in the instance's audit trail. The ones a product acts on:
| Event | When | What to wire |
|---|---|---|
ENTITLEMENT_USAGE_REPORT_ACCEPTED | Every accepted report | Your own analytics; the line-by-line receipt in a dispute |
ENTITLEMENT_USAGE_REPORT_REJECTED | A report refused above the grant's maximum allowed usage | The same dispute receipt, from the other side |
INSTANCE_ENTITLEMENT_USAGE_WARNING_THRESHOLD_REACHED | Usage crosses the entitlement's warning percentage (off by default) | An early nudge — in-app banner, sales alert |
INSTANCE_ENTITLEMENT_USAGE_REACHED | Usage lands exactly on the granted value | The upgrade prompt; a CRM task on the customer |
INSTANCE_ENTITLEMENT_CAP_EXCEEDED | Usage crosses the granted value inside a soft limit's allowance — once per crossing | Overage handling downstream |
INSTANCE_ENTITLEMENT_USAGE_PERIOD_ROLLED_OVER | A periodic counter closes a window | Period-end reporting; one entry per closed window, skipped windows included |
Two delivery paths, one stream: on Kaiten Cloud Kaiten Cloud subscribe a webhook endpoint to these events; on a self-hosted stack, consume them from RabbitMQ. Receive events walks through the first delivery on either path, and the event catalog carries every payload.
Design your own quota
Two objects carry the decisions. The entitlement carries the measurement — made once, for every License. The grant carries the amount and the enforcement — made per License.
On the entitlement — aggregation, how each append folds into the total:
| Method | The total becomes | Kaiten Sushi Shop |
|---|---|---|
SUM | The running sum — a counter | monthly-orders: report each order as 1, or a batch as its count |
COUNT | The number of reports, whatever their value | logins, sessions |
LATEST | The last value reported — a gauge | menu-items, delivery-drivers, locations: report the current count |
MAX / MIN | The highest or lowest value seen | peak concurrent kitchen screens |
AVERAGE | The mean of all reports | average order preparation time |
On the entitlement — warning threshold: warningThresholdPercent, the percentage of the granted value at which INSTANCE_ENTITLEMENT_USAGE_WARNING_THRESHOLD_REACHED fires. 0 (the default) disables it; the seeded Kaiten Sushi Shop catalog sets none, so its quotas raise no early warning until you set one.
On the entitlement — reset period: lifetime by default. A resetPeriod of HOUR, DAY, WEEK, MONTH, or YEAR makes the counter start over, with resetAnchor CALENDAR (UTC calendar windows) or LICENSE_START (windows phased off the instance's startLicenseDate). The rollover happens lazily: a read after a window closes already shows the new window, and the next report rolls the counter over and records every closed window. Declare it at creation — it is immutable once set — and not on a LATEST gauge, which has nothing to reset.
On the grant — value and allowance: the granted number, and limitCapExceededOveragePercent — 0 for a hard cap, a positive percentage for a soft limit that accepts usage up to value + value × percent / 100, -1 with a value of -1 for unlimited. Omitted, it defaults to 0, or to -1 for an unlimited value.
The quickstart's priority-orders is such an entitlement — SUM, monthly, calendar-aligned. Here it is again with a warning threshold and unit labels added:
curl -X POST "$KAITEN_API/entitlements" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Priority Orders",
"slug": "priority-orders",
"description": "Priority orders a restaurant can place per month.",
"type": "NUMBER",
"aggregationMethod": "SUM",
"warningThresholdPercent": 80,
"resetPeriod": "MONTH",
"resetAnchor": "CALENDAR",
"unitSingular": "priority order",
"unitPlural": "priority orders"
}'Then grant it per License, each with its own enforcement — a hard cap of 5 on the quickstart's Omakase License, and 50 with a 10 % allowance on Premium v2, the version the Premium family serves:
curl -i -X POST "$KAITEN_API/licenses/omakase/entitlements" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"entitlementSlug": "priority-orders", "value": {"type": "number", "value": 5}, "limitCapExceededOveragePercent": 0}'curl -i -X POST "$KAITEN_API/licenses/premium-v2/entitlements" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"entitlementSlug": "priority-orders", "value": {"type": "number", "value": 50}, "limitCapExceededOveragePercent": 10}'Each grant returns HTTP/1.1 204 No Content. A grant belongs to one License version: instances on another version of Premium — Kappa Kyoto's, on v1 — do not see it until that version grants it too. Report from your product — one append per order for exact accounting, or an hourly append of the period'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. monthly-orders is counted and reset the same way for every License; how many orders a License grants, and how far past that number a report may go, differs per tier — without three definitions of the same quota.
Where to go next
Usage & Metering
The observed side in one screen — the report path, the two numbers on the grant, and periodic counters.
Usage & Thresholds
The allowance arithmetic, the refusal, and the three crossing signals in depth.
Gate a feature by License
The BOOLEAN side of the same chain — a flag whose rule reads a grant.
Answer who had access
The accepted and refused reports above, read back as a receipt.
Gate a Feature by License
The canonical chain end to end — a BOOLEAN entitlement, two Licenses granting it differently, a flag whose rule reads the grant, two evaluations, and both decisions in the organization's audit trail.
Progressive Rollouts
Ship a feature in stages with one flag — a single customer first, then a deterministic percentage, a dated ramp, general availability, and the kill switch — with rules that read the contract or the caller's context.