fix(backend): Update Subscription and SubscriptionItem resources - #9196

Merged
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem
Jul 30, 2026
Merged

fix(backend): Update Subscription and SubscriptionItem resources#9196
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem

Conversation

@dstaley

@dstaleydstaley commented Jul 17, 2026

Copy link
Copy Markdown
Member

Description

This PR updates the parsing logic for BillingSubscription and BillingSubscriptionItem in the @clerk/backend package to align with the latest response shapes for the Backend API.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@vercel

vercelBot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 17, 2026 9:02pm
swingsetReadyReadyPreview, CommentJul 17, 2026 9:02pm

Request Review

@changeset-bot

changeset-botBot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d40a9c3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendMinor
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Billing subscription JSON contracts now include instance, pricing, totals, and seat fields. Shared billing converters normalize nested billing data, and subscription resources expose the converted structures through updated factories and constructors.

Changes

LayerSummary
Billing JSON contractsSubscription and subscription-item JSON types add instance identifiers and structured payment, pricing, lifetime-payment, and seat fields.
Billing conversion helpersNew helpers convert billing amounts, totals, credits, discounts, period totals, and seat breakdowns into typed objects.
Subscription resource parsingSubscription resources normalize the expanded billing data through updated constructors and fromJSON factories. A minor backend changeset was added.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:alexisintech

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the backend changes to Subscription and SubscriptionItem resources.
Description check✅ PassedThe description matches the PR by explaining the backend parsing updates for BillingSubscription and BillingSubscriptionItem.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/tame-bats-tell.md:
- Around line 2-5: Update the changeset entry for `@clerk/backend` from minor to
major to reflect the breaking BillingSubscription and BillingSubscriptionItem
API changes, and add migration guidance covering the constructor shape and
nextPayment.amount type updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 544eaf57-70d6-4fc0-820a-de60667aa155

📥 Commits

Reviewing files that changed from the base of the PR and between 26530ce and 1cbd099.

📒 Files selected for processing (5)
  • .changeset/tame-bats-tell.md
  • packages/backend/src/api/resources/CommerceSubscription.ts
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
  • packages/backend/src/api/resources/JSON.ts
  • packages/backend/src/util/billing.ts

Comment on lines +2 to +5
'@clerk/backend': minor
---

Update fields for BillingSubscription and BillingSubscriptionItem

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
cat packages/backend/package.json
rg -n -C 3 '\b(BillingSubscription|BillingSubscriptionItem)\b' packages/backend/src

Repository: clerk/javascript

Length of output: 24693


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscription.ts
printf'\n---\n'
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscriptionItem.ts
printf'\n---\n'
sed -n '140,190p' packages/backend/src/index.ts
printf'\n---\n'
sed -n '1,80p' .changeset/tame-bats-tell.md

Repository: clerk/javascript

Length of output: 10343


Require a major changeset for the Billing API surfaceBillingSubscription and BillingSubscriptionItem are exported from @clerk/backend, so the constructor shape and nextPayment.amount type change are breaking API changes. Update this changeset to major and add migration guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.changeset/tame-bats-tell.md around lines 2 - 5, Update the changeset entry
for `@clerk/backend` from minor to major to reflect the breaking
BillingSubscription and BillingSubscriptionItem API changes, and add migration
guidance covering the constructor shape and nextPayment.amount type updates.

Source: Coding guidelines

@pkg-pr-new

pkg-pr-newBot commented Jul 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9196

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9196

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9196

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9196

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9196

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9196

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9196

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9196

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9196

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9196

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9196

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9196

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9196

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9196

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9196

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9196

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9196

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9196

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9196

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9196

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9196

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9196

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9196

commit: d40a9c3

@github-actions

github-actionsBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-17T21:05:11.397Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes7
🟡 Non-breaking changes3
🟢 Additions8

Warning
7 breaking change(s) detected - Major version bump required

🤖 This report was reviewed by claude-sonnet-4-6.

🔴 Breaking changes index (7)

Every breaking change, up front. Full diffs are in the package sections below.

PackageSubpathChange
@clerk/backend.BillingSubscription.undefined
@clerk/backend.BillingSubscriptionItem.undefined
@clerk/backend.BillingSubscriptionItem.isFreeTrial
@clerk/backend.BillingSubscriptionItem.nextPayment
@clerk/backend.BillingSubscriptionItemJSON.amount
@clerk/backend.BillingSubscriptionItemJSON.is_free_trial
@clerk/backend.BillingSubscriptionItemJSON.next_payment

@clerk/backend

Current version: 3.11.7
Recommended bump: MAJOR → 4.0.0

🔴 Breaking Changes (7)

Changed: BillingSubscription.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionJSON['status'], payerId: string, createdAt: number, updatedAt: number, activeAt: number | null, pastDueAt: number | null, subscriptionItems: BillingSubscriptionItem[], nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null, eligibleForFreeTrial: boolean);

Static analyzer: Breaking change in constructor BillingSubscription.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionJSON['status']string; Parameter payerId type changed: stringimport("@clerk/backend").BillingSubscriptionJSON['status']; Parameter createdAt type changed: numberstring; Parameter activeAt type changed: null|numbernumber; Parameter subscriptionItems type changed: import("@clerk/backend").BillingSubscriptionItem[]null|number; Parameter nextPayment type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}import("@clerk/backend").BillingSubscriptionItem[]; Parameter eligibleForFreeTrial type changed: booleannull|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Required parameter eligibleForFreeTrial was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscription constructor, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscription(...) directly.

Migration: Add the new instanceId: string argument as the second parameter when constructing BillingSubscription instances.

Changed: BillingSubscriptionItem.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionItemJSON['status'], planPeriod: 'month' | 'annual', periodStart: number, nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined, amount: BillingMoneyAmount | undefined, plan: BillingPlan | null, planId: string | null, + priceId: string | null, 
createdAt: number, updatedAt: number, periodEnd: number | null, canceledAt: number | null, pastDueAt: number | null, endedAt: number | null, payerId: string | undefined, - isFreeTrial?: boolean | undefined, - lifetimePaid?: BillingMoneyAmount | undefined);+ isFreeTrial: boolean, + lifetimePaid?: BillingMoneyAmount | undefined, + seats?: BillingSubscriptionItemSeats | undefined);

Static analyzer: Breaking change in constructor BillingSubscriptionItem.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionItemJSON['status']string; Parameter planPeriod type changed: 'annual'|'month'import("@clerk/backend").BillingSubscriptionItemJSON['status']; Parameter periodStart type changed: number'annual'|'month'; Parameter nextPayment type changed: null|undefined|{amount:number;date:number;}number; Parameter amount type changed: import("@clerk/shared").BillingMoneyAmount|undefinednull|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Parameter plan type changed: import("@clerk/backend").BillingPlan|nullimport("@clerk/shared").BillingMoneyAmount|undefined; Parameter planId type changed: null|stringimport("@clerk/backend").BillingPlan|null; Parameter createdAt type changed: numbernull|string; Parameter updatedAt type changed: numbernull|string; Parameter periodEnd type changed: null|numbernumber; Parameter canceledAt type changed: null|numbernumber; Parameter payerId type changed: string|undefinednull|number; Parameter isFreeTrial is no longer optional; Parameter isFreeTrial type changed: boolean|undefinednull|number; Parameter lifetimePaid is no longer optional; Parameter lifetimePaid type changed: import("@clerk/shared").BillingMoneyAmount|undefinedstring|undefined; Required parameter isFreeTrial was added; Optional parameter lifetimePaid was added; Optional parameter seats was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscriptionItem constructor, and a new required parameter priceId was also inserted later, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscriptionItem(...) directly.

Migration: Add instanceId: string as the second argument and priceId: string | null after planId when constructing BillingSubscriptionItem instances.

Changed: BillingSubscriptionItem.isFreeTrial

- readonly isFreeTrial?: boolean | undefined;+ readonly isFreeTrial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItem.isFreeTrial: Member is no longer optional; Type changed: boolean|undefinedboolean

🤖 AI review (confirmed) (85%): Making isFreeTrial a required (non-optional) property on the BillingSubscriptionItem class narrows the type; consumers who previously typed this as boolean | undefined and assigned undefined will now fail, and code relying on the optional signature breaks.

Migration: Ensure all code that reads or assigns isFreeTrial treats it as always boolean, removing any undefined branches.

Changed: BillingSubscriptionItem.nextPayment

 readonly nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined;

Static analyzer: Breaking change in property BillingSubscriptionItem.nextPayment: Type changed: null|undefined|{amount:number;date:number;}null|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").Bi…

🤖 AI review (confirmed) (85%): The amount field in the nextPayment object changed from number to BillingMoneyAmount; consumers that previously read nextPayment.amount as a number will have a type mismatch.

Migration: Update consumer code to treat nextPayment.amount as a BillingMoneyAmount object rather than a plain number.

Changed: BillingSubscriptionItemJSON.amount

- amount: BillingMoneyAmountJSON;+ amount?: BillingMoneyAmountJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.amount: Member became optional; Type changed: import("@clerk/backend").~BillingMoneyAmountJSONimport("@clerk/backend").~BillingMoneyAmountJSON|null

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.amount changed from a required BillingMoneyAmountJSON to an optional BillingMoneyAmountJSON | null; consumers constructing objects of this interface that provided the required field now encounter type mismatches when checking optionality, and consumers reading it must now handle null/undefined.

Migration: Update consumer code that reads amount from BillingSubscriptionItemJSON to handle null | undefined cases.

Changed: BillingSubscriptionItemJSON.is_free_trial

- is_free_trial?: boolean;+ is_free_trial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.is_free_trial: Member is no longer optional

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.is_free_trial changed from optional to required; consumers constructing objects of this interface that omitted this field will now fail to compile.

Migration: Add a required is_free_trial: boolean field when constructing objects that conform to BillingSubscriptionItemJSON.

Changed: BillingSubscriptionItemJSON.next_payment

- next_payment?: {- amount: number;- date: number;- } | null;+ next_payment?: BillingSubscriptionItemNextPaymentJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.next_payment: Type changed: null|{amount:number;date:number;}import("@clerk/shared").BillingSubscriptionItemNextPaymentJSON|null

🤖 AI review (confirmed) (80%): The inline type { amount: number; date: number } was replaced by BillingSubscriptionItemNextPaymentJSON, which changes the shape of amount from number to a structured type; consumers reading or constructing this field must update their code.

Migration: Update code that reads next_payment.amount from BillingSubscriptionItemJSON to handle the new BillingSubscriptionItemNextPaymentJSON shape instead of a plain number.

🟡 Non-breaking Changes (3)

Modified: BillingSubscription.nextPayment

 readonly nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null;

Static analyzer: Breaking change in property BillingSubscription.nextPayment: Type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUn…

🤖 AI review (reclassified as non-breaking) (90%): The nextPayment property on BillingSubscription only gains two new optional fields (perUnitTotals? and totals?); existing consumers reading the property still see a compatible shape (the existing fields are unchanged), and since this is a readonly output property no consumer is required to supply those fields.

Modified: BillingSubscriptionItemJSON.lifetime_paid

- lifetime_paid: BillingMoneyAmountJSON | null;+ lifetime_paid?: BillingMoneyAmountJSON | null;

Static analyzer: Modified property BillingSubscriptionItemJSON.lifetime_paid: Member became optional

🤖 AI review (confirmed) (95%): Making lifetime_paid optional in the interface is non-breaking: consumers reading it already had to handle null, and consumers constructing this object type can now omit the field rather than being required to provide it.

Modified: BillingSubscriptionJSON.next_payment

 next_payment?: {
date: number;
amount: BillingMoneyAmountJSON;
+ per_unit_totals?: BillingPerUnitTotalJSON[];+ totals?: SharedBillingTotalsJSON;
};

Static analyzer: Breaking change in property BillingSubscriptionJSON.next_payment: Type changed: {date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;}{date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;per_unit_totals?:import("@clerk/shared").BillingPe…

🤖 AI review (reclassified as non-breaking) (90%): The next_payment object in BillingSubscriptionJSON only gains new optional fields (per_unit_totals? and totals?); existing consumers neither need to supply those fields when constructing nor are required to handle them when reading, so no well-typed consumer is broken.

🟢 Additions (8)

Added: BillingSubscription.instanceId

+ readonly instanceId: string;

Added property BillingSubscription.instanceId

Added: BillingSubscriptionItem.instanceId

+ readonly instanceId: string;

Added property BillingSubscriptionItem.instanceId

Added: BillingSubscriptionItem.priceId

+ readonly priceId: string | null;

Added property BillingSubscriptionItem.priceId

Added: BillingSubscriptionItem.seats

+ readonly seats?: BillingSubscriptionItemSeats | undefined;

Added property BillingSubscriptionItem.seats

Added: BillingSubscriptionItemJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionItemJSON.instance_id

Added: BillingSubscriptionItemJSON.price_id

+ price_id?: string;

Added property BillingSubscriptionItemJSON.price_id

Added: BillingSubscriptionItemJSON.seats

+ seats?: BillingSubscriptionItemSeatsJSON;

Added property BillingSubscriptionItemJSON.seats

Added: BillingSubscriptionJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionJSON.instance_id


Report generated by Break Check

Last ran on d40a9c3.

@dstaley
dstaley requested a review from a teamJuly 17, 2026 20:47
@dstaley

Copy link
Copy Markdown
MemberAuthor

@macroscope-app review

@macroscopeapp

Copy link
Copy Markdown

Manual reviews triggered for commit d40a9c3:

All prior checks · these links stay valid even if you push more commits.

@macroscopeapp

Copy link
Copy Markdown

Review in progress. Results will be posted as check runs when complete.

@macroscopeapp

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

This PR modifies billing-related resources which are treated as sensitive. Additionally, there's an unresolved comment noting that the type change to nextPayment.amount may constitute a breaking API change requiring a major version bump rather than minor.

You can customize Macroscope's approvability policy. Learn more.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/backend/src/api/resources/CommerceSubscriptionItem.ts (1)

38-44: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the backend API documentation for the new payment shape.

nextPayment.amount is now BillingMoneyAmount and includes perUnitTotals/totals, while clerk-docs still documents the older { amount, date } shape. Update the public documentation with this API change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts` around lines
38 - 44, Update the public clerk-docs documentation for the nextPayment API
shape to match CommerceSubscriptionItem: document amount as BillingMoneyAmount
and include the optional perUnitTotals and totals fields alongside date. Replace
the older { amount, date } representation without changing unrelated
subscription documentation.

Sources: Coding guidelines, Linked repositories

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts`:
- Around line 38-44: Update the public clerk-docs documentation for the
nextPayment API shape to match CommerceSubscriptionItem: document amount as
BillingMoneyAmount and include the optional perUnitTotals and totals fields
alongside date. Replace the older { amount, date } representation without
changing unrelated subscription documentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: e7a1b51e-6fc9-445d-a608-d21220fb711f

📥 Commits

Reviewing files that changed from the base of the PR and between 1cbd099 and d40a9c3.

📒 Files selected for processing (1)
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)

@dstaley
dstaley merged commit fe6ee54 into mainJul 30, 2026
55 checks passed
@dstaley
dstaley deleted the ds.fix/backend-sub-subitem branch July 30, 2026 18:13
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dstaley@mauricioabreu
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(backend): Update Subscription and SubscriptionItem resources - #9196

Merged
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem
Jul 30, 2026
Merged

fix(backend): Update Subscription and SubscriptionItem resources#9196
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem

Conversation

@dstaley

@dstaleydstaley commented Jul 17, 2026

Copy link
Copy Markdown
Member

Description

This PR updates the parsing logic for BillingSubscription and BillingSubscriptionItem in the @clerk/backend package to align with the latest response shapes for the Backend API.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@vercel

vercelBot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 17, 2026 9:02pm
swingsetReadyReadyPreview, CommentJul 17, 2026 9:02pm

Request Review

@changeset-bot

changeset-botBot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d40a9c3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendMinor
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Billing subscription JSON contracts now include instance, pricing, totals, and seat fields. Shared billing converters normalize nested billing data, and subscription resources expose the converted structures through updated factories and constructors.

Changes

LayerSummary
Billing JSON contractsSubscription and subscription-item JSON types add instance identifiers and structured payment, pricing, lifetime-payment, and seat fields.
Billing conversion helpersNew helpers convert billing amounts, totals, credits, discounts, period totals, and seat breakdowns into typed objects.
Subscription resource parsingSubscription resources normalize the expanded billing data through updated constructors and fromJSON factories. A minor backend changeset was added.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:alexisintech

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the backend changes to Subscription and SubscriptionItem resources.
Description check✅ PassedThe description matches the PR by explaining the backend parsing updates for BillingSubscription and BillingSubscriptionItem.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/tame-bats-tell.md:
- Around line 2-5: Update the changeset entry for `@clerk/backend` from minor to
major to reflect the breaking BillingSubscription and BillingSubscriptionItem
API changes, and add migration guidance covering the constructor shape and
nextPayment.amount type updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 544eaf57-70d6-4fc0-820a-de60667aa155

📥 Commits

Reviewing files that changed from the base of the PR and between 26530ce and 1cbd099.

📒 Files selected for processing (5)
  • .changeset/tame-bats-tell.md
  • packages/backend/src/api/resources/CommerceSubscription.ts
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
  • packages/backend/src/api/resources/JSON.ts
  • packages/backend/src/util/billing.ts

Comment on lines +2 to +5
'@clerk/backend': minor
---

Update fields for BillingSubscription and BillingSubscriptionItem

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
cat packages/backend/package.json
rg -n -C 3 '\b(BillingSubscription|BillingSubscriptionItem)\b' packages/backend/src

Repository: clerk/javascript

Length of output: 24693


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscription.ts
printf'\n---\n'
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscriptionItem.ts
printf'\n---\n'
sed -n '140,190p' packages/backend/src/index.ts
printf'\n---\n'
sed -n '1,80p' .changeset/tame-bats-tell.md

Repository: clerk/javascript

Length of output: 10343


Require a major changeset for the Billing API surfaceBillingSubscription and BillingSubscriptionItem are exported from @clerk/backend, so the constructor shape and nextPayment.amount type change are breaking API changes. Update this changeset to major and add migration guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.changeset/tame-bats-tell.md around lines 2 - 5, Update the changeset entry
for `@clerk/backend` from minor to major to reflect the breaking
BillingSubscription and BillingSubscriptionItem API changes, and add migration
guidance covering the constructor shape and nextPayment.amount type updates.

Source: Coding guidelines

@pkg-pr-new

pkg-pr-newBot commented Jul 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9196

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9196

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9196

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9196

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9196

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9196

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9196

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9196

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9196

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9196

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9196

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9196

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9196

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9196

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9196

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9196

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9196

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9196

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9196

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9196

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9196

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9196

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9196

commit: d40a9c3

@github-actions

github-actionsBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-17T21:05:11.397Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes7
🟡 Non-breaking changes3
🟢 Additions8

Warning
7 breaking change(s) detected - Major version bump required

🤖 This report was reviewed by claude-sonnet-4-6.

🔴 Breaking changes index (7)

Every breaking change, up front. Full diffs are in the package sections below.

PackageSubpathChange
@clerk/backend.BillingSubscription.undefined
@clerk/backend.BillingSubscriptionItem.undefined
@clerk/backend.BillingSubscriptionItem.isFreeTrial
@clerk/backend.BillingSubscriptionItem.nextPayment
@clerk/backend.BillingSubscriptionItemJSON.amount
@clerk/backend.BillingSubscriptionItemJSON.is_free_trial
@clerk/backend.BillingSubscriptionItemJSON.next_payment

@clerk/backend

Current version: 3.11.7
Recommended bump: MAJOR → 4.0.0

🔴 Breaking Changes (7)

Changed: BillingSubscription.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionJSON['status'], payerId: string, createdAt: number, updatedAt: number, activeAt: number | null, pastDueAt: number | null, subscriptionItems: BillingSubscriptionItem[], nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null, eligibleForFreeTrial: boolean);

Static analyzer: Breaking change in constructor BillingSubscription.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionJSON['status']string; Parameter payerId type changed: stringimport("@clerk/backend").BillingSubscriptionJSON['status']; Parameter createdAt type changed: numberstring; Parameter activeAt type changed: null|numbernumber; Parameter subscriptionItems type changed: import("@clerk/backend").BillingSubscriptionItem[]null|number; Parameter nextPayment type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}import("@clerk/backend").BillingSubscriptionItem[]; Parameter eligibleForFreeTrial type changed: booleannull|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Required parameter eligibleForFreeTrial was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscription constructor, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscription(...) directly.

Migration: Add the new instanceId: string argument as the second parameter when constructing BillingSubscription instances.

Changed: BillingSubscriptionItem.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionItemJSON['status'], planPeriod: 'month' | 'annual', periodStart: number, nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined, amount: BillingMoneyAmount | undefined, plan: BillingPlan | null, planId: string | null, + priceId: string | null, 
createdAt: number, updatedAt: number, periodEnd: number | null, canceledAt: number | null, pastDueAt: number | null, endedAt: number | null, payerId: string | undefined, - isFreeTrial?: boolean | undefined, - lifetimePaid?: BillingMoneyAmount | undefined);+ isFreeTrial: boolean, + lifetimePaid?: BillingMoneyAmount | undefined, + seats?: BillingSubscriptionItemSeats | undefined);

Static analyzer: Breaking change in constructor BillingSubscriptionItem.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionItemJSON['status']string; Parameter planPeriod type changed: 'annual'|'month'import("@clerk/backend").BillingSubscriptionItemJSON['status']; Parameter periodStart type changed: number'annual'|'month'; Parameter nextPayment type changed: null|undefined|{amount:number;date:number;}number; Parameter amount type changed: import("@clerk/shared").BillingMoneyAmount|undefinednull|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Parameter plan type changed: import("@clerk/backend").BillingPlan|nullimport("@clerk/shared").BillingMoneyAmount|undefined; Parameter planId type changed: null|stringimport("@clerk/backend").BillingPlan|null; Parameter createdAt type changed: numbernull|string; Parameter updatedAt type changed: numbernull|string; Parameter periodEnd type changed: null|numbernumber; Parameter canceledAt type changed: null|numbernumber; Parameter payerId type changed: string|undefinednull|number; Parameter isFreeTrial is no longer optional; Parameter isFreeTrial type changed: boolean|undefinednull|number; Parameter lifetimePaid is no longer optional; Parameter lifetimePaid type changed: import("@clerk/shared").BillingMoneyAmount|undefinedstring|undefined; Required parameter isFreeTrial was added; Optional parameter lifetimePaid was added; Optional parameter seats was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscriptionItem constructor, and a new required parameter priceId was also inserted later, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscriptionItem(...) directly.

Migration: Add instanceId: string as the second argument and priceId: string | null after planId when constructing BillingSubscriptionItem instances.

Changed: BillingSubscriptionItem.isFreeTrial

- readonly isFreeTrial?: boolean | undefined;+ readonly isFreeTrial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItem.isFreeTrial: Member is no longer optional; Type changed: boolean|undefinedboolean

🤖 AI review (confirmed) (85%): Making isFreeTrial a required (non-optional) property on the BillingSubscriptionItem class narrows the type; consumers who previously typed this as boolean | undefined and assigned undefined will now fail, and code relying on the optional signature breaks.

Migration: Ensure all code that reads or assigns isFreeTrial treats it as always boolean, removing any undefined branches.

Changed: BillingSubscriptionItem.nextPayment

 readonly nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined;

Static analyzer: Breaking change in property BillingSubscriptionItem.nextPayment: Type changed: null|undefined|{amount:number;date:number;}null|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").Bi…

🤖 AI review (confirmed) (85%): The amount field in the nextPayment object changed from number to BillingMoneyAmount; consumers that previously read nextPayment.amount as a number will have a type mismatch.

Migration: Update consumer code to treat nextPayment.amount as a BillingMoneyAmount object rather than a plain number.

Changed: BillingSubscriptionItemJSON.amount

- amount: BillingMoneyAmountJSON;+ amount?: BillingMoneyAmountJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.amount: Member became optional; Type changed: import("@clerk/backend").~BillingMoneyAmountJSONimport("@clerk/backend").~BillingMoneyAmountJSON|null

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.amount changed from a required BillingMoneyAmountJSON to an optional BillingMoneyAmountJSON | null; consumers constructing objects of this interface that provided the required field now encounter type mismatches when checking optionality, and consumers reading it must now handle null/undefined.

Migration: Update consumer code that reads amount from BillingSubscriptionItemJSON to handle null | undefined cases.

Changed: BillingSubscriptionItemJSON.is_free_trial

- is_free_trial?: boolean;+ is_free_trial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.is_free_trial: Member is no longer optional

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.is_free_trial changed from optional to required; consumers constructing objects of this interface that omitted this field will now fail to compile.

Migration: Add a required is_free_trial: boolean field when constructing objects that conform to BillingSubscriptionItemJSON.

Changed: BillingSubscriptionItemJSON.next_payment

- next_payment?: {- amount: number;- date: number;- } | null;+ next_payment?: BillingSubscriptionItemNextPaymentJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.next_payment: Type changed: null|{amount:number;date:number;}import("@clerk/shared").BillingSubscriptionItemNextPaymentJSON|null

🤖 AI review (confirmed) (80%): The inline type { amount: number; date: number } was replaced by BillingSubscriptionItemNextPaymentJSON, which changes the shape of amount from number to a structured type; consumers reading or constructing this field must update their code.

Migration: Update code that reads next_payment.amount from BillingSubscriptionItemJSON to handle the new BillingSubscriptionItemNextPaymentJSON shape instead of a plain number.

🟡 Non-breaking Changes (3)

Modified: BillingSubscription.nextPayment

 readonly nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null;

Static analyzer: Breaking change in property BillingSubscription.nextPayment: Type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUn…

🤖 AI review (reclassified as non-breaking) (90%): The nextPayment property on BillingSubscription only gains two new optional fields (perUnitTotals? and totals?); existing consumers reading the property still see a compatible shape (the existing fields are unchanged), and since this is a readonly output property no consumer is required to supply those fields.

Modified: BillingSubscriptionItemJSON.lifetime_paid

- lifetime_paid: BillingMoneyAmountJSON | null;+ lifetime_paid?: BillingMoneyAmountJSON | null;

Static analyzer: Modified property BillingSubscriptionItemJSON.lifetime_paid: Member became optional

🤖 AI review (confirmed) (95%): Making lifetime_paid optional in the interface is non-breaking: consumers reading it already had to handle null, and consumers constructing this object type can now omit the field rather than being required to provide it.

Modified: BillingSubscriptionJSON.next_payment

 next_payment?: {
date: number;
amount: BillingMoneyAmountJSON;
+ per_unit_totals?: BillingPerUnitTotalJSON[];+ totals?: SharedBillingTotalsJSON;
};

Static analyzer: Breaking change in property BillingSubscriptionJSON.next_payment: Type changed: {date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;}{date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;per_unit_totals?:import("@clerk/shared").BillingPe…

🤖 AI review (reclassified as non-breaking) (90%): The next_payment object in BillingSubscriptionJSON only gains new optional fields (per_unit_totals? and totals?); existing consumers neither need to supply those fields when constructing nor are required to handle them when reading, so no well-typed consumer is broken.

🟢 Additions (8)

Added: BillingSubscription.instanceId

+ readonly instanceId: string;

Added property BillingSubscription.instanceId

Added: BillingSubscriptionItem.instanceId

+ readonly instanceId: string;

Added property BillingSubscriptionItem.instanceId

Added: BillingSubscriptionItem.priceId

+ readonly priceId: string | null;

Added property BillingSubscriptionItem.priceId

Added: BillingSubscriptionItem.seats

+ readonly seats?: BillingSubscriptionItemSeats | undefined;

Added property BillingSubscriptionItem.seats

Added: BillingSubscriptionItemJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionItemJSON.instance_id

Added: BillingSubscriptionItemJSON.price_id

+ price_id?: string;

Added property BillingSubscriptionItemJSON.price_id

Added: BillingSubscriptionItemJSON.seats

+ seats?: BillingSubscriptionItemSeatsJSON;

Added property BillingSubscriptionItemJSON.seats

Added: BillingSubscriptionJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionJSON.instance_id


Report generated by Break Check

Last ran on d40a9c3.

@dstaley
dstaley requested a review from a teamJuly 17, 2026 20:47
@dstaley

Copy link
Copy Markdown
MemberAuthor

@macroscope-app review

@macroscopeapp

Copy link
Copy Markdown

Manual reviews triggered for commit d40a9c3:

All prior checks · these links stay valid even if you push more commits.

@macroscopeapp

Copy link
Copy Markdown

Review in progress. Results will be posted as check runs when complete.

@macroscopeapp

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

This PR modifies billing-related resources which are treated as sensitive. Additionally, there's an unresolved comment noting that the type change to nextPayment.amount may constitute a breaking API change requiring a major version bump rather than minor.

You can customize Macroscope's approvability policy. Learn more.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/backend/src/api/resources/CommerceSubscriptionItem.ts (1)

38-44: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the backend API documentation for the new payment shape.

nextPayment.amount is now BillingMoneyAmount and includes perUnitTotals/totals, while clerk-docs still documents the older { amount, date } shape. Update the public documentation with this API change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts` around lines
38 - 44, Update the public clerk-docs documentation for the nextPayment API
shape to match CommerceSubscriptionItem: document amount as BillingMoneyAmount
and include the optional perUnitTotals and totals fields alongside date. Replace
the older { amount, date } representation without changing unrelated
subscription documentation.

Sources: Coding guidelines, Linked repositories

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts`:
- Around line 38-44: Update the public clerk-docs documentation for the
nextPayment API shape to match CommerceSubscriptionItem: document amount as
BillingMoneyAmount and include the optional perUnitTotals and totals fields
alongside date. Replace the older { amount, date } representation without
changing unrelated subscription documentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: e7a1b51e-6fc9-445d-a608-d21220fb711f

📥 Commits

Reviewing files that changed from the base of the PR and between 1cbd099 and d40a9c3.

📒 Files selected for processing (1)
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)

@dstaley
dstaley merged commit fe6ee54 into mainJul 30, 2026
55 checks passed
@dstaley
dstaley deleted the ds.fix/backend-sub-subitem branch July 30, 2026 18:13
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dstaley@mauricioabreu
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(backend): Update Subscription and SubscriptionItem resources - #9196

Merged
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem
Jul 30, 2026
Merged

fix(backend): Update Subscription and SubscriptionItem resources#9196
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem

Conversation

@dstaley

@dstaleydstaley commented Jul 17, 2026

Copy link
Copy Markdown
Member

Description

This PR updates the parsing logic for BillingSubscription and BillingSubscriptionItem in the @clerk/backend package to align with the latest response shapes for the Backend API.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@vercel

vercelBot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 17, 2026 9:02pm
swingsetReadyReadyPreview, CommentJul 17, 2026 9:02pm

Request Review

@changeset-bot

changeset-botBot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d40a9c3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendMinor
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Billing subscription JSON contracts now include instance, pricing, totals, and seat fields. Shared billing converters normalize nested billing data, and subscription resources expose the converted structures through updated factories and constructors.

Changes

LayerSummary
Billing JSON contractsSubscription and subscription-item JSON types add instance identifiers and structured payment, pricing, lifetime-payment, and seat fields.
Billing conversion helpersNew helpers convert billing amounts, totals, credits, discounts, period totals, and seat breakdowns into typed objects.
Subscription resource parsingSubscription resources normalize the expanded billing data through updated constructors and fromJSON factories. A minor backend changeset was added.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:alexisintech

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the backend changes to Subscription and SubscriptionItem resources.
Description check✅ PassedThe description matches the PR by explaining the backend parsing updates for BillingSubscription and BillingSubscriptionItem.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/tame-bats-tell.md:
- Around line 2-5: Update the changeset entry for `@clerk/backend` from minor to
major to reflect the breaking BillingSubscription and BillingSubscriptionItem
API changes, and add migration guidance covering the constructor shape and
nextPayment.amount type updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 544eaf57-70d6-4fc0-820a-de60667aa155

📥 Commits

Reviewing files that changed from the base of the PR and between 26530ce and 1cbd099.

📒 Files selected for processing (5)
  • .changeset/tame-bats-tell.md
  • packages/backend/src/api/resources/CommerceSubscription.ts
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
  • packages/backend/src/api/resources/JSON.ts
  • packages/backend/src/util/billing.ts

Comment on lines +2 to +5
'@clerk/backend': minor
---

Update fields for BillingSubscription and BillingSubscriptionItem

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
cat packages/backend/package.json
rg -n -C 3 '\b(BillingSubscription|BillingSubscriptionItem)\b' packages/backend/src

Repository: clerk/javascript

Length of output: 24693


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscription.ts
printf'\n---\n'
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscriptionItem.ts
printf'\n---\n'
sed -n '140,190p' packages/backend/src/index.ts
printf'\n---\n'
sed -n '1,80p' .changeset/tame-bats-tell.md

Repository: clerk/javascript

Length of output: 10343


Require a major changeset for the Billing API surfaceBillingSubscription and BillingSubscriptionItem are exported from @clerk/backend, so the constructor shape and nextPayment.amount type change are breaking API changes. Update this changeset to major and add migration guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.changeset/tame-bats-tell.md around lines 2 - 5, Update the changeset entry
for `@clerk/backend` from minor to major to reflect the breaking
BillingSubscription and BillingSubscriptionItem API changes, and add migration
guidance covering the constructor shape and nextPayment.amount type updates.

Source: Coding guidelines

@pkg-pr-new

pkg-pr-newBot commented Jul 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9196

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9196

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9196

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9196

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9196

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9196

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9196

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9196

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9196

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9196

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9196

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9196

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9196

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9196

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9196

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9196

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9196

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9196

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9196

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9196

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9196

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9196

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9196

commit: d40a9c3

@github-actions

github-actionsBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-17T21:05:11.397Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes7
🟡 Non-breaking changes3
🟢 Additions8

Warning
7 breaking change(s) detected - Major version bump required

🤖 This report was reviewed by claude-sonnet-4-6.

🔴 Breaking changes index (7)

Every breaking change, up front. Full diffs are in the package sections below.

PackageSubpathChange
@clerk/backend.BillingSubscription.undefined
@clerk/backend.BillingSubscriptionItem.undefined
@clerk/backend.BillingSubscriptionItem.isFreeTrial
@clerk/backend.BillingSubscriptionItem.nextPayment
@clerk/backend.BillingSubscriptionItemJSON.amount
@clerk/backend.BillingSubscriptionItemJSON.is_free_trial
@clerk/backend.BillingSubscriptionItemJSON.next_payment

@clerk/backend

Current version: 3.11.7
Recommended bump: MAJOR → 4.0.0

🔴 Breaking Changes (7)

Changed: BillingSubscription.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionJSON['status'], payerId: string, createdAt: number, updatedAt: number, activeAt: number | null, pastDueAt: number | null, subscriptionItems: BillingSubscriptionItem[], nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null, eligibleForFreeTrial: boolean);

Static analyzer: Breaking change in constructor BillingSubscription.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionJSON['status']string; Parameter payerId type changed: stringimport("@clerk/backend").BillingSubscriptionJSON['status']; Parameter createdAt type changed: numberstring; Parameter activeAt type changed: null|numbernumber; Parameter subscriptionItems type changed: import("@clerk/backend").BillingSubscriptionItem[]null|number; Parameter nextPayment type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}import("@clerk/backend").BillingSubscriptionItem[]; Parameter eligibleForFreeTrial type changed: booleannull|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Required parameter eligibleForFreeTrial was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscription constructor, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscription(...) directly.

Migration: Add the new instanceId: string argument as the second parameter when constructing BillingSubscription instances.

Changed: BillingSubscriptionItem.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionItemJSON['status'], planPeriod: 'month' | 'annual', periodStart: number, nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined, amount: BillingMoneyAmount | undefined, plan: BillingPlan | null, planId: string | null, + priceId: string | null, 
createdAt: number, updatedAt: number, periodEnd: number | null, canceledAt: number | null, pastDueAt: number | null, endedAt: number | null, payerId: string | undefined, - isFreeTrial?: boolean | undefined, - lifetimePaid?: BillingMoneyAmount | undefined);+ isFreeTrial: boolean, + lifetimePaid?: BillingMoneyAmount | undefined, + seats?: BillingSubscriptionItemSeats | undefined);

Static analyzer: Breaking change in constructor BillingSubscriptionItem.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionItemJSON['status']string; Parameter planPeriod type changed: 'annual'|'month'import("@clerk/backend").BillingSubscriptionItemJSON['status']; Parameter periodStart type changed: number'annual'|'month'; Parameter nextPayment type changed: null|undefined|{amount:number;date:number;}number; Parameter amount type changed: import("@clerk/shared").BillingMoneyAmount|undefinednull|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Parameter plan type changed: import("@clerk/backend").BillingPlan|nullimport("@clerk/shared").BillingMoneyAmount|undefined; Parameter planId type changed: null|stringimport("@clerk/backend").BillingPlan|null; Parameter createdAt type changed: numbernull|string; Parameter updatedAt type changed: numbernull|string; Parameter periodEnd type changed: null|numbernumber; Parameter canceledAt type changed: null|numbernumber; Parameter payerId type changed: string|undefinednull|number; Parameter isFreeTrial is no longer optional; Parameter isFreeTrial type changed: boolean|undefinednull|number; Parameter lifetimePaid is no longer optional; Parameter lifetimePaid type changed: import("@clerk/shared").BillingMoneyAmount|undefinedstring|undefined; Required parameter isFreeTrial was added; Optional parameter lifetimePaid was added; Optional parameter seats was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscriptionItem constructor, and a new required parameter priceId was also inserted later, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscriptionItem(...) directly.

Migration: Add instanceId: string as the second argument and priceId: string | null after planId when constructing BillingSubscriptionItem instances.

Changed: BillingSubscriptionItem.isFreeTrial

- readonly isFreeTrial?: boolean | undefined;+ readonly isFreeTrial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItem.isFreeTrial: Member is no longer optional; Type changed: boolean|undefinedboolean

🤖 AI review (confirmed) (85%): Making isFreeTrial a required (non-optional) property on the BillingSubscriptionItem class narrows the type; consumers who previously typed this as boolean | undefined and assigned undefined will now fail, and code relying on the optional signature breaks.

Migration: Ensure all code that reads or assigns isFreeTrial treats it as always boolean, removing any undefined branches.

Changed: BillingSubscriptionItem.nextPayment

 readonly nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined;

Static analyzer: Breaking change in property BillingSubscriptionItem.nextPayment: Type changed: null|undefined|{amount:number;date:number;}null|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").Bi…

🤖 AI review (confirmed) (85%): The amount field in the nextPayment object changed from number to BillingMoneyAmount; consumers that previously read nextPayment.amount as a number will have a type mismatch.

Migration: Update consumer code to treat nextPayment.amount as a BillingMoneyAmount object rather than a plain number.

Changed: BillingSubscriptionItemJSON.amount

- amount: BillingMoneyAmountJSON;+ amount?: BillingMoneyAmountJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.amount: Member became optional; Type changed: import("@clerk/backend").~BillingMoneyAmountJSONimport("@clerk/backend").~BillingMoneyAmountJSON|null

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.amount changed from a required BillingMoneyAmountJSON to an optional BillingMoneyAmountJSON | null; consumers constructing objects of this interface that provided the required field now encounter type mismatches when checking optionality, and consumers reading it must now handle null/undefined.

Migration: Update consumer code that reads amount from BillingSubscriptionItemJSON to handle null | undefined cases.

Changed: BillingSubscriptionItemJSON.is_free_trial

- is_free_trial?: boolean;+ is_free_trial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.is_free_trial: Member is no longer optional

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.is_free_trial changed from optional to required; consumers constructing objects of this interface that omitted this field will now fail to compile.

Migration: Add a required is_free_trial: boolean field when constructing objects that conform to BillingSubscriptionItemJSON.

Changed: BillingSubscriptionItemJSON.next_payment

- next_payment?: {- amount: number;- date: number;- } | null;+ next_payment?: BillingSubscriptionItemNextPaymentJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.next_payment: Type changed: null|{amount:number;date:number;}import("@clerk/shared").BillingSubscriptionItemNextPaymentJSON|null

🤖 AI review (confirmed) (80%): The inline type { amount: number; date: number } was replaced by BillingSubscriptionItemNextPaymentJSON, which changes the shape of amount from number to a structured type; consumers reading or constructing this field must update their code.

Migration: Update code that reads next_payment.amount from BillingSubscriptionItemJSON to handle the new BillingSubscriptionItemNextPaymentJSON shape instead of a plain number.

🟡 Non-breaking Changes (3)

Modified: BillingSubscription.nextPayment

 readonly nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null;

Static analyzer: Breaking change in property BillingSubscription.nextPayment: Type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUn…

🤖 AI review (reclassified as non-breaking) (90%): The nextPayment property on BillingSubscription only gains two new optional fields (perUnitTotals? and totals?); existing consumers reading the property still see a compatible shape (the existing fields are unchanged), and since this is a readonly output property no consumer is required to supply those fields.

Modified: BillingSubscriptionItemJSON.lifetime_paid

- lifetime_paid: BillingMoneyAmountJSON | null;+ lifetime_paid?: BillingMoneyAmountJSON | null;

Static analyzer: Modified property BillingSubscriptionItemJSON.lifetime_paid: Member became optional

🤖 AI review (confirmed) (95%): Making lifetime_paid optional in the interface is non-breaking: consumers reading it already had to handle null, and consumers constructing this object type can now omit the field rather than being required to provide it.

Modified: BillingSubscriptionJSON.next_payment

 next_payment?: {
date: number;
amount: BillingMoneyAmountJSON;
+ per_unit_totals?: BillingPerUnitTotalJSON[];+ totals?: SharedBillingTotalsJSON;
};

Static analyzer: Breaking change in property BillingSubscriptionJSON.next_payment: Type changed: {date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;}{date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;per_unit_totals?:import("@clerk/shared").BillingPe…

🤖 AI review (reclassified as non-breaking) (90%): The next_payment object in BillingSubscriptionJSON only gains new optional fields (per_unit_totals? and totals?); existing consumers neither need to supply those fields when constructing nor are required to handle them when reading, so no well-typed consumer is broken.

🟢 Additions (8)

Added: BillingSubscription.instanceId

+ readonly instanceId: string;

Added property BillingSubscription.instanceId

Added: BillingSubscriptionItem.instanceId

+ readonly instanceId: string;

Added property BillingSubscriptionItem.instanceId

Added: BillingSubscriptionItem.priceId

+ readonly priceId: string | null;

Added property BillingSubscriptionItem.priceId

Added: BillingSubscriptionItem.seats

+ readonly seats?: BillingSubscriptionItemSeats | undefined;

Added property BillingSubscriptionItem.seats

Added: BillingSubscriptionItemJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionItemJSON.instance_id

Added: BillingSubscriptionItemJSON.price_id

+ price_id?: string;

Added property BillingSubscriptionItemJSON.price_id

Added: BillingSubscriptionItemJSON.seats

+ seats?: BillingSubscriptionItemSeatsJSON;

Added property BillingSubscriptionItemJSON.seats

Added: BillingSubscriptionJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionJSON.instance_id


Report generated by Break Check

Last ran on d40a9c3.

@dstaley
dstaley requested a review from a teamJuly 17, 2026 20:47
@dstaley

Copy link
Copy Markdown
MemberAuthor

@macroscope-app review

@macroscopeapp

Copy link
Copy Markdown

Manual reviews triggered for commit d40a9c3:

All prior checks · these links stay valid even if you push more commits.

@macroscopeapp

Copy link
Copy Markdown

Review in progress. Results will be posted as check runs when complete.

@macroscopeapp

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

This PR modifies billing-related resources which are treated as sensitive. Additionally, there's an unresolved comment noting that the type change to nextPayment.amount may constitute a breaking API change requiring a major version bump rather than minor.

You can customize Macroscope's approvability policy. Learn more.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/backend/src/api/resources/CommerceSubscriptionItem.ts (1)

38-44: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the backend API documentation for the new payment shape.

nextPayment.amount is now BillingMoneyAmount and includes perUnitTotals/totals, while clerk-docs still documents the older { amount, date } shape. Update the public documentation with this API change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts` around lines
38 - 44, Update the public clerk-docs documentation for the nextPayment API
shape to match CommerceSubscriptionItem: document amount as BillingMoneyAmount
and include the optional perUnitTotals and totals fields alongside date. Replace
the older { amount, date } representation without changing unrelated
subscription documentation.

Sources: Coding guidelines, Linked repositories

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts`:
- Around line 38-44: Update the public clerk-docs documentation for the
nextPayment API shape to match CommerceSubscriptionItem: document amount as
BillingMoneyAmount and include the optional perUnitTotals and totals fields
alongside date. Replace the older { amount, date } representation without
changing unrelated subscription documentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: e7a1b51e-6fc9-445d-a608-d21220fb711f

📥 Commits

Reviewing files that changed from the base of the PR and between 1cbd099 and d40a9c3.

📒 Files selected for processing (1)
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)

@dstaley
dstaley merged commit fe6ee54 into mainJul 30, 2026
55 checks passed
@dstaley
dstaley deleted the ds.fix/backend-sub-subitem branch July 30, 2026 18:13
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dstaley@mauricioabreu
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(backend): Update Subscription and SubscriptionItem resources - #9196

Merged
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem
Jul 30, 2026
Merged

fix(backend): Update Subscription and SubscriptionItem resources#9196
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem

Conversation

@dstaley

@dstaleydstaley commented Jul 17, 2026

Copy link
Copy Markdown
Member

Description

This PR updates the parsing logic for BillingSubscription and BillingSubscriptionItem in the @clerk/backend package to align with the latest response shapes for the Backend API.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@vercel

vercelBot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 17, 2026 9:02pm
swingsetReadyReadyPreview, CommentJul 17, 2026 9:02pm

Request Review

@changeset-bot

changeset-botBot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d40a9c3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendMinor
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Billing subscription JSON contracts now include instance, pricing, totals, and seat fields. Shared billing converters normalize nested billing data, and subscription resources expose the converted structures through updated factories and constructors.

Changes

LayerSummary
Billing JSON contractsSubscription and subscription-item JSON types add instance identifiers and structured payment, pricing, lifetime-payment, and seat fields.
Billing conversion helpersNew helpers convert billing amounts, totals, credits, discounts, period totals, and seat breakdowns into typed objects.
Subscription resource parsingSubscription resources normalize the expanded billing data through updated constructors and fromJSON factories. A minor backend changeset was added.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:alexisintech

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the backend changes to Subscription and SubscriptionItem resources.
Description check✅ PassedThe description matches the PR by explaining the backend parsing updates for BillingSubscription and BillingSubscriptionItem.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/tame-bats-tell.md:
- Around line 2-5: Update the changeset entry for `@clerk/backend` from minor to
major to reflect the breaking BillingSubscription and BillingSubscriptionItem
API changes, and add migration guidance covering the constructor shape and
nextPayment.amount type updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 544eaf57-70d6-4fc0-820a-de60667aa155

📥 Commits

Reviewing files that changed from the base of the PR and between 26530ce and 1cbd099.

📒 Files selected for processing (5)
  • .changeset/tame-bats-tell.md
  • packages/backend/src/api/resources/CommerceSubscription.ts
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
  • packages/backend/src/api/resources/JSON.ts
  • packages/backend/src/util/billing.ts

Comment on lines +2 to +5
'@clerk/backend': minor
---

Update fields for BillingSubscription and BillingSubscriptionItem

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
cat packages/backend/package.json
rg -n -C 3 '\b(BillingSubscription|BillingSubscriptionItem)\b' packages/backend/src

Repository: clerk/javascript

Length of output: 24693


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscription.ts
printf'\n---\n'
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscriptionItem.ts
printf'\n---\n'
sed -n '140,190p' packages/backend/src/index.ts
printf'\n---\n'
sed -n '1,80p' .changeset/tame-bats-tell.md

Repository: clerk/javascript

Length of output: 10343


Require a major changeset for the Billing API surfaceBillingSubscription and BillingSubscriptionItem are exported from @clerk/backend, so the constructor shape and nextPayment.amount type change are breaking API changes. Update this changeset to major and add migration guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.changeset/tame-bats-tell.md around lines 2 - 5, Update the changeset entry
for `@clerk/backend` from minor to major to reflect the breaking
BillingSubscription and BillingSubscriptionItem API changes, and add migration
guidance covering the constructor shape and nextPayment.amount type updates.

Source: Coding guidelines

@pkg-pr-new

pkg-pr-newBot commented Jul 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9196

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9196

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9196

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9196

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9196

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9196

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9196

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9196

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9196

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9196

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9196

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9196

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9196

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9196

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9196

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9196

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9196

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9196

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9196

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9196

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9196

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9196

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9196

commit: d40a9c3

@github-actions

github-actionsBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-17T21:05:11.397Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes7
🟡 Non-breaking changes3
🟢 Additions8

Warning
7 breaking change(s) detected - Major version bump required

🤖 This report was reviewed by claude-sonnet-4-6.

🔴 Breaking changes index (7)

Every breaking change, up front. Full diffs are in the package sections below.

PackageSubpathChange
@clerk/backend.BillingSubscription.undefined
@clerk/backend.BillingSubscriptionItem.undefined
@clerk/backend.BillingSubscriptionItem.isFreeTrial
@clerk/backend.BillingSubscriptionItem.nextPayment
@clerk/backend.BillingSubscriptionItemJSON.amount
@clerk/backend.BillingSubscriptionItemJSON.is_free_trial
@clerk/backend.BillingSubscriptionItemJSON.next_payment

@clerk/backend

Current version: 3.11.7
Recommended bump: MAJOR → 4.0.0

🔴 Breaking Changes (7)

Changed: BillingSubscription.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionJSON['status'], payerId: string, createdAt: number, updatedAt: number, activeAt: number | null, pastDueAt: number | null, subscriptionItems: BillingSubscriptionItem[], nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null, eligibleForFreeTrial: boolean);

Static analyzer: Breaking change in constructor BillingSubscription.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionJSON['status']string; Parameter payerId type changed: stringimport("@clerk/backend").BillingSubscriptionJSON['status']; Parameter createdAt type changed: numberstring; Parameter activeAt type changed: null|numbernumber; Parameter subscriptionItems type changed: import("@clerk/backend").BillingSubscriptionItem[]null|number; Parameter nextPayment type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}import("@clerk/backend").BillingSubscriptionItem[]; Parameter eligibleForFreeTrial type changed: booleannull|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Required parameter eligibleForFreeTrial was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscription constructor, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscription(...) directly.

Migration: Add the new instanceId: string argument as the second parameter when constructing BillingSubscription instances.

Changed: BillingSubscriptionItem.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionItemJSON['status'], planPeriod: 'month' | 'annual', periodStart: number, nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined, amount: BillingMoneyAmount | undefined, plan: BillingPlan | null, planId: string | null, + priceId: string | null, 
createdAt: number, updatedAt: number, periodEnd: number | null, canceledAt: number | null, pastDueAt: number | null, endedAt: number | null, payerId: string | undefined, - isFreeTrial?: boolean | undefined, - lifetimePaid?: BillingMoneyAmount | undefined);+ isFreeTrial: boolean, + lifetimePaid?: BillingMoneyAmount | undefined, + seats?: BillingSubscriptionItemSeats | undefined);

Static analyzer: Breaking change in constructor BillingSubscriptionItem.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionItemJSON['status']string; Parameter planPeriod type changed: 'annual'|'month'import("@clerk/backend").BillingSubscriptionItemJSON['status']; Parameter periodStart type changed: number'annual'|'month'; Parameter nextPayment type changed: null|undefined|{amount:number;date:number;}number; Parameter amount type changed: import("@clerk/shared").BillingMoneyAmount|undefinednull|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Parameter plan type changed: import("@clerk/backend").BillingPlan|nullimport("@clerk/shared").BillingMoneyAmount|undefined; Parameter planId type changed: null|stringimport("@clerk/backend").BillingPlan|null; Parameter createdAt type changed: numbernull|string; Parameter updatedAt type changed: numbernull|string; Parameter periodEnd type changed: null|numbernumber; Parameter canceledAt type changed: null|numbernumber; Parameter payerId type changed: string|undefinednull|number; Parameter isFreeTrial is no longer optional; Parameter isFreeTrial type changed: boolean|undefinednull|number; Parameter lifetimePaid is no longer optional; Parameter lifetimePaid type changed: import("@clerk/shared").BillingMoneyAmount|undefinedstring|undefined; Required parameter isFreeTrial was added; Optional parameter lifetimePaid was added; Optional parameter seats was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscriptionItem constructor, and a new required parameter priceId was also inserted later, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscriptionItem(...) directly.

Migration: Add instanceId: string as the second argument and priceId: string | null after planId when constructing BillingSubscriptionItem instances.

Changed: BillingSubscriptionItem.isFreeTrial

- readonly isFreeTrial?: boolean | undefined;+ readonly isFreeTrial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItem.isFreeTrial: Member is no longer optional; Type changed: boolean|undefinedboolean

🤖 AI review (confirmed) (85%): Making isFreeTrial a required (non-optional) property on the BillingSubscriptionItem class narrows the type; consumers who previously typed this as boolean | undefined and assigned undefined will now fail, and code relying on the optional signature breaks.

Migration: Ensure all code that reads or assigns isFreeTrial treats it as always boolean, removing any undefined branches.

Changed: BillingSubscriptionItem.nextPayment

 readonly nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined;

Static analyzer: Breaking change in property BillingSubscriptionItem.nextPayment: Type changed: null|undefined|{amount:number;date:number;}null|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").Bi…

🤖 AI review (confirmed) (85%): The amount field in the nextPayment object changed from number to BillingMoneyAmount; consumers that previously read nextPayment.amount as a number will have a type mismatch.

Migration: Update consumer code to treat nextPayment.amount as a BillingMoneyAmount object rather than a plain number.

Changed: BillingSubscriptionItemJSON.amount

- amount: BillingMoneyAmountJSON;+ amount?: BillingMoneyAmountJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.amount: Member became optional; Type changed: import("@clerk/backend").~BillingMoneyAmountJSONimport("@clerk/backend").~BillingMoneyAmountJSON|null

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.amount changed from a required BillingMoneyAmountJSON to an optional BillingMoneyAmountJSON | null; consumers constructing objects of this interface that provided the required field now encounter type mismatches when checking optionality, and consumers reading it must now handle null/undefined.

Migration: Update consumer code that reads amount from BillingSubscriptionItemJSON to handle null | undefined cases.

Changed: BillingSubscriptionItemJSON.is_free_trial

- is_free_trial?: boolean;+ is_free_trial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.is_free_trial: Member is no longer optional

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.is_free_trial changed from optional to required; consumers constructing objects of this interface that omitted this field will now fail to compile.

Migration: Add a required is_free_trial: boolean field when constructing objects that conform to BillingSubscriptionItemJSON.

Changed: BillingSubscriptionItemJSON.next_payment

- next_payment?: {- amount: number;- date: number;- } | null;+ next_payment?: BillingSubscriptionItemNextPaymentJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.next_payment: Type changed: null|{amount:number;date:number;}import("@clerk/shared").BillingSubscriptionItemNextPaymentJSON|null

🤖 AI review (confirmed) (80%): The inline type { amount: number; date: number } was replaced by BillingSubscriptionItemNextPaymentJSON, which changes the shape of amount from number to a structured type; consumers reading or constructing this field must update their code.

Migration: Update code that reads next_payment.amount from BillingSubscriptionItemJSON to handle the new BillingSubscriptionItemNextPaymentJSON shape instead of a plain number.

🟡 Non-breaking Changes (3)

Modified: BillingSubscription.nextPayment

 readonly nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null;

Static analyzer: Breaking change in property BillingSubscription.nextPayment: Type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUn…

🤖 AI review (reclassified as non-breaking) (90%): The nextPayment property on BillingSubscription only gains two new optional fields (perUnitTotals? and totals?); existing consumers reading the property still see a compatible shape (the existing fields are unchanged), and since this is a readonly output property no consumer is required to supply those fields.

Modified: BillingSubscriptionItemJSON.lifetime_paid

- lifetime_paid: BillingMoneyAmountJSON | null;+ lifetime_paid?: BillingMoneyAmountJSON | null;

Static analyzer: Modified property BillingSubscriptionItemJSON.lifetime_paid: Member became optional

🤖 AI review (confirmed) (95%): Making lifetime_paid optional in the interface is non-breaking: consumers reading it already had to handle null, and consumers constructing this object type can now omit the field rather than being required to provide it.

Modified: BillingSubscriptionJSON.next_payment

 next_payment?: {
date: number;
amount: BillingMoneyAmountJSON;
+ per_unit_totals?: BillingPerUnitTotalJSON[];+ totals?: SharedBillingTotalsJSON;
};

Static analyzer: Breaking change in property BillingSubscriptionJSON.next_payment: Type changed: {date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;}{date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;per_unit_totals?:import("@clerk/shared").BillingPe…

🤖 AI review (reclassified as non-breaking) (90%): The next_payment object in BillingSubscriptionJSON only gains new optional fields (per_unit_totals? and totals?); existing consumers neither need to supply those fields when constructing nor are required to handle them when reading, so no well-typed consumer is broken.

🟢 Additions (8)

Added: BillingSubscription.instanceId

+ readonly instanceId: string;

Added property BillingSubscription.instanceId

Added: BillingSubscriptionItem.instanceId

+ readonly instanceId: string;

Added property BillingSubscriptionItem.instanceId

Added: BillingSubscriptionItem.priceId

+ readonly priceId: string | null;

Added property BillingSubscriptionItem.priceId

Added: BillingSubscriptionItem.seats

+ readonly seats?: BillingSubscriptionItemSeats | undefined;

Added property BillingSubscriptionItem.seats

Added: BillingSubscriptionItemJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionItemJSON.instance_id

Added: BillingSubscriptionItemJSON.price_id

+ price_id?: string;

Added property BillingSubscriptionItemJSON.price_id

Added: BillingSubscriptionItemJSON.seats

+ seats?: BillingSubscriptionItemSeatsJSON;

Added property BillingSubscriptionItemJSON.seats

Added: BillingSubscriptionJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionJSON.instance_id


Report generated by Break Check

Last ran on d40a9c3.

@dstaley
dstaley requested a review from a teamJuly 17, 2026 20:47
@dstaley

Copy link
Copy Markdown
MemberAuthor

@macroscope-app review

@macroscopeapp

Copy link
Copy Markdown

Manual reviews triggered for commit d40a9c3:

All prior checks · these links stay valid even if you push more commits.

@macroscopeapp

Copy link
Copy Markdown

Review in progress. Results will be posted as check runs when complete.

@macroscopeapp

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

This PR modifies billing-related resources which are treated as sensitive. Additionally, there's an unresolved comment noting that the type change to nextPayment.amount may constitute a breaking API change requiring a major version bump rather than minor.

You can customize Macroscope's approvability policy. Learn more.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/backend/src/api/resources/CommerceSubscriptionItem.ts (1)

38-44: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the backend API documentation for the new payment shape.

nextPayment.amount is now BillingMoneyAmount and includes perUnitTotals/totals, while clerk-docs still documents the older { amount, date } shape. Update the public documentation with this API change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts` around lines
38 - 44, Update the public clerk-docs documentation for the nextPayment API
shape to match CommerceSubscriptionItem: document amount as BillingMoneyAmount
and include the optional perUnitTotals and totals fields alongside date. Replace
the older { amount, date } representation without changing unrelated
subscription documentation.

Sources: Coding guidelines, Linked repositories

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts`:
- Around line 38-44: Update the public clerk-docs documentation for the
nextPayment API shape to match CommerceSubscriptionItem: document amount as
BillingMoneyAmount and include the optional perUnitTotals and totals fields
alongside date. Replace the older { amount, date } representation without
changing unrelated subscription documentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: e7a1b51e-6fc9-445d-a608-d21220fb711f

📥 Commits

Reviewing files that changed from the base of the PR and between 1cbd099 and d40a9c3.

📒 Files selected for processing (1)
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)

@dstaley
dstaley merged commit fe6ee54 into mainJul 30, 2026
55 checks passed
@dstaley
dstaley deleted the ds.fix/backend-sub-subitem branch July 30, 2026 18:13
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dstaley@mauricioabreu
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(backend): Update Subscription and SubscriptionItem resources - #9196

Merged
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem
Jul 30, 2026
Merged

fix(backend): Update Subscription and SubscriptionItem resources#9196
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem

Conversation

@dstaley

@dstaleydstaley commented Jul 17, 2026

Copy link
Copy Markdown
Member

Description

This PR updates the parsing logic for BillingSubscription and BillingSubscriptionItem in the @clerk/backend package to align with the latest response shapes for the Backend API.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@vercel

vercelBot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 17, 2026 9:02pm
swingsetReadyReadyPreview, CommentJul 17, 2026 9:02pm

Request Review

@changeset-bot

changeset-botBot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d40a9c3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendMinor
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Billing subscription JSON contracts now include instance, pricing, totals, and seat fields. Shared billing converters normalize nested billing data, and subscription resources expose the converted structures through updated factories and constructors.

Changes

LayerSummary
Billing JSON contractsSubscription and subscription-item JSON types add instance identifiers and structured payment, pricing, lifetime-payment, and seat fields.
Billing conversion helpersNew helpers convert billing amounts, totals, credits, discounts, period totals, and seat breakdowns into typed objects.
Subscription resource parsingSubscription resources normalize the expanded billing data through updated constructors and fromJSON factories. A minor backend changeset was added.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:alexisintech

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the backend changes to Subscription and SubscriptionItem resources.
Description check✅ PassedThe description matches the PR by explaining the backend parsing updates for BillingSubscription and BillingSubscriptionItem.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/tame-bats-tell.md:
- Around line 2-5: Update the changeset entry for `@clerk/backend` from minor to
major to reflect the breaking BillingSubscription and BillingSubscriptionItem
API changes, and add migration guidance covering the constructor shape and
nextPayment.amount type updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 544eaf57-70d6-4fc0-820a-de60667aa155

📥 Commits

Reviewing files that changed from the base of the PR and between 26530ce and 1cbd099.

📒 Files selected for processing (5)
  • .changeset/tame-bats-tell.md
  • packages/backend/src/api/resources/CommerceSubscription.ts
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
  • packages/backend/src/api/resources/JSON.ts
  • packages/backend/src/util/billing.ts

Comment on lines +2 to +5
'@clerk/backend': minor
---

Update fields for BillingSubscription and BillingSubscriptionItem

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
cat packages/backend/package.json
rg -n -C 3 '\b(BillingSubscription|BillingSubscriptionItem)\b' packages/backend/src

Repository: clerk/javascript

Length of output: 24693


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscription.ts
printf'\n---\n'
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscriptionItem.ts
printf'\n---\n'
sed -n '140,190p' packages/backend/src/index.ts
printf'\n---\n'
sed -n '1,80p' .changeset/tame-bats-tell.md

Repository: clerk/javascript

Length of output: 10343


Require a major changeset for the Billing API surfaceBillingSubscription and BillingSubscriptionItem are exported from @clerk/backend, so the constructor shape and nextPayment.amount type change are breaking API changes. Update this changeset to major and add migration guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.changeset/tame-bats-tell.md around lines 2 - 5, Update the changeset entry
for `@clerk/backend` from minor to major to reflect the breaking
BillingSubscription and BillingSubscriptionItem API changes, and add migration
guidance covering the constructor shape and nextPayment.amount type updates.

Source: Coding guidelines

@pkg-pr-new

pkg-pr-newBot commented Jul 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9196

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9196

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9196

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9196

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9196

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9196

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9196

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9196

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9196

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9196

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9196

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9196

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9196

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9196

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9196

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9196

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9196

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9196

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9196

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9196

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9196

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9196

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9196

commit: d40a9c3

@github-actions

github-actionsBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-17T21:05:11.397Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes7
🟡 Non-breaking changes3
🟢 Additions8

Warning
7 breaking change(s) detected - Major version bump required

🤖 This report was reviewed by claude-sonnet-4-6.

🔴 Breaking changes index (7)

Every breaking change, up front. Full diffs are in the package sections below.

PackageSubpathChange
@clerk/backend.BillingSubscription.undefined
@clerk/backend.BillingSubscriptionItem.undefined
@clerk/backend.BillingSubscriptionItem.isFreeTrial
@clerk/backend.BillingSubscriptionItem.nextPayment
@clerk/backend.BillingSubscriptionItemJSON.amount
@clerk/backend.BillingSubscriptionItemJSON.is_free_trial
@clerk/backend.BillingSubscriptionItemJSON.next_payment

@clerk/backend

Current version: 3.11.7
Recommended bump: MAJOR → 4.0.0

🔴 Breaking Changes (7)

Changed: BillingSubscription.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionJSON['status'], payerId: string, createdAt: number, updatedAt: number, activeAt: number | null, pastDueAt: number | null, subscriptionItems: BillingSubscriptionItem[], nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null, eligibleForFreeTrial: boolean);

Static analyzer: Breaking change in constructor BillingSubscription.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionJSON['status']string; Parameter payerId type changed: stringimport("@clerk/backend").BillingSubscriptionJSON['status']; Parameter createdAt type changed: numberstring; Parameter activeAt type changed: null|numbernumber; Parameter subscriptionItems type changed: import("@clerk/backend").BillingSubscriptionItem[]null|number; Parameter nextPayment type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}import("@clerk/backend").BillingSubscriptionItem[]; Parameter eligibleForFreeTrial type changed: booleannull|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Required parameter eligibleForFreeTrial was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscription constructor, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscription(...) directly.

Migration: Add the new instanceId: string argument as the second parameter when constructing BillingSubscription instances.

Changed: BillingSubscriptionItem.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionItemJSON['status'], planPeriod: 'month' | 'annual', periodStart: number, nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined, amount: BillingMoneyAmount | undefined, plan: BillingPlan | null, planId: string | null, + priceId: string | null, 
createdAt: number, updatedAt: number, periodEnd: number | null, canceledAt: number | null, pastDueAt: number | null, endedAt: number | null, payerId: string | undefined, - isFreeTrial?: boolean | undefined, - lifetimePaid?: BillingMoneyAmount | undefined);+ isFreeTrial: boolean, + lifetimePaid?: BillingMoneyAmount | undefined, + seats?: BillingSubscriptionItemSeats | undefined);

Static analyzer: Breaking change in constructor BillingSubscriptionItem.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionItemJSON['status']string; Parameter planPeriod type changed: 'annual'|'month'import("@clerk/backend").BillingSubscriptionItemJSON['status']; Parameter periodStart type changed: number'annual'|'month'; Parameter nextPayment type changed: null|undefined|{amount:number;date:number;}number; Parameter amount type changed: import("@clerk/shared").BillingMoneyAmount|undefinednull|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Parameter plan type changed: import("@clerk/backend").BillingPlan|nullimport("@clerk/shared").BillingMoneyAmount|undefined; Parameter planId type changed: null|stringimport("@clerk/backend").BillingPlan|null; Parameter createdAt type changed: numbernull|string; Parameter updatedAt type changed: numbernull|string; Parameter periodEnd type changed: null|numbernumber; Parameter canceledAt type changed: null|numbernumber; Parameter payerId type changed: string|undefinednull|number; Parameter isFreeTrial is no longer optional; Parameter isFreeTrial type changed: boolean|undefinednull|number; Parameter lifetimePaid is no longer optional; Parameter lifetimePaid type changed: import("@clerk/shared").BillingMoneyAmount|undefinedstring|undefined; Required parameter isFreeTrial was added; Optional parameter lifetimePaid was added; Optional parameter seats was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscriptionItem constructor, and a new required parameter priceId was also inserted later, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscriptionItem(...) directly.

Migration: Add instanceId: string as the second argument and priceId: string | null after planId when constructing BillingSubscriptionItem instances.

Changed: BillingSubscriptionItem.isFreeTrial

- readonly isFreeTrial?: boolean | undefined;+ readonly isFreeTrial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItem.isFreeTrial: Member is no longer optional; Type changed: boolean|undefinedboolean

🤖 AI review (confirmed) (85%): Making isFreeTrial a required (non-optional) property on the BillingSubscriptionItem class narrows the type; consumers who previously typed this as boolean | undefined and assigned undefined will now fail, and code relying on the optional signature breaks.

Migration: Ensure all code that reads or assigns isFreeTrial treats it as always boolean, removing any undefined branches.

Changed: BillingSubscriptionItem.nextPayment

 readonly nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined;

Static analyzer: Breaking change in property BillingSubscriptionItem.nextPayment: Type changed: null|undefined|{amount:number;date:number;}null|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").Bi…

🤖 AI review (confirmed) (85%): The amount field in the nextPayment object changed from number to BillingMoneyAmount; consumers that previously read nextPayment.amount as a number will have a type mismatch.

Migration: Update consumer code to treat nextPayment.amount as a BillingMoneyAmount object rather than a plain number.

Changed: BillingSubscriptionItemJSON.amount

- amount: BillingMoneyAmountJSON;+ amount?: BillingMoneyAmountJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.amount: Member became optional; Type changed: import("@clerk/backend").~BillingMoneyAmountJSONimport("@clerk/backend").~BillingMoneyAmountJSON|null

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.amount changed from a required BillingMoneyAmountJSON to an optional BillingMoneyAmountJSON | null; consumers constructing objects of this interface that provided the required field now encounter type mismatches when checking optionality, and consumers reading it must now handle null/undefined.

Migration: Update consumer code that reads amount from BillingSubscriptionItemJSON to handle null | undefined cases.

Changed: BillingSubscriptionItemJSON.is_free_trial

- is_free_trial?: boolean;+ is_free_trial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.is_free_trial: Member is no longer optional

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.is_free_trial changed from optional to required; consumers constructing objects of this interface that omitted this field will now fail to compile.

Migration: Add a required is_free_trial: boolean field when constructing objects that conform to BillingSubscriptionItemJSON.

Changed: BillingSubscriptionItemJSON.next_payment

- next_payment?: {- amount: number;- date: number;- } | null;+ next_payment?: BillingSubscriptionItemNextPaymentJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.next_payment: Type changed: null|{amount:number;date:number;}import("@clerk/shared").BillingSubscriptionItemNextPaymentJSON|null

🤖 AI review (confirmed) (80%): The inline type { amount: number; date: number } was replaced by BillingSubscriptionItemNextPaymentJSON, which changes the shape of amount from number to a structured type; consumers reading or constructing this field must update their code.

Migration: Update code that reads next_payment.amount from BillingSubscriptionItemJSON to handle the new BillingSubscriptionItemNextPaymentJSON shape instead of a plain number.

🟡 Non-breaking Changes (3)

Modified: BillingSubscription.nextPayment

 readonly nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null;

Static analyzer: Breaking change in property BillingSubscription.nextPayment: Type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUn…

🤖 AI review (reclassified as non-breaking) (90%): The nextPayment property on BillingSubscription only gains two new optional fields (perUnitTotals? and totals?); existing consumers reading the property still see a compatible shape (the existing fields are unchanged), and since this is a readonly output property no consumer is required to supply those fields.

Modified: BillingSubscriptionItemJSON.lifetime_paid

- lifetime_paid: BillingMoneyAmountJSON | null;+ lifetime_paid?: BillingMoneyAmountJSON | null;

Static analyzer: Modified property BillingSubscriptionItemJSON.lifetime_paid: Member became optional

🤖 AI review (confirmed) (95%): Making lifetime_paid optional in the interface is non-breaking: consumers reading it already had to handle null, and consumers constructing this object type can now omit the field rather than being required to provide it.

Modified: BillingSubscriptionJSON.next_payment

 next_payment?: {
date: number;
amount: BillingMoneyAmountJSON;
+ per_unit_totals?: BillingPerUnitTotalJSON[];+ totals?: SharedBillingTotalsJSON;
};

Static analyzer: Breaking change in property BillingSubscriptionJSON.next_payment: Type changed: {date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;}{date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;per_unit_totals?:import("@clerk/shared").BillingPe…

🤖 AI review (reclassified as non-breaking) (90%): The next_payment object in BillingSubscriptionJSON only gains new optional fields (per_unit_totals? and totals?); existing consumers neither need to supply those fields when constructing nor are required to handle them when reading, so no well-typed consumer is broken.

🟢 Additions (8)

Added: BillingSubscription.instanceId

+ readonly instanceId: string;

Added property BillingSubscription.instanceId

Added: BillingSubscriptionItem.instanceId

+ readonly instanceId: string;

Added property BillingSubscriptionItem.instanceId

Added: BillingSubscriptionItem.priceId

+ readonly priceId: string | null;

Added property BillingSubscriptionItem.priceId

Added: BillingSubscriptionItem.seats

+ readonly seats?: BillingSubscriptionItemSeats | undefined;

Added property BillingSubscriptionItem.seats

Added: BillingSubscriptionItemJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionItemJSON.instance_id

Added: BillingSubscriptionItemJSON.price_id

+ price_id?: string;

Added property BillingSubscriptionItemJSON.price_id

Added: BillingSubscriptionItemJSON.seats

+ seats?: BillingSubscriptionItemSeatsJSON;

Added property BillingSubscriptionItemJSON.seats

Added: BillingSubscriptionJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionJSON.instance_id


Report generated by Break Check

Last ran on d40a9c3.

@dstaley
dstaley requested a review from a teamJuly 17, 2026 20:47
@dstaley

Copy link
Copy Markdown
MemberAuthor

@macroscope-app review

@macroscopeapp

Copy link
Copy Markdown

Manual reviews triggered for commit d40a9c3:

All prior checks · these links stay valid even if you push more commits.

@macroscopeapp

Copy link
Copy Markdown

Review in progress. Results will be posted as check runs when complete.

@macroscopeapp

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

This PR modifies billing-related resources which are treated as sensitive. Additionally, there's an unresolved comment noting that the type change to nextPayment.amount may constitute a breaking API change requiring a major version bump rather than minor.

You can customize Macroscope's approvability policy. Learn more.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/backend/src/api/resources/CommerceSubscriptionItem.ts (1)

38-44: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the backend API documentation for the new payment shape.

nextPayment.amount is now BillingMoneyAmount and includes perUnitTotals/totals, while clerk-docs still documents the older { amount, date } shape. Update the public documentation with this API change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts` around lines
38 - 44, Update the public clerk-docs documentation for the nextPayment API
shape to match CommerceSubscriptionItem: document amount as BillingMoneyAmount
and include the optional perUnitTotals and totals fields alongside date. Replace
the older { amount, date } representation without changing unrelated
subscription documentation.

Sources: Coding guidelines, Linked repositories

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts`:
- Around line 38-44: Update the public clerk-docs documentation for the
nextPayment API shape to match CommerceSubscriptionItem: document amount as
BillingMoneyAmount and include the optional perUnitTotals and totals fields
alongside date. Replace the older { amount, date } representation without
changing unrelated subscription documentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: e7a1b51e-6fc9-445d-a608-d21220fb711f

📥 Commits

Reviewing files that changed from the base of the PR and between 1cbd099 and d40a9c3.

📒 Files selected for processing (1)
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)

@dstaley
dstaley merged commit fe6ee54 into mainJul 30, 2026
55 checks passed
@dstaley
dstaley deleted the ds.fix/backend-sub-subitem branch July 30, 2026 18:13
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dstaley@mauricioabreu
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(backend): Update Subscription and SubscriptionItem resources - #9196

Merged
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem
Jul 30, 2026
Merged

fix(backend): Update Subscription and SubscriptionItem resources#9196
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem

Conversation

@dstaley

@dstaleydstaley commented Jul 17, 2026

Copy link
Copy Markdown
Member

Description

This PR updates the parsing logic for BillingSubscription and BillingSubscriptionItem in the @clerk/backend package to align with the latest response shapes for the Backend API.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@vercel

vercelBot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 17, 2026 9:02pm
swingsetReadyReadyPreview, CommentJul 17, 2026 9:02pm

Request Review

@changeset-bot

changeset-botBot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d40a9c3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendMinor
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Billing subscription JSON contracts now include instance, pricing, totals, and seat fields. Shared billing converters normalize nested billing data, and subscription resources expose the converted structures through updated factories and constructors.

Changes

LayerSummary
Billing JSON contractsSubscription and subscription-item JSON types add instance identifiers and structured payment, pricing, lifetime-payment, and seat fields.
Billing conversion helpersNew helpers convert billing amounts, totals, credits, discounts, period totals, and seat breakdowns into typed objects.
Subscription resource parsingSubscription resources normalize the expanded billing data through updated constructors and fromJSON factories. A minor backend changeset was added.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:alexisintech

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the backend changes to Subscription and SubscriptionItem resources.
Description check✅ PassedThe description matches the PR by explaining the backend parsing updates for BillingSubscription and BillingSubscriptionItem.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/tame-bats-tell.md:
- Around line 2-5: Update the changeset entry for `@clerk/backend` from minor to
major to reflect the breaking BillingSubscription and BillingSubscriptionItem
API changes, and add migration guidance covering the constructor shape and
nextPayment.amount type updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 544eaf57-70d6-4fc0-820a-de60667aa155

📥 Commits

Reviewing files that changed from the base of the PR and between 26530ce and 1cbd099.

📒 Files selected for processing (5)
  • .changeset/tame-bats-tell.md
  • packages/backend/src/api/resources/CommerceSubscription.ts
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
  • packages/backend/src/api/resources/JSON.ts
  • packages/backend/src/util/billing.ts

Comment on lines +2 to +5
'@clerk/backend': minor
---

Update fields for BillingSubscription and BillingSubscriptionItem

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
cat packages/backend/package.json
rg -n -C 3 '\b(BillingSubscription|BillingSubscriptionItem)\b' packages/backend/src

Repository: clerk/javascript

Length of output: 24693


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscription.ts
printf'\n---\n'
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscriptionItem.ts
printf'\n---\n'
sed -n '140,190p' packages/backend/src/index.ts
printf'\n---\n'
sed -n '1,80p' .changeset/tame-bats-tell.md

Repository: clerk/javascript

Length of output: 10343


Require a major changeset for the Billing API surfaceBillingSubscription and BillingSubscriptionItem are exported from @clerk/backend, so the constructor shape and nextPayment.amount type change are breaking API changes. Update this changeset to major and add migration guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.changeset/tame-bats-tell.md around lines 2 - 5, Update the changeset entry
for `@clerk/backend` from minor to major to reflect the breaking
BillingSubscription and BillingSubscriptionItem API changes, and add migration
guidance covering the constructor shape and nextPayment.amount type updates.

Source: Coding guidelines

@pkg-pr-new

pkg-pr-newBot commented Jul 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9196

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9196

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9196

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9196

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9196

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9196

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9196

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9196

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9196

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9196

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9196

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9196

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9196

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9196

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9196

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9196

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9196

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9196

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9196

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9196

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9196

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9196

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9196

commit: d40a9c3

@github-actions

github-actionsBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-17T21:05:11.397Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes7
🟡 Non-breaking changes3
🟢 Additions8

Warning
7 breaking change(s) detected - Major version bump required

🤖 This report was reviewed by claude-sonnet-4-6.

🔴 Breaking changes index (7)

Every breaking change, up front. Full diffs are in the package sections below.

PackageSubpathChange
@clerk/backend.BillingSubscription.undefined
@clerk/backend.BillingSubscriptionItem.undefined
@clerk/backend.BillingSubscriptionItem.isFreeTrial
@clerk/backend.BillingSubscriptionItem.nextPayment
@clerk/backend.BillingSubscriptionItemJSON.amount
@clerk/backend.BillingSubscriptionItemJSON.is_free_trial
@clerk/backend.BillingSubscriptionItemJSON.next_payment

@clerk/backend

Current version: 3.11.7
Recommended bump: MAJOR → 4.0.0

🔴 Breaking Changes (7)

Changed: BillingSubscription.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionJSON['status'], payerId: string, createdAt: number, updatedAt: number, activeAt: number | null, pastDueAt: number | null, subscriptionItems: BillingSubscriptionItem[], nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null, eligibleForFreeTrial: boolean);

Static analyzer: Breaking change in constructor BillingSubscription.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionJSON['status']string; Parameter payerId type changed: stringimport("@clerk/backend").BillingSubscriptionJSON['status']; Parameter createdAt type changed: numberstring; Parameter activeAt type changed: null|numbernumber; Parameter subscriptionItems type changed: import("@clerk/backend").BillingSubscriptionItem[]null|number; Parameter nextPayment type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}import("@clerk/backend").BillingSubscriptionItem[]; Parameter eligibleForFreeTrial type changed: booleannull|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Required parameter eligibleForFreeTrial was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscription constructor, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscription(...) directly.

Migration: Add the new instanceId: string argument as the second parameter when constructing BillingSubscription instances.

Changed: BillingSubscriptionItem.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionItemJSON['status'], planPeriod: 'month' | 'annual', periodStart: number, nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined, amount: BillingMoneyAmount | undefined, plan: BillingPlan | null, planId: string | null, + priceId: string | null, 
createdAt: number, updatedAt: number, periodEnd: number | null, canceledAt: number | null, pastDueAt: number | null, endedAt: number | null, payerId: string | undefined, - isFreeTrial?: boolean | undefined, - lifetimePaid?: BillingMoneyAmount | undefined);+ isFreeTrial: boolean, + lifetimePaid?: BillingMoneyAmount | undefined, + seats?: BillingSubscriptionItemSeats | undefined);

Static analyzer: Breaking change in constructor BillingSubscriptionItem.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionItemJSON['status']string; Parameter planPeriod type changed: 'annual'|'month'import("@clerk/backend").BillingSubscriptionItemJSON['status']; Parameter periodStart type changed: number'annual'|'month'; Parameter nextPayment type changed: null|undefined|{amount:number;date:number;}number; Parameter amount type changed: import("@clerk/shared").BillingMoneyAmount|undefinednull|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Parameter plan type changed: import("@clerk/backend").BillingPlan|nullimport("@clerk/shared").BillingMoneyAmount|undefined; Parameter planId type changed: null|stringimport("@clerk/backend").BillingPlan|null; Parameter createdAt type changed: numbernull|string; Parameter updatedAt type changed: numbernull|string; Parameter periodEnd type changed: null|numbernumber; Parameter canceledAt type changed: null|numbernumber; Parameter payerId type changed: string|undefinednull|number; Parameter isFreeTrial is no longer optional; Parameter isFreeTrial type changed: boolean|undefinednull|number; Parameter lifetimePaid is no longer optional; Parameter lifetimePaid type changed: import("@clerk/shared").BillingMoneyAmount|undefinedstring|undefined; Required parameter isFreeTrial was added; Optional parameter lifetimePaid was added; Optional parameter seats was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscriptionItem constructor, and a new required parameter priceId was also inserted later, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscriptionItem(...) directly.

Migration: Add instanceId: string as the second argument and priceId: string | null after planId when constructing BillingSubscriptionItem instances.

Changed: BillingSubscriptionItem.isFreeTrial

- readonly isFreeTrial?: boolean | undefined;+ readonly isFreeTrial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItem.isFreeTrial: Member is no longer optional; Type changed: boolean|undefinedboolean

🤖 AI review (confirmed) (85%): Making isFreeTrial a required (non-optional) property on the BillingSubscriptionItem class narrows the type; consumers who previously typed this as boolean | undefined and assigned undefined will now fail, and code relying on the optional signature breaks.

Migration: Ensure all code that reads or assigns isFreeTrial treats it as always boolean, removing any undefined branches.

Changed: BillingSubscriptionItem.nextPayment

 readonly nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined;

Static analyzer: Breaking change in property BillingSubscriptionItem.nextPayment: Type changed: null|undefined|{amount:number;date:number;}null|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").Bi…

🤖 AI review (confirmed) (85%): The amount field in the nextPayment object changed from number to BillingMoneyAmount; consumers that previously read nextPayment.amount as a number will have a type mismatch.

Migration: Update consumer code to treat nextPayment.amount as a BillingMoneyAmount object rather than a plain number.

Changed: BillingSubscriptionItemJSON.amount

- amount: BillingMoneyAmountJSON;+ amount?: BillingMoneyAmountJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.amount: Member became optional; Type changed: import("@clerk/backend").~BillingMoneyAmountJSONimport("@clerk/backend").~BillingMoneyAmountJSON|null

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.amount changed from a required BillingMoneyAmountJSON to an optional BillingMoneyAmountJSON | null; consumers constructing objects of this interface that provided the required field now encounter type mismatches when checking optionality, and consumers reading it must now handle null/undefined.

Migration: Update consumer code that reads amount from BillingSubscriptionItemJSON to handle null | undefined cases.

Changed: BillingSubscriptionItemJSON.is_free_trial

- is_free_trial?: boolean;+ is_free_trial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.is_free_trial: Member is no longer optional

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.is_free_trial changed from optional to required; consumers constructing objects of this interface that omitted this field will now fail to compile.

Migration: Add a required is_free_trial: boolean field when constructing objects that conform to BillingSubscriptionItemJSON.

Changed: BillingSubscriptionItemJSON.next_payment

- next_payment?: {- amount: number;- date: number;- } | null;+ next_payment?: BillingSubscriptionItemNextPaymentJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.next_payment: Type changed: null|{amount:number;date:number;}import("@clerk/shared").BillingSubscriptionItemNextPaymentJSON|null

🤖 AI review (confirmed) (80%): The inline type { amount: number; date: number } was replaced by BillingSubscriptionItemNextPaymentJSON, which changes the shape of amount from number to a structured type; consumers reading or constructing this field must update their code.

Migration: Update code that reads next_payment.amount from BillingSubscriptionItemJSON to handle the new BillingSubscriptionItemNextPaymentJSON shape instead of a plain number.

🟡 Non-breaking Changes (3)

Modified: BillingSubscription.nextPayment

 readonly nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null;

Static analyzer: Breaking change in property BillingSubscription.nextPayment: Type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUn…

🤖 AI review (reclassified as non-breaking) (90%): The nextPayment property on BillingSubscription only gains two new optional fields (perUnitTotals? and totals?); existing consumers reading the property still see a compatible shape (the existing fields are unchanged), and since this is a readonly output property no consumer is required to supply those fields.

Modified: BillingSubscriptionItemJSON.lifetime_paid

- lifetime_paid: BillingMoneyAmountJSON | null;+ lifetime_paid?: BillingMoneyAmountJSON | null;

Static analyzer: Modified property BillingSubscriptionItemJSON.lifetime_paid: Member became optional

🤖 AI review (confirmed) (95%): Making lifetime_paid optional in the interface is non-breaking: consumers reading it already had to handle null, and consumers constructing this object type can now omit the field rather than being required to provide it.

Modified: BillingSubscriptionJSON.next_payment

 next_payment?: {
date: number;
amount: BillingMoneyAmountJSON;
+ per_unit_totals?: BillingPerUnitTotalJSON[];+ totals?: SharedBillingTotalsJSON;
};

Static analyzer: Breaking change in property BillingSubscriptionJSON.next_payment: Type changed: {date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;}{date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;per_unit_totals?:import("@clerk/shared").BillingPe…

🤖 AI review (reclassified as non-breaking) (90%): The next_payment object in BillingSubscriptionJSON only gains new optional fields (per_unit_totals? and totals?); existing consumers neither need to supply those fields when constructing nor are required to handle them when reading, so no well-typed consumer is broken.

🟢 Additions (8)

Added: BillingSubscription.instanceId

+ readonly instanceId: string;

Added property BillingSubscription.instanceId

Added: BillingSubscriptionItem.instanceId

+ readonly instanceId: string;

Added property BillingSubscriptionItem.instanceId

Added: BillingSubscriptionItem.priceId

+ readonly priceId: string | null;

Added property BillingSubscriptionItem.priceId

Added: BillingSubscriptionItem.seats

+ readonly seats?: BillingSubscriptionItemSeats | undefined;

Added property BillingSubscriptionItem.seats

Added: BillingSubscriptionItemJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionItemJSON.instance_id

Added: BillingSubscriptionItemJSON.price_id

+ price_id?: string;

Added property BillingSubscriptionItemJSON.price_id

Added: BillingSubscriptionItemJSON.seats

+ seats?: BillingSubscriptionItemSeatsJSON;

Added property BillingSubscriptionItemJSON.seats

Added: BillingSubscriptionJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionJSON.instance_id


Report generated by Break Check

Last ran on d40a9c3.

@dstaley
dstaley requested a review from a teamJuly 17, 2026 20:47
@dstaley

Copy link
Copy Markdown
MemberAuthor

@macroscope-app review

@macroscopeapp

Copy link
Copy Markdown

Manual reviews triggered for commit d40a9c3:

All prior checks · these links stay valid even if you push more commits.

@macroscopeapp

Copy link
Copy Markdown

Review in progress. Results will be posted as check runs when complete.

@macroscopeapp

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

This PR modifies billing-related resources which are treated as sensitive. Additionally, there's an unresolved comment noting that the type change to nextPayment.amount may constitute a breaking API change requiring a major version bump rather than minor.

You can customize Macroscope's approvability policy. Learn more.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/backend/src/api/resources/CommerceSubscriptionItem.ts (1)

38-44: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the backend API documentation for the new payment shape.

nextPayment.amount is now BillingMoneyAmount and includes perUnitTotals/totals, while clerk-docs still documents the older { amount, date } shape. Update the public documentation with this API change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts` around lines
38 - 44, Update the public clerk-docs documentation for the nextPayment API
shape to match CommerceSubscriptionItem: document amount as BillingMoneyAmount
and include the optional perUnitTotals and totals fields alongside date. Replace
the older { amount, date } representation without changing unrelated
subscription documentation.

Sources: Coding guidelines, Linked repositories

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts`:
- Around line 38-44: Update the public clerk-docs documentation for the
nextPayment API shape to match CommerceSubscriptionItem: document amount as
BillingMoneyAmount and include the optional perUnitTotals and totals fields
alongside date. Replace the older { amount, date } representation without
changing unrelated subscription documentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: e7a1b51e-6fc9-445d-a608-d21220fb711f

📥 Commits

Reviewing files that changed from the base of the PR and between 1cbd099 and d40a9c3.

📒 Files selected for processing (1)
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)

@dstaley
dstaley merged commit fe6ee54 into mainJul 30, 2026
55 checks passed
@dstaley
dstaley deleted the ds.fix/backend-sub-subitem branch July 30, 2026 18:13
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dstaley@mauricioabreu
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(backend): Update Subscription and SubscriptionItem resources - #9196

Merged
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem
Jul 30, 2026
Merged

fix(backend): Update Subscription and SubscriptionItem resources#9196
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem

Conversation

@dstaley

@dstaleydstaley commented Jul 17, 2026

Copy link
Copy Markdown
Member

Description

This PR updates the parsing logic for BillingSubscription and BillingSubscriptionItem in the @clerk/backend package to align with the latest response shapes for the Backend API.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@vercel

vercelBot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 17, 2026 9:02pm
swingsetReadyReadyPreview, CommentJul 17, 2026 9:02pm

Request Review

@changeset-bot

changeset-botBot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d40a9c3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendMinor
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Billing subscription JSON contracts now include instance, pricing, totals, and seat fields. Shared billing converters normalize nested billing data, and subscription resources expose the converted structures through updated factories and constructors.

Changes

LayerSummary
Billing JSON contractsSubscription and subscription-item JSON types add instance identifiers and structured payment, pricing, lifetime-payment, and seat fields.
Billing conversion helpersNew helpers convert billing amounts, totals, credits, discounts, period totals, and seat breakdowns into typed objects.
Subscription resource parsingSubscription resources normalize the expanded billing data through updated constructors and fromJSON factories. A minor backend changeset was added.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:alexisintech

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the backend changes to Subscription and SubscriptionItem resources.
Description check✅ PassedThe description matches the PR by explaining the backend parsing updates for BillingSubscription and BillingSubscriptionItem.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/tame-bats-tell.md:
- Around line 2-5: Update the changeset entry for `@clerk/backend` from minor to
major to reflect the breaking BillingSubscription and BillingSubscriptionItem
API changes, and add migration guidance covering the constructor shape and
nextPayment.amount type updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 544eaf57-70d6-4fc0-820a-de60667aa155

📥 Commits

Reviewing files that changed from the base of the PR and between 26530ce and 1cbd099.

📒 Files selected for processing (5)
  • .changeset/tame-bats-tell.md
  • packages/backend/src/api/resources/CommerceSubscription.ts
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
  • packages/backend/src/api/resources/JSON.ts
  • packages/backend/src/util/billing.ts

Comment on lines +2 to +5
'@clerk/backend': minor
---

Update fields for BillingSubscription and BillingSubscriptionItem

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
cat packages/backend/package.json
rg -n -C 3 '\b(BillingSubscription|BillingSubscriptionItem)\b' packages/backend/src

Repository: clerk/javascript

Length of output: 24693


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscription.ts
printf'\n---\n'
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscriptionItem.ts
printf'\n---\n'
sed -n '140,190p' packages/backend/src/index.ts
printf'\n---\n'
sed -n '1,80p' .changeset/tame-bats-tell.md

Repository: clerk/javascript

Length of output: 10343


Require a major changeset for the Billing API surfaceBillingSubscription and BillingSubscriptionItem are exported from @clerk/backend, so the constructor shape and nextPayment.amount type change are breaking API changes. Update this changeset to major and add migration guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.changeset/tame-bats-tell.md around lines 2 - 5, Update the changeset entry
for `@clerk/backend` from minor to major to reflect the breaking
BillingSubscription and BillingSubscriptionItem API changes, and add migration
guidance covering the constructor shape and nextPayment.amount type updates.

Source: Coding guidelines

@pkg-pr-new

pkg-pr-newBot commented Jul 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9196

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9196

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9196

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9196

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9196

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9196

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9196

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9196

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9196

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9196

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9196

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9196

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9196

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9196

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9196

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9196

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9196

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9196

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9196

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9196

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9196

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9196

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9196

commit: d40a9c3

@github-actions

github-actionsBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-17T21:05:11.397Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes7
🟡 Non-breaking changes3
🟢 Additions8

Warning
7 breaking change(s) detected - Major version bump required

🤖 This report was reviewed by claude-sonnet-4-6.

🔴 Breaking changes index (7)

Every breaking change, up front. Full diffs are in the package sections below.

PackageSubpathChange
@clerk/backend.BillingSubscription.undefined
@clerk/backend.BillingSubscriptionItem.undefined
@clerk/backend.BillingSubscriptionItem.isFreeTrial
@clerk/backend.BillingSubscriptionItem.nextPayment
@clerk/backend.BillingSubscriptionItemJSON.amount
@clerk/backend.BillingSubscriptionItemJSON.is_free_trial
@clerk/backend.BillingSubscriptionItemJSON.next_payment

@clerk/backend

Current version: 3.11.7
Recommended bump: MAJOR → 4.0.0

🔴 Breaking Changes (7)

Changed: BillingSubscription.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionJSON['status'], payerId: string, createdAt: number, updatedAt: number, activeAt: number | null, pastDueAt: number | null, subscriptionItems: BillingSubscriptionItem[], nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null, eligibleForFreeTrial: boolean);

Static analyzer: Breaking change in constructor BillingSubscription.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionJSON['status']string; Parameter payerId type changed: stringimport("@clerk/backend").BillingSubscriptionJSON['status']; Parameter createdAt type changed: numberstring; Parameter activeAt type changed: null|numbernumber; Parameter subscriptionItems type changed: import("@clerk/backend").BillingSubscriptionItem[]null|number; Parameter nextPayment type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}import("@clerk/backend").BillingSubscriptionItem[]; Parameter eligibleForFreeTrial type changed: booleannull|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Required parameter eligibleForFreeTrial was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscription constructor, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscription(...) directly.

Migration: Add the new instanceId: string argument as the second parameter when constructing BillingSubscription instances.

Changed: BillingSubscriptionItem.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionItemJSON['status'], planPeriod: 'month' | 'annual', periodStart: number, nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined, amount: BillingMoneyAmount | undefined, plan: BillingPlan | null, planId: string | null, + priceId: string | null, 
createdAt: number, updatedAt: number, periodEnd: number | null, canceledAt: number | null, pastDueAt: number | null, endedAt: number | null, payerId: string | undefined, - isFreeTrial?: boolean | undefined, - lifetimePaid?: BillingMoneyAmount | undefined);+ isFreeTrial: boolean, + lifetimePaid?: BillingMoneyAmount | undefined, + seats?: BillingSubscriptionItemSeats | undefined);

Static analyzer: Breaking change in constructor BillingSubscriptionItem.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionItemJSON['status']string; Parameter planPeriod type changed: 'annual'|'month'import("@clerk/backend").BillingSubscriptionItemJSON['status']; Parameter periodStart type changed: number'annual'|'month'; Parameter nextPayment type changed: null|undefined|{amount:number;date:number;}number; Parameter amount type changed: import("@clerk/shared").BillingMoneyAmount|undefinednull|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Parameter plan type changed: import("@clerk/backend").BillingPlan|nullimport("@clerk/shared").BillingMoneyAmount|undefined; Parameter planId type changed: null|stringimport("@clerk/backend").BillingPlan|null; Parameter createdAt type changed: numbernull|string; Parameter updatedAt type changed: numbernull|string; Parameter periodEnd type changed: null|numbernumber; Parameter canceledAt type changed: null|numbernumber; Parameter payerId type changed: string|undefinednull|number; Parameter isFreeTrial is no longer optional; Parameter isFreeTrial type changed: boolean|undefinednull|number; Parameter lifetimePaid is no longer optional; Parameter lifetimePaid type changed: import("@clerk/shared").BillingMoneyAmount|undefinedstring|undefined; Required parameter isFreeTrial was added; Optional parameter lifetimePaid was added; Optional parameter seats was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscriptionItem constructor, and a new required parameter priceId was also inserted later, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscriptionItem(...) directly.

Migration: Add instanceId: string as the second argument and priceId: string | null after planId when constructing BillingSubscriptionItem instances.

Changed: BillingSubscriptionItem.isFreeTrial

- readonly isFreeTrial?: boolean | undefined;+ readonly isFreeTrial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItem.isFreeTrial: Member is no longer optional; Type changed: boolean|undefinedboolean

🤖 AI review (confirmed) (85%): Making isFreeTrial a required (non-optional) property on the BillingSubscriptionItem class narrows the type; consumers who previously typed this as boolean | undefined and assigned undefined will now fail, and code relying on the optional signature breaks.

Migration: Ensure all code that reads or assigns isFreeTrial treats it as always boolean, removing any undefined branches.

Changed: BillingSubscriptionItem.nextPayment

 readonly nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined;

Static analyzer: Breaking change in property BillingSubscriptionItem.nextPayment: Type changed: null|undefined|{amount:number;date:number;}null|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").Bi…

🤖 AI review (confirmed) (85%): The amount field in the nextPayment object changed from number to BillingMoneyAmount; consumers that previously read nextPayment.amount as a number will have a type mismatch.

Migration: Update consumer code to treat nextPayment.amount as a BillingMoneyAmount object rather than a plain number.

Changed: BillingSubscriptionItemJSON.amount

- amount: BillingMoneyAmountJSON;+ amount?: BillingMoneyAmountJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.amount: Member became optional; Type changed: import("@clerk/backend").~BillingMoneyAmountJSONimport("@clerk/backend").~BillingMoneyAmountJSON|null

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.amount changed from a required BillingMoneyAmountJSON to an optional BillingMoneyAmountJSON | null; consumers constructing objects of this interface that provided the required field now encounter type mismatches when checking optionality, and consumers reading it must now handle null/undefined.

Migration: Update consumer code that reads amount from BillingSubscriptionItemJSON to handle null | undefined cases.

Changed: BillingSubscriptionItemJSON.is_free_trial

- is_free_trial?: boolean;+ is_free_trial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.is_free_trial: Member is no longer optional

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.is_free_trial changed from optional to required; consumers constructing objects of this interface that omitted this field will now fail to compile.

Migration: Add a required is_free_trial: boolean field when constructing objects that conform to BillingSubscriptionItemJSON.

Changed: BillingSubscriptionItemJSON.next_payment

- next_payment?: {- amount: number;- date: number;- } | null;+ next_payment?: BillingSubscriptionItemNextPaymentJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.next_payment: Type changed: null|{amount:number;date:number;}import("@clerk/shared").BillingSubscriptionItemNextPaymentJSON|null

🤖 AI review (confirmed) (80%): The inline type { amount: number; date: number } was replaced by BillingSubscriptionItemNextPaymentJSON, which changes the shape of amount from number to a structured type; consumers reading or constructing this field must update their code.

Migration: Update code that reads next_payment.amount from BillingSubscriptionItemJSON to handle the new BillingSubscriptionItemNextPaymentJSON shape instead of a plain number.

🟡 Non-breaking Changes (3)

Modified: BillingSubscription.nextPayment

 readonly nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null;

Static analyzer: Breaking change in property BillingSubscription.nextPayment: Type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUn…

🤖 AI review (reclassified as non-breaking) (90%): The nextPayment property on BillingSubscription only gains two new optional fields (perUnitTotals? and totals?); existing consumers reading the property still see a compatible shape (the existing fields are unchanged), and since this is a readonly output property no consumer is required to supply those fields.

Modified: BillingSubscriptionItemJSON.lifetime_paid

- lifetime_paid: BillingMoneyAmountJSON | null;+ lifetime_paid?: BillingMoneyAmountJSON | null;

Static analyzer: Modified property BillingSubscriptionItemJSON.lifetime_paid: Member became optional

🤖 AI review (confirmed) (95%): Making lifetime_paid optional in the interface is non-breaking: consumers reading it already had to handle null, and consumers constructing this object type can now omit the field rather than being required to provide it.

Modified: BillingSubscriptionJSON.next_payment

 next_payment?: {
date: number;
amount: BillingMoneyAmountJSON;
+ per_unit_totals?: BillingPerUnitTotalJSON[];+ totals?: SharedBillingTotalsJSON;
};

Static analyzer: Breaking change in property BillingSubscriptionJSON.next_payment: Type changed: {date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;}{date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;per_unit_totals?:import("@clerk/shared").BillingPe…

🤖 AI review (reclassified as non-breaking) (90%): The next_payment object in BillingSubscriptionJSON only gains new optional fields (per_unit_totals? and totals?); existing consumers neither need to supply those fields when constructing nor are required to handle them when reading, so no well-typed consumer is broken.

🟢 Additions (8)

Added: BillingSubscription.instanceId

+ readonly instanceId: string;

Added property BillingSubscription.instanceId

Added: BillingSubscriptionItem.instanceId

+ readonly instanceId: string;

Added property BillingSubscriptionItem.instanceId

Added: BillingSubscriptionItem.priceId

+ readonly priceId: string | null;

Added property BillingSubscriptionItem.priceId

Added: BillingSubscriptionItem.seats

+ readonly seats?: BillingSubscriptionItemSeats | undefined;

Added property BillingSubscriptionItem.seats

Added: BillingSubscriptionItemJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionItemJSON.instance_id

Added: BillingSubscriptionItemJSON.price_id

+ price_id?: string;

Added property BillingSubscriptionItemJSON.price_id

Added: BillingSubscriptionItemJSON.seats

+ seats?: BillingSubscriptionItemSeatsJSON;

Added property BillingSubscriptionItemJSON.seats

Added: BillingSubscriptionJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionJSON.instance_id


Report generated by Break Check

Last ran on d40a9c3.

@dstaley
dstaley requested a review from a teamJuly 17, 2026 20:47
@dstaley

Copy link
Copy Markdown
MemberAuthor

@macroscope-app review

@macroscopeapp

Copy link
Copy Markdown

Manual reviews triggered for commit d40a9c3:

All prior checks · these links stay valid even if you push more commits.

@macroscopeapp

Copy link
Copy Markdown

Review in progress. Results will be posted as check runs when complete.

@macroscopeapp

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

This PR modifies billing-related resources which are treated as sensitive. Additionally, there's an unresolved comment noting that the type change to nextPayment.amount may constitute a breaking API change requiring a major version bump rather than minor.

You can customize Macroscope's approvability policy. Learn more.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/backend/src/api/resources/CommerceSubscriptionItem.ts (1)

38-44: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the backend API documentation for the new payment shape.

nextPayment.amount is now BillingMoneyAmount and includes perUnitTotals/totals, while clerk-docs still documents the older { amount, date } shape. Update the public documentation with this API change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts` around lines
38 - 44, Update the public clerk-docs documentation for the nextPayment API
shape to match CommerceSubscriptionItem: document amount as BillingMoneyAmount
and include the optional perUnitTotals and totals fields alongside date. Replace
the older { amount, date } representation without changing unrelated
subscription documentation.

Sources: Coding guidelines, Linked repositories

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts`:
- Around line 38-44: Update the public clerk-docs documentation for the
nextPayment API shape to match CommerceSubscriptionItem: document amount as
BillingMoneyAmount and include the optional perUnitTotals and totals fields
alongside date. Replace the older { amount, date } representation without
changing unrelated subscription documentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: e7a1b51e-6fc9-445d-a608-d21220fb711f

📥 Commits

Reviewing files that changed from the base of the PR and between 1cbd099 and d40a9c3.

📒 Files selected for processing (1)
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)

@dstaley
dstaley merged commit fe6ee54 into mainJul 30, 2026
55 checks passed
@dstaley
dstaley deleted the ds.fix/backend-sub-subitem branch July 30, 2026 18:13
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dstaley@mauricioabreu
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(backend): Update Subscription and SubscriptionItem resources - #9196

Merged
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem
Jul 30, 2026
Merged

fix(backend): Update Subscription and SubscriptionItem resources#9196
dstaley merged 2 commits into
mainfrom
ds.fix/backend-sub-subitem

Conversation

@dstaley

@dstaleydstaley commented Jul 17, 2026

Copy link
Copy Markdown
Member

Description

This PR updates the parsing logic for BillingSubscription and BillingSubscriptionItem in the @clerk/backend package to align with the latest response shapes for the Backend API.

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@vercel

vercelBot commented Jul 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreview, CommentJul 17, 2026 9:02pm
swingsetReadyReadyPreview, CommentJul 17, 2026 9:02pm

Request Review

@changeset-bot

changeset-botBot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d40a9c3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
NameType
@clerk/backendMinor
@clerk/astroPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/honoPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitaiBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Billing subscription JSON contracts now include instance, pricing, totals, and seat fields. Shared billing converters normalize nested billing data, and subscription resources expose the converted structures through updated factories and constructors.

Changes

LayerSummary
Billing JSON contractsSubscription and subscription-item JSON types add instance identifiers and structured payment, pricing, lifetime-payment, and seat fields.
Billing conversion helpersNew helpers convert billing amounts, totals, credits, discounts, period totals, and seat breakdowns into typed objects.
Subscription resource parsingSubscription resources normalize the expanded billing data through updated constructors and fromJSON factories. A minor backend changeset was added.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:alexisintech

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the backend changes to Subscription and SubscriptionItem resources.
Description check✅ PassedThe description matches the PR by explaining the backend parsing updates for BillingSubscription and BillingSubscriptionItem.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/tame-bats-tell.md:
- Around line 2-5: Update the changeset entry for `@clerk/backend` from minor to
major to reflect the breaking BillingSubscription and BillingSubscriptionItem
API changes, and add migration guidance covering the constructor shape and
nextPayment.amount type updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 544eaf57-70d6-4fc0-820a-de60667aa155

📥 Commits

Reviewing files that changed from the base of the PR and between 26530ce and 1cbd099.

📒 Files selected for processing (5)
  • .changeset/tame-bats-tell.md
  • packages/backend/src/api/resources/CommerceSubscription.ts
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
  • packages/backend/src/api/resources/JSON.ts
  • packages/backend/src/util/billing.ts

Comment on lines +2 to +5
'@clerk/backend': minor
---

Update fields for BillingSubscription and BillingSubscriptionItem

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
cat packages/backend/package.json
rg -n -C 3 '\b(BillingSubscription|BillingSubscriptionItem)\b' packages/backend/src

Repository: clerk/javascript

Length of output: 24693


🏁 Script executed:

#!/bin/bashset -euo pipefail
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscription.ts
printf'\n---\n'
sed -n '1,140p' packages/backend/src/api/resources/CommerceSubscriptionItem.ts
printf'\n---\n'
sed -n '140,190p' packages/backend/src/index.ts
printf'\n---\n'
sed -n '1,80p' .changeset/tame-bats-tell.md

Repository: clerk/javascript

Length of output: 10343


Require a major changeset for the Billing API surfaceBillingSubscription and BillingSubscriptionItem are exported from @clerk/backend, so the constructor shape and nextPayment.amount type change are breaking API changes. Update this changeset to major and add migration guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.changeset/tame-bats-tell.md around lines 2 - 5, Update the changeset entry
for `@clerk/backend` from minor to major to reflect the breaking
BillingSubscription and BillingSubscriptionItem API changes, and add migration
guidance covering the constructor shape and nextPayment.amount type updates.

Source: Coding guidelines

@pkg-pr-new

pkg-pr-newBot commented Jul 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9196

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9196

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9196

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9196

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9196

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9196

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9196

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9196

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9196

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9196

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9196

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9196

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9196

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9196

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9196

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9196

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9196

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9196

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9196

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9196

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9196

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9196

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9196

commit: d40a9c3

@github-actions

github-actionsBot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-17T21:05:11.397Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes7
🟡 Non-breaking changes3
🟢 Additions8

Warning
7 breaking change(s) detected - Major version bump required

🤖 This report was reviewed by claude-sonnet-4-6.

🔴 Breaking changes index (7)

Every breaking change, up front. Full diffs are in the package sections below.

PackageSubpathChange
@clerk/backend.BillingSubscription.undefined
@clerk/backend.BillingSubscriptionItem.undefined
@clerk/backend.BillingSubscriptionItem.isFreeTrial
@clerk/backend.BillingSubscriptionItem.nextPayment
@clerk/backend.BillingSubscriptionItemJSON.amount
@clerk/backend.BillingSubscriptionItemJSON.is_free_trial
@clerk/backend.BillingSubscriptionItemJSON.next_payment

@clerk/backend

Current version: 3.11.7
Recommended bump: MAJOR → 4.0.0

🔴 Breaking Changes (7)

Changed: BillingSubscription.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionJSON['status'], payerId: string, createdAt: number, updatedAt: number, activeAt: number | null, pastDueAt: number | null, subscriptionItems: BillingSubscriptionItem[], nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null, eligibleForFreeTrial: boolean);

Static analyzer: Breaking change in constructor BillingSubscription.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionJSON['status']string; Parameter payerId type changed: stringimport("@clerk/backend").BillingSubscriptionJSON['status']; Parameter createdAt type changed: numberstring; Parameter activeAt type changed: null|numbernumber; Parameter subscriptionItems type changed: import("@clerk/backend").BillingSubscriptionItem[]null|number; Parameter nextPayment type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}import("@clerk/backend").BillingSubscriptionItem[]; Parameter eligibleForFreeTrial type changed: booleannull|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Required parameter eligibleForFreeTrial was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscription constructor, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscription(...) directly.

Migration: Add the new instanceId: string argument as the second parameter when constructing BillingSubscription instances.

Changed: BillingSubscriptionItem.undefined

 constructor(
id: string, + instanceId: string, 
status: BillingSubscriptionItemJSON['status'], planPeriod: 'month' | 'annual', periodStart: number, nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined, amount: BillingMoneyAmount | undefined, plan: BillingPlan | null, planId: string | null, + priceId: string | null, 
createdAt: number, updatedAt: number, periodEnd: number | null, canceledAt: number | null, pastDueAt: number | null, endedAt: number | null, payerId: string | undefined, - isFreeTrial?: boolean | undefined, - lifetimePaid?: BillingMoneyAmount | undefined);+ isFreeTrial: boolean, + lifetimePaid?: BillingMoneyAmount | undefined, + seats?: BillingSubscriptionItemSeats | undefined);

Static analyzer: Breaking change in constructor BillingSubscriptionItem.undefined: Parameter status type changed: import("@clerk/backend").BillingSubscriptionItemJSON['status']string; Parameter planPeriod type changed: 'annual'|'month'import("@clerk/backend").BillingSubscriptionItemJSON['status']; Parameter periodStart type changed: number'annual'|'month'; Parameter nextPayment type changed: null|undefined|{amount:number;date:number;}number; Parameter amount type changed: import("@clerk/shared").BillingMoneyAmount|undefinednull|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").BillingPerUnitTotal[];totals?:import("@clerk/shared").BillingTotals;}; Parameter plan type changed: import("@clerk/backend").BillingPlan|nullimport("@clerk/shared").BillingMoneyAmount|undefined; Parameter planId type changed: null|stringimport("@clerk/backend").BillingPlan|null; Parameter createdAt type changed: numbernull|string; Parameter updatedAt type changed: numbernull|string; Parameter periodEnd type changed: null|numbernumber; Parameter canceledAt type changed: null|numbernumber; Parameter payerId type changed: string|undefinednull|number; Parameter isFreeTrial is no longer optional; Parameter isFreeTrial type changed: boolean|undefinednull|number; Parameter lifetimePaid is no longer optional; Parameter lifetimePaid type changed: import("@clerk/shared").BillingMoneyAmount|undefinedstring|undefined; Required parameter isFreeTrial was added; Optional parameter lifetimePaid was added; Optional parameter seats was added

🤖 AI review (confirmed) (95%): A new required parameter instanceId was inserted at position 2 in the BillingSubscriptionItem constructor, and a new required parameter priceId was also inserted later, shifting all subsequent parameters and breaking any consumer that calls new BillingSubscriptionItem(...) directly.

Migration: Add instanceId: string as the second argument and priceId: string | null after planId when constructing BillingSubscriptionItem instances.

Changed: BillingSubscriptionItem.isFreeTrial

- readonly isFreeTrial?: boolean | undefined;+ readonly isFreeTrial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItem.isFreeTrial: Member is no longer optional; Type changed: boolean|undefinedboolean

🤖 AI review (confirmed) (85%): Making isFreeTrial a required (non-optional) property on the BillingSubscriptionItem class narrows the type; consumers who previously typed this as boolean | undefined and assigned undefined will now fail, and code relying on the optional signature breaks.

Migration: Ensure all code that reads or assigns isFreeTrial treats it as always boolean, removing any undefined branches.

Changed: BillingSubscriptionItem.nextPayment

 readonly nextPayment: {
- amount: number;+ amount: BillingMoneyAmount;
date: number;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null | undefined;

Static analyzer: Breaking change in property BillingSubscriptionItem.nextPayment: Type changed: null|undefined|{amount:number;date:number;}null|undefined|{amount:import("@clerk/shared").BillingMoneyAmount;date:number;perUnitTotals?:import("@clerk/shared").Bi…

🤖 AI review (confirmed) (85%): The amount field in the nextPayment object changed from number to BillingMoneyAmount; consumers that previously read nextPayment.amount as a number will have a type mismatch.

Migration: Update consumer code to treat nextPayment.amount as a BillingMoneyAmount object rather than a plain number.

Changed: BillingSubscriptionItemJSON.amount

- amount: BillingMoneyAmountJSON;+ amount?: BillingMoneyAmountJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.amount: Member became optional; Type changed: import("@clerk/backend").~BillingMoneyAmountJSONimport("@clerk/backend").~BillingMoneyAmountJSON|null

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.amount changed from a required BillingMoneyAmountJSON to an optional BillingMoneyAmountJSON | null; consumers constructing objects of this interface that provided the required field now encounter type mismatches when checking optionality, and consumers reading it must now handle null/undefined.

Migration: Update consumer code that reads amount from BillingSubscriptionItemJSON to handle null | undefined cases.

Changed: BillingSubscriptionItemJSON.is_free_trial

- is_free_trial?: boolean;+ is_free_trial: boolean;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.is_free_trial: Member is no longer optional

🤖 AI review (confirmed) (85%): BillingSubscriptionItemJSON.is_free_trial changed from optional to required; consumers constructing objects of this interface that omitted this field will now fail to compile.

Migration: Add a required is_free_trial: boolean field when constructing objects that conform to BillingSubscriptionItemJSON.

Changed: BillingSubscriptionItemJSON.next_payment

- next_payment?: {- amount: number;- date: number;- } | null;+ next_payment?: BillingSubscriptionItemNextPaymentJSON | null;

Static analyzer: Breaking change in property BillingSubscriptionItemJSON.next_payment: Type changed: null|{amount:number;date:number;}import("@clerk/shared").BillingSubscriptionItemNextPaymentJSON|null

🤖 AI review (confirmed) (80%): The inline type { amount: number; date: number } was replaced by BillingSubscriptionItemNextPaymentJSON, which changes the shape of amount from number to a structured type; consumers reading or constructing this field must update their code.

Migration: Update code that reads next_payment.amount from BillingSubscriptionItemJSON to handle the new BillingSubscriptionItemNextPaymentJSON shape instead of a plain number.

🟡 Non-breaking Changes (3)

Modified: BillingSubscription.nextPayment

 readonly nextPayment: {
date: number;
amount: BillingMoneyAmount;
+ perUnitTotals?: BillingPerUnitTotal[];+ totals?: BillingTotals;
} | null;

Static analyzer: Breaking change in property BillingSubscription.nextPayment: Type changed: null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;}null|{date:number;amount:import("@clerk/shared").BillingMoneyAmount;perUnitTotals?:import("@clerk/shared").BillingPerUn…

🤖 AI review (reclassified as non-breaking) (90%): The nextPayment property on BillingSubscription only gains two new optional fields (perUnitTotals? and totals?); existing consumers reading the property still see a compatible shape (the existing fields are unchanged), and since this is a readonly output property no consumer is required to supply those fields.

Modified: BillingSubscriptionItemJSON.lifetime_paid

- lifetime_paid: BillingMoneyAmountJSON | null;+ lifetime_paid?: BillingMoneyAmountJSON | null;

Static analyzer: Modified property BillingSubscriptionItemJSON.lifetime_paid: Member became optional

🤖 AI review (confirmed) (95%): Making lifetime_paid optional in the interface is non-breaking: consumers reading it already had to handle null, and consumers constructing this object type can now omit the field rather than being required to provide it.

Modified: BillingSubscriptionJSON.next_payment

 next_payment?: {
date: number;
amount: BillingMoneyAmountJSON;
+ per_unit_totals?: BillingPerUnitTotalJSON[];+ totals?: SharedBillingTotalsJSON;
};

Static analyzer: Breaking change in property BillingSubscriptionJSON.next_payment: Type changed: {date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;}{date:number;amount:import("@clerk/backend").~BillingMoneyAmountJSON;per_unit_totals?:import("@clerk/shared").BillingPe…

🤖 AI review (reclassified as non-breaking) (90%): The next_payment object in BillingSubscriptionJSON only gains new optional fields (per_unit_totals? and totals?); existing consumers neither need to supply those fields when constructing nor are required to handle them when reading, so no well-typed consumer is broken.

🟢 Additions (8)

Added: BillingSubscription.instanceId

+ readonly instanceId: string;

Added property BillingSubscription.instanceId

Added: BillingSubscriptionItem.instanceId

+ readonly instanceId: string;

Added property BillingSubscriptionItem.instanceId

Added: BillingSubscriptionItem.priceId

+ readonly priceId: string | null;

Added property BillingSubscriptionItem.priceId

Added: BillingSubscriptionItem.seats

+ readonly seats?: BillingSubscriptionItemSeats | undefined;

Added property BillingSubscriptionItem.seats

Added: BillingSubscriptionItemJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionItemJSON.instance_id

Added: BillingSubscriptionItemJSON.price_id

+ price_id?: string;

Added property BillingSubscriptionItemJSON.price_id

Added: BillingSubscriptionItemJSON.seats

+ seats?: BillingSubscriptionItemSeatsJSON;

Added property BillingSubscriptionItemJSON.seats

Added: BillingSubscriptionJSON.instance_id

+ instance_id: string;

Added property BillingSubscriptionJSON.instance_id


Report generated by Break Check

Last ran on d40a9c3.

@dstaley
dstaley requested a review from a teamJuly 17, 2026 20:47
@dstaley

Copy link
Copy Markdown
MemberAuthor

@macroscope-app review

@macroscopeapp

Copy link
Copy Markdown

Manual reviews triggered for commit d40a9c3:

All prior checks · these links stay valid even if you push more commits.

@macroscopeapp

Copy link
Copy Markdown

Review in progress. Results will be posted as check runs when complete.

@macroscopeapp

Copy link
Copy Markdown

Approvability

Verdict: Needs human review

This PR modifies billing-related resources which are treated as sensitive. Additionally, there's an unresolved comment noting that the type change to nextPayment.amount may constitute a breaking API change requiring a major version bump rather than minor.

You can customize Macroscope's approvability policy. Learn more.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/backend/src/api/resources/CommerceSubscriptionItem.ts (1)

38-44: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Update the backend API documentation for the new payment shape.

nextPayment.amount is now BillingMoneyAmount and includes perUnitTotals/totals, while clerk-docs still documents the older { amount, date } shape. Update the public documentation with this API change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts` around lines
38 - 44, Update the public clerk-docs documentation for the nextPayment API
shape to match CommerceSubscriptionItem: document amount as BillingMoneyAmount
and include the optional perUnitTotals and totals fields alongside date. Replace
the older { amount, date } representation without changing unrelated
subscription documentation.

Sources: Coding guidelines, Linked repositories

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/backend/src/api/resources/CommerceSubscriptionItem.ts`:
- Around line 38-44: Update the public clerk-docs documentation for the
nextPayment API shape to match CommerceSubscriptionItem: document amount as
BillingMoneyAmount and include the optional perUnitTotals and totals fields
alongside date. Replace the older { amount, date } representation without
changing unrelated subscription documentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: e7a1b51e-6fc9-445d-a608-d21220fb711f

📥 Commits

Reviewing files that changed from the base of the PR and between 1cbd099 and d40a9c3.

📒 Files selected for processing (1)
  • packages/backend/src/api/resources/CommerceSubscriptionItem.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)

@dstaley
dstaley merged commit fe6ee54 into mainJul 30, 2026
55 checks passed
@dstaley
dstaley deleted the ds.fix/backend-sub-subitem branch July 30, 2026 18:13
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@dstaley@mauricioabreu