Skip to main content
July 21, 2026

New Features

Activate Subscription Updates After Payment

client.subscriptions.update() now supports paymentBehavior. Use the exported PAYMENT_BEHAVIORS.ACTIVATE_ON_PAYMENT constant to keep the current subscription active until payment for the update succeeds.
The SDK serializes the option as payment_behavior: "activate_on_payment". If payment is not completed and the pending update expires, the existing subscription remains active and unchanged.
July 14, 2026

New Features

List, Retrieve, and Create Subscriptions

The subscriptions module now supports the complete read and create workflow:
  • client.subscriptions.list() retrieves a customer’s paginated subscription list with optional pagination controls.
  • client.subscriptions.retrieve() retrieves a subscription by its Kelviq UUID.
  • client.subscriptions.create() creates a subscription directly for a customer.
New TypeScript models cover paginated results, create payloads, product and plan details, features, files, links, and issued licenses.
July 10, 2026

New Features

Create One-Time Charges

A new client.charges.create() method can immediately charge a customer’s saved payment method without creating a checkout session.
The customer must already have a usable payment method on file. chargePeriod is restricted to "ONE_TIME"; use the subscriptions API for recurring billing. The SDK includes typed request and response models for charge records.
July 4, 2026

New Features

Caching & Offline Resilience

The SDK now includes a two-tier cache that keeps entitlement checks fast and lets your app keep working during transient network/API outages.
  • L1 — in-memory (on by default): entitlements are cached per-process. Fresh entries (within the TTL) are served without a network call; if the API is unreachable or returns a 5xx, the last-known value is served as a fallback.
  • L2 — distributed (optional): a shared store (e.g. Redis) so the cache and the offline usage queue are shared across processes and containers, and survive restarts.
Read-through order is L1 → L2 → API, with write-through to both on every successful fetch.
Set enableCache: false to disable caching entirely.

Offline Usage Queue

Usage/event reports that fail due to a network error are automatically queued and replayed on the next report call (or via client.reporting.flush()), so measurements are not lost during brief outages. Cached entitlement usage is updated optimistically so access checks stay consistent while offline.

Distributed Cache — RedisStore

A ready-to-use RedisStore (backed by ioredis, an optional dependency) can be shared across multiple instances / containers so they use one cache and one durable usage queue.
You can also bring your own backend by implementing the public CacheStore interface and passing it as cacheStore. All instances that should share a cache must use the same prefix.
June 5, 2026

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.
Unlike other payload fields, the keys inside 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.
May 10, 2026

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.
The SDK validates trialEnd client-side — invalid datetime strings or past datetimes throw InvalidRequestError before the request is sent.
May 4, 2026

New Features

Checkout Session — New Optional Parameters

Three new optional fields on CreateCheckoutSessionPayload 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.
April 25, 2026

New Features

Webhook Verification

A new validateEvent 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 a string or Buffer. Must be the unparsed body — do not pass a pre-parsed JSON object.
  • headers — The request headers object (e.g. req.headers in Express). Header lookup is case-insensitive.
  • secret — Your webhook signing secret (kq_whsec_...) from the Kelviq dashboard.
Returns the parsed event as 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.
April 13, 2026

New Features

License Management Module

A new client.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 with id, licenseKey, activatedOn, expiresOn, activationUsage, activationLimit, enabled, customer, plan, and subscription.
  • LicenseCustomer{ customerId, name?, email? } nested in LicenseDetails.
  • LicensePlan — Expanded with description, version, isLatest, and product (nested LicensePlanProduct).
  • 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. 1 from "1 month"), or null.
  • recurrenceType — Recurrence period in uppercase (e.g. "MONTH"), or null.
April 2, 2026

New Features

Customer Portal Module

A new client.portal module lets you create pre-authenticated customer portal sessions server-side and redirect customers directly to their self-serve portal.portal.createSession({ customerId })
Returns CreatePortalSessionResponse with:
  • token — Session token authenticating the portal session.
  • email — The customer’s email address.
  • customerPortalUrl — Pre-authenticated URL to redirect the customer to.
February 27, 2026

New Features

Entitlements Aggregation Engine

When a customer has multiple subscriptions, the API can return duplicate featureId entries. The SDK now automatically aggregates them into a single Entitlement object per feature:
  • Numeric fields (usageLimit, currentUsage, remaining) are summed across all entries
  • hasAccess is true if any raw entry grants access
  • hardLimit is true if 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.):
The old 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:
For the original raw API response, use getRawEntitlement().

getAllEntitlements() Removed

Replaced by getEntitlements(), which returns Record<string, Entitlement> — a record keyed by featureId with aggregated values and an .items[] array:
For the original raw API response, use 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/except syntax in subscription update documentation — replaced with JavaScript try...catch.
  • Added missing try/catch error 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 Truetrue, boolboolean in hasAccess documentation.
  • 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.