Kaiten

Monetization

Add-ons

Extensions sold on top of a License and held by an Instance in a quantity — families and versions, what one unit grants and how it composes with the License, prices and compatibility, and how a change of quantity is billed.

An add-on is something a customer buys on top of their License and holds in a quantity: five more delivery drivers per pack, priority support. Like a License, it is versioned. A family is the product; each version says what one unit grants, what it is sold for, and which License families it fits. An Instance holds at most one version of a family, in a quantity from 1 up to the version's maximum, and every unit counts in what the Instance is entitled to.

Once Kaiten Sushi Shop sells its Licenses through Kaiten, it offers two families. The demo seed writes both, so on a seeded stack the calls that create them below answer 409 CreateAddon.SlugConflict: read the families instead, or follow along with a slug of your own:

FamilyOne unit grantsPrices (EUR)Fits
Extra delivery drivers (extra-delivery-drivers), version Packs of five5 more delivery-drivers (ADD), at most 10 units19.00 a month, 190.00 a yearStandard and Premium; listed in the public catalog
Priority support (priority-support)the priority support-tier configuration (OVERRIDE)99.00 a monthPremium
The console's Add-ons catalog: the Extra delivery drivers and Priority support families, each with its versions table and its default version badgeThe console's Add-ons catalog: the Extra delivery drivers and Priority support families, each with its versions table and its default version badge

Add-ons are part of native billing: their routes answer where billing is enabled for your organization, and 403 Billing.Disabled or Billing.NotEntitled otherwise — see billing error families. Examples use $KAITEN_API and $KAITEN_TOKEN as set up in the quickstart; responses are illustrative.

Families and versions

A family and its versions follow the rules of a License family. The first version opens the family and gives it its slug; each later version is created by naming the family (familySlug or familyId), takes {familySlug}-v{n} unless you send a slug, and starts empty: nothing is copied from the version before it. Each version is a DRAFT, PUBLISHED, or ARCHIVED, and one published version can be the family's default.

Version fieldMeaning
pricingTypeFREE, PAID, or CUSTOM (sold on request). A PAID add-on attached to a subscription needs a default price for the subscription's period
maxQuantityThe most units an instance can hold, at least 1; null for no maximum. Lowering it leaves the quantities instances already hold as they are
lifecycleStateA draft can be attached to an instance nobody bills, to try it; a published one is on sale; an archived one stays on the instances that hold it and cannot be attached again
isDefaultThe version the family puts forward — and the one the public catalog lists

Two settings belong to the family, shared by its versions, and are changed with PATCH /addon-families/{familySlug}: isPublic, whether the public catalog lists the family's default version with its active prices — a family is private until you list it — and increaseBilling, how a mid-period increase is billed.

What is sold freezes once it is billed. While an instance with a live subscription holds a version, its grants and prices cannot change: the write is refused with 409 and a .BillingActive code. The way forward is a new version, then moving the instances onto it — detach the old version, attach the new one.

The Extra delivery drivers add-on, version Packs of five: its details card with the pricing, the maximum quantity of 10 and Mid-period increases set to True-up at the next renewal, beside its Entitlements, Prices and Compatible licenses tabsThe Extra delivery drivers add-on, version Packs of five: its details card with the pricing, the maximum quantity of 10 and Mid-period increases set to True-up at the next renewal, beside its Entitlements, Prices and Compatible licenses tabs

What one unit grants

A version grants entitlements per unit, each shaped like a License grant:

Entitlement typeOne unit grantsSeveral units, several add-ons
NUMBER, NUMBER_AI_CREDITA number, or -1 for unlimited, with an overrideBehaviorCounted once per unit: a pack of 5 held twice contributes 10
BOOLEANtrue or falseAny add-on that grants true makes it true
CONFIGA JSON objectThe add-on attached last replaces the License's object

overrideBehavior says how a number combines with the License's grant: ADD adds value × quantity to it, OVERRIDE replaces it with value × quantity, and MAX — the default — keeps the larger of the two. A grant may also carry its own overage allowance (limitCapExceededOveragePercent); left out, the instance keeps the License grant's.

How the layers compose

An instance's effective value for an entitlement is built in layers, in this order:

  1. The License grant — the base.
  2. Add-ons, number by number: the OVERRIDE grants first — among several, the one worth the most (value × quantity) wins, whatever the order they were attached in — then MAX, then ADD. A value of -1 anywhere makes the result unlimited, and a grant of 0 is ignored.
  3. Boosts of the vouchers the instance redeemed.
  4. The instance's entitlement exception, when one is in force.

The overage allowance follows the add-ons too: it is the one of the add-on attached last that sets a finite one, else the License grant's. An add-on can also grant an entitlement the License does not; the instance then holds it with source: addon.

Sakura Tokyo's production instance holds two packs of drivers on top of Premium v2, which grants 10 delivery-drivers with a 20 % allowance. Its usage read — abridged — says how the limit was reached:

curl "$KAITEN_API/instances/sakura-tokyo-prod/entitlements/delivery-drivers/usage" \
  -H "Authorization: Bearer $KAITEN_TOKEN"
{
  "entitlementSlug": "delivery-drivers",
  "licenseSlug": "premium-v2",
  "value": { "type": "number", "value": 8, "event_count": 5 },
  "limit": { "type": "number", "value": 20 },
  "limitCapExceededOveragePercent": 20,
  "source": "license",
  "provenance": {
    "license": { "value": { "type": "number", "value": 10 }, "limitCapExceededOveragePercent": 20, "…": "…" },
    "addons": [
      { "quantity": 2, "overrideBehavior": "ADD", "value": { "type": "number", "value": 5 }, "limitCapExceededOveragePercent": null, "…": "…" }
    ],
    "boosts": [],
    "exception": null,
    "number": { "license": 10, "afterAddons": 20, "boostSet": null, "boostAdd": null, "boostMultiply": null, "unlimited": false, "afterBoosts": 20, "effective": 20 }
  }
}

limit is the effective value every usage report is compared with: 20, so up to 24 with the 20 % allowance. source names the layer that makes the entitlement exist for the instance — license, addon, or exception — and provenance what each layer contributed. On the instance's Entitlements & Usage tab, a limit composed this way opens the same breakdown. When you define a grant, the console warns if an OVERRIDE value per unit, or an allowance, is lower than what a compatible License grants: the add-on would lower it on every instance that attaches it.

Prices and compatibility

A version's prices follow the rules of a License price: flat fees per billing period, with one default per period, and metered prices on an entitlement the version grants. A version bills in one currency, fixed by its first price. A price is created and deprecated, never edited, and the default of a period cannot be deprecated: it is what bills every instance holding the version.

An add-on attached to a subscription bills the default price of the subscription's period, in its currency. Attaching one to a billed instance is refused when that cannot work: a draft version (AttachInstanceAddon.AddonNotPublished), a version priced in another currency (.CurrencyMismatch), a PAID version with no default price for the subscription's period (.NoPriceForBillingPeriod), or an entitlement another price of the instance already meters (.MeteredEntitlementConflict).

Compatibility is declared against a License family, never a version: PUT /addons/{addonSlug}/compatible-license-families/{familySlug} lets instances on any version of that family attach it, so a new License version never leaves an add-on behind. Removing a family stops new attachments only. A version that fits no family can be published, and attached by no one.

Attach an add-on to an instance

curl -X POST "$KAITEN_API/instances/sakura-tokyo-prod/addons" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "addonSlug": "extra-delivery-drivers", "quantity": 2 }'

201 Created, and INSTANCE_ADDON_ADDED is emitted. The entitlements apply at once. One version per family: a second attachment of the family is refused with 409 AttachInstanceAddon.FamilyAlreadyAttached — change the quantity instead, or detach first to move to another version. A quantity above the maximum answers 422 .QuantityExceedsMax, and an instance whose License family the version does not fit, 422 .Incompatible.

  • PATCH /instances/{instanceSlug}/addons/{addonSlug} with { "quantity": 3 } changes the quantity at once, a decrease included (INSTANCE_ADDON_QUANTITY_CHANGED).
  • DELETE /instances/{instanceSlug}/addons/{addonSlug} removes it at once (INSTANCE_ADDON_REMOVED). Nothing is refunded, and the attachment stays readable with GET /instances/{instanceSlug}/addons?includeRemoved=true.

These are your changes, made in the console or with your credentials, and they apply at once. On the instance's Billing tab, the add-ons card makes the same changes.

How a quantity is billed

At each boundary, a subscription bills the quantity of each add-on held then, with no proration: a flat fee billed in advance bills it for the period that starts; one billed in arrears bills the period that ended, an add-on removed during it included, at the last quantity it held. Each lands as a line of the invoice.

Mid-period increases

A quantity raised — or an add-on attached — between two boundaries is held at once. How it is billed for the part of the period already under way is a setting of the family, increaseBilling, and applies to its versions whose flat fee is billed in advance:

SettingConsoleThe elapsed period
TRUE_UP — the defaultTrue-up at the next renewalThe next renewal adds a true-up line: the highest quantity held during the elapsed period, minus the quantity billed in advance for it, at the period's full price
NEXT_BOUNDARYBilled from the next boundaryNothing: billing of the new quantity starts at the next boundary

Sakura Tokyo subscribes yearly to Premium and starts with two packs of drivers: the activation invoice bills 2 × 190.00 in advance. A third pack added during the year is held at once. Extra delivery drivers being True-up at the next renewal, the renewal invoice carries a true-up line for the year that ended — 1 × 190.00, in full — beside 3 × 190.00 in advance for the year that starts. Set to Billed from the next boundary, the renewal would carry the 3 × 190.00 alone. A change of the setting applies from the next invoice composed.

The New add-on dialog: name, slug, description, pricing, version name, maximum quantity, Mid-period increases with its two choices, and Create as a draft tickedThe New add-on dialog: name, slug, description, pricing, version name, maximum quantity, Mid-period increases with its two choices, and Create as a draft ticked

Decreases and removals

A decrease you make takes effect at once: the entitlements drop, and the next invoice bills the new quantity. Nothing is refunded for the period already billed.

From the customer portal, your customer changes quantities through a customer session (PUT /public/session/addons/{addonSlug}). There, an attachment or an increase applies at once, while a decrease — or a removal, with 0 — waits for the next renewal: the instance keeps the quantity it holds, its entitlements and its billing until then, and pendingQuantity shows what is scheduled. Without a live subscription, every change applies at once. A change you make afterwards, in the console or the API, applies at once and cancels the scheduled one.

Build an add-on with the API

The family, as a draft that is given its grant, its prices, and the License families it fits before it goes on sale:

curl -X POST "$KAITEN_API/addons" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Extra delivery drivers",
    "slug": "extra-delivery-drivers",
    "description": "Five more delivery driver accounts per pack.",
    "versionName": "Packs of five",
    "pricingType": "PAID",
    "maxQuantity": 10,
    "lifecycleState": "DRAFT",
    "increaseBilling": "TRUE_UP"
  }'
{
  "id": "7c8d9e0f-1a2b-4c3d-8e4f-5a6b7c8d9e0f",
  "slug": "extra-delivery-drivers",
  "familySlug": "extra-delivery-drivers",
  "name": "Extra delivery drivers",
  "description": "Five more delivery driver accounts per pack.",
  "version": 1,
  "versionName": "Packs of five",
  "isDefault": false,
  "lifecycleState": "DRAFT",
  "pricingType": "PAID",
  "maxQuantity": 10,
  "increaseBilling": "TRUE_UP",
  "createdAt": "2026-10-27T09:00:00Z",
  "updatedAt": "2026-10-27T09:00:00Z"
}

201 Created and ADDON_CREATED. Then what one unit grants, its two prices, the families it fits, and the publication:

curl -X POST "$KAITEN_API/addons/extra-delivery-drivers/entitlements" \
  -H "Authorization: Bearer $KAITEN_TOKEN" -H "Content-Type: application/json" \
  -d '{ "entitlementSlug": "delivery-drivers", "value": { "type": "number", "value": 5 }, "overrideBehavior": "ADD" }'

curl -X POST "$KAITEN_API/addons/extra-delivery-drivers/prices" \
  -H "Authorization: Bearer $KAITEN_TOKEN" -H "Content-Type: application/json" \
  -d '{ "billingModel": "FLAT_FEE", "billingPeriod": "MONTHLY", "currency": "EUR", "unitAmountDecimal": "1900", "isDefault": true }'

curl -X POST "$KAITEN_API/addons/extra-delivery-drivers/prices" \
  -H "Authorization: Bearer $KAITEN_TOKEN" -H "Content-Type: application/json" \
  -d '{ "billingModel": "FLAT_FEE", "billingPeriod": "ANNUAL", "currency": "EUR", "unitAmountDecimal": "19000", "isDefault": true }'

curl -X PUT "$KAITEN_API/addons/extra-delivery-drivers/compatible-license-families/standard" \
  -H "Authorization: Bearer $KAITEN_TOKEN"
curl -X PUT "$KAITEN_API/addons/extra-delivery-drivers/compatible-license-families/premium" \
  -H "Authorization: Bearer $KAITEN_TOKEN"

curl -X POST "$KAITEN_API/addons/extra-delivery-drivers/publish" \
  -H "Authorization: Bearer $KAITEN_TOKEN"

Amounts are in minor units, as strings — "1900" is 19.00 EUR (money); a flat fee is billed in advance unless it says "billingTiming": "ARREARS". To list the family in the public catalog, make the published version the family's default — PUT /addons/extra-delivery-drivers with "isDefault": true beside its name, description, version name and maximum, which PUT replaces — then PATCH /addon-families/extra-delivery-drivers with { "isPublic": true }.

In the console, Catalog → Add-ons → New add-on creates the family as a draft, and the version's Entitlements, Prices, and Compatible licenses tabs carry the rest; publishing, the default flag, and the public catalog switch are on the version and the family.

Reference

Catalog routes take the addons scopes; the routes of an instance's add-ons take the instances scopes, as changing its License does.

OperationRoute
List, create add-on versionsGET / POST /addons
Read, replace, delete a version (one never attached, that grants nothing)GET / PUT / DELETE /addons/{addonSlug}
Move a version through its lifecyclePOST /addons/{addonSlug}/publish, /archive, /unarchive
Read families; change a family's settingsGET /addon-families, GET / PATCH /addon-families/{familySlug}
Grants per unitGET / POST /addons/{addonSlug}/entitlements, GET / PUT / DELETE …/entitlements/{entitlementSlug}
PricesGET / POST /addons/{addonSlug}/prices, POST …/prices/{priceId}/deprecate
Compatible License familiesGET /addons/{addonSlug}/compatible-license-families, PUT / DELETE …/{familySlug}
An instance's add-onsGET / POST /instances/{instanceSlug}/addons, PATCH / DELETE …/addons/{addonSlug}
The customer's own changes, in the portalPUT /public/session/addons/{addonSlug}

The catalog emits ADDON_CREATED, ADDON_UPDATED, ADDON_DELETED, ADDON_PUBLISHED, ADDON_ARCHIVED, ADDON_UNARCHIVED, ADDON_PRICE_CREATED, ADDON_PRICE_DEPRECATED, and ADDON_ENTITLEMENT_ASSIGNED, _UPDATED, _UNASSIGNED; an instance's add-ons emit INSTANCE_ADDON_ADDED, INSTANCE_ADDON_QUANTITY_CHANGED, and INSTANCE_ADDON_REMOVED. Payloads are in the event catalog; request and response shapes in the API reference.

Where to go next

The other two layers on top of a License: vouchers, whose boosts apply after add-ons, and entitlement exceptions, which apply last. How usage is measured against the effective value: Usage & Thresholds. Exact reference: the data model.

On this page