Plan changes and proration
Open in
A Customer changes plan or seats at any time, and proration settles the part of the period already paid, to the cent the preview showed.
One period, day by day
Follow one Customer through a period. The portal, the API, a Customer's agent and your operator all make these moves the same way.
| Day | The Customer | The bill |
|---|---|---|
| 1 | Starts the period on its plan | The plan for the whole period |
| 16 | Previews a move up, with more seats | Nothing yet: each line in words, the new allowance less what the period used, valid for 30 minutes |
| 16, confirmed | Sends the change that names the preview | Exactly the preview's total: the old plan's unused part credited, the new plan's rest charged, by the second |
| 20 | Adds one more seat | That seat, prorated as a move up |
| 25 | Removes a seat, or picks a smaller plan | Nothing now: the seat stays paid and usable, and the move down waits for renewal |
| 26 | Tries a second change | 409 change_scheduled, until the waiting move down is cancelled |
| Renewal | Starts the next period | The seat released, the smaller plan applied, and the new period charged |
Usage metered before a move up bills at the old plan's prices. proration.bill: charges the difference on an invoice that day; 'next' adds its lines to the next invoice.
Up, down and sideways
Every rule reads the Offer. Find the change, then read across.
| The change | It applies | It bills |
|---|---|---|
A move up, proration.up: | At once | The unused part of the old plan credited, the rest of the new one charged |
A move down, proration.down: | At the period's end | Nothing; with 'now', the unused part becomes credit on the Customer's balance, never cash |
| A seat added | At once | Prorated, as a move up |
| A seat removed | At renewal | Nothing more; the seat stays usable until then |
| Monthly to yearly | At once | The month's unused part credited |
| Yearly to monthly | At renewal | The new period at the monthly price |
| Any change during a trial | To the plan the trial converts to | Nothing until the trial ends |
| A move up while past due | Once the failed balance is paid | A move down is always allowed |
| Leaving a grandfathered price | At once, and the preview says so first | The price changes.grandfather kept is given up |
An agent changes a plan only inside a mandate that covers the new price; otherwise it gets 403 mandate_exceeded.
Previewed to the cent
The preview is the contract for the change. The change that names it charges its total and nothing else.
Example
The sales product moves a Customer up at once, bills the difference that day, and keeps a move down for renewal.
import { define } from 'apis.dev'
/** Deals for each Customer, on a free plan or a Team plan by the seat, changed at any time. */
export default define({
name: 'pipeline', domain: 'pipeline.apis.dev',
Company: { name: 'string', domain: 'url!#', deals: '<-Deal.company' },
Deal: {
title: 'string', amount: 'decimal(12, 2)', company: '->Company.deals',
stage: { open: [], qualify: 'opened', win: 'qualified', lose: ['opened', 'qualified'] },
},
offer: {
Free: { month: 0, seats: 1, includes: { 'Deal.open': 25 } },
Team: { month: 49, seats: 5, seat: 9, includes: { 'Deal.open': 1000 } },
'Deal.open': 0.001,
proration: { up: 'now', down: 'renewal', bill: 'now' },
},
})A Customer on Free previews Team with seven seats on 2026-10-16, halfway through its period.
HTTP/1.1 200 OK
Content-Type: application/ json
X-Cost: price= 0, currency= "USD"
X-Request-Id: req_e6a03b9d27c14f8805b2
{
"id": "prv_5",
"from": "Free",
"to": { "plan": "Team", "seats": 7 },
"applies": "2026-10-16T00: 00: 00Z",
"lines": [
{ "for": "Team, 16 of 31 days", "amount": 25.29 },
{ "for": "2 seats past 5, 16 of 31 days", "amount": 9.29 }
],
"total": 34.58,
"currency": "USD",
"bill": "now",
"next": { "at": "2026-11-01T00: 00: 00Z", "amount": 67 },
"includes": { "Deal.open": 975 },
"expires": "2026-10-16T00: 30: 00Z"
}The Customer's admin sends POST https:; your operator calls Subscription.preview on the Operator plane. POST /subscription/ with prv_5 charges exactly the total.
The lines a change leaves
Stripe carries every line on the invoice and the receipt, in words a finance team reads.
Each line names the plan, the days, the seats and the amount. Every line is also an Event on your sales record, so revenue and the bill agree.
A plan change is previewed, then applied or scheduled, or expired after 30 minutes, or cancelled. Each proration line is pending, then invoiced, then paid by Stripe or credited to the balance. A removed seat stays paid until renewal, then is released.
Agreements and price changes
The terms one Customer signs beyond the posted Offer, and the rules that carry every price change to every Customer.
Price and offer variants
Test another price, plan or trial on a share of new Customers: assignment kept for good, every exposure recorded, and the analysis per variant.