> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runledger.co/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runledger.co/_mcp/server.

# List configuration proposals

GET https://api.runledger.co/v1/config_proposals

Reference: https://docs.runledger.co/api-reference/config-proposals/list

## Authentication

- `Authorization` header (bearer token, required) — 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.

## Request

### Query parameters

- `status` (enum, optional) — Only proposals with this status.
  - Allowed values: `pending`, `approved`, `rejected`, `expired`
- `proposed_by_type` (enum, optional) — Only proposals from this kind of actor.
  - Allowed values: `user`, `agent`, `key`, `rule`, `system`
- `created_after` (datetime, optional) — Only objects created at or after this time.
- `created_before` (datetime, optional) — Only objects created before this time.
- `limit` (integer, optional, default: 20) — Maximum number of objects to return, from 1 to 100.
- `cursor` (string, optional) — The `next_cursor` from the previous page.

## Response

### 200

A page of proposals.

- `object` ("list", required)
- `data` (list of ConfigProposal, required)
- `has_more` (boolean, required)
- `next_cursor` (string, optional)

## Errors

### 400 Bad Request Error

The request was malformed or a parameter was invalid. A configuration that fails validation, such as two equally specific overrides with overlapping dates, returns `config_invalid`.

- `type` (string, required) — A URI for the error type. It links to the error codes table in the docs, where each `code` is listed with its cause. Branch on `code`, not on `type`.
- `title` (string, required) — A short summary of the error type.
- `status` (integer, required) — The HTTP status code.
- `code` (enum, required) — A stable, machine-readable error code. New codes may be added.
  - Allowed values: `invalid_request`, `parameter_missing`, `parameter_invalid`, `parameter_unknown`, `authentication_failed`, `permission_denied`, `insufficient_scope`, `proposal_required`, `config_invalid`, `livemode_mismatch`, `resource_not_found`, `resource_already_exists`, `idempotency_key_in_use`, `idempotency_key_reused`, `invalid_state`, `outcome_charge_locked`, `hold_not_active`, `insufficient_balance`, `credit_limit_exceeded`, `plan_locked`, `test_clock_advancing`, `provider_error`, `rate_limited`, `api_error`, `service_unavailable`
- `request_id` (string, required) — The ID of the request. Quote it to support, or look it up in request logs.
- `retryable` (boolean, required) — Whether sending the same request again, with the same idempotency key, may succeed.
- `detail` (string, optional) — An explanation of this occurrence. For people; don't parse it.
- `param` (string, optional) — The request parameter at fault, as a dotted path.
- `required_scope` (string, optional) — The scope the call needed, on `insufficient_scope`. Ask a person to widen the key, or reconnect with it.
- `errors` (list of ConfigIssue, optional) — Only with `config_invalid`: each problem the configuration document has, with the path of the part at fault, so an agent can fix the document without a second call.
- `blocked_by` (list of ErrorResponseBlockedByItems, optional) — On an `invalid_state` that names the objects in the way, the objects that must change first. Removing a payment provider lists every routing rule that uses it (`object: routing_rule`) and every customer whose own `payment_provider` names it (`object: customer`), in one list.
- `doc_url` (string, optional) — A link to the error codes table in the docs, where this `code` is listed with its cause.

### 401 Unauthorized Error

The API key is missing, invalid or revoked.

- `type` (string, required) — A URI for the error type. It links to the error codes table in the docs, where each `code` is listed with its cause. Branch on `code`, not on `type`.
- `title` (string, required) — A short summary of the error type.
- `status` (integer, required) — The HTTP status code.
- `code` (enum, required) — A stable, machine-readable error code. New codes may be added.
  - Allowed values: `invalid_request`, `parameter_missing`, `parameter_invalid`, `parameter_unknown`, `authentication_failed`, `permission_denied`, `insufficient_scope`, `proposal_required`, `config_invalid`, `livemode_mismatch`, `resource_not_found`, `resource_already_exists`, `idempotency_key_in_use`, `idempotency_key_reused`, `invalid_state`, `outcome_charge_locked`, `hold_not_active`, `insufficient_balance`, `credit_limit_exceeded`, `plan_locked`, `test_clock_advancing`, `provider_error`, `rate_limited`, `api_error`, `service_unavailable`
- `request_id` (string, required) — The ID of the request. Quote it to support, or look it up in request logs.
- `retryable` (boolean, required) — Whether sending the same request again, with the same idempotency key, may succeed.
- `detail` (string, optional) — An explanation of this occurrence. For people; don't parse it.
- `param` (string, optional) — The request parameter at fault, as a dotted path.
- `required_scope` (string, optional) — The scope the call needed, on `insufficient_scope`. Ask a person to widen the key, or reconnect with it.
- `errors` (list of ConfigIssue, optional) — Only with `config_invalid`: each problem the configuration document has, with the path of the part at fault, so an agent can fix the document without a second call.
- `blocked_by` (list of ErrorResponseBlockedByItems, optional) — On an `invalid_state` that names the objects in the way, the objects that must change first. Removing a payment provider lists every routing rule that uses it (`object: routing_rule`) and every customer whose own `payment_provider` names it (`object: customer`), in one list.
- `doc_url` (string, optional) — A link to the error codes table in the docs, where this `code` is listed with its cause.

### 403 Forbidden Error

The key can't do this. A restricted or agent key lacks the scope (`insufficient_scope`, with `required_scope`), a live key called a sandbox-only endpoint, an agent tried a live config write that needs a proposal (`proposal_required`), or something only a signed-in person can do.

- `type` (string, required) — A URI for the error type. It links to the error codes table in the docs, where each `code` is listed with its cause. Branch on `code`, not on `type`.
- `title` (string, required) — A short summary of the error type.
- `status` (integer, required) — The HTTP status code.
- `code` (enum, required) — A stable, machine-readable error code. New codes may be added.
  - Allowed values: `invalid_request`, `parameter_missing`, `parameter_invalid`, `parameter_unknown`, `authentication_failed`, `permission_denied`, `insufficient_scope`, `proposal_required`, `config_invalid`, `livemode_mismatch`, `resource_not_found`, `resource_already_exists`, `idempotency_key_in_use`, `idempotency_key_reused`, `invalid_state`, `outcome_charge_locked`, `hold_not_active`, `insufficient_balance`, `credit_limit_exceeded`, `plan_locked`, `test_clock_advancing`, `provider_error`, `rate_limited`, `api_error`, `service_unavailable`
- `request_id` (string, required) — The ID of the request. Quote it to support, or look it up in request logs.
- `retryable` (boolean, required) — Whether sending the same request again, with the same idempotency key, may succeed.
- `detail` (string, optional) — An explanation of this occurrence. For people; don't parse it.
- `param` (string, optional) — The request parameter at fault, as a dotted path.
- `required_scope` (string, optional) — The scope the call needed, on `insufficient_scope`. Ask a person to widen the key, or reconnect with it.
- `errors` (list of ConfigIssue, optional) — Only with `config_invalid`: each problem the configuration document has, with the path of the part at fault, so an agent can fix the document without a second call.
- `blocked_by` (list of ErrorResponseBlockedByItems, optional) — On an `invalid_state` that names the objects in the way, the objects that must change first. Removing a payment provider lists every routing rule that uses it (`object: routing_rule`) and every customer whose own `payment_provider` names it (`object: customer`), in one list.
- `doc_url` (string, optional) — A link to the error codes table in the docs, where this `code` is listed with its cause.

### 429 Too Many Requests Error

Too many requests. Wait for the number of seconds in `Retry-After`.

- `type` (string, required) — A URI for the error type. It links to the error codes table in the docs, where each `code` is listed with its cause. Branch on `code`, not on `type`.
- `title` (string, required) — A short summary of the error type.
- `status` (integer, required) — The HTTP status code.
- `code` (enum, required) — A stable, machine-readable error code. New codes may be added.
  - Allowed values: `invalid_request`, `parameter_missing`, `parameter_invalid`, `parameter_unknown`, `authentication_failed`, `permission_denied`, `insufficient_scope`, `proposal_required`, `config_invalid`, `livemode_mismatch`, `resource_not_found`, `resource_already_exists`, `idempotency_key_in_use`, `idempotency_key_reused`, `invalid_state`, `outcome_charge_locked`, `hold_not_active`, `insufficient_balance`, `credit_limit_exceeded`, `plan_locked`, `test_clock_advancing`, `provider_error`, `rate_limited`, `api_error`, `service_unavailable`
- `request_id` (string, required) — The ID of the request. Quote it to support, or look it up in request logs.
- `retryable` (boolean, required) — Whether sending the same request again, with the same idempotency key, may succeed.
- `detail` (string, optional) — An explanation of this occurrence. For people; don't parse it.
- `param` (string, optional) — The request parameter at fault, as a dotted path.
- `required_scope` (string, optional) — The scope the call needed, on `insufficient_scope`. Ask a person to widen the key, or reconnect with it.
- `errors` (list of ConfigIssue, optional) — Only with `config_invalid`: each problem the configuration document has, with the path of the part at fault, so an agent can fix the document without a second call.
- `blocked_by` (list of ErrorResponseBlockedByItems, optional) — On an `invalid_state` that names the objects in the way, the objects that must change first. Removing a payment provider lists every routing rule that uses it (`object: routing_rule`) and every customer whose own `payment_provider` names it (`object: customer`), in one list.
- `doc_url` (string, optional) — A link to the error codes table in the docs, where this `code` is listed with its cause.

### 500 Internal Server Error

Something went wrong on Runledger's side. Safe to retry with the same idempotency key.

- `type` (string, required) — A URI for the error type. It links to the error codes table in the docs, where each `code` is listed with its cause. Branch on `code`, not on `type`.
- `title` (string, required) — A short summary of the error type.
- `status` (integer, required) — The HTTP status code.
- `code` (enum, required) — A stable, machine-readable error code. New codes may be added.
  - Allowed values: `invalid_request`, `parameter_missing`, `parameter_invalid`, `parameter_unknown`, `authentication_failed`, `permission_denied`, `insufficient_scope`, `proposal_required`, `config_invalid`, `livemode_mismatch`, `resource_not_found`, `resource_already_exists`, `idempotency_key_in_use`, `idempotency_key_reused`, `invalid_state`, `outcome_charge_locked`, `hold_not_active`, `insufficient_balance`, `credit_limit_exceeded`, `plan_locked`, `test_clock_advancing`, `provider_error`, `rate_limited`, `api_error`, `service_unavailable`
- `request_id` (string, required) — The ID of the request. Quote it to support, or look it up in request logs.
- `retryable` (boolean, required) — Whether sending the same request again, with the same idempotency key, may succeed.
- `detail` (string, optional) — An explanation of this occurrence. For people; don't parse it.
- `param` (string, optional) — The request parameter at fault, as a dotted path.
- `required_scope` (string, optional) — The scope the call needed, on `insufficient_scope`. Ask a person to widen the key, or reconnect with it.
- `errors` (list of ConfigIssue, optional) — Only with `config_invalid`: each problem the configuration document has, with the path of the part at fault, so an agent can fix the document without a second call.
- `blocked_by` (list of ErrorResponseBlockedByItems, optional) — On an `invalid_state` that names the objects in the way, the objects that must change first. Removing a payment provider lists every routing rule that uses it (`object: routing_rule`) and every customer whose own `payment_provider` names it (`object: customer`), in one list.
- `doc_url` (string, optional) — A link to the error codes table in the docs, where this `code` is listed with its cause.

## Types

### ConfigProposal

- `id` (string, required)
- `object` ("config_proposal", required)
- `status` (enum, required) — `pending` until a person decides or 7 days pass, then `approved`, `rejected` or `expired`.
  - Allowed values: `pending`, `approved`, `rejected`, `expired`
- `document` (ConfigDocument, required) — Everything an agent or person configures, in the shape the create requests take. Leave a section out to leave it alone. Objects with a `key` are matched by key, webhook endpoints by `url`. Routing rules and notification rules are ordered lists that replace the current ones. Payment providers are connected with `POST /v1/payment_providers`, because a person completes that step.
- `changes` (list of ConfigChange, required)
- `warnings` (list of ConfigIssue, required)
- `proposed_by` (Actor, required) — 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`.
- `review_url` (string, required) — Opens the diff in the dashboard, where an Admin approves or rejects it.
- `expires_at` (datetime, required)
- `livemode` (boolean, required)
- `created_at` (datetime, required)
- `reason` (string, optional)
- `base_hash` (string, optional) — The configuration hash the proposal was made against. Approving after the configuration has changed returns `409` with `invalid_state`.
- `decided_by` (Actor, optional) — 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`.
- `decision_note` (string, optional)
- `decided_at` (datetime, optional)

### ConfigIssue

A problem or warning about a part of a configuration document.

- `code` (string, required) — A stable code. New codes may be added.
- `message` (string, required)
- `path` (string, optional) — Where in the document, as a dotted path.

### ErrorResponseBlockedByItems

- `object` (enum, required) — The kind of object in the way: a routing rule, or a customer with a `payment_provider` override.
  - Allowed values: `routing_rule`, `customer`
- `id` (string, required) — The object's ID, `rr_...` for a routing rule and `cus_...` for a customer.

### ConfigDocument

Everything an agent or person configures, in the shape the create requests take. Leave a section out to leave it alone. Objects with a `key` are matched by key, webhook endpoints by `url`. Routing rules and notification rules are ordered lists that replace the current ones. Payment providers are connected with `POST /v1/payment_providers`, because a person completes that step.

- `credit_types` (list of CreateCreditTypeRequest, optional)
- `outcome_definitions` (list of CreateOutcomeDefinitionRequest, optional)
- `plans` (list of CreatePlanRequest, optional)
- `webhook_endpoints` (list of CreateWebhookEndpointRequest, optional)
- `routing_rules` (list of CreateRoutingRuleRequest, optional)
- `notification_rules` (list of CreateNotificationRuleRequest, optional)
- `cost_models` (list of CreateCostModelRequest, optional) — Matched by key. A custom cost model's `rate_card` adds a new version only when its rates differ from the latest. A cost model from the provider price catalog (`catalog_model`) takes its rates from the catalog and carries your `overrides`.
- `price_rules` (list of CreatePriceRuleRequest, optional) — Matched by key. A change adds a version.
- `margin_guardrails` (list of CreateMarginGuardrailRequest, optional) — Matched by key.
- `spend_limits` (list of CreateSpendLimitRequest, optional) — Operator-set limits, matched by `key`, which each one needs here. Limits your customers set are data and aren't part of the document.
- `account_settings` (AccountSettings, optional)

### ConfigChange

One field that changes, with its value before and after. A removed object or field has `before` and no `after`: a replace (`PUT /v1/config`) lists each removal this way, before anything is applied.

- `path` (string, required)
- `before` (any, optional) — The current value. Left out when the field is new.
- `after` (any, optional) — The new value. Left out when the field is removed.

### Actor

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`.

- `type` (enum, required) — `user`: a signed-in person, including a person at Runledger support acting in your account, whose `name` reads `Runledger support`. `agent`: an agent connected through an OAuth session or an agent key. `key`: any other API key. `rule`: a reversal rule. `system`: Runledger itself, such as a clock.
  - Allowed values: `user`, `agent`, `key`, `rule`, `system`
- `id` (string, optional) — The user, key or agent session ID. Left out for `rule` and `system`, except a change a customer makes on the customer view, which carries the customer session's ID.
- `name` (string, optional) — A display name, such as a person's name, a key's name or the MCP client.
- `on_behalf_of` (string, optional) — The person an agent acts for, when known.
- `client` (string, optional) — The software that made the call, such as an MCP client or SDK.

### CreateCreditTypeRequest

- `key` (string, required) — Your stable identifier for the credit type. Can't change.
- `name` (string, required)
- `kind` (enum, required) — `custom` for your own unit, `monetary` for a prepaid balance in a currency.
  - Allowed values: `custom`, `monetary`
- `currency` (string, optional) — `monetary` only: the currency the balance is held in.
- `decimal_places` (integer, optional, default: 2) — Precision of amounts in this credit type.
- `unit_label` (string, optional) — How to label one unit on invoices and receipts.
- `metadata` (map from string to string, optional) — Up to 50 keys of your own, with keys up to 40 characters and values up to 500 characters.

### CreateOutcomeDefinitionRequest

Send `confirmation_window` and `reversal_horizon`, or a `preset` that fills in whichever of them you leave out. Without either, the request fails with `parameter_missing`. Values you send always win over the preset's.

- `key` (string, required) — Your stable identifier. Can't change.
- `name` (string, required)
- `event_name` (string, required) — The `track()` event that reports this outcome. Unique across outcome definitions.
- `criteria` (string, required) — What counts as this outcome, in the words you agreed with your customers. Stamped on every charge by version.
- `preset` (string, optional) — A preset `key`, such as `support_chat`, that fills in `confirmation_window`, `reversal_horizon` and `dispute_settings` where you don't send them. The stored definition holds explicit values and echoes the preset's key, so changing a preset later never changes it.
- `confirmation_window` (string, optional) — How long a new charge stays pending before it confirms by default, as an ISO 8601 duration from PT0S to P30D. Required unless `preset` is sent. Recommended PT1H to P7D; outside that range the response carries a warning.
- `reversal_horizon` (string, optional) — How long after confirmation a charge can still be reversed, as an ISO 8601 duration from PT0S to P180D. After it passes, the charge locks, unless a dispute is still open. Required unless `preset` is sent. Recommended P7D to P90D; outside that range the response carries a warning.
- `reversal_rules` (list of ReversalRule, optional)
- `dispute_settings` (DisputeSettings, optional)
- `metadata` (map from string to string, optional) — Up to 50 keys of your own, with keys up to 40 characters and values up to 500 characters.

### CreatePlanRequest

- `key` (string, required)
- `name` (string, required)
- `billing_mode` (enum, required) — `prepaid`: a credit balance that must cover charges, topped up or granted in advance. `postpaid`: charges accrue in money and are invoiced at period end.
  - Allowed values: `prepaid`, `postpaid`
- `currency` (string, required)
- `interval` (enum, required)
  - Allowed values: `month`, `year`
- `rates` (list of PlanRate, required)
- `description` (string, optional)
- `credit_type` (string, optional) — Prepaid. The credit type (ID or key) the balance is kept in.
- `recurring_fee` (Money, optional) — An amount of money in the currency's minor unit.
- `included_credits` (IncludedCredits, optional) — Prepaid. Credits granted at the start of each period.
- `top_up` (TopUpSettings, optional) — Prepaid. Lets customers buy more credits through checkout.
- `allowances` (list of Allowance, optional) — Refills and caps on the plan's balance. See [Usage windows](/guides/usage-windows).
- `pools` (list of PoolDefinition, optional) — Named balance pools, in drawdown order. See [Pools](/guides/pools).
- `per_seat` (boolean, optional) — `recurring_fee` and `included_credits` are per seat, multiplied by the subscription's `quantity`. Default false.
- `default_spend_limit` (PlanDefaultSpendLimit, optional) — The spend limit each new subscription on the plan starts with. A customer-set limit may replace it, up to `ceiling`.
- `overdraft_limit` (string, optional) — Prepaid. How far below zero the balance may go, in credits. Default "0".
- `credit_limit` (Money, optional) — An amount of money in the currency's minor unit.
- `minimum_commitment` (Money, optional) — An amount of money in the currency's minor unit.
- `invoice_grace_period` (string, optional) — Postpaid. How long after period end the invoice finalizes, so late events land. Default PT1H, maximum P3D.
- `metadata` (map from string to string, optional) — Up to 50 keys of your own, with keys up to 40 characters and values up to 500 characters.

### CreateWebhookEndpointRequest

- `url` (string, required) — An HTTPS URL.
- `enabled_events` (list of string, required) — Event types to send, or `["*"]` for all.
- `description` (string, optional)
- `metadata` (map from string to string, optional) — Up to 50 keys of your own, with keys up to 40 characters and values up to 500 characters.

### CreateRoutingRuleRequest

- `provider` (string, required) — A connected provider's name.
- `when` (RoutingConditions, optional) — Every condition you send must match. A rule with none matches everything.
- `position` (integer, optional) — Where the rule goes in the order. Defaults to last.

### CreateNotificationRuleRequest

- `event` (enum, required) — What can notify people. `dispute.decision_due` fires `lead_time` before a dispute's deadline. `proposal.pending` fires when a config proposal needs approval. `topup_chargeback.threshold_exceeded` fires when the top-up chargeback rate or the credits at risk pass the rule's `threshold`. `margin_guardrail.breached` fires when work falls below a margin guardrail's floor. `catalog.price_changed` fires when Runledger publishes a new price version for a catalog model one of your cost models uses. `auto_top_up.disabled` fires when a customer's auto top-up stops after repeated failed payments. Notification rules are for your team. Alerts about a customer's own balance or limits (`balance.low`, `usage.threshold_reached`, `spend_limit.threshold_reached`) are webhook events sent to your endpoints, and Runledger never emails your end customers.
  - Allowed values: `dispute.decision_due`, `reconciliation.breaks_found`, `proposal.pending`, `topup_chargeback.threshold_exceeded`, `margin_guardrail.breached`, `catalog.price_changed`, `auto_top_up.disabled`
- `channel` (string, required) — How the notification is delivered. The set is open and new channels may be added. Built in: `email`, which needs `recipients`, and `webhook`, which needs `webhook_endpoint`. Handle a channel you don't recognise by its name.
- `recipients` (list of string, optional) — `email` only: up to 20 addresses.
- `webhook_endpoint` (string, optional) — `webhook` only: the webhook endpoint (ID) that receives the notification.
- `lead_time` (string, optional) — `dispute.decision_due` only: how long before the deadline to send it, as an ISO 8601 duration. Default PT24H.
- `threshold` (NotificationThreshold, optional) — For `topup_chargeback.threshold_exceeded`. Send at least one. There is no default. Runledger applies no threshold you haven't set, and you choose the level for your own history.
- `payment_provider` (string, optional) — `topup_chargeback.threshold_exceeded` only: watch one provider. Leave out to watch the whole account.

### CreateCostModelRequest

- `key` (string, required) — A key you choose. Requests accept the key or the ID.
- `name` (string, required)
- `source` (enum, optional) — Where the rates come from. Default `catalog` when `catalog_model` is sent, and `custom` otherwise.
  - Allowed values: `catalog`, `custom`
- `catalog_model` (string, optional) — Takes the provider, model, kind, units, dimensions and rates from this catalog model (`catm_...`), and follows its price versions from each `effective_from`. With it, leave out `provider`, `model`, `kind`, `units`, `dimensions` and `rate_card`. Use `overrides` to change a rate.
- `overrides` (list of CostOverride, optional) — Your own rates for a catalog cost model. Not for a custom cost model, whose rate cards are already yours.
- `provider` (string, optional) — A custom cost model. The provider as `trace()` names it.
- `model` (string, optional) — A custom cost model. The model ID as the provider names it, as `trace()` sends it.
- `kind` (enum, optional) — What the model produces. Open: new values may be added.
  - Allowed values: `text`, `image`, `video`, `audio`, `speech`, `embedding`, `compute`, `agent_session`, `other`
- `units` (list of CostUnitDeclaration, optional)
- `dimensions` (list of CostDimension, optional)
- `rate_card` (RateCardInput, optional) — A custom cost model. The first rates. Without `effective_from` the card is active from the moment the model is created, and a given `effective_from` can't be in the past. In a configuration document, a `rate_card` adds a new version only when its rates differ from the latest, so applying the document twice changes nothing the second time.
- `metadata` (map from string to string, optional) — Up to 50 keys of your own, with keys up to 40 characters and values up to 500 characters.

### CreatePriceRuleRequest

- `name` (string, required)
- `currency` (string, required) — 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.
- `key` (string, required)
- `method` (enum, required) — `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: `cost_plus`, `table`, `formula`
- `description` (string, optional, nullable) — The label on invoices. On a change, `null` clears it.
- `basis` (enum, optional, default: 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: `catalog`, `run_cost`
- `inputs` (list of PriceRuleInput, optional) — `cost_plus` and `formula` rules on the `catalog` basis.
- `markup_percent` (string, optional) — `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_percent` (string, optional) — `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.
- `rows` (list of PriceTableRow, optional) — `table` rules.
- `formula` (string, optional) — `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_decimal` (string, optional, nullable) — The lowest price for one event, in minor units. The price is raised to it. On a change, `null` clears it.
- `cap_decimal` (string, optional, nullable) — The highest price for one event, in minor units. The price is lowered to it. On a change, `null` clears it.
- `credit_value_decimal` (string, optional) — 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_change` (enum, optional, default: 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: `hold`, `follow`
- `effective_from` (datetime, optional) — When this version starts to price events. Default now.

### CreateMarginGuardrailRequest

- `name` (string, required)
- `min_margin_percent` (string, required) — The lowest margin allowed, as a decimal from 0 to 1 of the price. `"0.25"` is 25%. Negative values are not accepted.
- `key` (string, required)
- `applies_to` (GuardrailScope, optional) — Every condition you send must match. A guardrail with none covers all work.
- `basis` (enum, optional, default: expected) — `expected`: the margin the work should have, from its price and its expected cost at `check()` time. `realized`: confirmed revenue minus cost over the trailing `window` for the work the guardrail covers, as the margin report counts it, so reversals lower it.
  - Allowed values: `expected`, `realized`
- `action` (enum, optional, default: warn) — `warn` adds warnings and a webhook. `block` also denies in `check()`.
  - Allowed values: `warn`, `block`
- `window` (string, optional) — An ISO 8601 duration. For `realized`, the trailing range the margin is read over. For `expected`, the range the observed average cost is read over when `check()` has no `estimated_usage`. Default P7D.
- `enabled` (boolean, optional, default: true)

### CreateSpendLimitRequest

- `amount` (Money, required) — The most money's worth of consumption allowed in the period. Its currency must be the customer's, or the plan's.
- `ceiling` (Money, optional) — The most a customer may set for themselves. A customer-set limit above the ceiling of the limit it replaces is `parameter_invalid`.
- `counts` (enum, optional, default: paid_usage) — What counts toward the limit. `paid_usage`: the money value of consumption drawn from paid credits and overage, so included and promotional usage keeps working. `all_usage`: the money value of everything. Buying credits never counts.
  - Allowed values: `paid_usage`, `all_usage`
- `action` (enum, optional, default: warn) — `warn` adds warnings and a webhook. `block` also denies in `check()`.
  - Allowed values: `warn`, `block`
- `alert_at` (list of string, optional) — Decimals from 0 to 1. `spend_limit.threshold_reached` is sent once per period for each.
- `enabled` (boolean, optional, default: true)
- `key` (string, optional) — Optional. Needed when the limit is part of a configuration document, which matches limits by key.
- `applies_to` (SpendLimitScope, optional) — Every condition you send must match, and a limit with none applies to the whole account. Limits are grouped by which of `agent_id`, `member_id`, `team_id` and `pool` they name. Inside a group the most specific one applies to a customer: its own `customer`, then its `plan`, then the account. Every group that applies must have room, so the strictest group decides.
- `period` (SpendLimitPeriod, optional)
- `set_by` (enum, optional, default: operator) — `operator`: you set it as policy. `customer`: your customer chose it, through your product or the customer view.
  - Allowed values: `operator`, `customer`

### AccountSettings

- `grant_expiry_notice` (string, optional) — How long before a credit grant expires to send `credit_grant.expiring`, as an ISO 8601 duration. Default `P7D`; `P0D` turns the notice off.
- `customer_view_branding` (CustomerViewBranding, optional) — The default branding for every customer view. A session can override it.

### ReversalRule

When a signal event with `event_name` arrives for a charge with the same customer and reference, and the condition holds, take the action. Rules run in order and the first match wins.

- `key` (string, required) — Your stable name for the rule, recorded on the reversals and disputes it creates.
- `event_name` (string, required) — The signal event this rule listens for.
- `action` (enum, required) — `reverse` reverses the charge at once. `open_dispute` opens a dispute for you to decide.
  - Allowed values: `reverse`, `open_dispute`
- `condition` (RuleCondition, optional) — A test on one property of the signal event. Numbers compare numerically.
- `applies_to` (enum, optional, default: any) — Which charges the rule applies to.
  - Allowed values: `pending`, `confirmed`, `any`
- `reason_code` (enum, optional) — Why a charge was reversed. New codes may be added.
  - Allowed values: `not_resolved`, `customer_unhappy`, `escalated_to_human`, `duplicate`, `incorrect_amount`, `other`

### DisputeSettings

- `decision_window` (string, optional) — How long you have to decide a dispute, as an ISO 8601 duration. It has no default: send it with the definition, or send a `preset`, which fills it in (`P7D` in `support_chat`). Minimum PT24H, maximum P30D. The minimum is there because `dispute.decision_due` fires 24 hours before the deadline. Recommended PT24H to P14D.
- `default_decision` (enum, optional, default: reverse) — What happens if nobody decides in time. Default `reverse`, so an ignored complaint doesn't stand.
  - Allowed values: `reverse`, `uphold`

### PlanRate

The price of one usage event or one outcome. Set exactly one of `event_name` or `outcome_definition`. Prepaid plans use `credits_per_unit`; postpaid plans use `unit_amount_decimal`, and may give each period a free `included_quantity`. A rate for an `event_name` can use `price_rule` instead of a fixed amount.

- `event_name` (string, optional) — A usage event, charged per unit of `quantity`.
- `outcome_definition` (string, optional) — An outcome definition (ID or key), charged per confirmed outcome.
- `credits_per_unit` (string, optional) — Prepaid. Credits per unit, as a decimal string.
- `unit_amount_decimal` (string, optional) — Postpaid. Minor units of the plan currency per unit, as a decimal string.
- `price_rule` (string, optional) — A price rule (ID or key) that works out the price from the event's quantity and properties. Use it instead of `credits_per_unit` or `unit_amount_decimal`, on an `event_name` rate. The rule's currency must be the plan's. The plan follows the rule's version in force when each event happens.
- `included_quantity` (string, optional) — Postpaid. Units per period charged at no cost before `unit_amount_decimal` applies, as a decimal string.
- `overage_unit_amount_decimal` (string, optional) — Postpaid. Minor units per unit past `included_quantity`. Defaults to `unit_amount_decimal`. Those units invoice as `overage` lines.
- `burn_multiplier` (string, optional) — Prepaid. Multiplies this rate's credits, on top of the pool's `burn_multiplier`. At `"2"`, a rate of 10 credits costs 20. Default `"1"`. Use it for a model that costs more than others.
- `description` (string, optional) — The label on invoices.

### Money

An amount of money in the currency's minor unit.

- `amount` (integer, required) — Minor units. 1500 is US$15.00; 1500 is ¥1,500.
- `currency` (string, required) — Lowercase ISO 4217 code.

### IncludedCredits

Prepaid. Credits granted at the start of each period.

- `amount` (string, required)
- `pool` (string, optional) — The pool (key) the credits join. Defaults to the plan's first pool.
- `rollover` (enum, optional, default: none) — What happens to unused included credits at period end.
  - Allowed values: `none`, `all`, `capped`
- `rollover_cap` (string, optional) — `capped` only: the most that carries over.

### TopUpSettings

Prepaid. Lets customers buy more credits through checkout.

- `unit_amount_decimal` (string, required) — Price per credit, in minor units of the plan currency.
- `minimum_credits` (string, optional)
- `maximum_credits` (string, optional)
- `pool` (string, optional) — The pool (key) purchased credits join. Defaults to the plan's last pool.
- `credits_expire_in` (string, optional) — How long purchased credits last, as an ISO 8601 duration from `P1D` to `P10Y`. The grant gets that `expires_at` when payment completes. Leave it out and top-up credits expire after one year (`P1Y`). A checkout session can override it. Setting an expiry that is lawful where your customers are is your responsibility.
- `low_balance_at` (string, optional) — Send `balance.low` when the pool's available balance falls to this or below, as a decimal string.
- `auto` (TopUpAutoSettings, optional) — Whether customers on the plan may have an auto top-up, and the most they can set.

### Allowance

A refill or a cap on a plan's prepaid balance. A `refill` grants `amount` into `pool` at the start of each window, as a credit grant with `source: allowance` that expires at the window's end unless `rollover` keeps some. A `cap` grants nothing and limits how much of the pool can be consumed in the window. A plan can hold several, and consumption counts against every window that applies: `check()` allows work only if each has room.

- `key` (string, required) — Unique on the plan. Webhooks and the usage summary name the allowance by it.
- `kind` (enum, required)
  - Allowed values: `refill`, `cap`
- `amount` (string, required) — Credits granted per window (`refill`) or the most that may be consumed per window (`cap`), as a decimal string.
- `window` (AllowanceWindow, required) — When an allowance refills, or the range a cap counts consumption over. `fixed` windows reset on a boundary. A `session` window opens at the first consumption after the previous one closed and lasts `duration`, like a five-hour session. A `sliding` window counts consumption over the trailing `duration`, and only a `cap` can use it.
- `pool` (string, optional) — The pool (by key) the allowance feeds or limits. A `refill` defaults to the plan's first pool, or `default` when the plan has none. A `cap` with no `pool` counts consumption from every pool.
- `scope` (enum, optional, default: customer) — `customer`: one window for the customer, shared by everyone in the organization. `per_member`: each distinct `member_id` gets its own window, and a refill grants `amount` for each member at the member's first `check()` or `track()` of the window. The members' refills land in the allowance's pool, which every member draws on, and a refill's window reports what its member used without capping them; a `per_member` cap holds each member to `amount`.
  - Allowed values: `customer`, `per_member`
- `rollover` (enum, optional, default: none) — `refill` allowances on `fixed` windows. What happens to unspent credits when the window ends. `all` and `capped` move them to a new grant with `source: rollover`.
  - Allowed values: `none`, `all`, `capped`
- `rollover_cap` (string, optional) — `capped` only: the most that carries over.
- `rollover_expires_after` (string, optional) — How long a rollover grant lasts, as an ISO 8601 duration. Default: until the end of the next window.
- `priority` (integer, optional, default: 50) — `refill` only. The priority of the grants the allowance creates. Lower numbers are drawn first inside a pool.
- `on_exhausted` (enum, optional, default: block) — `cap` only. What happens when the window is full. `block` (the default): `check()` denies the work with `allowance_exhausted`. `draw_next_pool`: skip the capped pool and draw from the pools after it in the plan's order, so a cap on `included` sends work to `purchased`. It needs `pool` to be set.
  - Allowed values: `block`, `draw_next_pool`
- `alert_at` (list of string, optional) — Decimals from 0 to 1. A webhook `usage.threshold_reached` is sent the first time `percent_used` reaches each one in a window. `["0.8", "1"]` is 80% and 100%.

### PoolDefinition

A named balance pool on a plan. The order of `pools` on the plan is the drawdown order. A pool is a balance pool and has nothing to do with the provider names in the price catalog.

- `key` (string, required)
- `name` (string, optional) — A label for dashboards and the customer view.
- `burn_multiplier` (string, optional) — Multiplies the credits charged when work is drawn from this pool. At `"1.6"`, a unit that costs 10 credits from another pool costs 16 here. Default `"1"`.

### PlanDefaultSpendLimit

The spend limit each new subscription on the plan starts with. A customer-set limit may replace it, up to `ceiling`.

- `amount` (Money, required) — An amount of money in the currency's minor unit.
- `ceiling` (Money, optional) — An amount of money in the currency's minor unit.
- `period` (SpendLimitPeriod, optional)
- `counts` (enum, optional, default: paid_usage) — What counts toward the limit. `paid_usage`: the money value of consumption drawn from paid credits and overage, so included and promotional usage keeps working. `all_usage`: the money value of everything. Buying credits never counts.
  - Allowed values: `paid_usage`, `all_usage`
- `action` (enum, optional, default: warn) — `warn` adds warnings and a webhook. `block` also denies in `check()`.
  - Allowed values: `warn`, `block`
- `alert_at` (list of string, optional)

### RoutingConditions

Every condition you send must match. A rule with none matches everything.

- `currency` (string, optional)
- `billing_country` (string, optional)
- `plan` (string, optional) — The plan (ID or key).
- `mode` (enum, optional) — The checkout mode.
  - Allowed values: `top_up`, `subscription`, `payment_method`

### NotificationThreshold

For `topup_chargeback.threshold_exceeded`. Send at least one. There is no default. Runledger applies no threshold you haven't set, and you choose the level for your own history.

- `rate` (string, optional) — The chargeback rate to alert at, as a decimal from 0 to 1.
- `credits_at_risk` (NotificationThresholdCreditsAtRisk, optional) — Alert when unspent credits from chargebacked top-ups pass this amount of one credit type.

### CostOverride

Your own rate, layered on a catalog cost model. For each usage line, an override that is in force at the line's timestamp and matches its unit and dimensions beats the catalog price, and the most specific match wins: one that names a unit and dimensions beats one that names only a unit, and that beats one that names neither. Two equally specific overrides whose dates overlap are a validation error: the write is refused with `400` and `config_invalid`, and nothing is saved. Send exactly one of `discount_percent` and `unit_amount_decimal`.

- `key` (string, required) — A key you choose, unique within the cost model. Cost lines name it when it priced them.
- `unit` (string, optional) — Limit the override to one unit. Leave it out for every unit of the model.
- `dimensions` (map from string to string, optional) — Limit the override to lines with these dimension values. Leave a dimension out to match any value.
- `discount_percent` (string, optional) — A decimal from 0 to 1 taken off the catalog rate of every matching line, tiers included. `"0.2"` is 20% below the catalog price.
- `unit_amount_decimal` (string, optional) — Replaces the catalog rate with this one, in minor units for `per` units. Needs `unit`. A unit the catalog model doesn't have is added, which is how you extend a catalog model.
- `per` (string, optional, default: 1) — How many units `unit_amount_decimal` covers, as a decimal string.
- `effective_from` (datetime, optional) — When the override starts to apply. Leave it out to apply from the moment it is saved.
- `effective_to` (datetime, optional) — When it stops applying, such as the end of a commitment. Leave it out for no end.
- `category` (enum, optional) — A label for reporting. It changes no price. Open: new values may be added.
  - Allowed values: `negotiated_discount`, `committed_spend`, `private_deployment`, `self_hosted`, `other`
- `reason` (string, optional) — Why, in your words. For example the contract it comes from. Recorded in the audit log.

### CostUnitDeclaration

One unit a cost model is metered in.

- `unit` (string, required) — What is counted. Use `input_tokens`, `output_tokens`, `cache_read_tokens`, `cache_write_tokens`, `reasoning_tokens`, `images`, `video_seconds`, `audio_seconds`, `gpu_seconds`, `session_hours`, `requests` or `searches`, or your own plural, such as `tool_calls`.
- `controlled_by` (enum, required) — Who decides the value. `customer`: your end customer's request sets it, so it is known before the work starts and `check()` can price it. Examples are the resolution, the quality, the seconds of video asked for and the number of reference images. `provider`: the model provider or your own infrastructure sets it while the work runs, so it is known only afterwards and `trace()` reports it. Examples are the input and output tokens and the GPU seconds.
  - Allowed values: `customer`, `provider`
- `description` (string, optional)

### CostDimension

A property that changes the rate for a unit, such as resolution, quality, context bucket or service tier.

- `key` (string, required)
- `controlled_by` (enum, required) — Who decides the value. `customer`: your end customer's request sets it, so it is known before the work starts and `check()` can price it. Examples are the resolution, the quality, the seconds of video asked for and the number of reference images. `provider`: the model provider or your own infrastructure sets it while the work runs, so it is known only afterwards and `trace()` reports it. Examples are the input and output tokens and the GPU seconds.
  - Allowed values: `customer`, `provider`
- `values` (list of string, optional) — The values the rate cards may use. A usage line with another value gets the `rate_not_found` warning.
- `description` (string, optional)

### RateCardInput

- `currency` (string, required) — Lowercase ISO 4217 code the rates are in.
- `rates` (list of CostRate, required)
- `effective_from` (datetime, optional) — When this version starts to price usage. It must be now or later, and after the start of every earlier version of the cost model, including a scheduled one. A new version applies only from this date onward. Recorded usage is never repriced: events and costs dated before it keep the earlier version. Leave it out to save a draft.
- `as_of` (date, optional) — The date you read these rates from the provider, so a person can see how fresh they are.
- `source_reference` (string, optional) — Where the rates came from, such as the provider's pricing page address or a contract reference.

### PriceRuleInput

One cost line a `catalog` rule prices. The event's dimension values are read from its `properties`, so name the cost model's dimensions there.

- `cost_model` (string, required) — The cost model (ID or key).
- `unit` (string, required)
- `quantity_from` (string, optional, default: quantity) — `quantity` takes the event's quantity. `properties.<name>` takes a numeric property of the event.

### PriceTableRow

One row of a `table` rule. Send `unit_amount_decimal` or `tiers`.

- `dimensions` (map from string to string, optional) — The property values this row is for. Leave a property out to match any value. The most specific row wins.
- `unit_amount_decimal` (string, optional) — Minor units of the rule's currency per unit of quantity.
- `tier_mode` (enum, optional, default: graduated)
  - Allowed values: `graduated`, `volume`
- `tiers` (list of RateTier, optional)

### GuardrailScope

Every condition you send must match. A guardrail with none covers all work.

- `event_name` (string, optional)
- `outcome_definition` (string, optional) — An outcome definition (ID or key).
- `plan` (string, optional) — A plan (ID or key).

### SpendLimitScope

Every condition you send must match, and a limit with none applies to the whole account. Limits are grouped by which of `agent_id`, `member_id`, `team_id` and `pool` they name. Inside a group the most specific one applies to a customer: its own `customer`, then its `plan`, then the account. Every group that applies must have room, so the strictest group decides.

- `customer` (string, optional) — A customer (`cus_...`).
- `plan` (string, optional) — A plan (ID or key).
- `agent_id` (string, optional)
- `member_id` (string, optional)
- `team_id` (string, optional)
- `pool` (string, optional) — A pool key.

### SpendLimitPeriod

- `duration` (enum, optional, default: P1M)
  - Allowed values: `P1D`, `P1W`, `P1M`
- `anchor` (enum, optional, default: calendar) — `calendar`: UTC midnight, Monday for a week, the first for a month. `billing_period`: the customer's subscription period. `customer`: a stable reset instant per customer.
  - Allowed values: `calendar`, `billing_period`, `customer`

### CustomerViewBranding

How the hosted customer view looks. Every field is optional. A field you leave out falls back to the account default in `account_settings.customer_view_branding`, and then to your account's name with the standard accent colour.

- `name` (string, optional) — The name shown at the top of the page, in place of your account's name.
- `logo_url` (string, optional) — An `https` URL for your logo. The page loads it from there.
- `accent_color` (string, optional) — The accent colour for buttons and links, as a six-digit hex value.

### RuleCondition

A test on one property of the signal event. Numbers compare numerically.

- `property` (string, required) — A key in the signal event's `properties`.
- `operator` (enum, required)
  - Allowed values: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `exists`
- `value` (string, optional) — The value to compare with, as a string. Not used with `exists`.

### TopUpAutoSettings

Whether customers on the plan may have an auto top-up, and the most they can set.

- `allowed` (boolean, optional, default: false)
- `maximum_monthly_limit` (Money, optional) — The highest `monthly_limit` a customer's auto top-up can have.

### AllowanceWindow

When an allowance refills, or the range a cap counts consumption over. `fixed` windows reset on a boundary. A `session` window opens at the first consumption after the previous one closed and lasts `duration`, like a five-hour session. A `sliding` window counts consumption over the trailing `duration`, and only a `cap` can use it.

- `type` (enum, required)
  - Allowed values: `fixed`, `session`, `sliding`
- `duration` (string, required) — An ISO 8601 duration from `PT1H` to `P1Y`, such as `PT5H`, `P1D`, `P1W` or `P1M`.
- `anchor` (enum, optional, default: billing_period) — `fixed` windows only. `billing_period`: the subscription's period start. `subscription_start`: the day the subscription started. `calendar`: UTC midnight, Monday for a week, the first for a month. `customer`: a stable reset instant chosen per customer when the subscription starts, so customers' windows don't all reset together. Default `billing_period`.
  - Allowed values: `billing_period`, `subscription_start`, `calendar`, `customer`

### NotificationThresholdCreditsAtRisk

Alert when unspent credits from chargebacked top-ups pass this amount of one credit type.

- `credit_type` (string, required)
- `amount` (string, required)

### CostRate

The price of one unit, for the usage lines whose dimensions match. A rate with fewer dimensions matches more lines, and the most specific matching rate wins. Send `unit_amount_decimal` or `tiers`.

- `unit` (string, required)
- `per` (string, optional, default: 1) — How many units `unit_amount_decimal` covers, as a decimal string. Use `"1000000"` for a price per million tokens.
- `unit_amount_decimal` (string, optional) — Minor units of the rate card's currency for `per` units, as a decimal string.
- `dimensions` (map from string to string, optional) — The dimension values this rate is for. Leave a dimension out to match any value.
- `tier_mode` (enum, optional, default: graduated) — With `tiers`. `graduated`: each band's price applies to the quantity inside that band. `volume`: the band the whole quantity falls in prices all of it, as when a long prompt is priced higher in full.
  - Allowed values: `graduated`, `volume`
- `tiers` (list of RateTier, optional)

### RateTier

One band of a quantity. The last band leaves out `up_to`.

- `unit_amount_decimal` (string, required) — Minor units per unit of the band, as a decimal string.
- `up_to` (string, optional) — The quantity this band runs up to, as a decimal string.

## Examples

**Response**

```json
{
  "object": "string",
  "data": [
    {
      "id": "cprop_2vK7nR4tYa",
      "object": "string",
      "status": "pending",
      "document": {
        "credit_types": [
          {
            "key": "ai_credits",
            "name": "AI credits",
            "kind": "custom",
            "currency": "string",
            "decimal_places": 2,
            "unit_label": "credit",
            "metadata": {}
          }
        ],
        "outcome_definitions": [
          {
            "key": "chat_resolution",
            "name": "Chat resolution",
            "event_name": "resolution.chat",
            "criteria": "The customer did not reply within 24 hours and did not ask for a person.",
            "preset": "support_chat",
            "confirmation_window": "PT24H",
            "reversal_horizon": "P30D",
            "reversal_rules": [
              {
                "key": "reopened",
                "event_name": "conversation.reopened",
                "action": "reverse",
                "condition": {
                  "property": "csat",
                  "operator": "eq",
                  "value": "2"
                },
                "applies_to": "any",
                "reason_code": "not_resolved"
              }
            ],
            "dispute_settings": {
              "decision_window": "P7D",
              "default_decision": "reverse"
            },
            "metadata": {}
          }
        ],
        "plans": [
          {
            "key": "enterprise_monthly",
            "name": "Enterprise",
            "billing_mode": "prepaid",
            "currency": "usd",
            "interval": "month",
            "rates": [
              {
                "event_name": "agent.task",
                "outcome_definition": "chat_resolution",
                "credits_per_unit": "10",
                "unit_amount_decimal": "95",
                "price_rule": "video_cost_plus",
                "included_quantity": "1000",
                "overage_unit_amount_decimal": "120",
                "burn_multiplier": "1",
                "description": "Chat resolutions"
              }
            ],
            "description": "string",
            "credit_type": "string",
            "recurring_fee": {
              "amount": 1500,
              "currency": "usd"
            },
            "included_credits": {
              "amount": "5000",
              "pool": "included",
              "rollover": "none",
              "rollover_cap": "2000"
            },
            "top_up": {
              "unit_amount_decimal": "1",
              "minimum_credits": "1000",
              "maximum_credits": "1000000",
              "pool": "purchased",
              "credits_expire_in": "P1Y",
              "low_balance_at": "1000",
              "auto": {
                "allowed": false,
                "maximum_monthly_limit": {
                  "amount": 1500,
                  "currency": "usd"
                }
              }
            },
            "allowances": [
              {
                "key": "weekly",
                "kind": "refill",
                "amount": "1500",
                "window": {
                  "type": "fixed",
                  "duration": "P1W",
                  "anchor": "billing_period"
                },
                "pool": "included",
                "scope": "customer",
                "rollover": "none",
                "rollover_cap": "500",
                "rollover_expires_after": "P1M",
                "priority": 50,
                "on_exhausted": "block",
                "alert_at": [
                  "0.8",
                  "1"
                ]
              }
            ],
            "pools": [
              {
                "key": "included",
                "name": "Included usage",
                "burn_multiplier": "1"
              }
            ],
            "per_seat": true,
            "default_spend_limit": {
              "amount": {
                "amount": 1500,
                "currency": "usd"
              },
              "ceiling": {
                "amount": 1500,
                "currency": "usd"
              },
              "period": {
                "duration": "P1M",
                "anchor": "calendar"
              },
              "counts": "paid_usage",
              "action": "warn",
              "alert_at": [
                "0.75",
                "0.9",
                "1"
              ]
            },
            "overdraft_limit": "0",
            "credit_limit": {
              "amount": 1500,
              "currency": "usd"
            },
            "minimum_commitment": {
              "amount": 1500,
              "currency": "usd"
            },
            "invoice_grace_period": "PT1H",
            "metadata": {}
          }
        ],
        "webhook_endpoints": [
          {
            "url": "https://api.example.com/webhooks/runledger",
            "enabled_events": [
              "outcome_charge.reversed",
              "dispute.decision_due",
              "invoice.finalized"
            ],
            "description": "string",
            "metadata": {}
          }
        ],
        "routing_rules": [
          {
            "provider": "adyen",
            "when": {
              "currency": "aud",
              "billing_country": "AU",
              "plan": "string",
              "mode": "top_up"
            },
            "position": 1
          }
        ],
        "notification_rules": [
          {
            "event": "dispute.decision_due",
            "channel": "email",
            "recipients": [
              "string"
            ],
            "webhook_endpoint": "we_4tJ9nR2cVx",
            "lead_time": "PT24H",
            "threshold": {
              "rate": "0.005",
              "credits_at_risk": {
                "credit_type": "ai_credits",
                "amount": "50000"
              }
            },
            "payment_provider": "string"
          }
        ],
        "cost_models": [
          {
            "key": "vidlab-v2",
            "name": "Vidlab v2 video",
            "source": "catalog",
            "catalog_model": "catm_5xT7qD2mKa",
            "overrides": [
              {
                "key": "textco_negotiated",
                "unit": "input_tokens",
                "dimensions": {
                  "resolution": "1080p"
                },
                "discount_percent": "0.2",
                "unit_amount_decimal": "10",
                "per": "1",
                "effective_from": "2024-01-15T09:30:00Z",
                "effective_to": "2024-01-15T09:30:00Z",
                "category": "negotiated_discount",
                "reason": "Negotiated 20% off to 31 December 2026"
              }
            ],
            "provider": "vidlab",
            "model": "vidlab-v2",
            "kind": "text",
            "units": [
              {
                "unit": "video_seconds",
                "controlled_by": "customer",
                "description": "string"
              }
            ],
            "dimensions": [
              {
                "key": "resolution",
                "controlled_by": "customer",
                "values": [
                  "720p",
                  "1080p"
                ],
                "description": "string"
              }
            ],
            "rate_card": {
              "currency": "usd",
              "rates": [
                {
                  "unit": "video_seconds",
                  "per": "1",
                  "unit_amount_decimal": "12",
                  "dimensions": {
                    "resolution": "1080p"
                  },
                  "tier_mode": "graduated",
                  "tiers": [
                    {
                      "unit_amount_decimal": "300",
                      "up_to": "200000"
                    }
                  ]
                }
              ],
              "effective_from": "2024-01-15T09:30:00Z",
              "as_of": "2026-10-20",
              "source_reference": "Vidlab pricing page, read 20 October 2026"
            },
            "metadata": {}
          }
        ],
        "price_rules": [
          {
            "name": "string",
            "currency": "usd",
            "key": "video_cost_plus",
            "method": "cost_plus",
            "description": "string",
            "basis": "catalog",
            "inputs": [
              {
                "cost_model": "vidlab-v2",
                "unit": "video_seconds",
                "quantity_from": "quantity"
              }
            ],
            "markup_percent": "0.4",
            "target_margin_percent": "0.3",
            "rows": [
              {
                "dimensions": {
                  "resolution": "1080p"
                },
                "unit_amount_decimal": "20",
                "tier_mode": "graduated",
                "tiers": [
                  {
                    "unit_amount_decimal": "300",
                    "up_to": "200000"
                  }
                ]
              }
            ],
            "formula": "max(cost * 1.4, 50)",
            "floor_decimal": "50",
            "cap_decimal": "string",
            "credit_value_decimal": "1",
            "on_cost_change": "hold",
            "effective_from": "2024-01-15T09:30:00Z"
          }
        ],
        "margin_guardrails": [
          {
            "name": "string",
            "min_margin_percent": "0.25",
            "key": "video_margin_floor",
            "applies_to": {
              "event_name": "video.generate",
              "outcome_definition": "string",
              "plan": "string"
            },
            "basis": "expected",
            "action": "warn",
            "window": "P7D",
            "enabled": true
          }
        ],
        "spend_limits": [
          {
            "amount": {
              "amount": 1500,
              "currency": "usd"
            },
            "ceiling": {
              "amount": 1500,
              "currency": "usd"
            },
            "counts": "paid_usage",
            "action": "warn",
            "alert_at": [
              "0.75",
              "0.9",
              "1"
            ],
            "enabled": true,
            "key": "team_monthly_cap",
            "applies_to": {
              "customer": "string",
              "plan": "string",
              "agent_id": "string",
              "member_id": "string",
              "team_id": "string",
              "pool": "string"
            },
            "period": {
              "duration": "P1M",
              "anchor": "calendar"
            },
            "set_by": "operator"
          }
        ],
        "account_settings": {
          "grant_expiry_notice": "P7D",
          "customer_view_branding": {
            "name": "Acme Support",
            "logo_url": "https://app.example.com/logo.png",
            "accent_color": "#17614f"
          }
        }
      },
      "changes": [
        {
          "path": "outcome_definitions.chat_resolution.confirmation_window",
          "before": null,
          "after": null
        }
      ],
      "warnings": [
        {
          "code": "confirmation_window_short",
          "message": "The charge confirms almost at once, so only the reversal horizon protects you.",
          "path": "outcome_definitions.0.confirmation_window"
        }
      ],
      "proposed_by": {
        "type": "user",
        "id": "usr_4tN8vB2cXk",
        "name": "Claude Code",
        "on_behalf_of": "usr_4tN8vB2cXk",
        "client": "claude-code"
      },
      "review_url": "string",
      "expires_at": "2024-01-15T09:30:00Z",
      "livemode": true,
      "created_at": "2024-01-15T09:30:00Z",
      "reason": "string",
      "base_hash": "string",
      "decided_by": {
        "type": "user",
        "id": "usr_4tN8vB2cXk",
        "name": "Claude Code",
        "on_behalf_of": "usr_4tN8vB2cXk",
        "client": "claude-code"
      },
      "decision_note": "string",
      "decided_at": "2024-01-15T09:30:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "string"
}
```

**SDK Code**

```python
import requests

url = "https://api.runledger.co/v1/config_proposals"

headers = {"Authorization": "Bearer <api_key>"}

response = requests.get(url, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.runledger.co/v1/config_proposals';
const options = {method: 'GET', headers: {Authorization: 'Bearer <api_key>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.runledger.co/v1/config_proposals"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <api_key>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.runledger.co/v1/config_proposals")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <api_key>'

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.runledger.co/v1/config_proposals")
  .header("Authorization", "Bearer <api_key>")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.runledger.co/v1/config_proposals', [
  'headers' => [
    'Authorization' => 'Bearer <api_key>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.runledger.co/v1/config_proposals");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <api_key>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <api_key>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.runledger.co/v1/config_proposals")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```