Membership Pause
Overview
This page documents subscription pause — a user choosing to skip billing for a set number of months while keeping their account active. This is distinct from account cancellation, which moves the user to PAUSED status. See User Lifecycle for cancellation.
|
A subscription pause lets an active user suspend their billing for 1–3 months. Their user status remains ACTIVE throughout. When the pause period ends, the Subscription Service fires a pause-resume event and billing resumes automatically.
MX cannot initiate a subscription pause. Only the user can request it. MX can only cancel a user’s membership (which sets user status to PAUSED).
Pause State Tracking
Two mechanisms track pause state in parallel:
| Mechanism | Role |
|---|---|
|
Source of truth for billing state. Consumed by the Subscription Service via the Kinesis Feeder. |
|
Tracks the pause lifecycle for the user-service and subscription-worker. Updated as the pause progresses. |
Pause State Lifecycle
(not paused)
│ User requests pause
▼
SUB_PENDING_PAUSE ← membership status + tag value
│ Next billing date arrives, collection skipped (pause-skipped event)
▼
SUB_PAUSED ← membership status + tag value
│ Pause duration expires (pause-resume event) OR user manually unpauses
▼
SUB_RESUMED / (empty) ← membership restored to good standing, tag archived
Pause Eligibility
A pause request is denied with 403 Forbidden in any of the following cases:
| Condition | Reason |
|---|---|
User status is not |
Inactive users cannot pause |
User’s state is not in the Growthbook |
Pause is only available in certain US states |
Requested duration exceeds 3 months |
Maximum pause is |
|
User has been explicitly blocked from pausing |
|
User is already paused |
Pause Flow (User-Initiated)
POST /{user_id}/user/membership/pause
Request
{
"pause_duration_months": 2
}
pause_duration_months must be between 1 and 3. If 0 or negative, it is stored as -1 (indefinite pause).
Flow
Decode request
│
▼
Get user ──not found──► 404
│
▼
Validate pause eligibility (status, state, duration) ──denied──► 403
│
▼
Check pause tags (PAUSE_SUB_DISABLED, already paused) ──denied──► 403
│
▼
Get next SCHEDULED subscription date (Subscription Service)
│
▼
Get current membership record (or create default BASE record if none exists)
│
▼
Upsert MEMBERSHIP_PAUSE_STATUS tag → SUB_PENDING_PAUSE (active)
│
▼
Write new membership record:
event_type: SUB_PAUSED
status: SUB_PENDING_PAUSE
pause_date: next subscription date
pause_duration_months: request value (or -1 if ≤ 0)
unpause_date: pause_date + N months (if duration > 0)
(tier and term carried forward)
│
▼
Send pause notification via Segment
│
▼
Return 201 Created
{ "pause_date": "...", "unpause_date": "..." }
Admin Pause Flow
POST /users/{user_id}/admin/membership/pause
The admin pause endpoint applies the same eligibility checks as the user-initiated flow. The difference is the request format and the Segment notification sent.
Unpause Flow (User-Initiated)
POST /{user_id}/user/membership/unpause
No request body required.
Flow
Get user ──not found──► 404
│
▼
User status is ACTIVE? ──No──► 403
│ Yes
▼
Get user tags (active only)
│
▼
MEMBERSHIP_PAUSE_STATUS tag exists? ──No──► 403 "user is not currently paused"
│ Yes
▼
Get next PAUSED subscription date (Subscription Service)
│
▼
Get current membership record
│
▼
Check tag value:
SUB_PAUSED (collection already skipped):
├── Upsert tag → SUB_PENDING_RESUME (archived)
└── membership.event_type = UNPAUSE_CHARGE
(Subscription Service will charge the user on next cycle)
SUB_PENDING_PAUSE (not yet skipped a collection):
├── Upsert tag → SUB_RESUMED (archived)
└── membership.event_type = UNPAUSE
SUB_PENDING_RESUME or SUB_RESUMED:
└── 403 "user is already unpaused"
│
▼
Write new membership record:
status: (empty — good standing)
pause_date: cleared
pause_duration_months: 0
is_pending_downgrade: preserved from previous record
downgrade_date: preserved from previous record
│
▼
Send unpause notification via Segment
│
▼
Return 201 Created
When a user unpauses after a collection has already been skipped (SUB_PAUSED), the UNPAUSE_CHARGE event type signals the Subscription Service to collect payment on the next cycle. When unpausing before any collection was skipped (SUB_PENDING_PAUSE), no charge is needed.
|
The is_pending_downgrade and downgrade_date fields are explicitly preserved during unpause so that a scheduled downgrade is not lost.
|
Automatic Resume (Subscription Service)
When the pause duration expires naturally, the Subscription Service fires a pause-resume event on the prod-subscriptions Kinesis stream. The subscription worker handles this event — see Event Flows: Subscription Worker.
Related Pages
-
User Lifecycle — Account cancellation (user status
PAUSED) -
Membership Downgrades — Pending downgrade preserved during unpause
-
Event-Driven Flows —
pause-skippedandpause-resumeevent handling -
Users & Data Model —
MEMBERSHIP_PAUSE_STATUStag reference