diff --git a/apps/web/components/settings/account.tsx b/apps/web/components/settings/account.tsx
index a7ae2d63..676e251a 100644
--- a/apps/web/components/settings/account.tsx
+++ b/apps/web/components/settings/account.tsx
@@ -197,6 +197,8 @@ export default function Account() {
} = useAuth()
const autumn = useCustomer()
const [isUpgrading, setIsUpgrading] = useState(false)
+ const [isCancelling, setIsCancelling] = useState(false)
+ const [isCancelDialogOpen, setIsCancelDialogOpen] = useState(false)
const [emailConfirm, setEmailConfirm] = useState("")
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false)
const [isClosingAccount, setIsClosingAccount] = useState(false)
@@ -287,6 +289,33 @@ export default function Account() {
}
}
+ // Enterprise is contract-based — direct those users to the portal/sales.
+ const cancellablePlanId =
+ currentPlan === "pro" || currentPlan === "scale"
+ ? (`api_${currentPlan}` as const)
+ : null
+
+ const handleCancelSubscription = async () => {
+ if (!cancellablePlanId) return
+ setIsCancelling(true)
+ try {
+ await autumn.updateSubscription({
+ planId: cancellablePlanId,
+ cancelAction: "cancel_end_of_cycle",
+ })
+ autumn.refetch?.()
+ setIsCancelDialogOpen(false)
+ toast.success(
+ `Subscription cancelled. ${planDisplayNames[currentPlan]} features remain active until the end of your billing period.`,
+ )
+ } catch (error) {
+ console.error(error)
+ toast.error("Failed to cancel subscription. Please try again.")
+ } finally {
+ setIsCancelling(false)
+ }
+ }
+
const handleDeleteAccount = async () => {
if (!user?.email || !emailMatches || membershipsPending) return
setIsClosingAccount(true)
@@ -546,25 +575,141 @@ export default function Account() {
-