Skip to main content
July 21, 2026

New Features

Activate Subscription Updates After Payment

The synchronous and asynchronous client.subscriptions.update() methods now support paymentBehavior="activate_on_payment". This keeps the current subscription active while payment for the update is pending.
The SDK validates the option and serializes it as payment_behavior. The new plan and features are applied only after payment succeeds; if the pending update expires, the existing subscription remains active and unchanged.
July 14, 2026

New Features

List, Retrieve, and Create Subscriptions

The synchronous and asynchronous subscription clients now support 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.
All three methods are also available on async_client and return validated Pydantic models. New 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

The synchronous and asynchronous clients now provide client.charges.create() for immediately charging a customer’s saved payment method without creating a checkout session.
Async variant:
The customer must already have a usable payment method on file. chargePeriod is restricted to "ONE_TIME"; use the subscriptions API for recurring billing. Pydantic request and response models validate parameters and provide typed 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 / gunicorn workers / containers, and survive restarts.
Read-through order is L1 → L2 → API, with write-through to both on every successful fetch.
Set enable_cache=False to disable caching entirely. Both the sync and async clients are supported.

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 / AsyncRedisStore

Ready-to-use Redis-backed stores can be shared across multiple processes / gunicorn workers / containers so they use one cache and one durable usage queue. Redis is an optional extra (pip install "kelviq-sdk[redis]").
You can also bring your own backend by implementing the public CacheStore interface and passing it as cache_store. All workers/tasks that should share a cache must use the same prefix.
June 5, 2026

New Features

Checkout Session — metadata Parameter

checkout.create_session() now accepts an optional metadata parameter 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.
Async variant:
The keys inside metadata are preserved exactly as provided — they are not transformed 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 parameter 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-31 23:59:59") to set a new trial end date. The datetime must be in the future. Naive datetimes are interpreted as UTC.
  • Omit the parameter to preserve the existing trial behavior on the subscription.
Async variant:
The SDK validates trialEnd client-side — invalid datetime strings or past datetimes raise InvalidRequestError before the request is sent.
May 4, 2026

New Features

Checkout Session — New Optional Parameters

Three new optional parameters on checkout.create_session() give you more control over the hosted checkout page.discounts_enabled — controls whether the coupon/discount code field is shown. Defaults to True on the server.lock_email — when True, the email field is pre-filled and locked so the customer cannot change it. Defaults to False.default_billing_country — ISO 3166-1 alpha-2 country code (e.g. "US", "GB") used to pre-fill the billing address country field.
Async variant:
April 25, 2026

New Features

Webhook Verification

A new validate_event 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 dictionary.
validate_event(payload, headers, secret)
  • payload — Raw request body as bytes or str. Must be the unparsed body — do not pass a pre-parsed dictionary.
  • headers — Any mapping of header name to value (e.g. request.headers in Flask). Header lookup is case-insensitive.
  • secret — Your webhook signing secret (kq_whsec_...) from the Kelviq dashboard.
Returns the parsed event as Dict[str, Any]. Raises WebhookVerificationError if a required header is missing, the signature format is invalid, or the signature does not match.

WebhookVerificationError

New exception class raised by validate_event when verification fails. Extends Exception.
April 13, 2026

New Features

License Management Module

A new client.license module provides full lifecycle management for software licenses. All methods are available in both synchronous and asynchronous clients.license.activate(licenseKey, customerId?, instanceName?, metadata?)Activates a license key and creates a new instance. Returns a LicenseActivateResponse (Pydantic model) 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 Pydantic Models

  • LicenseDetails — Full license object with id, licenseKey, activatedOn, expiresOn, activationUsage, activationLimit, enabled, customer, plan, and subscription.
  • LicenseCustomercustomerId, name, email nested within LicenseDetails.
  • LicensePlan — Expanded with description, version, isLatest, and product (nested LicensePlanProduct model).
  • LicensePlanProductid, identifier, name, taxCode, createdOn, modifiedOn.
  • LicenseActivateResponse, LicenseDeactivateResponse, LicenseValidateResponse — Typed Pydantic response models 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 None.
  • recurrenceType — Recurrence period in uppercase (e.g. "MONTH"), or None.
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. Available on both synchronous and asynchronous clients.portal.create_session(customerId)
Async variant:
Returns CreatePortalSessionResponse (Pydantic model) with:
  • token (str) — Session token authenticating the portal session.
  • email (str) — The customer’s email address.
  • customerPortalUrl (str) — Pre-authenticated URL to redirect the customer to.
February 27, 2026

New Features

Entitlements Aggregation Engine

When a customer has multiple active subscriptions, the API can return duplicate featureId entries. The SDK now automatically aggregates them into a single Entitlement model while preserving the raw data.
Aggregation rules:
Note: resetAt is intentionally not aggregated at the top level because each subscription item can have a different reset date. Access individual reset dates via entitlement.items[i].resetAt.

New Entitlement Pydantic Model

A new model representing an aggregated entitlement:
  • featureId, featureType, hasAccess, hardLimit, usageLimit, currentUsage, remaining
  • items: List[EntitlementDetail] — the raw entries that were aggregated into this model

get_entitlements(customerId) — All Entitlements as a Map

Returns a Dict[str, Entitlement] keyed by featureId. Each value is an aggregated model with an .items list containing the original raw entries.

get_raw_entitlement(customerId, featureId) — Raw API Response for a Feature

Returns the raw API response (CheckEntitlementsResponse) for a specific feature without any aggregation.

get_raw_entitlements(customerId) — Full Raw API Response

Returns the raw API response for all entitlements without any aggregation.

Enum Helpers for IDE Autocompletion

New str-based Enum classes:
  • ChargePeriodONE_TIME, MONTHLY, YEARLY, WEEKLY, DAILY, THREE_MONTHS, SIX_MONTHS
  • CancellationTypeIMMEDIATE, CURRENT_PERIOD_ENDS, SPECIFIC_DATE
Raw string values (e.g., "MONTHLY") continue to work everywhere — fully backward-compatible.

Changed

  • get_entitlement(customerId, featureId) now returns Optional[Entitlement] (aggregated) instead of raw CheckEntitlementsResponse. Use get_raw_entitlement() for the original response.
  • get_all_entitlements removed — Use get_entitlements() instead.
  • resetAt removed from top-level Entitlement — Access individual reset dates via the .items list.
February 27, 2026

New Features

Entitlements Aggregation Engine

When a customer has multiple active subscriptions, the API can return duplicate featureId entries. The SDK now automatically aggregates them into a single Entitlement model while preserving the raw data.
Aggregation rules:
Note: resetAt is intentionally not aggregated at the top level because each subscription item can have a different reset date. Access individual reset dates via entitlement.items[i].resetAt.

New Entitlement Pydantic Model

A new model representing an aggregated entitlement:
  • featureId, featureType, hasAccess, hardLimit, usageLimit, currentUsage, remaining
  • items: List[EntitlementDetail] — the raw entries that were aggregated into this model

get_entitlements(customerId) — All Entitlements as a Map

Returns a Dict[str, Entitlement] keyed by featureId. Each value is an aggregated model with an .items list containing the original raw entries.

get_raw_entitlement(customerId, featureId) — Raw API Response for a Feature

Returns the raw API response (CheckEntitlementsResponse) for a specific feature without any aggregation. Includes the customerId wrapper.

get_raw_entitlements(customerId) — Full Raw API Response

Returns the raw API response for all entitlements without any aggregation.

Enum Helpers for IDE Autocompletion

New str-based Enum classes that improve developer experience with IDE autocompletion and prevent typos:
  • ChargePeriodONE_TIME, MONTHLY, YEARLY, WEEKLY, DAILY, THREE_MONTHS, SIX_MONTHS
  • CancellationTypeIMMEDIATE, CURRENT_PERIOD_ENDS, SPECIFIC_DATE
Raw string values (e.g., "MONTHLY") continue to work everywhere — the enums are fully backward-compatible.

Changed

  • get_entitlement(customerId, featureId) now returns Optional[Entitlement] (aggregated) instead of raw CheckEntitlementsResponse. Use get_raw_entitlement() if you need the original response.
  • get_all_entitlements removed — Use get_entitlements() instead, which returns a Dict[str, Entitlement] keyed by featureId.
  • resetAt removed from top-level Entitlement — Access individual reset dates via the .items list.

Fixed

  • Removed invalid JavaScript-style comments (// Server-generated UUID) from JSON response blocks in documentation.
  • Fixed // comments to # comments in Python code snippets.
  • Fixed trailing comma in Checkout response JSON block.
  • Fixed typo: “newly created customer” → “updated customer” in customers.update return description.
  • Removed placeholder text from “Supported Functionalities” section.
  • Updated entitlements description to explain multi-subscription aggregation and the items attribute.