Add/Edit a Step
Add/Edit a Step
Why edit a step, what’s editable, and what locks it down
A step (UsageCommitmentStep) is a value tier within a schedule. It carries the commitmentValue, commitmentUnitType, pricePriority, prepaid config, and the base billing terms it tracks. Edit a step (rather than a schedule) when the change concerns the amount or prepaid config of one tier, without touching dates, cadence, or which event types are being tracked.
What’s editable on a step
Send a partial update to PATCH .../steps/:stepId. Only four fields, all optional:
commitmentValue: new commitment amountprepaidValue: new prepaid amountprepaidEnabled: toggle prepaid on or offpricePriority:LOWorHIGH
Intentionally NOT editable on a step, per the DTO’s own documentation:
commitmentUnitType: changing it would diverge from sibling steps in the schedule (all steps in a schedule must share one unit type).sequence: server-managed.- Base BT linkages: all steps in a schedule share the same event-type set, and a single step’s BT edit can’t preserve that invariant on its own. BT changes must go through the schedule-level
billingTermsToAddandbillingTermsToDelete(see Add billing terms to steps within a schedule).
Step values are absolute, not additive
Every value you send on this PATCH, pre- or post-invoice, is the new total, not a delta to add to the current value. If a step’s commitmentValue is 1000 and you PATCH with { "commitmentValue": 2000 }, the step’s value becomes 2000, not 3000. This holds even on the amendment path (below): the server computes the amendment delta internally as new − old for its own ledger bookkeeping, but the number you send is always the absolute target value, never an increment.
Edit versus amendment: what locks a step down, and how it degrades gracefully
Once a step has a finalized invoice against any of its billing terms (sent, DRAFT+READY, or notice-issued, through getFinalizedInvoiceFilter), the PATCH endpoint doesn’t reject the request outright. Instead, it routes through one of two paths:
- Pre-invoice: direct edit. The API rewrites
commitmentValue,prepaidValue,prepaidEnabled, andpricePriorityin place. Response:{ success: true, path: "edit" }. - Post-finalized-invoice: append-only amendment.
prepaidEnabledandpricePrioritybecome immutable; changing either returns400:"cannot update prepaidEnabled or pricePriority once the step is locked by a finalized invoice (sent, ready-to-send, or notice-issued); only commitmentValue and prepaidValue are amendable."commitmentValueandprepaidValueare still changeable, but instead of overwriting the original value, the API records the delta as aUsageCommitmentStepAmendmentrow, preserving the original invoiced baseline so historical invoices still reconcile. Response:{ success: true, path: "amendment" }. A no-op PATCH (deltas resolve to zero) short-circuits to{ success: true, path: "edit" }without creating an amendment row.
This is why “increase a commitment’s amount” (Example 2) works the same way whether or not the step has already been invoiced: you send the same PATCH either way, and the server decides whether it’s a direct edit or an amendment.
Separately, appending or deleting a step has its own lock: only the last (highest-sequence) step in a schedule can be deleted, and a schedule can never be left with zero steps.
When to add a new step versus a new schedule
Ask: is the amount tiering within the current date range, or does the date range, cadence, or prepay strategy itself need to change?
- New step: you’re adding a tier within the same schedule, same dates, same cadence. This is the “sequential commitment” pattern: usage fills the current step’s capacity, then rolls into the next step’s pricing (
enteredAt). - New schedule: the change is calendar-based (a new phase, different cadence, different prepay strategy); see Add/Edit a Schedule.
The hard constraint: a new step must share its schedule’s commitmentUnitType and resolve to the same event-type set as its siblings. If the new tier needs a different unit type or a fundamentally different set of event types, it can’t be a step in the existing schedule; it needs its own schedule instead.
Examples
1. Add a step (new sequential tier)
Required: scheduleId (which schedule on this commitment to append to), commitmentValue, commitmentUnitType, pricePriority, prepaidEnabled, billingTermIds (non-empty). Optional: prepaidValue, overageBillingTerms[] (each referencing a base BT already in billingTermIds through usageBillingTermId), containerConfig (naming and ERP-item overrides for the prepaid and true-up container BTs).
A successful call returns { payload: { stepId, sequence } }: sequence is server-assigned (the next ordinal in that schedule).
Caveats:
- The call returns
400:"Schedule X does not belong to commitment Y."ifscheduleIdisn’t found under this commitment. - The call returns
400:"commitmentUnitType mismatch on schedule X: existing steps use Y; cannot append a step with Z. All steps in a schedule must share the same commitmentUnitType." - The call returns
400:"non-prepaid UNITS steps can only have one associated event type..."if the new step isUNITS, non-prepaid, and its BTs map to more than one event type. - The call returns
400:"billing terms X and Y share event type Z and have overlapping date ranges..."if two of the step’s own BTs share an event type with overlapping windows. - If
prepaidEnabled: true, the schedule must already have aprepaymentScheduleType, or the call returns400:"Cannot append a prepaid step to schedule X: schedule has no prepaidScheduleType set..."; andprepaidValuemust be a positive number, or the call returns400:"prepaidEnabled=true requires a positive prepaidValue. Received: X." - Each
overageBillingTerms[].usageBillingTermIdmust also appear in this same step’sbillingTermIds, or the call returns400:"Step N: overage's usageBillingTermId X must also appear in the step's billingTermIds." - The call returns
404if the parent commitment isn’t found.
Steps are for adding sequential tiers without changing the schedule’s date range, for example: after the first $5k of usage this period, charge a different rate or BT for the next tier. If you need a new date range instead, use Add/Edit a Schedule.
2. Increase a commitment’s amount (edit a step’s commitmentValue)
The same call works whether the step is pre- or post-invoice; see the edit-vs-amendment explanation above. The response tells you which path the server took:
or
Caveats:
commitmentValuehere is the new absolute value, not an amount to add: sending15000sets the step’s value to15000regardless of what it was before. See above.- If the step has a finalized invoice and you also try to change
prepaidEnabledorpricePriorityin the same call, the API rejects the whole call, returning400:"cannot update prepaidEnabled or pricePriority once the step is locked by a finalized invoice..."Split thecommitmentValuechange into its own call. - The call returns
404:"Step X not found.", or400:"Step X does not belong to commitment Y."if thestepIdandcommitmentIdpair is wrong. - If a commitment spans multiple steps or schedules, decide which one you actually mean to bump: there’s no “increase the commitment’s total” shortcut, so edit the specific step whose tier should grow. If you need to add net-new capacity as a distinct tier rather than grow an existing one, add a step instead (Example 1).
3. Edit the prepaid amount
Or toggle prepaid on or off together with a value:
Caveats:
- Enabling prepaid on a step whose schedule has no
prepaymentScheduleTypefails, returning400:"Cannot enable prepaid on step X: schedule Y has no prepaidScheduleType set. Set the schedule's prepayment behavior (FULL_UPFRONT / PER_COMMITMENT_PERIOD / SPLIT_ACROSS_BILLING_PERIODS) before enabling prepaid." prepaidEnabled: truerequires a resolved positiveprepaidValue(from this call or the step’s existing value if you omit it), or the call returns400:"prepaidEnabled=true requires a positive prepaidValue. Resolved value (from dto or existing step state): X."prepaidEnabled: falseis inconsistent with a positiveprepaidValue, returning400:"prepaidEnabled=false is inconsistent with prepaidValue=X. Omit prepaidValue or set it to 0 when prepaid is disabled."- Once the step has a finalized invoice,
prepaidEnableditself becomes immutable (see above); onlyprepaidValuecan still change, and it changes through the amendment ledger rather than in place.
Common errors and troubleshooting
Error codes referenced in this guide
200: step patched or deleted successfully ({ success: true, path: "edit" | "amendment" }for PATCH)201: step appended successfully ({ stepId, sequence })400: validation error: shape, invariant (unit-type or event-type mismatch), or invoice-lock onprepaidEnabledandpricePriority404: step or commitment not found for this manufacturer or contract500: internal server error

