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

# Track events in a batch

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

Records up to 100 events in one request. Each event succeeds or fails on its own; the batch is not atomic. Results come back in request order.

Reference: https://docs.runledger.co/api-reference/events/batch

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

- `events` (list of TrackRequest, required)

## Response

### 200

One result per submitted event.

- `object` ("event_batch", required)
- `results` (list of TrackBatchItemResult, required)

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

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

### TrackRequest

Identify the customer with `customer_id` or `external_customer_id`.

- `idempotency_key` (string, required) — Your unique ID for this event. A repeat within 35 days returns the original event.
- `name` (string, required) — The event name. Your plans, outcome definitions and reversal rules decide what it does. Once recorded, an event's name never changes: to fix a mistake, send a correcting event.
- `customer_id` (string, optional)
- `external_customer_id` (string, optional)
- `timestamp` (datetime, optional) — When it happened. Defaults to now. Up to 35 days in the past and 5 minutes in the future. An event dated in a period whose invoice is already finalized is billed on the next invoice.
- `quantity` (string, optional) — Units, as a decimal string. Defaults to "1".
- `reference` (string, optional) — Your ID for the thing the event is about, such as a conversation or ticket. Signals find their outcome charge by `customer` and `reference`.
- `hold` (string, optional) — Capture this event's price from this hold (`hold_...`).
- `release_remainder` (boolean, optional, default: true) — Release what's left of the hold after capture. Set false to capture again from the same hold.
- `agent_id` (string, optional)
- `run_id` (string, optional)
- `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) — Up to 50 flat key-value pairs. Reversal rule conditions read these. Price rules read them too, as the dimension values and quantities that set the price.
- `evidence` (Evidence, optional) — What shows an outcome happened, stamped on the outcome charge. Keep it short and factual; it is shown to people deciding disputes. Free text is sanitized before it reaches dashboards or agent tools.

### TrackBatchItemResult

- `idempotency_key` (string, required)
- `status` (enum, required) — `created`, `duplicate` (the original event is returned) or `rejected`.
  - Allowed values: `created`, `duplicate`, `rejected`
- `event` (Event, optional)
- `error` (ErrorResponse, optional) — An RFC 9457 problem details object with Runledger's extension members.

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

### Evidence

What shows an outcome happened, stamped on the outcome charge. Keep it short and factual; it is shown to people deciding disputes. Free text is sanitized before it reaches dashboards or agent tools.

- `description` (string, optional) — A plain-language summary.
- `url` (string, optional) — A link to the record in your system, such as the conversation transcript.
- `properties` (map from string to any, optional) — Up to 50 flat key-value pairs, such as a satisfaction score or the agent's confidence.

### Event

- `id` (string, required)
- `object` ("event", required)
- `idempotency_key` (string, required)
- `name` (string, required) — The event name as recorded. It never changes after the event is recorded.
- `kind` (enum, required) — What the event turned out to be. `usage`: charged at once. `outcome`: created a pending outcome charge. `signal`: evaluated against reversal rules. `unpriced`: stored with no effect.
  - Allowed values: `usage`, `outcome`, `signal`, `unpriced`
- `customer` (string, required)
- `timestamp` (datetime, required)
- `quantity` (string, required)
- `effects` (EventEffects, required) — The IDs of everything the event created or changed.
- `warnings` (list of EventWarning, required)
- `livemode` (boolean, required)
- `created_at` (datetime, required)
- `reference` (string, optional)
- `agent_id` (string, optional)
- `member_id` (string, optional)
- `team_id` (string, optional)
- `run_id` (string, optional)
- `properties` (map from string to any, optional)
- `evidence` (Evidence, optional) — What shows an outcome happened, stamped on the outcome charge. Keep it short and factual; it is shown to people deciding disputes. Free text is sanitized before it reaches dashboards or agent tools.
- `pricing` (PricingDetail, optional) — How a price rule priced a usage event. Left out for other events.

### ErrorResponse

An RFC 9457 problem details object with Runledger's extension members.

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

### EventEffects

The IDs of everything the event created or changed.

- `ledger_entries` (list of string, required)
- `reversals` (list of string, required) — Reversals created by rules this signal triggered.
- `disputes` (list of string, required) — Disputes opened by rules this signal triggered.
- `hold` (string, optional) — The hold captured from.
- `outcome_charge` (string, optional) — The outcome charge created.

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

### PricingDetail

How a price rule priced a usage event.

- `price_rule` (string, required) — The rule's key.
- `price_rule_version` (integer, 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`
- `price` (string, required) — Decimal minor units, after the floor and the cap.
- `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`
- `lines` (list of CostLine, optional) — The cost lines the price was built from, for `cost_plus` and `formula` rules on the `catalog` basis.
- `cost` (string, optional) — Decimal minor units. The summed cost of `lines`.
- `floor_applied` (boolean, optional)
- `cap_applied` (boolean, optional)

### CostLine

How one usage line was priced.

- `unit` (string, required)
- `quantity` (string, required)
- `dimensions` (map from string to string, optional)
- `cost_model` (string, optional) — The cost model ID.
- `rate_card` (string, optional) — The rate card that priced the line.
- `rate_card_version` (integer, optional)
- `rate_source` (enum, optional) — Which rate priced the line. `catalog`: the provider price catalog, unchanged. `override`: one of the cost model's overrides, named in `override`. `custom`: a rate card you entered.
  - Allowed values: `catalog`, `override`, `custom`
- `override` (string, optional) — The key of the override that priced the line. Only when `rate_source` is `override`.
- `catalog_price` (string, optional) — The catalog price version behind the rate, for `catalog` and `override` lines.
- `cost` (DecimalMoney, optional) — Left out when no rate matched; the warning `rate_not_found` says so.

### DecimalMoney

An amount of money that can hold fractions of a minor unit, such as a per-token cost.

- `amount_decimal` (string, required) — Minor units as a decimal string. "0.042" is 0.042 US cents.
- `currency` (string, required)

## Examples

**Request**

```json
{
  "events": [
    {
      "idempotency_key": "conv_4821-resolved",
      "name": "resolution.chat"
    }
  ]
}
```

**Response**

```json
{
  "object": "string",
  "results": [
    {
      "idempotency_key": "string",
      "status": "created",
      "event": {
        "id": "ev_3mN8xQ2pLc",
        "object": "string",
        "idempotency_key": "string",
        "name": "resolution.chat",
        "kind": "usage",
        "customer": "string",
        "timestamp": "2024-01-15T09:30:00Z",
        "quantity": "1",
        "effects": {
          "ledger_entries": [
            "string"
          ],
          "reversals": [
            "string"
          ],
          "disputes": [
            "string"
          ],
          "hold": "string",
          "outcome_charge": "string"
        },
        "warnings": [
          {
            "code": "no_rate_for_event",
            "message": "string"
          }
        ],
        "livemode": true,
        "created_at": "2024-01-15T09:30:00Z",
        "reference": "string",
        "agent_id": "string",
        "member_id": "string",
        "team_id": "string",
        "run_id": "string",
        "properties": {},
        "evidence": {
          "description": "Customer confirmed the refund arrived and closed the chat.",
          "url": "https://app.example.com/conversations/conv_4821",
          "properties": {}
        },
        "pricing": {
          "price_rule": "video_cost_plus",
          "price_rule_version": 1,
          "method": "cost_plus",
          "price": "134.4",
          "basis": "catalog",
          "lines": [
            {
              "unit": "string",
              "quantity": "string",
              "dimensions": {},
              "cost_model": "cm_6bR3xN8vQe",
              "rate_card": "rc_4tG9wK2nPd",
              "rate_card_version": 1,
              "rate_source": "catalog",
              "override": "textco_negotiated",
              "catalog_price": "catp_9cW4hL6rBv",
              "cost": {
                "amount_decimal": "0.042",
                "currency": "usd"
              }
            }
          ],
          "cost": "96",
          "floor_applied": true,
          "cap_applied": true
        }
      },
      "error": {
        "type": "https://docs.runledger.co/reference/idempotency-and-errors#error-codes",
        "title": "Insufficient balance",
        "status": 402,
        "code": "invalid_request",
        "request_id": "req_7Hc2Lq0vTz8d",
        "retryable": true,
        "detail": "The hold needs 400 ai_credits but 120 are available.",
        "param": "events.3.timestamp",
        "required_scope": "config:write",
        "errors": [
          {
            "code": "confirmation_window_short",
            "message": "The charge confirms almost at once, so only the reversal horizon protects you.",
            "path": "outcome_definitions.0.confirmation_window"
          }
        ],
        "blocked_by": [
          {
            "object": "routing_rule",
            "id": "rr_3vT8nK2xWa"
          }
        ],
        "doc_url": "https://docs.runledger.co/reference/idempotency-and-errors#error-codes"
      }
    }
  ]
}
```

**SDK Code**

```python
import requests

url = "https://api.runledger.co/v1/events/batch"

payload = { "events": [
        {
            "idempotency_key": "conv_4821-resolved",
            "name": "resolution.chat"
        }
    ] }
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/events/batch';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <api_key>', 'Content-Type': 'application/json'},
  body: '{"events":[{"idempotency_key":"conv_4821-resolved","name":"resolution.chat"}]}'
};

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/events/batch"

	payload := strings.NewReader("{\n  \"events\": [\n    {\n      \"idempotency_key\": \"conv_4821-resolved\",\n      \"name\": \"resolution.chat\"\n    }\n  ]\n}")

	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/events/batch")

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 = "{\n  \"events\": [\n    {\n      \"idempotency_key\": \"conv_4821-resolved\",\n      \"name\": \"resolution.chat\"\n    }\n  ]\n}"

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/events/batch")
  .header("Authorization", "Bearer <api_key>")
  .header("Content-Type", "application/json")
  .body("{\n  \"events\": [\n    {\n      \"idempotency_key\": \"conv_4821-resolved\",\n      \"name\": \"resolution.chat\"\n    }\n  ]\n}")
  .asString();
```

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.runledger.co/v1/events/batch', [
  'body' => '{
  "events": [
    {
      "idempotency_key": "conv_4821-resolved",
      "name": "resolution.chat"
    }
  ]
}',
  'headers' => [
    'Authorization' => 'Bearer <api_key>',
    'Content-Type' => 'application/json',
  ],
]);

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

```csharp
using RestSharp;

var client = new RestClient("https://api.runledger.co/v1/events/batch");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <api_key>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"events\": [\n    {\n      \"idempotency_key\": \"conv_4821-resolved\",\n      \"name\": \"resolution.chat\"\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <api_key>",
  "Content-Type": "application/json"
]
let parameters = ["events": [
    [
      "idempotency_key": "conv_4821-resolved",
      "name": "resolution.chat"
    ]
  ]] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "https://api.runledger.co/v1/events/batch")! 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()
```