# Subscription Cancellation Flow

**Date:** 2026-05-31  
**Status:** Draft  
**Spec:** 69  
**Tier:** Business+ (Freelancer has nothing to cancel)  
**Depends on:** `zync-subscription`, `settings-module`, `system-communications-notifications`, `foundation-auth-rbac`  
**Referenced by:** `zync-subscription`

---

## Overview

Full cancellation UX for paid Business subscriptions. Spec 33 (`zync-subscription`) defines: `DELETE /api/zync-subscription`, the `canceled_at` data field, and a single-line mention of "Cancel subscription link → confirmation dialog". This spec fills in the complete flow: offboarding survey, impact summary, confirmation, post-cancellation state, and reactivation.

Route: embedded within `/settings/plan` (existing page, spec 33).

---

## Cancellation Trigger

OWNER only. "Cancel subscription" link in `/settings/plan` — below the current plan card. Shown only when `status = 'active' | 'trialing'` and `tier ≠ 'freelancer'`.

---

## Step 1: Impact Summary Sheet

Bottom sheet / modal overlay (not a full-page redirect):

```
┌──────────────────────────────────────────────────────────────┐
│  Cancel Business subscription?                               │
│                                                              │
│  Your plan continues until {period_end_date}.               │
│  After that, you'll be moved to Freelancer (free).          │
│                                                              │
│  You'll lose access to:                                      │
│  • Lead forms & inbound webhooks                            │
│  • Contracts and e-signing                                  │
│  • Expense approval workflow                                 │
│  • Custom email domain and SMTP                             │
│  • API keys and outbound webhooks                           │
│  • Multi-currency invoicing                                  │
│                                                              │
│  Your data stays safe. You can reactivate anytime.          │
│                                                              │
│  Before you go — is there anything we could fix?            │
│  ○ Too expensive                                            │
│  ○ Missing a feature I need                                  │
│  ○ Switching to another tool                                 │
│  ○ Only needed it temporarily                               │
│  ○ Other: [_______________________________________]          │
│                                                              │
│  [Never mind, keep my plan]   [Continue to cancel →]        │
└──────────────────────────────────────────────────────────────┘
```

Survey response stored in `zync_subscriptions.cancellation_reason TEXT` + `cancellation_reason_freetext TEXT`. Not required (can skip survey and proceed).

---

## Step 2: Confirmation

```
┌──────────────────────────────────────────────────────────────┐
│  Confirm cancellation                                        │
│                                                              │
│  Your Business plan will end on {period_end_date}.          │
│  You won't be charged again after that date.                │
│                                                              │
│  [Go back]          [Cancel my subscription]                 │
└──────────────────────────────────────────────────────────────┘
```

"Cancel my subscription" → `DELETE /api/zync-subscription` (spec 33).

On success:
- `zync_subscriptions.canceled_at = now()`
- `zync_subscriptions.status = 'canceled'` (effective at period end, not immediately)
- Confirmation email sent to OWNER: "Your subscription has been cancelled"

---

## Post-Cancellation State in `/settings/plan`

After cancellation (but before period end — subscription still active):

```
┌──────────────────────────────────────────────────────────────┐
│  Business Plan                         [Reactivate ▾]       │
│  Status: Cancels on {period_end_date}                       │
│                                                              │
│  You have full Business access until {period_end_date}.     │
│  After that date, your account moves to Freelancer.         │
└──────────────────────────────────────────────────────────────┘
```

---

## Reactivation

"Reactivate" button available while `status = 'canceled'` and `tier ≠ 'freelancer'` (i.e. period not yet ended):

```
┌──────────────────────────────────────────────────────────────┐
│  Reactivate Business subscription?                           │
│                                                              │
│  Your subscription will continue as normal.                  │
│  You'll be billed on {next_renewal_date}.                    │
│                                                              │
│  [Cancel]        [Reactivate]                                │
└──────────────────────────────────────────────────────────────┘
```

"Reactivate" → `POST /api/zync-subscription/reactivate`:
- Clears `canceled_at`
- Sets `status = 'active'`
- Toast: "Your subscription has been reactivated."

After period end (already downgraded to freelancer): reactivation = full upgrade flow (spec 33).

---

## Cancellation Email

Triggered on `DELETE /api/zync-subscription`. Sent via `TenantEmailAdapter` (spec 51) to tenant OWNER:

Subject: "Your Zync Business subscription has been cancelled"  
Body: confirmation of cancellation date + period end date + "Reactivate" link + data retention assurance.

---

## API Endpoints

```
DELETE /api/zync-subscription
  → cancel subscription (spec 33 endpoint, OWNER only)
     Extends spec 33: also stores cancellation_reason from request body

POST /api/zync-subscription/reactivate
  → undo cancellation (before period end)
     Requires: status='canceled', period not yet ended
     Sets: canceled_at=NULL, status='active'
     OWNER only
```

---

## Foundation Deltas

**New columns on `zync_subscriptions`:**
```sql
ALTER TABLE zync_subscriptions ADD COLUMN cancellation_reason TEXT;
ALTER TABLE zync_subscriptions ADD COLUMN cancellation_reason_freetext TEXT;
```

---

## Architecture Decisions

| Decision | Choice | Reason |
|----------|--------|--------|
| Two-step (impact + confirm) | Not single confirm | Impact summary reduces accidental cancellations and surfaces "is there something we could fix?" — high-value retention touchpoint with minimal UX friction |
| Survey optional | Not required | Forced surveys increase abandonment of the legitimate path; opt-in gets honest signals from motivated respondents |
| Cancellation effective at period end | Not immediate | Standard SaaS behavior; tenant paid for the period, receives access until its end |
| Reactivation until period end | Not always | After period end, full upgrade flow needed (no payment method on file may have expired); before period end, reactivation is a no-cost DB update |
