Repository navigation
Releases: nozle-dev/nozle-js
Release list
React SDK 0.15.0
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/node0.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 withis_current: false, even from aplansprop.
Added
PricingPlan.versionandis_current, andBillingSubscription.subscription.plan_versionandentitlements.
Node.js SDK 0.16.0
Added
- Plan versions.
Planhasversionandis_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 throwsNozleApiError422 (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, andpreviewMigration,createMigration,listMigrations,getMigrationandcancelMigrationmove the subscriptions of one version to another, each at the end of its billing period. Migration items carrymove_status(pending,moved,canceled). A second migration of a code while one runs throwsNozleApiError409 (plan_version_migration_running).- The billing handler's
subscriptionroute includessubscription.plan_version, the version the customer is billed on, andsubscription.entitlements, that version's entitlements with its usage limits (read from Core with the secret key;nullwhen they cannot be read).
Node.js SDK 0.13.1
Fixed
wrapOpenAIandwrapAnthropicno longer change the client passed in. They return a new view of it whosecreaterecords usage for that wrapper'scustomerId, as the Go SDK returns a copy. Before, each wrap replacedcreateon 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, andwithResponse(),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
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.
BillingProviderreadsmeta.sdk_appearancefrom the plan catalog request (GET /api/v1/plans), the same cached requestPricingTableandusePlansuse, so a page makes one call. The saved theme, variables andplanCardLayoutare the lowest layer:appearanceonBillingProviderand on a component win. With nothing saved, or when the request fails, no variables are written and the components look as before. PasssavedAppearance={false}onBillingProviderto never fetch or apply it. PricingTabletakeshighlightStyle,highlightLabel(an empty saved label means no badge),annualDiscountLabelandshowTogglefrom 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#rgbor#rrggbb, radius and width variables aspx,remoremlengths,planCardLayout, and the four pricing table options. Anything else is ignored, so a saved value never writes free CSS. PlanCardtakesisUpgrade, with the same button states asPricingTable.- Saved appearance for a single component. The dashboard can save a theme, variables, card layout and pricing table options for one component (
components.<Name>inmeta.sdk_appearance, for examplePricingTableorUsageMeter). They apply to that component only, above the saved global appearance and belowappearanceonBillingProviderand on the component. A component rendered inside another (aUsageMeterinUsageDashboard, anUpgradeModalinUpgradeButton) 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 thepanellayout the "Active" label of the current plan takes the primary colour. BillingProviderrequests the plan catalog (cached, never retried, errors not shown) unlesssavedAppearance={false}, also on pages that only use components such asUsageMeter.
React SDK 0.11.0
Breaking
- Components no longer read Tailwind classes, host CSS or shadcn's
--primary,--primary-foregroundand--bordervariables. Pass your colours asappearance.variablesinstead (for examplecolorPrimary: "var(--primary)"). Without anappearance, components use thelightlook: primary#4f46e5. The billing portal's green accent (#526949) is gone; setcolorPrimaryor the older--nozle-portal-accentto keep it. @tanstack/react-query(v5) is a required peer dependency.usePlansnow runs on TanStack Query throughBillingProvider's query client and keeps its return shape, plusrefetch.PricingTableno longer marks a plan "Custom" because its name or code contains "enterprise" or "custom". List the codes of plans sold on request incontactSalesPlans; their card shows "Custom" and a "Contact Sales" button that opensenterpriseEmail.PlanCardtakesplan(a catalog plan) withisCurrent,returnUrl,onSelectand the optionalfeatures. It no longer takesid,name,monthlyPrice,annualPrice,description,isAnnualorchildren, and thePlantype export is removed.PlanComparisontakesplans(catalog plans) and the optionalfeatures. A plan that lacks an entitlement shows a cross.featuresrows are keyed by plancode, notid.
Added
appearanceonBillingProviderand on every component that renders UI. It takes athemefrom@nozle-js/react/themes(light,dark,minimal,neobrutal,nozleornozleDark; an array stacks them, last wins), your ownvariables, and per-elementelements, where a string adds a class name and an object adds inline styles. Every part has a fixednozle-<key>class, such asnozle-pricingTableornozle-planCard__price. A component's ownappearancegoes 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.Checkoutpasses the resolved theme to Stripe's Payment Element (Stripe'snighttheme on a dark background).useAppearanceand theAppearance,Theme,Variables,Elements,ElementKey,UseAppearanceandElementPropstypes are exported.PricingTabletakeshighlightStyle:outline(default) draws the highlighted card's border incolorHighlight;filledpaints the card incolorHighlightand its text and button incolorHighlightForeground. Both variables default to the primary colours.highlightLabelsets the badge on the highlighted card (default "Most Popular";nullshows no badge).- Two more themes,
nozleandnozleDark, give the Nozle dashboard look (flat cards, a hard ink shadow on buttons, Nunito). New variablesbuttonRadiusandbuttonBorderWidth(every button),buttonBorderColorandbuttonShadow(primary buttons only),colorSecondaryandcolorInput(secondary buttons), andlabelFont,labelTextTransform,labelLetterSpacingandfontFamilyNumbersfor table headers, status labels and numbers; a theme'splanCardLayout: "panel"lays plan cards out like the dashboard, withACTIVEon the current plan.neobrutalprimary buttons get a black border and offset shadow. - Text roles. New variables
fontTitle,fontHeading,fontMetric,fontBody,fontMetaandfontAction(CSSfontshorthands, written as--nozle-font-titleand so on) set the type of every element, withlabelFontas the label role; each defaults to a size infontFamily(fontMetricinfontFamilyNumberswhen 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 thePricingTabletoggle, theUsageAlertdismiss button, theCheckoutButtonstatus and error,Checkout, the billing portal and its dialogs.nozleandnozleDarkset the dashboard scale (title 20/900, heading 14/900, price 24/800 in Nunito, body 13, meta 11.5, buttons 14/800);neobrutalsets bolder titles and headings. - Customer billing hooks built on TanStack Query:
useSubscription(withpast_due),useEntitlement(featureCode),useCreditBalance(creditSystemCode?),useCreditHistory()(paged,operationsready forCreditHistory),useUsage({ period })(current period forUsageBreakdown, past periods forUsageTrend),useUpgradePreview(planCode),useInvoices({ page }),useInvoiceDownload()(resolves an invoice's PDF URL, ornullwhile it is generated) anduseBillingRefresh(). They read your backend route set asBillingProvider billingEndpoint(withbillingHeadersfor a CSRF token), served bycreateBillingHandlerfrom@nozle-js/node, so the browser holds no secret key and sees only the signed-in customer.BillingProvidertakes an optionalqueryClientto share your app's cache; the hooks pass their client explicitly and never replace yourQueryClientProvider. Failed requests throwBillingRequestError(status,code,requestId); a 4xx is not retried. CurrentPlanshows 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,detailsandactionscan be passed instead.currentPlanMeters(entitlements, usage)returns its meter rows for your own layout.PricingTable,PlanCardandPlanComparisonread what each plan gives from the public catalog: entitlements, credit grants and the usage prices of every charge model exceptcustomanddynamic.PricingTableaccepts an optionalfeatures(an array aligned to the rendered plans, or an object keyed by plan code),PlanCardan optionalfeatureslist andPlanComparisonoptionalfeaturesrows. When passed, they replace the generated lines.planFeatures(plan)andcomparisonRows(plans)return the same lines and rows for your own layout. TheCatalogEntitlement,CatalogCreditGrantRule,CatalogCharge,CatalogPrivilegeandComparisonRowtypes are exported, andCatalogPlanandPricingPlandeclare the optionalentitlements,credit_grant_rulesandchargesfields.PricingTableandPlanComparisonsort plans by base price, cheapest first (PricingTableputscontactSalesPlanslast), instead of showing the catalog's newest-first order. Plans in different currencies stay grouped, and ties keep the catalog order. Passsortto use your own order.sortPlansByPrice(plans)is exported. Afeaturesarray is aligned to the sorted order.CreditBalanceandCreditHistoryrender the SDK's credit balance and credit operations (CreditBalanceData,CreditOperationData), each with loading, error and empty states;CreditHistoryhas a load-more button for the SDK'snext_cursor.usageItems(entitlements, results)buildsUsageDashboardrows from a plan's metered entitlements andcan()results.UsageDashboardtakeserrorandemptyMessage, andUsageMeterandUsageDashboardaccept anullor missinglimitand showUnlimited.UsageBreakdown(a table of each feature's units and amount for one period, with the total) andUsageTrend(a bar chart of the amount, or the units of onefeatureCode, per period) render the usage your backend reads from Core'scurrent_usageandpast_usage(CustomerUsageData,ChargeUsageData). Both have loading, error and empty states and no chart library.PricingTable,PlanComparison,CurrentPlan,Checkout,UpgradeModalandBillingPortalshow "Powered by" and the Nozle logo when the merchant's Nozle plan keeps the branding (meta.nozle_brandingin the publishable-key catalog,nozleBrandingin the portal). Only the outermost of nested components shows it.useNozleBranding()andPoweredByNozleare exported for your own UI. The portal's plain "Powered by Nozle" footer is replaced.PricingTableandusePlansshare one cached catalog request.- Changing plan in
PlanChangeControl(and the billing portal) lists the plans as choices with price, upgrade or downgrade, and, inside aBillingProvider, each plan's description and features from the catalog; the confirmation step has a Back button.UpgradeModaltakesonBack. The portal subscription card puts View usage, Change plan and Cancel subscription in one action row.
Changed
PricingTablegives 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,PlanComparisonand 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. UsageDashboardhas the same card frame as the other data components. AUsageMetervalue 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. WhencolorMutedForegroundis not set it iscolorForegroundmixed 62% intocolorCardinstead of a fixed#6b7280; thelightanddarkthemes 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...
Node.js SDK 0.13.0
Added
nozle.captureFetch(options, fetch?)returns afetchthat records the token usage of every successful LLM response. Pass it as thefetchoption 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-compatibleusage(any OpenAI-compatible API, including the Responses API), Anthropicusageand GeminiusageMetadata. Streams are read as the app reads them; chat completion streams are asked for usage (stream_options.include_usage, turn off withincludeStreamUsage: false). When the app aborts a stream, the usage already seen is still recorded. The customer comes fromcustomerId(a string or a function per request) or theX-Nozle-Customer-Idrequest header, which is not sent to the provider. Recording never changes or fails the provider response; failures go toonError.detectUsageandnormalizeGeminiUsageand theDetectedUsageandProviderUsageFormattypes 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.captureFetchreports once per client and event code when no Feature has that code (onErrorgets an error withcode: "feature_not_found"): the usage is stored but not billed until a metered Feature with that code exists. Recording is not blocked.wrapOpenAIandwrapAnthropictake the sameonError; without it their failures stay silent as before.createBillingHandler(nozle, { getCustomerId })returns a web-standard(request) => Responsehandler that serves the@nozle-js/reactbilling hooks with your secret key. The last path segment picks the route:subscription(withpast_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) andinvoice-download(404 for another customer's invoice).BILLING_ROUTESlists them. The customer always comes fromgetCustomerId, never from the request. Works in Next.js route handlers, Hono, Remix, Bun and Deno.getSubscriptions(customerId),currentUsage(customerId, { subscriptionId? })andpastUsage(customerId, { subscriptionId?, periods? })read Core subscriptions and usage; their types (CoreSubscription,CustomerUsage) come from the OpenAPI spec.nozle.invoices.list(customerId, options),get(invoiceId)anddownloadUrl(invoiceId)(the PDF URL, ornullwhile it is generated).listtakespaymentOverdueto return only invoices past their due date.nozle.alerts.list / get / create / update / deletemanage usage alerts of a subscription ({ subscriptionId }) and balance alerts of a wallet ({ customerId, walletCode }). Nozle sendsalert.triggeredwhen one fires.nozle.webhooks.verify({ body, headers, hmacKey? })checksX-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 throwsWebhookVerificationErrorwith acode.createWebhookHandler(nozle, { hmacKey?, onEvent })returns a Fetch API handler that answers 200, 400 for a forged request, or 500 whenonEventthrows.apiKeyis optional. When it is not passed,new Nozle()reads theNOZLE_SECRET_KEYenvironment variable and throws if that is not set either. An explicitapiKeyis used as before.hostoption (defaulthttps://api.nozle.app). It fills inbaseUrl(${host}/engine) andeventsUrl(${host}/core) when they are not given.baseUrlandeventsUrlkeep their meaning and win overhost.- Opt-in retries. Set
maxRetries(default0, no retries) and optionallyretryDelayMs(default 500, doubling up to 10 seconds, or theRetry-Afterheader when sent) onnew Nozle({...}). Network errors, timeouts,429and5xxare retried, only for requests that are safe to repeat: reads, calls that carry anIdempotency-Key(such asusage.track), event, cost event and customer writes, and the read-onlyusage.checkand subscription transition preview.subscribe(),checkout(),checkAndDeduct()and other writes without a key are never retried. Thetimeoutoption now applies to each attempt. NozleApiErroraddsrequestId(theX-Request-Idresponse header, which Core sends and the Engine does not yet) andretryAfter(seconds, fromRetry-After).
Changed
- Every call that gets a non-2xx response now throws
NozleApiError(status,code,errorDetails,hasCode()), not only checkout and margin calls. This coverstrack,usage.track,usage.check,can,costEvents.track,subscribe,cancelSubscription,ping,plans,checkAndDeduct, the subscription lookup,customers,creditSystems,credits,entitiesandentitySubscriptions. The message keeps its<operation> failed: <status> <statusText>form, and the class still extendsError, so existingcatchcode keeps working; the subscription lookup message now carries the status text too. - Usage events recorded by
wrapOpenAI,wrapAnthropicandcaptureFetchuse the provider's response id as the transaction id (llm-<format>-<id>), so the same response is never recorded twice. Events also carryprovider_hostwhen known. CanResult.limitandCanResult.remainingare typednumber | null. The API has always sentnullfor a feature without a limit, while the type saidnumber | undefined. Code that checked=== undefinedfor "no limit" should check== null.
Deprecated
checkAndDeduct()is marked@deprecated; useusage.check()andusage.track()with a credit system.
Removed
PingResult.versionandSubscriptionOptions.blocked_reasonare 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 409idempotency_key_reused. The client remembers the last 1000 keys; a retry from another process or after a restart must passtimestamp.wrapOpenAIandwrapAnthropicreturn the SDK's ownAPIPromiseandStream, sowithResponse(),asResponse(),chat.completions.parse(),messages.stream()/finalMessage(),stream.controllerandtoReadableStream()work on a wrapped client. Usage is recorded once, when the response is read (not forasResponse(), 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 packagewrapAnthropicwraps, instead of the unrelatedanthropicpackage.
Node.js SDK 0.12.0
Added
customers.upsert()acceptsbillingConfiguration(paymentProvider,paymentProviderCode,providerCustomerId,syncWithProvider,providerPaymentMethods) and sends it as Core'sbilling_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 SDKworkflow. - Fresh consumer install of
@nozle-js/node@0.12.0sendsbilling_configurationoncustomers.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
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
React 0.10.0 adds customer subscription self-service through authenticated merchant backends.
- Native
BillingPortalwith subscriptions, usage, invoices, billing details and wallets. CancellationControlfor end-of-period Cancel and Keep, with exact confirmation-date guards.PlanChangeControlfor signed upgrade quotes, payment-gated activation, scheduled downgrades and exact pending-change withdrawal.- Scoped checkout verification/recovery, plus extended
Checkout,UpgradeButtonandUpgradeModalintegration.
Install:
npm install @nozle-js/react@0.10.0 react react-dom @stripe/stripe-js @stripe/react-stripe-jsUse 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
Node.js 0.9.0 adds selected-subscription management for the React billing portal and custom merchant backends.
subscriptionOptionsreturns the current plan, pending change and policy-authorized targets.previewSubscriptionChangereturns a signed quote with exact amounts and dates.withdrawPendingSubscriptionChangeremoves 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.0Existing 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.
Validated on Node 18/20/22, including merchant integration, type and package-build checks. Published through npm trusted publishing with provenance.