New Features
Checkout Session — metadata Parameter
CreateCheckoutSessionPayload now accepts an optional metadata field for attaching arbitrary key-value pairs to a checkout session. The metadata is returned verbatim on the checkout.completed webhook payload’s metadata field, letting you correlate a completed checkout back to your own records.metadata are preserved exactly as provided — they are not converted to snake_case on the way to the API, so they survive the round-trip back through webhooks unchanged.New Features
Subscription Update — trialEnd Parameter
subscriptions.update() now accepts an optional trialEnd field on UpdateSubscriptionPayload to control the trial period when updating a subscription.Pass either:- The literal string
"now"to end any active trial immediately. - An ISO 8601 datetime string (e.g.
"2025-12-31T23:59:59Z") to set a new trial end date. The datetime must be in the future. Naive datetimes (no timezone designator) are interpreted as UTC. - Omit the field to preserve the existing trial behavior on the subscription.
trialEnd client-side — invalid datetime strings or past datetimes throw InvalidRequestError before the request is sent.New Features
Checkout Session — New Optional Parameters
Three new optional fields onCreateCheckoutSessionPayload give you more control over the hosted checkout page.discountsEnabled — controls whether the coupon/discount code field is shown. Defaults to true.lockEmail — when true, the email field is pre-filled and locked so the customer cannot change it. Defaults to false.defaultBillingCountry — ISO 3166-1 alpha-2 country code (e.g. "US", "GB") used to pre-fill the billing address country field.New Features
Webhook Verification
A newvalidateEvent helper lets you securely verify incoming webhook requests from Kelviq in one step. It validates the HMAC-SHA256 signature using the three headers Kelviq attaches to every webhook delivery, then returns the parsed event object.validateEvent(payload, headers, secret)payload— Raw request body as astringorBuffer. Must be the unparsed body — do not pass a pre-parsed JSON object.headers— The request headers object (e.g.req.headersin Express). Header lookup is case-insensitive.secret— Your webhook signing secret (kq_whsec_...) from the Kelviq dashboard.
Record<string, unknown>. Throws WebhookVerificationError if a required header is missing, the signature format is invalid, or the signature does not match.WebhookVerificationError
New error class thrown by validateEvent when verification fails. Extends Error.New Features
License Management Module
A newclient.license module provides full lifecycle management for software licenses.license.activate({ licenseKey, customerId?, instanceName?, metadata? })Activates a license key and creates a new instance. Returns a LicenseActivateResponse containing the instanceId, activatedAt, expiresOn, and the full LicenseDetails object.license.deactivate({ licenseKey, instanceId })Deactivates a specific license instance. Returns a LicenseDeactivateResponse with message and deactivatedAt.license.validate({ licenseKey, instanceId? })Validates a license key and optionally a specific instance. Returns a LicenseValidateResponse with valid, code, detail, metadata, and the full LicenseDetails.New TypeScript Interfaces
LicenseDetails— Full license object withid,licenseKey,activatedOn,expiresOn,activationUsage,activationLimit,enabled,customer,plan, andsubscription.LicenseCustomer—{ customerId, name?, email? }nested inLicenseDetails.LicensePlan— Expanded withdescription,version,isLatest, andproduct(nestedLicensePlanProduct).LicensePlanProduct—{ id, identifier, name, taxCode?, createdOn?, modifiedOn? }.LicenseActivateResponse,LicenseDeactivateResponse,LicenseValidateResponse— Typed responses for each operation.
SubscriptionData New Fields
Three new fields derived from the subscription’s recurrence string are now included in SubscriptionData (returned within LicenseDetails.subscription and customer subscription summaries):billingType—"SUBSCRIPTION"if the plan has a recurrence,"ONE_TIME"otherwise.recurrenceUnit— Integer unit from the recurrence string (e.g.1from"1 month"), ornull.recurrenceType— Recurrence period in uppercase (e.g."MONTH"), ornull.
New Features
Customer Portal Module
A newclient.portal module lets you create pre-authenticated customer portal sessions server-side and redirect customers directly to their self-serve portal.portal.createSession({ customerId })CreatePortalSessionResponse with:token— Session token authenticating the portal session.email— The customer’s email address.customerPortalUrl— Pre-authenticated URL to redirect the customer to.
New Features
Entitlements Aggregation Engine
When a customer has multiple subscriptions, the API can return duplicatefeatureId entries. The SDK now automatically aggregates them into a single Entitlement object per feature:- Numeric fields (
usageLimit,currentUsage,remaining) are summed across all entries hasAccessistrueif any raw entry grants accesshardLimitistrueif any entry sets it- All raw entries are preserved in the
.items[]array
Entitlement Interface
New type representing an aggregated entitlement with an items: EntitlementDetail[] field containing the raw entries that were aggregated.getRawEntitlement({ customerId, featureId })
Returns the raw API response for a specific feature (CheckEntitlementsResponse with customerId wrapper), without any aggregation:getRawEntitlements({ customerId })
Returns the raw API response for all entitlements, without any aggregation:client.subscription Deprecated Alias
A backward-compatible getter that maps client.subscription to client.subscriptions, so existing code continues to work during migration.Breaking Changes
client.subscription Renamed to client.subscriptions
The subscriptions module now uses the plural form for consistency with other modules (client.customers, client.entitlements, etc.):client.subscription accessor still works as a deprecated alias.getEntitlement() Returns Entitlement | null
Previously returned CheckEntitlementsResponse (the raw API shape with a customerId wrapper). Now returns a single aggregated Entitlement object with an .items[] array, or null if the feature is not found:getRawEntitlement().getAllEntitlements() Removed
Replaced by getEntitlements(), which returns Record<string, Entitlement> — a record keyed by featureId with aggregated values and an .items[] array:getRawEntitlements().FeatureType Changed: "LIMIT" → "CUSTOMIZABLE"
The FeatureType union no longer includes "LIMIT". Update any code that matches on this value:resetAt Removed from Aggregated Entitlement
Since resetAt can differ across subscriptions, it is only available on individual items:Fixed
- Fixed Python-style
try/exceptsyntax in subscription update documentation — replaced with JavaScripttry...catch. - Added missing
try/catcherror handling to subscription cancel documentation example. - Removed invalid JSON comments (
// Server-generated UUID) from documentation response blocks. - Fixed trailing comma in checkout session JSON response example.
- Fixed
"Node SDK Use"typo →"Node SDK User"in create customer documentation. - Fixed
True→true,bool→booleaninhasAccessdocumentation. - Replaced “Pydantic model” references with “TypeScript Interface” or “Object”.
- Replaced “Dictionary” with “Object” in parameter descriptions.
- Fixed Python-style
ACCESS_TOKEN = "..."→const ACCESS_TOKEN = "...";in setup example.