Provider price changes and simulation
Provider prices change. This guide shows how a catalog update reaches your cost models, the policy that decides what happens to your prices, and how to replay recorded events and costs against a candidate before it goes live.
Every amount in the examples is illustrative, and so are the providers (Vidlab, Textco) and the catalog entries shown: none is a real provider price.
Before you start
- You know the parts of pricing from cost and who controls each variable.
- You have a cost model and a price rule, and some recorded usage and costs to replay.
When a provider changes its prices
Providers change prices, sometimes on a date they announce in advance. Runledger keeps three things apart: the rates you pay, what you charge, and history.
The catalog moves for you. When a provider changes a price, Runledger publishes a new catalog price version with the date it starts, its source and what changed:
Every account with a cost model on that catalog model gets a matching rate card straight away, scheduled until effective_from, and a catalog.price_changed webhook. A notification rule on the same event emails people. The rate card shows what changed and what it touches:
- Your costs follow the catalog from its date. Costs and events dated before
effective_fromkeep the version that priced them, and nothing already recorded is repriced. Your overrides still apply: aunit_amount_decimaloverride keeps its rate when the catalog moves, and adiscount_percentoverride is taken off the new catalog rate. - What you charge follows your policy, not the catalog. Each price rule has
on_cost_change. Withhold, the default, the rule keeps pricing from the rate card versions in force when you last saved it (priced_fromon the rule shows them), so customer prices don’t move and the margin does. A guardrail then tells you when it crosses the floor. Withfollow, the price follows the new card from its date. Underhold, repricing is a deliberate step: a person updates the rule, which makes a new version, ideally with aneffective_fromthat gives customers notice. The default isholdso that an update to the catalog never changes a customer’s price without a person choosing it. - Simulate before you choose. Replay recent usage against the new rates under each policy, naming the new catalog price in
cost_adjustments. - A correction is a new version too. If Runledger finds a catalog price was wrong, it publishes a corrected version that takes effect from the date it is published, never from an earlier date. Like every rate card version, it can’t start in the past. Costs already recorded are not repriced, and the
changeslist says what moved.
Decide what happens to your prices
In the dashboard, a catalog price change opens a review in four steps: what changed, what it touches, simulate first, and decide for each price rule it touches. You choose one of three for each rule:
- Hold is the default. Customer prices stay where they are and the margin moves. Holding writes no change to the rule, so it has an audit event of its own:
catalog.price_change_held, with theactorwho held it and the price change (catp_...). The choice, who made it and when, shows on the review. Through the API it isPOST /v1/catalog/prices/{catalog_price}/holdwith theprice_ruleand an optionalnote; a rule already onfollowis refused with409andinvalid_state, since it reprices from the new card’s date anyway. Read every decision on a change withGET /v1/audit_events?resource={catalog_price}. - Follow sets
on_cost_changetofollow, with aneffective_fromthat gives customers notice. - Reprice opens the rule to edit, which makes a new version of the rule that applies from its
effective_fromonward. It changes what you charge from then, never usage already recorded.
An Admin decides in live. A Collaborator can review it, run the simulation, and propose the decision for an Admin to approve. A Collaborator’s hold on a rule that is already on hold is recorded at once, since it changes no price. There is no separate un-hold: a held rule is changed by deciding again. Choosing follow or reprice is an ordinary price rule update, recorded in the audit log as one, and choosing hold again records another catalog.price_change_held. Costs already recorded keep their rates whatever you choose.
A custom cost model changes the old way. You add the rates yourself, as a rate card with the date they start. The response is the same shape with source: "custom":
Rules for a new rate card version. It must start after every earlier version of the cost model, including one that is still scheduled, and its effective_from can’t be in the past. A request that breaks either rule returns 400 with parameter_invalid on effective_from. A new version applies only from its start date onward. Recorded usage is never repriced: usage already recorded keeps the version that priced it.
A draft rate card (one created without effective_from) can be activated with POST /v1/rate_cards/{id}/activate, under the same two rules. The rate_card.activated webhook fires when a card becomes active. In live, an agent can’t write a rate card or an override directly, so it proposes one like any other configuration change. Every change is in the audit log with who made it.
Simulation
A simulation replays recorded events and costs from a past range against a candidate: some new configuration, some cost adjustments, or both. It writes nothing to balances, invoices or live configuration.
The result sets the margin the range had next to the margin it would have had:
- Costs priced from your cost models are priced again from their usage lines. Costs you reported yourself with
coststay as reported. - A cost adjustment can be a
multiplier, or acatalog_pricefor a catalog cost model: replay the range with the lines priced from that catalog price version, such as one scheduled for next month. Your overrides still apply. - Outcome charges and their reversals stay as recorded, so the margin still counts reversals.
- A candidate
documentcan carry cost models, price rules and guardrails, which replace the objects with the same key for the simulation only. - A range is at most 31 days. A simulation can return
pending; fetch it withGET /v1/simulations/{id}, or wait forsimulation.completed. - It works with sandbox and live keys, because it only reads.
Next steps
- Catalog, cost models and overrides covers the objects a price change touches.
- Configuration covers the dry run, apply and proposal steps that follow a simulation.