Skip to main content
This page covers the API. For the billing logic behind it, see Billing changes. For the same change in the app, see Subscriptions.
POST /v1/subscriptions/{id}/units sets a subscription’s unit count. It needs the subscriptions:write scope. Send the new total, not the difference. Saasybill applies the same rules as the Update Units dialog in the app. You choose whether to prorate, or leave it out and let the organisation’s proration defaults decide. The plan type and the direction of the change decide the document.
Units are for a plan’s recurring fee. A plan with no recurring fee (no_base_fee) has no units, and the request is refused. Metered usage isn’t set as units. Report it as events instead.

Request fields

proration_behaviour is refused unless prorate is true. effective_date is refused when the change isn’t prorated.
units can be lower than the subscription’s minimum. See Minimum units.

What happens

Saasybill classifies the new total against the units already paid for this period.

Increasing units

Prorated amounts follow the proration formula: the difference in value, multiplied by the days remaining, divided by the days in the period.
If you increase units without prorating, proration is unavailable for further increases until the next renewal. A prorated request in that state is refused with 422.

Decreasing units

The subscription’s units update straight away, but the customer has paid for the period. What happens next depends on prorate. A credit only covers units that were paid for. Units already removed without a credit aren’t credited again.
If you decrease units without prorating, proration is unavailable for further decreases until the next renewal.

Volume plans

On a volume plan, a unit change that crosses into another band re-prices every unit, so the total can move the opposite way to the units. An increase that crosses into a cheaper band lowers the total. Saasybill raises a prorated credit note instead of an invoice.
  • prorate can’t be false for this change. It answers 422 proration_required. When you leave prorate out, Saasybill prorates the change whatever the proration defaults say.
  • The credit is always issued immediately, so proration_behaviour is fixed to charge_immediately.
  • The credit note is held as a credit balance on the subscription and applied to future invoices. change.credit_note holds its id.
A decrease that crosses into a dearer band raises the total. It is a charge, so the upgrade rules apply: proration is optional, and the upgrade default decides it when you leave prorate out.

Units already paid for

Units at or below the number already paid for this period are never charged twice. If a customer drops from 10 to 8 units and then returns to 10, nothing more is billed until renewal.

When proration is fixed

proration_behaviour is fixed to charge_immediately when:
  • the subscription is in its final period, because there is no renewal to charge at
  • the renewal invoice has already been created, because there is no renewal invoice left to add to

Minimum units

A minimum is a billing floor, not a limit on units. You can set units below the subscription’s minimum, including to 0. Each period bills the larger of units and minimum_units. minimum_units on the subscription is the minimum in force: the plan’s, or the subscription’s own when that is higher. units stays what you set. For example, a subscription has a minimum of 10 units and is paid for 15.
  • Prorating a decrease to 6 credits only the 5 units between 15 and 10. The drop from 10 to 6 changes units and credits nothing.
  • Setting 6 and later prorating an increase to 15 charges only the 5 units between 10 and 15.
  • Moving from 8 to 5 changes units and raises no document. The period already bills the minimum.
A change that costs nothing extra has nothing to prorate. Naming prorate: true for it is refused with 422 proration_not_available. Send prorate: false, or leave prorate out. units_unchanged compares your total with units, not with the minimum.
You can’t change a subscription’s own minimum through the API after it is created. A new subscription takes minimum_units at creation. See Create a subscription.

Proration defaults

When a request leaves prorate out, the organisation’s proration defaults decide whether to prorate. They are off unless an admin switches them on, so an integration that never sends prorate behaves as it always has. An owner or admin can change them under Developer Settings, then Configuration. The setting is called Proration Behaviour. It has two parts. The rules:
  • A prorate in the request always wins. It is checked exactly as it always was, and the defaults aren’t consulted.
  • The outcome picks the setting, not the direction. A change that charges follows Upgrades. A credit you could decline follows Downgrades.
  • A default prorates from today. effective_date defaults to today in the subscription’s timezone. You can still send effective_date to choose another day in the current period.
  • A default yields instead of refusing. If the change can’t be prorated, the default doesn’t prorate and the request goes through. That is when there is nothing to prorate, when a non-prorated change has already locked proration for the period, or when the timing is fixed. A volume increase that lowers the total is credited immediately whatever the settings say.
  • A change left unprorated counts as non-prorated. It locks proration in that direction until the next renewal, exactly as prorate: false would.
  • Fields that contradict the outcome are still refused. proration_behaviour without prorate: true is refused whatever the settings say. So is effective_date when the defaults leave the change unprorated, with 422 effective_date_not_applicable.
The response says what happened. change.proration_source is organisation_default when the settings decided, and change.prorated says whether the change was prorated.
The defaults apply to API requests only. The Update Units dialog in the app always asks.
A sandbox copies the setting when you copy settings to it. See Testing.

After the renewal invoice is created

Saasybill creates a renewal invoice ahead of the renewal date, set by Invoice Creation Date in Company Settings. Once that has happened, a unit change touches two periods: the current one, and the next one that’s already been invoiced. Then a document has to carry the change. You choose it per request with document, or once for the organisation. Before the renewal invoice exists, document is refused. After it exists, document is optional.

Set a default for the organisation

Amending changes a document the customer may already hold, so the default is separate. An owner or admin can change it under Developer Settings, then Configuration. The setting is called Changes After Invoicing. A document in the request always wins over the setting.
If the setting is Amend and the renewal invoice can no longer be changed, for example because it has a payment or credit on it, Saasybill raises a separate invoice instead. It doesn’t refuse the request. The response says so in change.document_fallback_reason. A request that names amend itself is refused in that case, and the error says why.
A sandbox copies the setting when you copy settings to it. See Testing.
With amend, change.renewal_invoice is the updated renewal invoice. With separate, change.invoice is the new invoice. change.document tells you which happened.

The response

The response is the updated subscription with a change object.
renewal_value in the response is what the next renewal will bill at the new units.

Example

Send an Idempotency-Key built from your own change event. The key is optional here, but a retried seat change without one could be applied twice.

When a change is refused

A unit change is refused with 422 when it breaks a rule. See the unit change codes for each.
If your platform sends the current total on every sync, treat units_unchanged as success. The subscription already has the units you wanted.
A 409 xero_accounts_not_mapped means an admin needs to map the Xero accounts on the Integrations tab before units can change.