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

# Check, and optionally hold

POST https://api.runledger.co/v1/check
Content-Type: application/json

Decides whether a customer can start a piece of work and, with `hold: true`, reserves what it will cost. Describe the work in one of three ways: an `event_name` and `quantity` (priced from the customer's plan), an `outcome_definition` (one outcome at the plan's rate), or a raw `amount`. A denial is a normal `200` response with `allowed: false` and a `reason`. A hold counts against the available balance until `track()` captures it, you release it, or it expires (default 15 minutes, up to 30 days). Two concurrent checks cannot both reserve the same credits: the second sees the first hold and is denied if the balance can't cover both.

**How far the work fits.** Every response carries `affordability`: the balance, pools, every applicable allowance and spend limits before and after the work, what is short, and `would_exceed`, which is `true` only when something would block the work. Without `hold: true` nothing is reserved, so the figures are a point-in-time read of what would be left if the work ran, not a reservation. With `hold: true` and `allowed: true` the hold is placed, and the figures are what is left after it.

Reference: https://docs.runledger.co/api-reference/check

## 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

### Body (application/json)

This endpoint expects a CheckRequest.

- `customer_id` (string, optional) — Runledger's customer ID.
- `external_customer_id` (string, optional) — Your own ID for the customer.
- `event_name` (string, optional) — The usage event you're about to track. Priced from the customer's plan.
- `quantity` (string, optional) — Units of `event_name`, as a decimal string. Defaults to "1".
- `outcome_definition` (string, optional) — The outcome definition (ID or key) the work may produce. Reserves one outcome at the plan's rate.
- `amount` (string, optional) — A raw amount in the customer's balance unit, as a decimal string. Credits for prepaid plans; minor units for postpaid plans.
- `hold` (boolean, optional, default: false) — Reserve the amount if allowed.
- `hold_expires_in` (string, optional) — How long the hold lasts, as an ISO 8601 duration. Default PT15M, maximum P30D. This one can be in hours or minutes, but extending the hold later takes whole days only (`POST /v1/holds/{hold_id}/extend`).
- `agent_id` (string, optional) — Your ID for the agent doing the work. Recorded on the hold for reporting.
- `run_id` (string, optional) — Your ID for this run. Joins the hold, events and costs for margin reporting.
- `member_id` (string, optional) — Your ID for the person using your product, inside this customer. Spend limits, per-member allowances and usage summaries can use it. Recorded on the hold, event and ledger entries.
- `team_id` (string, optional) — Your ID for a team inside this customer. A label for limits and reporting. Runledger doesn't model teams.
- `properties` (map from string to any, optional) — The same flat key-value pairs you will send on `track()`, so a price rule can read the dimensions your customer chose, such as `resolution`.
- `estimated_usage` (list of UsageLine, optional) — What you expect the work to use, priced from the cost catalog to estimate its cost for margin guardrails. Without it, `check()` falls back to the observed average cost of recent work of this kind.
- `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.

## Response

### 200

The decision, and the hold if one was placed.

- `object` ("check", required)
- `allowed` (boolean, required) — Whether the work can start.
- `customer` (string, required) — The customer ID.
- `required` (string, required) — What the work costs, as a decimal string in the unit of the check. `"0"` when the check is denied with `no_active_subscription` or `no_rate_for_event`, because there is no plan or rate to price the work from.
- `affordability` (Affordability, required) — How far the work fits, in every response, allowed or denied: the balance, pools, allowances and spend limits before and after the work. It is always present. See `Affordability` for every field.
- `livemode` (boolean, required)
- `reason` (enum, optional) — Why `allowed` is false. Present only on denials. `allowance_exhausted`: a plan allowance with `on_exhausted: block` has no room in its window. `spend_limit_reached`: a spend limit with `action: block` has no room left in its period. `margin_below_floor` is the denial a margin guardrail with `action: block` produces. The same guardrail adds the warning `margin_below_guardrail` when its action is `warn`, and on `track()`.
  - Allowed values: `insufficient_balance`, `credit_limit_reached`, `no_active_subscription`, `subscription_past_due`, `no_rate_for_event`, `margin_below_floor`, `allowance_exhausted`, `spend_limit_reached`
- `credit_type` (string, optional) — The credit type the amounts are in (prepaid plans).
- `currency` (string, optional) — The currency the amounts are in, in minor units (postpaid plans).
- `available` (string, optional) — The available balance before any hold from this call. Left out when a postpaid plan has no credit limit.
- `hold` (Hold, optional) — The hold, if one was placed. Present only with `hold: true` and `allowed: true`.
- `denied_by` (CheckDenial, optional) — What denied the work, so your product can say which limit was hit and when it lifts. Present only on denials from an allowance or a spend limit.
- `margin` (CheckMargin, optional) — The expected margin, when a margin guardrail covers the work.
- `warnings` (list of EventWarning, optional) — For example `margin_below_guardrail` or `no_cost_estimate`.

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

### 404 Not Found Error

No object with that ID exists in this environment.

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

### 409 Conflict Error

The object is in a state that doesn't allow this (`invalid_state`, for example approving a stale config proposal or deleting a price rule a plan rate still uses), or a request with the same idempotency key is still in progress.

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

### 422 Unprocessable Entity Error

The idempotency key was already used with a different request body.

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

### UsageLine

A quantity of one unit, with the dimension values that decide its rate.

- `unit` (string, required)
- `quantity` (string, required) — A decimal string.
- `dimensions` (map from string to string, optional)

### Affordability

How far the work fits, in every `check()` response, allowed or denied. It is always present, and so are all of its fields: a value that doesn't apply is `null` and a list with nothing in it is `[]`. **Units.** Balance and pool amounts are decimal strings in the unit of the check: the customer's `credit_type` credits on a prepaid plan, and minor units of the plan's `currency` on a postpaid plan. Allowance amounts are decimal strings of credits, or of units of the event for an included quantity. Spend limit amounts are decimal strings in minor units of that limit's own `currency`. Currencies are never converted or added together. **Before and after.** Every `_before` figure is read before this call's own hold and counts every other active hold, the same as `available` on the response. Every `_after` figure is the `_before` figure minus what the work uses, whether or not `hold` was true. With `hold: true` and `allowed: true`, the hold is for `required`, so `_after` is what the next caller sees. Without it, nothing is reserved and `_after` is what would be left if the work ran, read at that moment and not reserved. A denied check places no hold, but its `_after` figures are worked out the same way. **When nothing can be worked out.** For the denials `no_active_subscription` and `no_rate_for_event` there is no balance or price to measure against: `required` is `"0"`, the `balance` figures are `null`, `pools`, `allowances` and `spend_limits` are `[]`, and `would_exceed` is `false`.

- `balance` (AffordabilityBalance, required) — The customer's available balance, before and after the work.
- `pools` (list of AffordabilityPool, required) — Each balance pool the customer holds credits in, in the plan's drawdown order, and how much of the work each would cover. `[]` for a postpaid plan, and for a prepaid customer whose credits sit in one pool and whose plan has no pools.
- `allowances` (list of AffordabilityAllowance, required) — Every allowance window that applies to this work, each with the room left in it: the plan's `refill` and `cap` allowances, and the included quantity of the rate for an event on a postpaid plan. It is the same set of windows `GET /v1/customers/{id}/usage_summary` lists. `on_exhausted` on each entry says what happens past the window: stop the work, move it to the next pool, or bill it as overage. `[]` when no window applies.
- `spend_limits` (list of AffordabilitySpendLimit, required) — Each enabled spend limit that applies to this customer and this work, whether its `action` is `block` or `warn`, and the room left in its period. `[]` when none applies.
- `would_exceed` (boolean, required) — `true` only when something would block the work: the balance would be overrun (`balance.shortfall` above `"0"`), an `allowances` entry with `on_exhausted: block` has a `shortfall` above `"0"`, or a `spend_limits` entry with `action: block` has a `shortfall` above `"0"`. A shortfall that doesn't block doesn't set it: a spend limit with `action: warn`, an allowance that bills overage, and an allowance that draws from the next pool all list their `shortfall` and leave `would_exceed` `false`. So `would_exceed` is `true` exactly when `allowed` is `false` for lack of room, and `false` on an allowed check. It is `false` too for denials that aren't about room, such as `subscription_past_due` and `margin_below_floor`.
- `reason` (enum, required, nullable) — The same as `reason` on the response: why the check denied the work, or `null` when it allowed it. When several tests would deny, which one it names isn't specified.
  - Allowed values: `insufficient_balance`, `credit_limit_reached`, `no_active_subscription`, `subscription_past_due`, `no_rate_for_event`, `margin_below_floor`, `allowance_exhausted`, `spend_limit_reached`

### Hold

- `id` (string, required)
- `object` ("hold", required)
- `customer` (string, required)
- `status` (enum, required) — `active` reserves. `captured` was used by `track()`, and for an outcome the reservation moves to the outcome charge. `released` was let go by you or by `track()`. `expired` lapsed and was released automatically.
  - Allowed values: `active`, `captured`, `released`, `expired`
- `amount` (string, required) — The current total held, as a decimal string.
- `captured_amount` (string, required) — How much `track()` has captured so far.
- `expires_at` (datetime, required)
- `livemode` (boolean, required)
- `created_at` (datetime, required)
- `credit_type` (string, optional)
- `currency` (string, optional)
- `event_name` (string, optional)
- `outcome_definition` (string, optional)
- `agent_id` (string, optional)
- `member_id` (string, optional)
- `team_id` (string, optional)
- `run_id` (string, optional)
- `captured_at` (datetime, optional)
- `released_at` (datetime, 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.

### CheckDenial

What denied the work, so your product can say which limit was hit and when it lifts. Present only on denials from an allowance or a spend limit.

- `type` (enum, required)
  - Allowed values: `allowance`, `spend_limit`
- `id` (string, required) — The allowance key, or the spend limit ID.
- `resets_at` (datetime, optional) — When the window or period resets and the work can run again.
- `member_id` (string, optional)
- `remaining` (string, optional) — How much room the window or period had left, as credits for an allowance and minor units of the limit's currency for a spend limit.

### CheckMargin

The margin `check()` expected for the work. Money is in decimal minor units of `currency`; prepaid credits are valued at the cost basis of the grants they would be drawn from, as on a run. Reversals aren't predicted here: a reversed outcome charge lowers the margin the reports show afterwards.

- `evaluated` (boolean, required) — False when there was no cost to compare the price with, and the warning `no_cost_estimate` says so.
- `currency` (string, optional)
- `price` (string, optional)
- `expected_cost` (string, optional)
- `expected_margin` (string, optional)
- `margin_percent` (string, optional) — Expected margin over the price, from 0 to 1. Negative when the work would sell below cost.
- `cost_source` (enum, optional) — `estimated_usage`: priced from the request's usage lines. `catalog`: priced from the rule's cost lines. `observed_average`: the average cost of recent work of this kind.
  - Allowed values: `estimated_usage`, `catalog`, `observed_average`
- `guardrails` (list of CheckMarginGuardrailsItems, optional)

### EventWarning

- `code` (enum, required) — A stable warning code. New codes may be added. `margin_below_guardrail` is the warning for a margin guardrail whose action is `warn`, and for any breach seen by `track()`; the denial from a `block` guardrail is `margin_below_floor`. `spend_limit_exceeded` and `allowance_exhausted` are what `track()` reports when work that already happened took a limit or window past its amount.
  - Allowed values: `no_rate_for_event`, `no_active_subscription`, `reference_not_found`, `reversal_horizon_passed`, `outcome_charge_already_reversed`, `duplicate_outcome_for_reference`, `capture_exceeded_hold`, `balance_overdrawn`, `rate_not_found`, `margin_below_guardrail`, `no_cost_estimate`, `catalog_model_available`, `spend_limit_exceeded`, `allowance_exhausted`
- `message` (string, required)

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

### AffordabilityBalance

The customer's available balance. All three are `null` when there is no balance to measure against.

- `available_before` (string, required, nullable) — The available balance before this call's hold, the same figure as `available` on the response: posted balance minus active holds on a prepaid plan, and credit limit minus unbilled charges minus active holds on a postpaid plan. It can be negative when the balance is overdrawn. `null` for a postpaid plan with no credit limit, and for the denials listed on `Affordability`.
- `available_after` (string, required, nullable) — `available_before` minus `required`. It can be negative, and `shortfall` says by how much. `null` when `available_before` is `null`.
- `shortfall` (string, required, nullable) — How much the work is over what is available: `required` minus `available_before` when that is above zero, and `"0"` when the work fits. `"0"` when there is no limit to pass (`available_before` is `null` for a postpaid plan with no credit limit). `null` for the denials listed on `Affordability`.

### AffordabilityPool

One balance pool. The work draws from the pools in drawdown order. Each pool covers as much as it has available before the next is used, and a pool with a `cap` allowance that has `on_exhausted: draw_next_pool` covers no more than the room left in that cap (none when it has none).

- `pool` (string, required) — The pool key, `default` when the plan has none.
- `available_before` (string, required) — The pool's posted credits minus active holds on it, before this call's hold. Credits.
- `would_draw` (string, required) — The credits of `required` this pool would cover, in drawdown order: the lesser of its `available_before` and what the earlier pools left uncovered. Across all pools it adds up to `required` minus `balance.shortfall`.
- `available_after` (string, required) — `available_before` minus `would_draw`. Never below zero.

### AffordabilityAllowance

One allowance window that applies to the work, and the room left in it. `shortfall` is how much of the work is past the window, and `on_exhausted` says what happens to that amount: `block` stops the work (and sets `would_exceed`), `draw_next_pool` moves it to the next pool, and `bill_overage` bills it as overage. Only `block` sets `would_exceed`.

- `key` (string, required) — The allowance key, or the event name for an `included` quantity.
- `kind` (enum, required) — `refill` and `cap` come from the plan's allowances. `included` is the included quantity of a postpaid rate.
  - Allowed values: `refill`, `cap`, `included`
- `event_name` (string, required, nullable) — The event the window counts, for an `included` quantity. `null` for a `refill` or `cap`.
- `pool` (string, required, nullable) — The pool a `refill` fills or a `cap` limits. `null` when the window isn't tied to a pool, such as a `cap` that counts consumption from every pool, or an `included` quantity.
- `on_exhausted` (enum, required) — What happens to the part of the work past the window. For a `cap`, the allowance's own `on_exhausted`: `block` denies the work with `allowance_exhausted`, `draw_next_pool` moves the work to the next pool. A `refill` is `draw_next_pool`: credits past it come from the next pool, and the balance decides whether they are there. An `included` quantity is `bill_overage`: the units past it are billed as overage at the rate's overage price.
  - Allowed values: `block`, `draw_next_pool`, `bill_overage`
- `member_id` (string, required, nullable) — The member whose window this is, for a per-member allowance. `null` for a customer-wide window.
- `counted` (string, required) — What the work counts in this window: all of `required` for a `refill`, a `cap` with no `pool`, and an `included` quantity, which is `quantity` in units of the event. For a `cap` with a `pool` it is the share that pool would cover, its `would_draw` without the cap. Credits for a `refill` and a `cap`, and units of the event for an `included` quantity.
- `remaining_before` (string, required) — Credits (units for `included`) left in the window, before this call's hold and after every other active hold. The same as `remaining` in the usage summary.
- `remaining_after` (string, required) — `remaining_before` minus `counted`. It can be negative: that is how far past the window the work would go.
- `shortfall` (string, required) — How far the work is past the window: `counted` minus `remaining_before` when that is above zero, otherwise `"0"`. It is the amount `on_exhausted` applies to: the part that would be blocked, moved to the next pool, or billed as overage. It is the negative of `remaining_after` when that is below zero.
- `resets_at` (datetime, required) — When the window resets and the room is back.

### AffordabilitySpendLimit

One spend limit, and the room left in its current period.

- `id` (string, required) — The spend limit ID.
- `key` (string, required, nullable) — The limit's key, or `null` when it has none.
- `action` (enum, required, default: warn) — `warn` adds warnings and a webhook. `block` also denies in `check()`.
  - Allowed values: `warn`, `block`
- `counts` (enum, required, 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`
- `currency` (string, required) — The limit's currency. Every amount in this entry is in its minor units.
- `counted` (string, required) — The money value of the work that counts toward the limit, as the limit's `counts` sets: only the part drawn from paid credits and overage for `paid_usage`, and all of it for `all_usage`. Decimal minor units of `currency`.
- `remaining_before` (string, required) — The limit's amount minus what is spent and held this period, before this call's hold. Decimal minor units of `currency`.
- `remaining_after` (string, required) — `remaining_before` minus `counted`. It can be negative.
- `shortfall` (string, required) — `counted` minus `remaining_before` when that is above zero, otherwise `"0"`. It sets `would_exceed` only when `action` is `block`. A `warn` limit lists it and the work is allowed.
- `resets_at` (datetime, required) — When the period resets.

### CheckMarginGuardrailsItems

- `key` (string, required)
- `status` (enum, required)
  - Allowed values: `ok`, `warn`, `block`, `not_evaluated`
- `min_margin_percent` (string, optional)
- `margin_percent` (string, optional)

## Examples

**Request**

```json
{}
```

**Response**

```json
{
  "object": "string",
  "allowed": true,
  "customer": "string",
  "required": "40",
  "affordability": {
    "balance": {
      "available_before": "1260",
      "available_after": "1220",
      "shortfall": "0"
    },
    "pools": [
      {
        "pool": "purchased",
        "available_before": "1260",
        "would_draw": "40",
        "available_after": "1220"
      }
    ],
    "allowances": [
      {
        "key": "session",
        "kind": "refill",
        "event_name": "string",
        "pool": "included",
        "on_exhausted": "block",
        "member_id": "user_2231",
        "counted": "40",
        "remaining_before": "30",
        "remaining_after": "-10",
        "shortfall": "10",
        "resets_at": "2024-01-15T09:30:00Z"
      }
    ],
    "spend_limits": [
      {
        "id": "sl_4kP7wD2nRt",
        "key": "team_monthly_cap",
        "action": "warn",
        "counts": "paid_usage",
        "currency": "usd",
        "counted": "120",
        "remaining_before": "3400",
        "remaining_after": "3280",
        "shortfall": "0",
        "resets_at": "2024-01-15T09:30:00Z"
      }
    ],
    "would_exceed": true,
    "reason": "insufficient_balance"
  },
  "livemode": true,
  "reason": "insufficient_balance",
  "credit_type": "string",
  "currency": "string",
  "available": "1260",
  "hold": {
    "id": "hold_4kR8tW2xMn",
    "object": "string",
    "customer": "string",
    "status": "active",
    "amount": "40",
    "captured_amount": "0",
    "expires_at": "2024-01-15T09:30:00Z",
    "livemode": true,
    "created_at": "2024-01-15T09:30:00Z",
    "credit_type": "string",
    "currency": "string",
    "event_name": "string",
    "outcome_definition": "string",
    "agent_id": "string",
    "member_id": "string",
    "team_id": "string",
    "run_id": "string",
    "captured_at": "2024-01-15T09:30:00Z",
    "released_at": "2024-01-15T09:30:00Z",
    "metadata": {}
  },
  "denied_by": {
    "type": "allowance",
    "id": "session",
    "resets_at": "2024-01-15T09:30:00Z",
    "member_id": "string",
    "remaining": "0"
  },
  "margin": {
    "evaluated": true,
    "currency": "usd",
    "price": "134.4",
    "expected_cost": "96",
    "expected_margin": "38.4",
    "margin_percent": "0.2857",
    "cost_source": "estimated_usage",
    "guardrails": [
      {
        "key": "video_margin_floor",
        "status": "ok",
        "min_margin_percent": "string",
        "margin_percent": "string"
      }
    ]
  },
  "warnings": [
    {
      "code": "no_rate_for_event",
      "message": "string"
    }
  ]
}
```

**SDK Code**

```python
import requests

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

payload = {}
headers = {
    "Authorization": "Bearer <api_key>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.runledger.co/v1/check';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <api_key>', 'Content-Type': 'application/json'},
  body: '{}'
};

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"
	"strings"
	"net/http"
	"io"
)

func main() {

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

	payload := strings.NewReader("{}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <api_key>")
	req.Header.Add("Content-Type", "application/json")

	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/check")

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

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <api_key>'
request["Content-Type"] = 'application/json'
request.body = "{}"

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.post("https://api.runledger.co/v1/check")
  .header("Authorization", "Bearer <api_key>")
  .header("Content-Type", "application/json")
  .body("{}")
  .asString();
```

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.runledger.co/v1/check', [
  'body' => '{}',
  'headers' => [
    'Authorization' => 'Bearer <api_key>',
    'Content-Type' => 'application/json',
  ],
]);

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

```csharp
using RestSharp;

var client = new RestClient("https://api.runledger.co/v1/check");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <api_key>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <api_key>",
  "Content-Type": "application/json"
]
let parameters = [] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

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

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()
```