Kaiten

Monetization

Subscriptions

How Kaiten bills one Instance — the flat fee it is pinned to, its periods and boundaries, its statuses from trial to cancellation, payment terms, moves to another price, the fallback License, and what a live subscription freezes — verified against the API.

A subscription is the billing of one Instance, kept by Kaiten — never by the payment provider. An Instance has at most one: a canceled subscription is subscribed again on the same record. It is pinned to one flat fee of the Instance's License version, its base price, which sets its billing period and its currency; the version's active metered prices bill with it. Its periods are counted from its anchor, and each one ends at a boundary, where the period closes into an invoice. Reading a subscription needs read:billing, every change write:billing, and both need billing to be on.

Examples use $KAITEN_API and $KAITEN_TOKEN as set up in the quickstart. The demo seed writes the catalog but sells nothing: the subscriptions below are the ones Kaiten Sushi Shop holds once it sells its Licenses through Kaiten, on the seeded prices. Responses are illustrative — ids and timestamps depend on your stack.

Statuses

StatusWhat it meansLive
TRIALA trial: nothing is billed, and the usage of the trial is never billed. The first invoice is issued when it endsYes
INCOMPLETEA checkout with an amount due waits for its first payment. Nothing it bought applies before that payment, and an unpaid one ends after 23 hoursNo
ACTIVEBilled every periodYes
PAST_DUEOne of its invoices is overdue. Nothing the Instance may use changesYes
CANCELEDEnded: no further invoice. It can be subscribed againNo

A live subscription — TRIAL, ACTIVE, or PAST_DUE — is one that bills: it freezes its Instance and the License version it bills. INCOMPLETE is reached only through a self-serve checkout; the operations on this page never enter it.

In the console, the Billing tab of an instance shows its subscription — status, provider, how it is collected and its terms, the base price, the current period and the next boundary — next to the upcoming invoice, the add-ons it holds, its voucher redemptions, and its invoices across every time it was subscribed:

The Billing tab of Sakura Tokyo Production: an active subscription through Stripe, the invoice sent to the customer and payable within 30 days, both the organization's defaults, the Premium, yearly base price at €2,900.00 a year in advance, the current period and the next boundary; the upcoming renewal of €3,280.00; two packs of Extra delivery drivers at €190.00 a year; no redemption; and the paid activation invoice of €3,280.00.The Billing tab of Sakura Tokyo Production: an active subscription through Stripe, the invoice sent to the customer and payable within 30 days, both the organization's defaults, the Premium, yearly base price at €2,900.00 a year in advance, the current period and the next boundary; the upcoming renewal of €3,280.00; two packs of Extra delivery drivers at €190.00 a year; no redemption; and the paid activation invoice of €3,280.00.

Subscribe an instance

POST /instances/{slug}/billing starts billing an instance on an active flat fee of its License version, which must be published. Kaiten Sushi Shop subscribed Demo Restaurant to Standard v1, monthly, charged to its card through Stripe, with the WELCOME20 voucher:

PRICE_ID=$(curl -s "$KAITEN_API/licenses/standard/prices?billingModel=FLAT_FEE" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  | jq -r '.[] | select(.isDefault and .billingPeriod == "MONTHLY") | .id')

curl -X POST "$KAITEN_API/instances/demo-restaurant-prod/billing" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "basePriceId": "'"$PRICE_ID"'",
    "providerKind": "STRIPE",
    "collectionMethod": "CHARGE_AUTOMATICALLY",
    "voucherCode": "WELCOME20"
  }'
{
  "id": "…",
  "instanceSlug": "demo-restaurant-prod",
  "customerSlug": "demo-restaurant",
  "status": "ACTIVE",
  "providerKind": "STRIPE",
  "collectionMethod": "CHARGE_AUTOMATICALLY",
  "daysUntilDue": 30,
  "billingPeriod": "MONTHLY",
  "currency": "EUR",
  "anchorAt": "2026-09-08T08:30:00Z",
  "currentPeriodStart": "2026-09-08T08:30:00Z",
  "currentPeriodEnd": "2026-10-08T08:30:00Z",
  "trialEndsAt": null,
  "activationInvoice": {
    "id": "…",
    "kind": "ACTIVATION",
    "status": "PAID",
    "currency": "EUR",
    "subtotal": 4900,
    "discountTotal": 980,
    "total": 3920
  }
}

HTTP/1.1 201 Created, trimmed here to the members discussed, and INSTANCE_BILLING_STARTED is emitted. The base bills in advance, so the first period's invoice — the ACTIVATION — is composed in the same transaction: 49.00 EUR, less the voucher's 20 %, 39.20 EUR. Charged automatically, it is pushed to Stripe and charged before the answer when Stripe answers within 20 seconds; its status says how that went, here PAID. The call needs Stripe connected, a payment method on the customer, and the voucher to exist — see Stripe and Vouchers.

The body takes, beside basePriceId:

MemberWhat it setsDefault
providerKindWho collects the invoices: STRIPE, or NOOP — no provider, your own system through the handoff queueNOOP
collectionMethodSEND_INVOICE — the invoice is sent, payable within the terms — or CHARGE_AUTOMATICALLY — the provider charges the payment method on file, which only Stripe doesThe organization's default, read at each invoice
daysUntilDueThe payment terms, 0 to 365 days between an invoice's issue and its due dateThe organization's default, read at each invoice
startAtThe anchor, truncated to the second: now, or at most one billing period back — a contract that began earlierNow
trialDaysA trial of 1 to 365 days, or 0 for none — see TrialsThe version's trialPeriodDays
addOnsAdd-ons to attach with it, with their quantities; one refused refuses the whole subscribeNone
voucherCodeA voucher to redeem with it; a price voucher applies from the activation invoiceNone

What the subscribe refuses:

StatusCodeWhen
409SubscribeInstance.AlreadySubscribedThe instance already has a live subscription
422SubscribeInstance.LicenseNotPublishedThe instance's version is a draft or archived — only a version on sale is subscribed to
404 / 422SubscribeInstance.PriceNotFound, .PriceNotOnInstanceLicense, .PriceNotFlatFee, .PriceDeprecatedThe base is not an active flat fee of the instance's version
422SubscribeInstance.StartAtInFuture, .StartAtTooEarly, .StartAtBeforeCancellationThe start is in the future, more than one billing period back, or before the end of the instance's previous subscription
422SubscribeInstance.InvalidTrialDays, .InvalidDaysUntilDueOut of 0 to 365
422SubscribeInstance.ProviderNotConnected, .CollectionMethodUnsupported, .BillingEmailMissing, .PaymentMethodRequired, .UnsupportedCurrencyThe provider is not connected, cannot charge, needs the customer's billing e-mail to send the invoice or a payment method to charge, or refuses the currency
422SubscribeInstance.AddonInvalid, .VoucherInvalidAn add-on or the voucher cannot be applied; errors[0].value.code carries the reason

In the console, Subscribe on the Billing tab of an instance asks for the base price — the active flat fees of its version, the default first — the trial, the payment terms, the start in UTC, the add-ons, and a voucher code. It subscribes with no payment provider: its invoices go to the handoff queue, and Provider and terms moves the subscription to Stripe afterwards, from its next invoice.

The Subscribe dialog of Sakura Tokyo Demo: payment provider Manual, its invoices recorded and handed to your own system; base price Starter at €0.00 a month in advance; a trial of 0 days; payment terms left empty for the organization default of 30 days; billing starting now, in UTC, or up to one billing period back.The Subscribe dialog of Sakura Tokyo Demo: payment provider Manual, its invoices recorded and handed to your own system; base price Starter at €0.00 a month in advance; a trial of 0 days; payment terms left empty for the organization default of 30 days; billing starting now, in UTC, or up to one billing period back.

Periods and boundaries

The first period starts at the anchor and lasts the base price's billing period — one, three, six, or twelve months, the day clamped to the end of a shorter month. currentPeriodStart and currentPeriodEnd bound the period under way; currentPeriodEnd is the next boundary, the instant the period closes and its invoice is composed. Usage reported before the subscription started is never billed. A subscription started one period back whose first period has already ended closes it on the next pass.

While a period that ended is being closed, every change to the subscription is refused with 409 <Operation>.BoundaryPending and Retry-After: 60, and nothing is written: wait, and send the same request again.

Trials

A trial lasts 1 to 365 days from the anchor. During it the subscription is TRIAL and its period is the trial: nothing is billed, and the usage of the trial is never billed. Where the subscribe names no trialDays, the subscription takes the trial of its License version, if it has one.

  • One trial per customer and License family. A customer who already had a trial of the family — even one canceled at once — gets no default trial again; only an explicit trialDays grants another.
  • A reminder before the end. INSTANCE_BILLING_TRIAL_WILL_END is emitted once, the family's trialReminderDays before the trial ends — three by default, as the family's setting stood when the trial started — or at once for a shorter trial. reminderAt on the subscription says when. The event carries what a reminder e-mail needs: the customer and its billing e-mail, the instance, the License, the days left, the amount the conversion would charge, and the card it would charge.
  • The end converts it. At trialEndsAt the subscription becomes ACTIVE, anchored at the trial's end, and its first invoice — the ACTIVATION — is issued.
  • Without a payment method, the family decides. A trial charged automatically, for a total above 0, whose customer has no active payment method at its end, follows its family's trialEndWithoutPaymentMethod: CANCEL, the default, cancels it at the trial's end with no invoice — cancellationReason TRIAL_ENDED_WITHOUT_PAYMENT_METHOD — and the instance falls back; CONVERT makes it ACTIVE, and its first charge then fails.

A trial cannot move to another price (409 SchedulePlanChange.TrialInProgress): cancel it and subscribe again with the new one. Canceled, a trial ends at once, with no invoice.

The Billing tab of Ninja Osaka Delivery Pilot: Trial until a date three days away, nothing billed during the trial and its usage never billed, the first invoice issued at its end; the subscription in Trial through Stripe, charged automatically, on Standard, monthly at €49.00; and the upcoming activation invoice of €49.00.The Billing tab of Ninja Osaka Delivery Pilot: Trial until a date three days away, nothing billed during the trial and its usage never billed, the first invoice issued at its end; the subscription in Trial through Stripe, charged automatically, on Standard, monthly at €49.00; and the upcoming activation invoice of €49.00.

The capture shows Kaiten Sushi Shop's delivery pilot: a 14-day trial of Standard v2 for ninja-osaka-delivery, with a card on file. It converts in three days, and its reminder went out this morning.

Payment terms and provider

How an invoice is collected is the subscription's own, or the organization's default where it has none: the collection method — SEND_INVOICE or CHARGE_AUTOMATICALLY — and the payment terms, the days until an invoice is due. The defaults are the organization's billing settings — SEND_INVOICE and 30 days until changed (GET /billing/settings, PUT /billing/settings) — and a subscription without its own terms reads them at each invoice. With no provider, an invoice is always sent: nothing charges it.

PATCH /instances/{slug}/billing changes a live subscription's provider, collection method, and payment terms from its next invoice on; a collection method or payment terms sent as null restore the organization's default. The invoices already composed keep their own terms and stay with the provider that issued them. A move to Stripe registers the customer with Stripe first, and 503 UpdateInstanceBilling.ProviderUnavailable leaves everything as it was when Stripe cannot be reached. A change of provider emits INSTANCE_BILLING_PROVIDER_CHANGED. In the console, it is Provider and terms on the Billing tab — Payment terms where Stripe is not offered.

Past due

An ACTIVE subscription with an overdue invoice becomes PAST_DUE, with pastDueSince; it is ACTIVE again once none of its invoices is overdue, and each move emits INSTANCE_BILLING_STATUS_CHANGED — reason INVOICE_OVERDUE, then SETTLED. An invoice sent for payment is overdue once its due date has passed unpaid; one charged automatically, once its charge is refused or once a grace period after its issue has passed unpaid — two hours by default on a self-hosted stack (KAITEN_BILLING_AUTO_COLLECTION_GRACE). Invoices has the details.

PAST_DUE changes no entitlement: the Instance keeps what its License, its add-ons, and its exceptions grant. What follows — a reminder, a payment recorded by hand, a cancellation — is your process.

The Billing tab of Kappa Kyoto Production: Past due since yesterday, the renewal invoice unpaid since it fell due, a link to it, and Access is unchanged; the subscription Past due, with no payment provider, the invoice sent to the customer and payable within 30 days, on Premium, monthly at €240.00; and the upcoming renewal of €240.00.The Billing tab of Kappa Kyoto Production: Past due since yesterday, the renewal invoice unpaid since it fell due, a link to it, and Access is unchanged; the subscription Past due, with no payment provider, the invoice sent to the customer and payable within 30 days, on Premium, monthly at €240.00; and the upcoming renewal of €240.00.

Kappa Kyoto runs the archived Premium v1 at 240.00 EUR a month, with no payment provider: its invoices go to Kaiten Sushi Shop's accounting system through the handoff queue. Its August invoice — 244.65 EUR, the base and 31 orders above the allowance at 0.15 EUR — is overdue, so the subscription is PAST_DUE, and the instance keeps every entitlement of Premium v1.

Move to another price

PUT /instances/{slug}/billing/scheduled-change schedules a move to another flat fee — of any published License version, the same family or another, in the subscription's currency — for the next boundary, never before. That boundary's renewal bills the old price's arrears and the new price's advance, and moves the instance to the new price's version; nothing is prorated. From there, the periods are those of the new price, counted from the boundary. Scheduling another price replaces the change, scheduling the same one changes nothing, and DELETE on the same path drops it. Move Demo Restaurant to the yearly fee of Standard v1 at its next boundary:

YEARLY_PRICE_ID=$(curl -s "$KAITEN_API/licenses/standard/prices?billingModel=FLAT_FEE" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  | jq -r '.[] | select(.isDefault and .billingPeriod == "ANNUAL") | .id')

curl -X PUT "$KAITEN_API/instances/demo-restaurant-prod/billing/scheduled-change" \
  -H "Authorization: Bearer $KAITEN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"licensePriceId": "'"$YEARLY_PRICE_ID"'"}'

200 OK with the subscription, whose scheduledChange names the price, its version, and effectiveAt, the boundary; INSTANCE_BILLING_PLAN_CHANGE_SCHEDULED is emitted. The move is refused during a trial (409 SchedulePlanChange.TrialInProgress), while a cancellation is scheduled (409 .CancellationScheduled), on the subscription's own price (422 .SamePrice), in another currency (422 .CurrencyMismatch), on a price that is not an active flat fee of a published version (.PriceNotFlatFee, .PriceDeprecated, .LicenseNotPublished), and when an add-on the instance holds does not fit the new version's family (422 .AddonIncompatible) or an entitlement would be metered twice (422 .MeteredEntitlementConflict). While the change is scheduled, its price cannot be deprecated nor its version archived, and that version is frozen as a billed one. In the console, the Billing tab offers the same move, among the flat fees of the versions on sale, and says when it applies.

Cancel and reactivate

POST /instances/{slug}/billing/cancel ends a subscription — billing only. Its mode says when:

modeWhat happens
AT_PERIOD_END, the defaultThe period paid for runs out. cancelAtPeriodEnd is set and INSTANCE_BILLING_CANCELLATION_SCHEDULED emitted; at the boundary a FINAL invoice bills what is left in arrears — the usage of the period, and a fee billed in arrears — which may be nothing, and the subscription is CANCELED. Asking again changes nothing
IMMEDIATEThe FINAL invoice is issued now, a fee billed in arrears in full, and nothing already billed is refunded. It cannot be undone

A trial is canceled at once, with no invoice, whatever the mode. Either way a move to another price that was scheduled is dropped (INSTANCE_BILLING_PLAN_CHANGE_CANCELED), and INSTANCE_BILLING_CANCELED carries the mode and the final invoice. A reason of up to 500 characters is kept as cancellationReason (422 CancelSubscription.InvalidReason beyond); a subscription already canceled answers 409 CancelSubscription.NotActive.

Until the boundary, POST /instances/{slug}/billing/reactivate reverts a cancellation scheduled for the period's end and emits INSTANCE_BILLING_CANCELLATION_REVERTED. A subscription already canceled is subscribed again instead (409 ReactivateSubscription.Canceled).

Apart from the fallback, a cancellation changes billing only: the add-ons the instance holds, its voucher redemptions, and its license dates stay as they are, and the invoices already issued stay collectible. The console's cancel dialog offers two follow-ups beside the cancellation, each a request of its own: remove the add-ons, and set the end date of the License. A customer can cancel at the period's end, and reactivate, from your product through a customer session.

The cancel dialog of Demo Restaurant Production: When — at the end of the period, with its date; what it does — the period is paid for, a final invoice bills what was used in arrears, and the subscription can be reactivated until then; an optional reason of up to 500 characters; and, beside the cancellation, the options to remove the add-ons and to set the license end date.The cancel dialog of Demo Restaurant Production: When — at the end of the period, with its date; what it does — the period is paid for, a final invoice bills what was used in arrears, and the subscription can be reactivated until then; an optional reason of up to 500 characters; and, beside the cancellation, the options to remove the add-ons and to set the license end date.

The fallback License

When a subscription ends — at the end of its period, at once, during its trial, or at the end of a trial without a payment method — and the family of the instance's License names a fallback, the instance moves to the fallback family's default published version, in the same transaction. INSTANCE_UPDATED is emitted with licenseChangeReason: BILLING_FALLBACK; only the instance's License changes, and the add-ons the fallback's family does not accept are removed at the cancellation's instant. Kaiten Sushi Shop's Standard and Premium fall back to Starter: when Demo Restaurant's subscription ends, demo-restaurant-prod runs Starter.

Where the family names no fallback — or where the fallback no longer qualifies, a version of it no longer FREE or no default published — the instance keeps its License, unbilled. A checkout that ends unpaid moves nothing: what it bought never applied.

What a live subscription freezes

  • The instance's customer and License. A PUT /instances/{slug} that changes customerId or licenseId is refused with 409 UpdateInstance.BillingActive. The License changes through a move to another price.
  • What the version sells. Its grants, and new prices on it, are refused with 409 …BillingActive — see License Types.
  • Deletion. An instance is kept while its subscription lives or an invoice of it is not settled: 409 DeleteInstance.BillingActive, whose errors[0].value names the status and the unsettled invoices. A customer is kept the same way (409 DeleteCustomer.BillingActive). Once the subscription is canceled and its invoices settled, the instance can be deleted; its subscription and invoices stay readable.

Read it

GET /instances/{slug}/billing returns the subscription, live or canceled, with its effective terms and whether they are its own (collectionMethodOverride, daysUntilDueOverride); an instance never subscribed answers 404 GetInstanceBilling.NotFound. GET /instances/{slug}/billing/upcoming-invoice composes the invoice the next boundary will issue from the usage so far, and writes nothing; its wouldHold names any meter whose usage journal would hold it.

GET /customers/{slug}/billing returns a customer's billing e-mail — where its invoices are sent — and, for each payment provider, its identifier there and the labels of its payment method: brand, last four digits, expiry, never the card number. How a payment method is added, replaced, or removed is on Stripe.

The page of the customer Demo Restaurant: its details with the billing e-mail billing@demo-restaurant.example; its payment method, a Visa ending in 4242, active, with Replace, Manage in Stripe, and Remove; its instance Demo Restaurant Production on Standard, billing Active; and its paid activation invoice of €39.20 after €9.80 of discounts, collected through Stripe.The page of the customer Demo Restaurant: its details with the billing e-mail billing@demo-restaurant.example; its payment method, a Visa ending in 4242, active, with Replace, Manage in Stripe, and Remove; its instance Demo Restaurant Production on Standard, billing Active; and its paid activation invoice of €39.20 after €9.80 of discounts, collected through Stripe.

API endpoints

MethodEndpointOperationDescription
GET/instances/{slug}/billinggetInstanceBillingThe instance's subscription, live or canceled
POST/instances/{slug}/billingsubscribeInstanceSubscribe an instance, or subscribe a canceled one again
PATCH/instances/{slug}/billingupdateInstanceBillingChange the provider, collection method, and payment terms, from the next invoice
PUT/instances/{slug}/billing/scheduled-changeschedulePlanChangeSchedule a move to another price for the next boundary
DELETE/instances/{slug}/billing/scheduled-changecancelPlanChangeDrop the scheduled move
POST/instances/{slug}/billing/cancelcancelSubscriptionCancel, at the period's end or at once
POST/instances/{slug}/billing/reactivatereactivateSubscriptionRevert a cancellation scheduled for the period's end
GET/instances/{slug}/billing/upcoming-invoicegetUpcomingInvoiceThe invoice the next boundary will issue, from the usage so far
GET/customers/{slug}/billinggetCustomerBillingA customer's billing e-mail and payment method labels
GET/billing/settingsgetBillingSettingsThe organization's default collection method and payment terms
PUT/billing/settingsupdateBillingSettingsReplace them; subscriptions without terms of their own take them at their next invoice

Reads need read:billing, changes write:billing.

Events

Event nameEvent typeWhen
INSTANCE_BILLING_STARTEDcom.kaiten.instance.billing.v1.startedAn instance is subscribed, or subscribed again (resubscribed)
INSTANCE_BILLING_STATUS_CHANGEDcom.kaiten.instance.billing.v1.status_changedA trial converts, a checkout is paid, or a subscription enters or leaves PAST_DUE; from, to, and reason say how
INSTANCE_BILLING_TRIAL_WILL_ENDcom.kaiten.instance.billing.v1.trial_will_endOnce per trial, at reminderAt
INSTANCE_BILLING_CANCELLATION_SCHEDULEDcom.kaiten.instance.billing.v1.cancellation_scheduledA subscription is set to cancel at the end of its period
INSTANCE_BILLING_CANCELLATION_REVERTEDcom.kaiten.instance.billing.v1.cancellation_revertedThat cancellation is reverted
INSTANCE_BILLING_CANCELEDcom.kaiten.instance.billing.v1.canceledA subscription is canceled — at its period's end, at once, or during or at the end of its trial
INSTANCE_BILLING_PLAN_CHANGE_SCHEDULEDcom.kaiten.instance.billing.v1.plan_change_scheduledA move to another price is scheduled, or replaced
INSTANCE_BILLING_PLAN_CHANGE_CANCELEDcom.kaiten.instance.billing.v1.plan_change_canceledA scheduled move is dropped — by request, or by a cancellation
INSTANCE_BILLING_PLAN_CHANGEDcom.kaiten.instance.billing.v1.plan_changedA scheduled move is applied at a boundary
INSTANCE_BILLING_PROVIDER_CHANGEDcom.kaiten.instance.billing.v1.provider_changedA subscription moves to another payment provider

INSTANCE_UPDATED, with licenseChangeReason: BILLING_FALLBACK, records the fallback move on the instance's own audit trail; the trial reminder is recorded there too, and the other subscription events for the organization. The event catalog has every payload.

A subscription moves its instance to another License at three moments only, each on purpose: a move to another price, at its boundary; the end of the subscription, to the fallback; and a checkout, to the version it bought — once paid, when something is due. A payment state — past due included — never changes what the instance may use.

Where to go next

What each boundary bills, and how an invoice is settled: Invoices. Who collects it: Stripe or no provider. How a customer subscribes from your website or product: Self-serve checkout. Exact reference: the billing tables of the data model.

On this page