Repository navigation
feat: the billing release, against Kaiten 26.10.8 - #10
Merged
Merged
Conversation
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>
fuzcap
approved these changes
Nov 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR change?
openapi/synced bytask sync:openapifromkaitencloud/kaiten@f685ceb8, tag26.10.8, which is also kaitenmaintoday: the Core API goes from 107 to 228 operations, the Platform API from 10 to 11.kaitencloud.webhooks(WebhookEvent,WEBHOOK_EVENTS),VOUCHER_DELETEDincluded. Every model keeps itstypeandnameLiterals and the docstring shape "Triggered when … . Delivered as<type>."; each event now carries the delivery's traceheaders.kaitencloud.types: a model for every new response, and the new*Paramshapes. The generator (scripts/generate_models.py) now:ENUM_VOCABULARY), and renders nullable and scalar unions;…Delivered(e.g.InstanceEntitlementCapExceeded);via,checkoutSessionId,customerSessionId) a subclass of that resource's model, checked structurally, so an event'sdatais still an instance of the class 1.0.0 typed it with.client.Billing.Invoices.List→client.billing.invoices.list()); see the table below.client.billing.publicandclient.billing.sessionreach 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 inX-Kaiten-Publishable-Key, never as a bearer token.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=).TransactionIdReusedError,UsageReportResult, and three*Paramshapes for vouchers (VoucherGrantParam,RedemptionRulesParam,MinimumAmountParam).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, fourLicenseFamilyViewsettings, the rollover event'sreason); the CHANGELOG says to use 1.1.0 with Kaiten 26.10.8 or later.[1.1.0](it absorbs the unreleased targeting entries),__version__ = "1.1.0".pyproject.tomllets ruff accept the×and−the contract writes in descriptions.Operations added, per area
client.billing.get_settings(),update_settings(),get_capabilities(),get_health(),sync(),close_periods()client.billing.subscriptions.*client.billing.invoices.*: list, get, export, void, recompose, mark_paid, write_off, release_hold, retry_push, sync, list_line_reportsclient.billing.handoff.list(),claim(),ack()client.billing.license_prices.*,preview_invoice()client.billing.addons.*client.billing.instance_addons.*DELETE /vouchers/{voucherId}, redemptions and lookupclient.billing.vouchers.*client.billing.instance_vouchers.*client.billing.customers.*client.billing.publishable_keys.*client.billing.customer_sessions.*client.billing.checkout_sessions.*pk_,ksc_)client.billing.public.*kst_)client.billing.session.*instances.list_usage_reports(),export_usage_reports(),export_organization_usage_reports()instances.list_entitlement_exceptions(),create_entitlement_exception(),revoke_entitlement_exception()license_families.update()platform.billing.close_periods()Plus the keyed usage report on the existing operation:
instances.report_usage_detailed(transaction_id=)sendsIdempotency-Keyand returns the usage withreplayedandmetadata_dropped;report_usage()is unchanged.Left out, on purpose
GetMe,GetAuthorization,ListUsers,GetUser,UpdateUser,PutUserAuthorization,SetUserAssignments,SetUserEnabled,ListRoles,CreateRole,UpdateRole,DeleteRole): kaiten's ownapi/internal/sdkcoverage/operation_coverage_test.golists them asconsoleAuthoringOperations, and sdk-go v0.2.0 doesn't wrap them. They are excluded inopenapi/coverage.yamlwith that reason, next to the notification feed and OFREP.GET /instances/{instanceSlug}/license-validation, is still a draft; wrap it once it merges and ships.bytesrather 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.SubscriptionTerms.collectionMethodis nullable and documented "null: the organization's default", but itsenumomitsnull. The API accepts null, sosubscriptions.update(collection_method=None)sends it, and the contract tests' write validator lets a nullable enum take null.Idempotency-Keyheader) 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?
kaitencloud.webhooks.WebhookEvent, each model'stypeandnameLiterals, 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.Testing
task check, which is what CI'slintandtestjobs 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.tests/test_billing.pycovers the public credentials, exports, invoice filters, keyed reports and theafterSeqhistory.uv buildandtwine check --strict: wheel and sdistkaitencloud-1.1.0pass.typing.get_argson theWebhookEventunion, thetype/nameLiteral args, the docstring split on. Delivered as): 117 events, the same set asWEBHOOK_EVENTSand the contract, each with a non-empty description.How the release happens
DCO,lint,buildandtestchecks.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..github/workflows/release.yml: it checks thatv1.1.0matches__version__, runsuv sync --lockedandpytest, builds, runstwine check --strict, then publishes with PyPI trusted publishing (no stored token), in thepypienvironment, which only deploysv*tags and waits for a required reviewer (Alex (@Alexkuva) or fuzcap).kaitencloud>=1.1.0and empty_NOT_YET_IN_SDK.Checklist
CONTRIBUTING.md.Signed-off-byline.🤖 Generated with Claude Code · ✅ Tested and approved by Alex (@Alexkuva), maintainer