Draft schedules

How to prepare a pricing plan schedule without it taking effect, get it reviewed, and put it live in a controlled step.


Why this matters

A subscription schedule created without a status is live immediately: the moment it exists it is part of the subscription and will bill. That makes a multi-step change risky, because a half-finished renegotiation is indistinguishable from a live one, and it leaves nowhere for a change to sit while someone reviews it.

A schedule carries an explicit status. A DRAFT schedule is stored against the subscription but is not part of billing until it is activated, so a change can be built up, checked, and released as one deliberate act.

How it works

StatusMeaning
DRAFTStored on the subscription, not part of billing
ACTIVEIn effect; bills according to its dates and pricings

status defaults to ACTIVE when the field is absent, so integrations written before this existed keep behaving exactly as they did. Activation is one-way in the sense that there is an activate action and no matching deactivate: a released change is corrected by patching or ending the schedule, not by pulling it back into review.

Two read-only fields sit alongside status.

approval_status carries the state of the approval request associated with the schedule: PENDING_REVIEW, APPROVED, DECLINED or CANCELLED. It records what a reviewer decided, not whether the schedule is live.

is_current says whether the schedule is in effect right now, combining its start_at/end_at window with an ACTIVE status. Read it when you want to know what the customer is on today without evaluating both yourself.

Create a draft

Pass status: "DRAFT" when creating the schedule.

Create a draft schedule
$curl -X POST https://test.api.solvimon.com/v1/pricing-plan-schedules \
> -H "X-API-KEY: <apiKey>" \
> -H "Content-Type: application/json" \
> -d '{
> "pricing_plan_subscription_id": "ppsu_example",
> "pricing_plan_version_id": "ppve_example",
> "status": "DRAFT",
> "start_at": "2026-10-01T00:00:00Z",
> "enabled_pricings": [
> { "pricing_id": "pric_enterprise_seat" }
> ]
> }'
Response
1{
2 "object_type": "PRICING_PLAN_SCHEDULE",
3 "id": "ppsc_example",
4 "pricing_plan_subscription_id": "ppsu_example",
5 "pricing_plan_version_id": "ppve_example",
6 "status": "DRAFT",
7 "start_at": "2026-10-01T00:00:00Z",
8 "enabled_pricings": [
9 { "pricing_id": "pric_enterprise_seat" }
10 ]
11}

The schedule can be patched like any other while it is a draft, so a negotiation can be reflected on it as it develops. See Create a pricing plan schedule and Update a pricing plan schedule for the full schema.

Activate a draft

Activating is a dedicated action rather than a status patch, because putting a change live is the step you want to audit and to permission separately.

Activate one schedule
$curl -X POST https://test.api.solvimon.com/v1/pricing-plan-schedules/ppsc_example/activate \
> -H "X-API-KEY: <apiKey>"

A change that spans several schedules should go live in one piece. Activate them together by subscription:

Activate every draft schedule of a subscription
$curl -X POST "https://test.api.solvimon.com/v1/pricing-plan-schedules/activate?pricing_plan_subscription_id=ppsu_example" \
> -H "X-API-KEY: <apiKey>"

This batch is all-or-nothing: either every draft schedule of that subscription becomes ACTIVE or none of them do. A partial activation would leave the subscription billing against half a change, which is the outcome the batch exists to prevent. The response is the list of schedules as they now stand.

These two activate endpoints are new and are not yet covered by the generated API reference, so there is no schema page to link to yet. Both return the affected schedules.

Permissions

Two permissions separate preparing a change from releasing it, so the people who negotiate a contract are not necessarily the people who can make it bill.

PermissionGoverns
PRICING_PLAN_SCHEDULE.WRITE_ACTIVECreating, copying or migrating a schedule that is not a draft
PRICING_PLAN_SCHEDULE.STATUS.UPDATEActivating a draft schedule

A user holding neither can still work on drafts: creating a schedule with status: "DRAFT" does not require WRITE_ACTIVE, which is what makes the review flow usable. Creating one without a status does, because the default is ACTIVE.

Quote schedules are exempt from both. They are governed by the quote permissions instead, so an approval policy on quotes continues to work unchanged.

Interaction with subscription initialisation

A subscription cannot start life billing against a schedule that nobody has released. When a subscription is initialized with status ACTIVE, its earliest schedule by start_at must be ACTIVE too; a draft there is rejected with a validation error on pricing_plan_schedules naming the offending start_at.

Later schedules may be drafts. A subscription that is live today with a renegotiation pending for next quarter is exactly the case this supports. See Subscription initialisation for the rest of the rules.

Copy and migrate

copy and migrate carry the source schedule’s status across when you do not set one explicitly, so copying a draft gives you another draft and copying an active schedule gives you an active one. Set status on the request to override that.

The permission check follows the effective status, not the one you sent: copying an active schedule without naming a status still requires WRITE_ACTIVE, because the result would be an active schedule.

Via Desk

Draft schedules are built and released from the subscription screen. Add a schedule as a draft, edit it while it stays one, and activate it from the row actions or the context menu. The schedule list marks drafts apart from live schedules, so a pending renegotiation is visible without being read as the customer’s current terms.

Activation follows the permissions above. A user without PRICING_PLAN_SCHEDULE.STATUS.UPDATE can prepare and edit a draft but sees the activate action disabled, rather than discovering the restriction when the save fails.

Edge cases

  • ACTIVE is the default. Omitting status on create produces a live schedule, so a workflow that means to stage a change has to be explicit about it.
  • There is no deactivate action. Correct a mistake on an activated schedule by patching or ending it. Patching a live schedule requires WRITE_ACTIVE, where patching a draft does not.
  • Drafts still occupy their dates. A draft with the same start_at as another schedule is a conflict waiting to happen at activation time, not at creation time. Check the subscription’s schedules before activating a batch.
  • approval_status is read-only. It reflects the approval request attached to the schedule; it is not a way to activate one.