Skip to content

Releases: nozle-dev/nozle-js

React SDK 0.15.0

Choose a tag to compare

@devwatts devwatts released this 23 Oct 20:32
992a59b

Fixed

  • <CurrentPlan /> showed a customer billed on an older version of the plan the current version's usage limits (the public catalog lists current versions only). With a billing handler from @nozle-js/node 0.16.0 it shows the limits of the customer's own version; with a handler that sends the version but not its entitlements, the meters show usage without another version's limits. The pricing table never shows a plan with is_current: false, even from a plans prop.

Added

  • PricingPlan.version and is_current, and BillingSubscription.subscription.plan_version and entitlements.

Node.js SDK 0.16.0

Choose a tag to compare

@devwatts devwatts released this 23 Oct 20:32
992a59b

Added

  • Plan versions. Plan has version and is_current; plans() returns current versions only.
  • subscribe(customerId, planCode, { planVersion }) sells a chosen version of the plan code instead of the current one (needs Engine 2.7.0). The same customer and code again keep the subscription's version; another version of an existing subscription throws NozleApiError 422 (plan_version_change_not_supported), an unknown version 404 (plan_version_not_found).
  • client.planVersions (secret keys): list(code) lists every version with its price and active subscriptions, get(code, version) reads one, and previewMigration, createMigration, listMigrations, getMigration and cancelMigration move the subscriptions of one version to another, each at the end of its billing period. Migration items carry move_status (pending, moved, canceled). A second migration of a code while one runs throws NozleApiError 409 (plan_version_migration_running).
  • The billing handler's subscription route includes subscription.plan_version, the version the customer is billed on, and subscription.entitlements, that version's entitlements with its usage limits (read from Core with the secret key; null when they cannot be read).

Node.js SDK 0.13.1

Choose a tag to compare

@pratham2609 pratham2609 released this 21 Oct 06:30
bbc1958

Fixed

  • wrapOpenAI and wrapAnthropic no longer change the client passed in. They return a new view of it whose create records usage for that wrapper's customerId, as the Go SDK returns a copy. Before, each wrap replaced create on the shared client, so after wrapping one client for several customers a single call recorded an event for every one of them. Calls on the original client now record nothing, wrapping an already wrapped client wraps its original, and withResponse(), parse(), stream() and streaming keep working and record once. Use the returned client: code that ignored the return value and kept calling the original client no longer records usage.

React 0.13.0

Choose a tag to compare

@pratham2609 pratham2609 released this 19 Oct 07:24
c99f5b1

Added

  • Saved appearance. The theme, colours and pricing table options a merchant saves in the Nozle dashboard (Developers > Customize SDK) reach every component without code. BillingProvider reads meta.sdk_appearance from the plan catalog request (GET /api/v1/plans), the same cached request PricingTable and usePlans use, so a page makes one call. The saved theme, variables and planCardLayout are the lowest layer: appearance on BillingProvider and on a component win. With nothing saved, or when the request fails, no variables are written and the components look as before. Pass savedAppearance={false} on BillingProvider to never fetch or apply it.
  • PricingTable takes highlightStyle, highlightLabel (an empty saved label means no badge), annualDiscountLabel and showToggle from the saved options when the props are unset; props win.
  • The SDK reads only a closed schema: a theme name (light, dark, minimal, neobrutal, nozle, nozleDark), colour variables as #rgb or #rrggbb, radius and width variables as px, rem or em lengths, planCardLayout, and the four pricing table options. Anything else is ignored, so a saved value never writes free CSS.
  • PlanCard takes isUpgrade, with the same button states as PricingTable.
  • Saved appearance for a single component. The dashboard can save a theme, variables, card layout and pricing table options for one component (components.<Name> in meta.sdk_appearance, for example PricingTable or UsageMeter). They apply to that component only, above the saved global appearance and below appearance on BillingProvider and on the component. A component rendered inside another (a UsageMeter in UsageDashboard, an UpgradeModal in UpgradeButton) uses its own saved settings.

Changed

  • Plan buttons follow the current plan. Without a current plan every plan (except contact sales, which stays outline) gets the primary button, with the theme's buttonShadow, and the highlighted plan keeps its highlight colours. With a current plan, plans above it get the primary button, the current plan gets the primary colour without the shadow, and plans at or below it, a highlighted one included, get the secondary button. Before, only the highlighted plan was primary when there was no current plan. In the panel layout the "Active" label of the current plan takes the primary colour.
  • BillingProvider requests the plan catalog (cached, never retried, errors not shown) unless savedAppearance={false}, also on pages that only use components such as UsageMeter.

React SDK 0.11.0

Choose a tag to compare

@pratham2609 pratham2609 released this 17 Oct 16:20
bac9d94

Breaking

  • Components no longer read Tailwind classes, host CSS or shadcn's --primary, --primary-foreground and --border variables. Pass your colours as appearance.variables instead (for example colorPrimary: "var(--primary)"). Without an appearance, components use the light look: primary #4f46e5. The billing portal's green accent (#526949) is gone; set colorPrimary or the older --nozle-portal-accent to keep it.
  • @tanstack/react-query (v5) is a required peer dependency. usePlans now runs on TanStack Query through BillingProvider's query client and keeps its return shape, plus refetch.
  • PricingTable no longer marks a plan "Custom" because its name or code contains "enterprise" or "custom". List the codes of plans sold on request in contactSalesPlans; their card shows "Custom" and a "Contact Sales" button that opens enterpriseEmail.
  • PlanCard takes plan (a catalog plan) with isCurrent, returnUrl, onSelect and the optional features. It no longer takes id, name, monthlyPrice, annualPrice, description, isAnnual or children, and the Plan type export is removed.
  • PlanComparison takes plans (catalog plans) and the optional features. A plan that lacks an entitlement shows a cross. features rows are keyed by plan code, not id.

Added

  • appearance on BillingProvider and on every component that renders UI. It takes a theme from @nozle-js/react/themes (light, dark, minimal, neobrutal, nozle or nozleDark; an array stacks them, last wins), your own variables, and per-element elements, where a string adds a class name and an object adds inline styles. Every part has a fixed nozle-<key> class, such as nozle-pricingTable or nozle-planCard__price. A component's own appearance goes on top of the provider's. Components render without Tailwind or host CSS; they read --nozle-* CSS variables, and the older --nozle-portal-* and --nozle-pricing-* names still work. Checkout passes the resolved theme to Stripe's Payment Element (Stripe's night theme on a dark background). useAppearance and the Appearance, Theme, Variables, Elements, ElementKey, UseAppearance and ElementProps types are exported.
  • PricingTable takes highlightStyle: outline (default) draws the highlighted card's border in colorHighlight; filled paints the card in colorHighlight and its text and button in colorHighlightForeground. Both variables default to the primary colours. highlightLabel sets the badge on the highlighted card (default "Most Popular"; null shows no badge).
  • Two more themes, nozle and nozleDark, give the Nozle dashboard look (flat cards, a hard ink shadow on buttons, Nunito). New variables buttonRadius and buttonBorderWidth (every button), buttonBorderColor and buttonShadow (primary buttons only), colorSecondary and colorInput (secondary buttons), and labelFont, labelTextTransform, labelLetterSpacing and fontFamilyNumbers for table headers, status labels and numbers; a theme's planCardLayout: "panel" lays plan cards out like the dashboard, with ACTIVE on the current plan. neobrutal primary buttons get a black border and offset shadow.
  • Text roles. New variables fontTitle, fontHeading, fontMetric, fontBody, fontMeta and fontAction (CSS font shorthands, written as --nozle-font-title and so on) set the type of every element, with labelFont as the label role; each defaults to a size in fontFamily (fontMetric in fontFamilyNumbers when set, in tabular figures). Every component root sets the body role, so no text takes its size, font or colour from the host page, including the PricingTable toggle, the UsageAlert dismiss button, the CheckoutButton status and error, Checkout, the billing portal and its dialogs. nozle and nozleDark set the dashboard scale (title 20/900, heading 14/900, price 24/800 in Nunito, body 13, meta 11.5, buttons 14/800); neobrutal sets bolder titles and headings.
  • Customer billing hooks built on TanStack Query: useSubscription (with past_due), useEntitlement(featureCode), useCreditBalance(creditSystemCode?), useCreditHistory() (paged, operations ready for CreditHistory), useUsage({ period }) (current period for UsageBreakdown, past periods for UsageTrend), useUpgradePreview(planCode), useInvoices({ page }), useInvoiceDownload() (resolves an invoice's PDF URL, or null while it is generated) and useBillingRefresh(). They read your backend route set as BillingProvider billingEndpoint (with billingHeaders for a CSRF token), served by createBillingHandler from @nozle-js/node, so the browser holds no secret key and sees only the signed-in customer. BillingProvider takes an optional queryClient to share your app's cache; the hooks pass their client explicitly and never replace your QueryClientProvider. Failed requests throw BillingRequestError (status, code, requestId); a 4xx is not retried.
  • CurrentPlan shows the customer's plan, status, price, scheduled change, usage against the plan's limits and a near-limit notice, and an alert when the subscription is past due. It reads the billing hooks by default; subscription, usage, details and actions can be passed instead. currentPlanMeters(entitlements, usage) returns its meter rows for your own layout.
  • PricingTable, PlanCard and PlanComparison read what each plan gives from the public catalog: entitlements, credit grants and the usage prices of every charge model except custom and dynamic. PricingTable accepts an optional features (an array aligned to the rendered plans, or an object keyed by plan code), PlanCard an optional features list and PlanComparison optional features rows. When passed, they replace the generated lines.
  • planFeatures(plan) and comparisonRows(plans) return the same lines and rows for your own layout. The CatalogEntitlement, CatalogCreditGrantRule, CatalogCharge, CatalogPrivilege and ComparisonRow types are exported, and CatalogPlan and PricingPlan declare the optional entitlements, credit_grant_rules and charges fields.
  • PricingTable and PlanComparison sort plans by base price, cheapest first (PricingTable puts contactSalesPlans last), instead of showing the catalog's newest-first order. Plans in different currencies stay grouped, and ties keep the catalog order. Pass sort to use your own order. sortPlansByPrice(plans) is exported. A features array is aligned to the sorted order.
  • CreditBalance and CreditHistory render the SDK's credit balance and credit operations (CreditBalanceData, CreditOperationData), each with loading, error and empty states; CreditHistory has a load-more button for the SDK's next_cursor. usageItems(entitlements, results) builds UsageDashboard rows from a plan's metered entitlements and can() results. UsageDashboard takes error and emptyMessage, and UsageMeter and UsageDashboard accept a null or missing limit and show Unlimited.
  • UsageBreakdown (a table of each feature's units and amount for one period, with the total) and UsageTrend (a bar chart of the amount, or the units of one featureCode, per period) render the usage your backend reads from Core's current_usage and past_usage (CustomerUsageData, ChargeUsageData). Both have loading, error and empty states and no chart library.
  • PricingTable, PlanComparison, CurrentPlan, Checkout, UpgradeModal and BillingPortal show "Powered by" and the Nozle logo when the merchant's Nozle plan keeps the branding (meta.nozle_branding in the publishable-key catalog, nozleBranding in the portal). Only the outermost of nested components shows it. useNozleBranding() and PoweredByNozle are exported for your own UI. The portal's plain "Powered by Nozle" footer is replaced. PricingTable and usePlans share one cached catalog request.
  • Changing plan in PlanChangeControl (and the billing portal) lists the plans as choices with price, upgrade or downgrade, and, inside a BillingProvider, each plan's description and features from the catalog; the confirmation step has a Back button. UpgradeModal takes onBack. The portal subscription card puts View usage, Change plan and Cancel subscription in one action row.

Changed

  • PricingTable gives plans priced above the current plan the primary button and the current and cheaper plans the plain one (without a current plan, only the highlighted plan gets the primary button).
  • Tables (CreditHistory, UsageBreakdown, PlanComparison and the billing portal's usage and invoice tables) follow the Nozle dashboard table: a bordered frame, a tinted header band, 20px cell padding, one-line cells and right-aligned numbers in tabular figures; each theme styles the header labels its own way. The billing portal's invoice search is a single search field with an icon.
  • UsageDashboard has the same card frame as the other data components. A UsageMeter value is in the text colour below 80% and turns to the warning colour at 80% and the danger colour at 95%, instead of always taking the bar colour.
  • Muted text follows colorForeground. When colorMutedForeground is not set it is colorForeground mixed 62% into colorCard instead of a fixed #6b7280; the light and dark themes use the same mix. Error and success text use contrast-adjusted colours like warning text already did. Sizes are now the same for the same kind of text across components, so the default look changes slightly: plan names are 16px, prices and balances 28px, buttons 14px, badges and status pills use the label role. The panel plan card no longer hard-codes the dashboard sizes; the themes supply them.

Deprecated

  • nozlePreset (@nozle-js/react/styles/preset) has no effect on the components, which no longer use Tailwind. It is kept so existing imports still build. Th...
Read more

Node.js SDK 0.13.0

Choose a tag to compare

@pratham2609 pratham2609 released this 17 Oct 16:20
bac9d94

Added

  • nozle.captureFetch(options, fetch?) returns a fetch that records the token usage of every successful LLM response. Pass it as the fetch option of the OpenAI or Anthropic SDK, a Vercel AI SDK provider, LangChain, or any client that accepts one. Usage is recognised by the response shape: OpenAI-compatible usage (any OpenAI-compatible API, including the Responses API), Anthropic usage and Gemini usageMetadata. Streams are read as the app reads them; chat completion streams are asked for usage (stream_options.include_usage, turn off with includeStreamUsage: false). When the app aborts a stream, the usage already seen is still recorded. The customer comes from customerId (a string or a function per request) or the X-Nozle-Customer-Id request header, which is not sent to the provider. Recording never changes or fails the provider response; failures go to onError. detectUsage and normalizeGeminiUsage and the DetectedUsage and ProviderUsageFormat types are exported for your own parsing.
  • nozle.recordUsage({ customerId, model, inputTokens, outputTokens, ... }) records usage your code read itself, and rejects if it could not be recorded.
  • captureFetch reports once per client and event code when no Feature has that code (onError gets an error with code: "feature_not_found"): the usage is stored but not billed until a metered Feature with that code exists. Recording is not blocked. wrapOpenAI and wrapAnthropic take the same onError; without it their failures stay silent as before.
  • createBillingHandler(nozle, { getCustomerId }) returns a web-standard (request) => Response handler that serves the @nozle-js/react billing hooks with your secret key. The last path segment picks the route: subscription (with past_due, true when the customer has an unpaid invoice past its due date), entitlement, credits, credit-history, usage, preview, invoices (the customer's finalized invoices) and invoice-download (404 for another customer's invoice). BILLING_ROUTES lists them. The customer always comes from getCustomerId, never from the request. Works in Next.js route handlers, Hono, Remix, Bun and Deno.
  • getSubscriptions(customerId), currentUsage(customerId, { subscriptionId? }) and pastUsage(customerId, { subscriptionId?, periods? }) read Core subscriptions and usage; their types (CoreSubscription, CustomerUsage) come from the OpenAPI spec.
  • nozle.invoices.list(customerId, options), get(invoiceId) and downloadUrl(invoiceId) (the PDF URL, or null while it is generated). list takes paymentOverdue to return only invoices past their due date.
  • nozle.alerts.list / get / create / update / delete manage usage alerts of a subscription ({ subscriptionId }) and balance alerts of a wallet ({ customerId, walletCode }). Nozle sends alert.triggered when one fires.
  • nozle.webhooks.verify({ body, headers, hmacKey? }) checks X-Nozle-Signature (jwt: Nozle's public key, read once with the secret key and cached; hmac: the signing secret) and returns the event (id, type, objectType, organizationId, object, payload), or throws WebhookVerificationError with a code. createWebhookHandler(nozle, { hmacKey?, onEvent }) returns a Fetch API handler that answers 200, 400 for a forged request, or 500 when onEvent throws.
  • apiKey is optional. When it is not passed, new Nozle() reads the NOZLE_SECRET_KEY environment variable and throws if that is not set either. An explicit apiKey is used as before.
  • host option (default https://api.nozle.app). It fills in baseUrl (${host}/engine) and eventsUrl (${host}/core) when they are not given. baseUrl and eventsUrl keep their meaning and win over host.
  • Opt-in retries. Set maxRetries (default 0, no retries) and optionally retryDelayMs (default 500, doubling up to 10 seconds, or the Retry-After header when sent) on new Nozle({...}). Network errors, timeouts, 429 and 5xx are retried, only for requests that are safe to repeat: reads, calls that carry an Idempotency-Key (such as usage.track), event, cost event and customer writes, and the read-only usage.check and subscription transition preview. subscribe(), checkout(), checkAndDeduct() and other writes without a key are never retried. The timeout option now applies to each attempt.
  • NozleApiError adds requestId (the X-Request-Id response header, which Core sends and the Engine does not yet) and retryAfter (seconds, from Retry-After).

Changed

  • Every call that gets a non-2xx response now throws NozleApiError (status, code, errorDetails, hasCode()), not only checkout and margin calls. This covers track, usage.track, usage.check, can, costEvents.track, subscribe, cancelSubscription, ping, plans, checkAndDeduct, the subscription lookup, customers, creditSystems, credits, entities and entitySubscriptions. The message keeps its <operation> failed: <status> <statusText> form, and the class still extends Error, so existing catch code keeps working; the subscription lookup message now carries the status text too.
  • Usage events recorded by wrapOpenAI, wrapAnthropic and captureFetch use the provider's response id as the transaction id (llm-<format>-<id>), so the same response is never recorded twice. Events also carry provider_host when known.
  • CanResult.limit and CanResult.remaining are typed number | null. The API has always sent null for a feature without a limit, while the type said number | undefined. Code that checked === undefined for "no limit" should check == null.

Deprecated

  • checkAndDeduct() is marked @deprecated; use usage.check() and usage.track() with a credit system.

Removed

  • PingResult.version and SubscriptionOptions.blocked_reason are removed from the types. The API has never sent either field.

Fixed

  • usage.track() reuses the timestamp it filled in when the same idempotency key is sent again on the same client, so a retry no longer fails with 409 idempotency_key_reused. The client remembers the last 1000 keys; a retry from another process or after a restart must pass timestamp.
  • wrapOpenAI and wrapAnthropic return the SDK's own APIPromise and Stream, so withResponse(), asResponse(), chat.completions.parse(), messages.stream() / finalMessage(), stream.controller and toReadableStream() work on a wrapped client. Usage is recorded once, when the response is read (not for asResponse(), which leaves the body to you); a stream records when it is fully iterated. Errors reject only to the caller.
  • The optional peer dependency is @anthropic-ai/sdk, the package wrapAnthropic wraps, instead of the unrelated anthropic package.

Node.js SDK 0.12.0

Choose a tag to compare

@devwatts devwatts released this 14 Oct 09:30
0cddf41

Added

  • customers.upsert() accepts billingConfiguration (paymentProvider, paymentProviderCode, providerCustomerId, syncWithProvider, providerPaymentMethods) and sends it as Core's billing_configuration, so a customer can be linked to an existing Stripe customer in the same call. Upserts without it send the same body as before. (#39)

Verification

  • Published to npm from the tagged merge commit by the Publish node SDK workflow.
  • Fresh consumer install of @nozle-js/node@0.12.0 sends billing_configuration on customers.upsert().
  • Before release: tested live against production Core with a test-mode Stripe connection (link, repeat upsert, plain upsert); one link per Stripe customer, no Stripe customers created.

Install: npm install @nozle-js/node@0.12.0

Node.js SDK 0.9.1

Choose a tag to compare

@pratham2609 pratham2609 released this 11 Oct 20:43
dad9a5e

Node.js SDK 0.9.1

Fixes Feature Event timestamps sent to Core. RFC 3339 timestamps, including those created by the OpenAI and Anthropic wrappers, are converted to Unix seconds before delivery. Numeric timestamps remain unchanged; malformed strings fail before a request is sent.

Install with npm install @nozle-js/node@0.9.1.

Published to npm with provenance through trusted publishing from the node-v0.9.1 tag. The release workflow completed successfully; package availability was verified against the npm registry.

React SDK 0.10.0

Choose a tag to compare

@devwatts devwatts released this 29 Sep 19:26
60fa248

React 0.10.0 adds customer subscription self-service through authenticated merchant backends.

  • Native BillingPortal with subscriptions, usage, invoices, billing details and wallets.
  • CancellationControl for end-of-period Cancel and Keep, with exact confirmation-date guards.
  • PlanChangeControl for signed upgrade quotes, payment-gated activation, scheduled downgrades and exact pending-change withdrawal.
  • Scoped checkout verification/recovery, plus extended Checkout, UpgradeButton and UpgradeModal integration.

Install:

npm install @nozle-js/react@0.10.0 react react-dom @stripe/stripe-js @stripe/react-stripe-js

Use Node 0.9.0 or Python 0.9.0 for the merchant adapter. Secret keys remain on your backend. The hosted API supports these flows; self-hosted deployments need the matching subscription-management endpoints.

Integration guide · Release PR

Validated on Node 18/20/22, with React, merchant integration, type and package-build checks. Published through npm trusted publishing with provenance.

Node.js SDK 0.9.0

Choose a tag to compare

@devwatts devwatts released this 29 Sep 19:24
60fa248

Node.js 0.9.0 adds selected-subscription management for the React billing portal and custom merchant backends.

  • subscriptionOptions returns the current plan, pending change and policy-authorized targets.
  • previewSubscriptionChange returns a signed quote with exact amounts and dates.
  • withdrawPendingSubscriptionChange removes the confirmed pending change by its internal UUID.
  • Existing checkout/transition APIs accept the selected subscription, quote and cancellation-date guard. Checkout verification/status support customer and subscription scoping.

Install:

npm install @nozle-js/node@0.9.0

Existing method calls remain compatible. Keep secret keys on the server and preserve idempotency keys for uncertain retries. The hosted API supports these flows; self-hosted deployments need the matching backend endpoints.

Method examples · Release PR

Validated on Node 18/20/22, including merchant integration, type and package-build checks. Published through npm trusted publishing with provenance.