Skip to navigation

Create a price rule

A price rule works out the price of a usage event. cost_plus prices the cost of the usage from the cost catalog plus a markup or a target margin. table looks the price up by dimension values, and formula evaluates an expression. Any of them can have a floor and a cap. A plan rate uses a rule through price_rule. In live mode an agent’s write is refused with 403 and proposal_required: put the same object in a config proposal. People change live configuration in the dashboard.

Authentication

AuthorizationBearer

A secret key (rl_sk_test_..., rl_sk_live_...) or a restricted key with limited scopes (rl_rk_test_..., rl_rk_live_...). A restricted key can carry agent: true and an expires_at, and is what a coding agent should hold. Keys end in a checksum so secret scanners can recognize them. People create, roll and revoke keys in the dashboard; GET /v1/api_keys lists them, masked.

Headers

Runledger-Dry-RunbooleanOptional

Send true to see what the write would do without doing it. The response is the normal body with dry_run: true, changes (path, before, after), warnings and errors added, and nothing is stored. Validation runs as it would for a real write. The request is kept in request logs, tagged Dry run. When the same change is then made for real, its audit event has dry_run_preceded: true.

Request

This endpoint expects an object.
namestringRequired<=128 characters
currencystringRequired

Lowercase ISO 4217 code. Must match the currency of the rate cards the rule reads and of the plans that use it: Runledger doesn’t convert between currencies, so a rule that mixes them is a validation error.

keystringRequired<=64 characters
methodenumRequired

cost_plus: the cost of the usage plus a markup or a target margin. table: a price looked up by dimension values. formula: an expression.

Allowed values:
descriptionstring or nullOptional<=200 characters

The label on invoices. On a change, null clears it.

basisenumOptionalDefaults to catalog

What cost_plus and formula rules take the cost from. catalog: the cost models and the event’s quantity and properties, the same at check() and track(). run_cost: the costs trace() recorded under the event’s run_id, for pass-through pricing after the work. check() can’t know that cost yet, so it prices from estimated_usage or, without it, from the observed average.

Allowed values:
inputslist of objectsOptional

cost_plus and formula rules on the catalog basis.

markup_percentstringOptional

cost_plus. The price is the cost times one plus this, as a decimal string: "0.4" is cost plus 40%. Send this or target_margin_percent.

target_margin_percentstringOptional

cost_plus. The price is the cost over one minus this, so the margin is this share of the price, as a decimal from 0 to 1. "0.3" is a 30% margin.

rowslist of objectsOptional

table rules.

formulastringOptional<=500 characters

formula rules. An expression over cost, quantity and numeric event properties written properties.<name>, with +, -, *, /, brackets, min(), max() and ceil(). It returns minor units of currency.

floor_decimalstring or nullOptional

The lowest price for one event, in minor units. The price is raised to it. On a change, null clears it.

cap_decimalstring or nullOptional

The highest price for one event, in minor units. The price is lowered to it. On a change, null clears it.

credit_value_decimalstringOptional

Prepaid plans. Minor units of currency that one credit is worth, used to turn the price into credits. Defaults to the plan’s top-up price per credit.

on_cost_changeenumOptionalDefaults to hold

What the rule does when a new rate card changes the cost it prices from, whether you added it or Runledger published a new catalog price. hold: it keeps pricing from the versions in force when you last saved it, so the margin moves and a person decides whether to change the price from a later date. follow: the price follows the new card from its effective_from.

Allowed values:
effective_fromdatetimeOptional
When this version starts to price events. Default now.

Response

The new price rule, at version 1.
idstring
object"price_rule"
keystring
namestring
versioninteger
Starts at 1. Every update adds one.
methodenum

cost_plus: the cost of the usage plus a markup or a target margin. table: a price looked up by dimension values. formula: an expression.

Allowed values:
currencystring
The rule's currency. It matches its rate cards and the plans that use it. Prices are never converted.
on_cost_changeenumDefaults to hold

What the rule does when a new rate card changes the cost it prices from, whether you added it or Runledger published a new catalog price. hold: it keeps pricing from the versions in force when you last saved it, so the margin moves and a person decides whether to change the price from a later date. follow: the price follows the new card from its effective_from.

Allowed values:
livemodeboolean
created_atdatetime
descriptionstringOptional
effective_fromdatetimeOptional
basisenumOptionalDefaults to catalog

What cost_plus and formula rules take the cost from. catalog: the cost models and the event’s quantity and properties, the same at check() and track(). run_cost: the costs trace() recorded under the event’s run_id, for pass-through pricing after the work. check() can’t know that cost yet, so it prices from estimated_usage or, without it, from the observed average.

Allowed values:
inputslist of objectsOptional
markup_percentstringOptional
target_margin_percentstringOptional
rowslist of objectsOptional
formulastringOptional
floor_decimalstringOptional
cap_decimalstringOptional
credit_value_decimalstringOptional
priced_fromlist of objectsOptional

The rate card versions this version prices from. Under hold, they stay until the rule is saved again.

actorobjectOptional

Who made a change. Runledger fills it in from the credential, so a client can’t claim to be someone else: an API key always acts as itself, the dashboard calls the API as the signed-in person, and the MCP server passes the client’s name and the person it acts for. A request that sends a Runledger-Actor header is refused with 400 parameter_unknown.

Errors

400
Bad Request Error
401
Unauthorized Error
403
Forbidden Error
409
Conflict Error
422
Unprocessable Entity Error
429
Too Many Requests Error
500
Internal Server Error