subscription.plan_change_scheduled
Fired when a downgrade or interval change is scheduled for the end of the billing period.
Payload
All webhook payloads follow a consistent top-level structure with event-specific data nested within the data object.
The subscription ID.
The customer ID. Returns your externalId if you provided one when creating the customer, otherwise returns the Commet publicId.
Current status — the subscription stays usable.
The plan currently in effect (id and name).
The plan that takes effect at effectiveAt (id and name).
The current billing interval.
The new billing interval, if the change includes one. Null when only the plan changes.
ISO 8601 datetime when the change executes (the billing period end).
{
"event": "subscription.plan_change_scheduled",
"timestamp": "2026-04-15T12:00:00.000Z",
"organizationId": "org_abc123",
"mode": "live",
"apiVersion": "2026-05-25",
"data": {
"subscriptionId": "sub_1a2b3c4d",
"customerId": "user_123",
"status": "active",
"currentPlan": {
"id": "plan_pro",
"name": "Pro"
},
"scheduledPlan": {
"id": "plan_starter",
"name": "Starter"
},
"billingInterval": "monthly",
"scheduledBillingInterval": null,
"effectiveAt": "2026-04-25T00:00:00.000Z"
}
}Scheduled plan change lifecycle
Commet is fair by default: changes that benefit the customer (upgrades) apply immediately, changes that reduce what they get (downgrades, shorter intervals) apply at the end of the period they already paid for.
| Moment | Event | What to do |
|---|---|---|
| Downgrade requested | subscription.plan_change_scheduled | Show "changing to {scheduledPlan.name} on {effectiveAt}". Keep current plan access. |
| A different change replaces it | subscription.plan_change_revoked + subscription.plan_change_scheduled | Update the notice to the new target plan. |
| Billing period ends | subscription.plan_changed | Apply the new plan's entitlements. |
Immediate upgrades skip this event entirely — they fire subscription.plan_changed right away.
How is this guide?