Kaiten

Monetization

Pricing Tiers

Build a tier from one catalog of entitlements, one License per tier, and one grant per pair — the Kaiten Sushi Shop tiers as the worked example, every call verified against the API.

A tier in Kaiten is a License with entitlements granted at tier-specific values. There is no separate "tier" record: the grant — the row between a License and an entitlement — is where the Premium tier gives 1,000 monthly orders, and tolerates 20 % more is written. This page builds Kaiten Sushi Shop's three tiers from scratch, then manages the grants.

Examples use $KAITEN_API and $KAITEN_TOKEN as set up in the quickstart. With the Kaiten Sushi Shop dataset loaded (the demo seed profile, or the Cloud demo sandbox) these entities already exist — creating one again returns 409 Conflict; read them instead and follow along. The slugs starter, standard, and premium name the first version of each License family: on the seeded catalog premium is Premium v1, archived and still run by Kappa Kyoto, and the version on sale is premium-v2.

The Kaiten Sushi Shop tiers

Six entitlements, defined once. Three Licenses — the version each family serves. One grant per pair — a value, and for each NUMBER an overage allowance that says how far past the value a usage report may go:

EntitlementTypeStarter (COMMUNITY)Standard v1 (DEVELOPMENT)Premium v2 (PAID)
menu-itemsNUMBER · LATEST5 · 0 %10 · 10 %50 · 20 %
monthly-ordersNUMBER · SUM · monthly20 · 0 %100 · 10 %1,000 · 20 %
delivery-driversNUMBER · LATEST2 · 0 %5 · 10 %10 · 20 %
locationsNUMBER · LATEST1 · 0 %3 · 10 %unlimited
delivery-trackingBOOLEANfalsefalsetrue
support-tierCONFIGcommunity forumemail, 48 hemail + Slack, 4 h

The other versions each change one grant: Standard v2 grants delivery-tracking true, the courier tracking it was revised for; Premium v1 grants 5 delivery drivers; and the draft Premium v3 grants 100 menu items. Read the table by column and you have a tier; read it by row and you have one entitlement whose measurement never changes — only the granted value and its enforcement do. Starter's grants are hard caps (0 %): a report that would exceed the value is refused. Standard and Premium are soft limits: a report is accepted up to value + value × percent / 100 — 110 orders on Standard, 1,200 on Premium — and refused beyond. Premium's locations is unlimited: the value -1 with an allowance of -1, never refused. Usage & Thresholds carries the arithmetic.

Build the tiers

Define the entitlements

The catalog is shared by every tier, so it comes first. A NUMBER entitlement carries its measurement semantics — aggregation, warning threshold, optional reset period — and a BOOLEAN or CONFIG entitlement carries none. Nothing about enforcement is written here; that comes with each grant in step 3:

curl -X POST "$KAITEN_API/entitlements" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Monthly Orders",
    "slug": "monthly-orders",
    "description": "Orders a restaurant can process per month.",
    "type": "NUMBER",
    "aggregationMethod": "SUM",
    "resetPeriod": "MONTH",
    "resetAnchor": "CALENDAR",
    "icon": "lucide:shopping-cart",
    "unitSingular": "order",
    "unitPlural": "orders",
    "userFacing": true,
    "displayOrder": 2
  }'
curl -X POST "$KAITEN_API/entitlements" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Delivery Tracking",
    "slug": "delivery-tracking",
    "description": "Real-time delivery tracking for end customers.",
    "type": "BOOLEAN",
    "icon": "lucide:navigation",
    "userFacing": true,
    "displayOrder": 5
  }'

Each returns 201 Created with the entitlement and its id. Repeat for menu-items, delivery-drivers, locations (all NUMBER with LATEST aggregation — each report states the current count — and no reset period) and support-tier (CONFIG). The Kaiten Sushi Shop definitions present each entitlement to customers — an icon, userFacing, a displayOrder from 1 to 6, and unit labels on the NUMBER ones — and set no warning threshold; add warningThresholdPercent to have Kaiten signal before a cap is reached (Usage & Thresholds). Types, aggregation, and windows in depth: the Entitlements deep-dive.

Create one License per tier

curl -X POST "$KAITEN_API/licenses" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Starter",
    "slug": "starter",
    "description": "Entry tier for single-location restaurants getting started.",
    "type": "COMMUNITY",
    "isDefault": true
  }'
curl -X POST "$KAITEN_API/licenses" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Premium",
    "slug": "premium",
    "description": "Multi-location restaurant groups with delivery fleets of up to five drivers.",
    "type": "PAID",
    "isDefault": false
  }'

standard (DEVELOPMENT) follows the same shape. Each call names no family, so it opens one, with the License as its version 1 and the License's slug as the family's. The seeded catalog then grows two of them with familySlug — Standard v2, Premium v2 and the draft v3 — and puts Premium v2 forward as its family's default; License Types covers how a family grows.

Grant each entitlement at the tier's value

The grant is one POST per License–entitlement pair. The body names the entitlement and carries a typed value whose type matches the entitlement's type — and, for a NUMBER, the grant's enforcement as limitCapExceededOveragePercent. Starter caps monthly-orders at 20 with no allowance:

curl -i -X POST "$KAITEN_API/licenses/starter/entitlements" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"entitlementSlug": "monthly-orders", "value": {"type": "number", "value": 20}, "limitCapExceededOveragePercent": 0}'

Premium grants 1,000 and tolerates 20 % more — reports are accepted up to 1,200:

curl -i -X POST "$KAITEN_API/licenses/premium/entitlements" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"entitlementSlug": "monthly-orders", "value": {"type": "number", "value": 1000}, "limitCapExceededOveragePercent": 20}'

Premium's locations is unlimited — the value is the sentinel -1, and the percent must be -1 with it:

curl -i -X POST "$KAITEN_API/licenses/premium/entitlements" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"entitlementSlug": "locations", "value": {"type": "number", "value": -1}, "limitCapExceededOveragePercent": -1}'

A BOOLEAN or CONFIG grant carries no percent — there is nothing to exceed:

curl -i -X POST "$KAITEN_API/licenses/premium/entitlements" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"entitlementSlug": "delivery-tracking", "value": {"type": "boolean", "value": true}}'
curl -i -X POST "$KAITEN_API/licenses/premium/entitlements" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "entitlementSlug": "support-tier",
    "value": {"type": "object", "value": {"tier": "priority", "channels": ["email", "slack"], "response_sla_hours": 4}}
  }'

Each grant returns HTTP/1.1 204 No Content and emits LICENSE_ENTITLEMENT_ASSIGNED. An entitlement can be granted once per License; granting monthly-orders on premium a second time is refused with 409 Conflict — use PUT to change the value. When limitCapExceededOveragePercent is omitted on a NUMBER grant it defaults to 0 — a hard cap — or to -1 for an unlimited value; it must be -1 exactly when the value is -1, and 0 or more otherwise. Read a License's grants back:

curl "$KAITEN_API/licenses/premium/entitlements" \
  -H "Authorization: Bearer $KAITEN_TOKEN"
{
  "items": [
    {
      "licenseId": "b2f3e4d5-c6a7-4b8c-9d0e-1f2a3b4c5d6e",
      "licenseSlug": "premium",
      "entitlementSlug": "monthly-orders",
      "entitlementName": "Monthly Orders",
      "entitlementType": "NUMBER",
      "value": {"type": "number", "value": 1000},
      "limitCapExceededOveragePercent": 20,
      "createdBy": {"id": "…", "name": "…"},
      "createdAt": "2026-09-08T10:10:00Z",
      "updatedBy": {"id": "…", "name": "…"},
      "updatedAt": "2026-09-08T10:10:00Z"
    },
    {
      "licenseId": "b2f3e4d5-c6a7-4b8c-9d0e-1f2a3b4c5d6e",
      "licenseSlug": "premium",
      "entitlementSlug": "locations",
      "entitlementName": "Locations",
      "entitlementType": "NUMBER",
      "value": {"type": "number", "value": -1},
      "limitCapExceededOveragePercent": -1,
      "createdBy": {"id": "…", "name": "…"},
      "createdAt": "2026-09-08T10:10:30Z",
      "updatedBy": {"id": "…", "name": "…"},
      "updatedAt": "2026-09-08T10:10:30Z"
    },
    {
      "licenseId": "b2f3e4d5-c6a7-4b8c-9d0e-1f2a3b4c5d6e",
      "licenseSlug": "premium",
      "entitlementSlug": "delivery-tracking",
      "entitlementName": "Delivery Tracking",
      "entitlementType": "BOOLEAN",
      "value": {"type": "boolean", "value": true},
      "createdBy": {"id": "…", "name": "…"},
      "createdAt": "2026-09-08T10:11:00Z",
      "updatedBy": {"id": "…", "name": "…"},
      "updatedAt": "2026-09-08T10:11:00Z"
    }
  ],
  "hasMore": false
}
The page of Premium v2, the default version of the Premium family: under Entitlements & limits, Monthly Orders at 1,000 with a 20% overage allowance, Locations unlimited, and Delivery Tracking enabled.The page of Premium v2, the default version of the Premium family: under Entitlements & limits, Monthly Orders at 1,000 with a 20% overage allowance, Locations unlimited, and Delivery Tracking enabled.

Attach instances to a tier

An instance is created under exactly one License version, referenced by id. Attach a new customer to the version the family serves: GET /license-families/premium returns it as currentVersion — premium-v2 on the seeded catalog, the premium you created in step 2 on an empty organization — so the call never has to name a version slug. $CUSTOMER_ID is the id of the sakura-tokyo customer (GET /customers/sakura-tokyo):

LICENSE_ID=$(curl -s "$KAITEN_API/license-families/premium" \
  -H "Authorization: Bearer $KAITEN_TOKEN" | jq -r '.currentVersion.id')

curl -X POST "$KAITEN_API/instances" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sakura Tokyo Production",
    "slug": "sakura-tokyo-prod",
    "description": "Production environment for the Sakura Tokyo restaurant group.",
    "customerId": "'"$CUSTOMER_ID"'",
    "licenseId": "'"$LICENSE_ID"'",
    "startLicenseDate": "2026-07-01T00:00:00Z",
    "endLicenseDate": "2027-07-01T00:00:00Z"
  }'

From here on, every read of sakura-tokyo-prod resolves its entitlements through the grants of that version: usage reports are compared with 1,000 monthly orders and accepted up to 1,200, locations is never refused, delivery-tracking reads true, and a flag rule can act on any of them. Moving the instance to another tier, or to another version, is a PUT /instances/{slug} with a different licenseId — a published version, or a draft to try it; an archived version is refused with 422 Unprocessable Entity. The grants the instance sees change at once, and no other record moves. Customers & Instances covers the instance side.

Value shapes

The grant's value is discriminated by its type, which must match the entitlement:

Entitlement typeGrant valuelimitCapExceededOveragePercentMeaning
NUMBER, NUMBER_AI_CREDIT{"type": "number", "value": 1000}0 hard cap · > 0 soft limit, accepted up to value + value × percent / 100 · -1 with a value of -1, unlimitedThe quota and its enforcement
BOOLEAN{"type": "boolean", "value": true}none (omitted)Included, or not
CONFIG{"type": "object", "value": {"tier": "priority", "channels": ["email", "slack"], "response_sla_hours": 4}}none (omitted)A structured value your product reads as-is

A NUMBER grant is what usage is measured against — read from an instance, its value appears as limit next to the observed value; the percent is read on the grant itself. A BOOLEAN or CONFIG grant is read, never metered: reporting usage against one is refused.

Manage the grants

Change a value or its enforcement — PUT on the grant, with the typed value and the percent. The body is a full replacement: a PUT that omits limitCapExceededOveragePercent resets it to the default (0, or -1 for an unlimited value), so echo the current percent unless you mean to change it. The change is visible to every instance on that License from the next read or report; nothing on the License record itself changes, and no new version is created:

curl -i -X PUT "$KAITEN_API/licenses/premium/entitlements/monthly-orders" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"value": {"type": "number", "value": 1500}, "limitCapExceededOveragePercent": 20}'

HTTP/1.1 204 No Content, and LICENSE_ENTITLEMENT_UPDATED is emitted. The same call turns a soft limit into a hard cap (0), or a capped grant into an unlimited one (-1 with a value of -1) — enforcement is a property of the grant, so nothing on the entitlement or on the other Licenses moves.

Remove a grant — DELETE on the same path. Instances on that License no longer see the entitlement: their usage list stops returning it and a read of it answers 404, while a NUMBER entitlement's usage record stays stored:

curl -i -X DELETE "$KAITEN_API/licenses/premium/entitlements/monthly-orders" \
  -H "Authorization: Bearer $KAITEN_TOKEN"

HTTP/1.1 204 No Content, and LICENSE_ENTITLEMENT_UNASSIGNED is emitted.

To offer new terms without touching existing customers, leave the grants of the License they run under as they are. Create the next version of its family (familySlug) as a draft, grant the new values there, publish it, and make it the family's default when it should be the version served. License Types shows the sequence.

Events

Event nameEvent typeWhen
LICENSE_ENTITLEMENT_ASSIGNEDcom.kaiten.license.entitlement.v1.assignedAn entitlement is granted on a License
LICENSE_ENTITLEMENT_UPDATEDcom.kaiten.license.entitlement.v1.updatedA granted value is replaced
LICENSE_ENTITLEMENT_UNASSIGNEDcom.kaiten.license.entitlement.v1.unassignedA grant is removed

All three land in the audit trail as well — the grant's history is the contract's history.

Where to go next

Meter the NUMBER grants and act at the cap: Meter usage and enforce limits. Let a flag read a grant: Gate a feature by License. Exact reference: the license_entitlement table.

On this page