Skip to content

feat: the billing release, against Kaiten 26.10.8 - #10

Merged
Alex (Alexkuva) merged 1 commit into
mainfrom
feat/kaiten-26.10.8-contract
Nov 2, 2026
Merged

Alex (Alexkuva) merged 1 commit into
mainfrom
feat/kaiten-26.10.8-contract

Conversation

@Alexkuva

Copy link
Copy Markdown
Contributor

What does this PR change?

  • Contract: openapi/ synced by task sync:openapi from kaitencloud/kaiten@f685ceb8, tag 26.10.8, which is also kaiten main today: the Core API goes from 107 to 228 operations, the Platform API from 10 to 11.
  • Webhooks: 55 → 117 typed events in kaitencloud.webhooks (WebhookEvent, WEBHOOK_EVENTS), VOUCHER_DELETED included. Every model keeps its type and name Literals and the docstring shape "Triggered when … . Delivered as <type>."; each event now carries the delivery's trace headers.
  • Models: 94 → 327 exports in kaitencloud.types: a model for every new response, and the new *Param shapes. The generator (scripts/generate_models.py) now:
    • names the enums the billing schemas share by property and values (ENUM_VOCABULARY), and renders nullable and scalar unions;
    • keeps the 1.0.0 names of the 12 webhook payloads the contract renamed …Delivered (e.g. InstanceEntitlementCapExceeded);
    • makes a delivered payload that is a resource plus who wrote the change (via, checkoutSessionId, customerSessionId) a subclass of that resource's model, checked structurally, so an event's data is still an instance of the class 1.0.0 typed it with.
  • Client: every public operation of the release is wrapped, in sync and async, named after sdk-go v0.2.0 (client.Billing.Invoices.List → client.billing.invoices.list()); see the table below.
    • client.billing.public and client.billing.session reach the routes under /public. The transport checks their credential by path, as Kaiten's public middleware does (kst_ on /public/session, ksc_ on one checkout session, pk_ elsewhere), refuses the mix-up before sending, and sends a publishable key in X-Kaiten-Publishable-Key, never as a bearer token.
    • Existing methods gain optional arguments only: customers.create/update(billing_email=), licenses.create/update(pricing_type=, trial_period_days=, requires_payment_method=, self_serve_cta_url=), metadata_fields.list(include_archived=).
    • New: TransactionIdReusedError, UsageReportResult, and three *Param shapes for vouchers (VoucherGrantParam, RedemptionRulesParam, MinimumAmountParam).
  • Compatibility: checked by a script against main: no public name removed or renamed, no parameter, return type or base class changed, no new required parameter. One behavior to know: the 26.10.8 models require fields older deployments don't send (EntitlementUsage.source, AuditTrail.actor, ConnectorSettings.configured, four LicenseFamilyView settings, the rollover event's reason); the CHANGELOG says to use 1.1.0 with Kaiten 26.10.8 or later.
  • Docs and version: README (billing guide, keyed reports and usage history, credentials, error tree, retries, API reference), CHANGELOG [1.1.0] (it absorbs the unreleased targeting entries), __version__ = "1.1.0". pyproject.toml lets ruff accept the × and − the contract writes in descriptions.

Operations added, per area

Area Operations SDK methods
Billing settings, capabilities, health, provider sync, period close 6 client.billing.get_settings(), update_settings(), get_capabilities(), get_health(), sync(), close_periods()
Subscriptions: subscribe, read, update, plan changes, cancel, reactivate, upcoming invoice, an instance's invoices 9 client.billing.subscriptions.*
Invoices and their actions 11 client.billing.invoices.*: list, get, export, void, recompose, mark_paid, write_off, release_hold, retry_push, sync, list_line_reports
Accounting handoff queue 3 client.billing.handoff.list(), claim(), ack()
License prices and the invoice preview 6 client.billing.license_prices.*, preview_invoice()
Add-ons: versions, families, grants, prices, compatibility 22 client.billing.addons.*
Add-ons on an instance 4 client.billing.instance_addons.*
Vouchers, incl. DELETE /vouchers/{voucherId}, redemptions and lookup 10 client.billing.vouchers.*
Vouchers on an instance 3 client.billing.instance_vouchers.*
Customer billing and payment-method / portal sessions 5 client.billing.customers.*
Publishable keys 4 client.billing.publishable_keys.*
Customer sessions 2 client.billing.customer_sessions.*
Checkout sessions (organization view) 2 client.billing.checkout_sessions.*
Public catalog and anonymous checkout (pk_, ksc_) 4 client.billing.public.*
Customer session routes (kst_) 11 client.billing.session.*
Usage history 3 instances.list_usage_reports(), export_usage_reports(), export_organization_usage_reports()
Entitlement exceptions 3 instances.list_entitlement_exceptions(), create_entitlement_exception(), revoke_entitlement_exception()
License family settings 1 license_families.update()
Platform API: close an organization's periods 1 platform.billing.close_periods()
Total 110 109 Core + 1 Platform; the SDK now wraps 210/228 Core and 11/11 Platform operations

Plus the keyed usage report on the existing operation: instances.report_usage_detailed(transaction_id=) sends Idempotency-Key and returns the usage with replayed and metadata_dropped; report_usage() is unchanged.

Left out, on purpose

  • The 12 user and role operations (GetMe, GetAuthorization, ListUsers, GetUser, UpdateUser, PutUserAuthorization, SetUserAssignments, SetUserEnabled, ListRoles, CreateRole, UpdateRole, DeleteRole): kaiten's own api/internal/sdkcoverage/operation_coverage_test.go lists them as consoleAuthoringOperations, and sdk-go v0.2.0 doesn't wrap them. They are excluded in openapi/coverage.yaml with that reason, next to the notification feed and OFREP.
  • Follow-up: feat(instances): expose authoritative license term on instance reads kaiten#84, GET /instances/{instanceSlug}/license-validation, is still a draft; wrap it once it merges and ships.
  • Not done here, add when needed: exports return the body as bytes rather than streaming to a file; a keyed usage report is not retried automatically (sdk-go retries once a server has echoed a key; here the caller resends under the same key); list_line_reports() walks the JSON pages and doesn't expose the endpoint's CSV format.
  • Contract quirk for kaiten: SubscriptionTerms.collectionMethod is nullable and documented "null: the organization's default", but its enum omits null. The API accepts null, so subscriptions.update(collection_method=None) sends it, and the contract tests' write validator lets a nullable enum take null.
  • Overlap: draft feat: usage report transaction IDs and the usage history #5 (usage report keys and history) targeted an earlier contract, where the key was a body field. This PR covers that surface against 26.10.8 (key in the Idempotency-Key header) and keeps feat: usage report transaction IDs and the usage history #5's names (transaction_id, report_usage_detailed, list_usage_reports, export_*). Draft feat(ai): credit reporting and optional provider wrappers (KTN-201) #9 (AI credits) will need a rebase on it.

Why?

  • The Kaiten MCP server (feat: webhook tools, with the catalogue of events to subscribe to mcp#15) builds its webhook event catalogue from this SDK's generated models: it reads kaitencloud.webhooks.WebhookEvent, each model's type and name Literals, and what fires it from the docstring. sdk-python 1.0.0 predates native billing and models 55 events, so the MCP carries a bridge of the other 62. Once 1.1.0 is on PyPI, the MCP drops the bridge.
  • The SDK should cover what the upcoming Kaiten release ships, not only its events: billing, add-ons, vouchers, public pages, the usage history and entitlement exceptions.

Testing

  • task check, which is what CI's lint and test jobs run: generated code up to date (generate_models.py --check, unasync.py --check), ruff check, ruff format --check, mypy --strict (84 files), pytest --cov: 1379 passed, 99% coverage.
  • A contract case for every new operation, in both clients, validating each request body against the operation's schema; the case now also checks the credential travels where the operation's security scheme says. The coverage and README checks walk nested namespaces. tests/test_billing.py covers the public credentials, exports, invoice filters, keyed reports and the afterSeq history.
  • Python 3.10, 3.11, 3.12, 3.13 and 3.14: 1379 passed on each. Lowest direct dependencies (httpx 0.27.0, pydantic 2.11.0, Python 3.10): 1379 passed.
  • uv build and twine check --strict: wheel and sdist kaitencloud-1.1.0 pass.
  • A script reproducing the MCP's read (typing.get_args on the WebhookEvent union, the type/name Literal args, the docstring split on . Delivered as): 117 events, the same set as WEBHOOK_EVENTS and the contract, each with a non-empty description.

How the release happens

  • Merge after a code-owner approval (@kaitencloud/engineering) and the DCO, lint, build and test checks.
  • Tag the merge commit: git tag -a v1.1.0 -m v1.1.0 <merge-sha> && git push origin v1.1.0. The CHANGELOG section is dated 2026-11-01: adjust it before tagging on another day.
  • The tag starts .github/workflows/release.yml: it checks that v1.1.0 matches __version__, runs uv sync --locked and pytest, builds, runs twine check --strict, then publishes with PyPI trusted publishing (no stored token), in the pypi environment, which only deploys v* tags and waits for a required reviewer (Alex (@Alexkuva) or fuzcap).
  • Then feat: webhook tools, with the catalogue of events to subscribe to mcp#15 can require kaitencloud>=1.1.0 and empty _NOT_YET_IN_SDK.

Checklist

  • I have read CONTRIBUTING.md.
  • Every commit in this PR includes a valid DCO Signed-off-by line.
  • I have the right to submit all material in this PR.
  • I have not included secrets or confidential data.
  • I have updated tests where appropriate.
  • I have updated documentation where appropriate.
  • I have preserved required third-party licenses and attributions.

🤖 Generated with Claude Code · ✅ Tested and approved by Alex (@Alexkuva), maintainer

The SDK now models everything Kaiten 26.10.8 publishes: native billing,
keyed usage reports and the usage history, entitlement exceptions, and
all 117 webhook events, up from 55.

- openapi/ is synced from kaitencloud/kaiten@f685ceb8, tag 26.10.8 and
  kaiten main, by task sync:openapi, and the models regenerated.
- The generator names the enums the billing schemas share by property and
  values, renders nullable and scalar unions, keeps the 1.0.0 names of the
  webhook payloads the contract renamed ...Delivered, and makes a payload
  that is a resource plus who wrote the change a subclass of that
  resource's model. No name 1.0.0 published is gone, and an event's data
  is still an instance of the class it was.
- client.billing, a namespace per resource after sdk-go v0.2.0's naming:
  subscriptions, invoices, handoff, license_prices, addons,
  instance_addons, vouchers, instance_vouchers, customers,
  publishable_keys, customer_sessions and checkout_sessions. public and
  session reach the routes under /public; the transport checks their
  credential (pk_, ksc_, kst_) by path, and sends a publishable key in
  X-Kaiten-Publishable-Key, never as a bearer token.
- instances: report_usage_detailed() under an Idempotency-Key, with
  TransactionIdReusedError; the usage history, walked by afterSeq and
  exported; entitlement exceptions. license_families.update(), license
  commercial terms, a customer's billing e-mail, archived metadata fields,
  and platform.billing.close_periods().
- coverage.yaml maps 210 of the 228 Core operations and all 11 Platform
  ones. The twelve user and role operations are excluded as console
  authoring, as kaiten's own coverage test excludes them.
- Tests: a contract case for every new operation, the credential checked
  where the operation's security scheme puts it, nested namespaces in the
  coverage and README checks, and test_billing.py for what the contract
  table cannot see.
- README, CHANGELOG and version 1.1.0.

Signed-off-by: Alexandre Bergere <alexandre.bergere@kaiten.sh>
@Alexkuva
Alex (Alexkuva) requested a review from a team as a code owner November 1, 2026 20:02
@Alexkuva
Alex (Alexkuva) merged commit 0eaafa3 into main Nov 2, 2026
13 checks passed
@Alexkuva
Alex (Alexkuva) deleted the feat/kaiten-26.10.8-contract branch November 2, 2026 12:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants