Conventions
The same rules apply to every endpoint.
Requests and responses
- JSON bodies,
snake_casefields, HTTPS only. - One API host for both environments,
https://api.runledger.co. The key decides whether a request runs in the sandbox or live, and every object carrieslivemode. The dashboard is athttps://app.runledger.co, the developer MCP server athttps://mcp.runledger.co/mcp, and the hosted customer view athttps://billing.runledger.co/s/{session}. - Updates use
PATCHand change only the fields you send. Sendnullto clear a field. The one exception isPUT /v1/config, which takes a whole configuration document. - Config writes accept
Runledger-Dry-Run: trueto show what they would do without doing it. See Configuration. Everycheck()response carries anaffordabilityblock, with no header needed. See Checks and holds. - Fields with no value are left out of responses, never sent as
null, except the ones the reference documents as nullable, such as the figures inaffordabilityandmarginon a run. - State changes are explicit actions on the object, such as
POST /v1/holds/{id}/releaseorPOST /v1/outcome_charges/{id}/reverse. Status fields can’t be written directly.
IDs
Every object ID is a string with a type prefix, so you can tell what an ID refers to by looking at it. Treat the part after the prefix as opaque; its length may change.
Pools and allowances are named by a key you choose inside a plan, such as included or weekly, and have no ID of their own. member_id, team_id and agent_id are your own IDs, up to 128 characters, that you send on calls to attribute usage. See Members, teams and agents.
Customers also take your own ID as external_id. Calls made on a customer’s behalf, such as check(), track() and checkout(), accept either customer_id or external_customer_id. URL paths and list filters take Runledger’s cus_ ID, which you can look up from yours with GET /v1/customers?external_id=. Credit types, plans and outcome definitions have a key you choose, such as chat_resolution, and requests accept the key or the ID.
Money, credits and quantities
- Money is an integer in the currency’s minor unit with a lowercase ISO 4217 code:
{"amount": 1500, "currency": "usd"}is US$15.00. Zero-decimal currencies follow ISO 4217, so{"amount": 1500, "currency": "jpy"}is ¥1,500. - Unit prices in money can be fractions of a minor unit, so they are decimal strings in minor units:
unit_amount_decimal: "95"is US$0.95, and"0.042"is 0.042 US cents. - Credits and quantities are decimal strings, such as
"12.5", with up to the credit type’sdecimal_places. - Rounding happens once, on each invoice line, half up to the minor unit. Ledger amounts are never rounded.
Time
- Timestamps are RFC 3339 strings in UTC:
2026-11-02T09:00:00Z. - Durations are ISO 8601:
PT15M,PT24H,P30D. - Ledger entries carry both
effective_at(when it happened in business terms) andposted_at(when Runledger recorded it).
Lists
List endpoints return the newest objects first:
Pass next_cursor back as cursor for the next page. limit is 1 to 100, default 20. Cursors are opaque and don’t grant access to anything. The SDKs page automatically:
Lists don’t return a total count unless you ask. Add include=total_count to the main lists (events, outcome charges, customers, disputes, reversals, request logs and webhook events) and the response carries total_count, capped, with total_count_approximate: true when the real number is higher. For counts and amounts over time, use GET /v1/reports/outcomes instead of adding up pages. Reads of a single object always reflect your writes; lists may lag by a few seconds, so don’t use a list to confirm a write you just made.
Routing rules are the one list that isn’t newest first: they come in order, first match first.
Metadata
Most objects take metadata: up to 50 keys of your own, keys up to 40 characters and values up to 500. Don’t put personal data or secrets in it.
Versioning
Send Runledger-Version: 2026-10-18 on every request; the SDKs do this for you. Without it, a request uses the version your account is pinned to, which you can read from GET /v1/account.
Within a version, changes are additive only: new endpoints, new optional fields, new enum values, new error and event types. Handle values you don’t recognize, and ignore fields you don’t use. A change that removes or renames anything, adds a required field, or changes a type ships as a new dated version, and old versions get advance notice through Deprecation and Sunset headers.
Rate limits
A request over the limit gets 429 with a Retry-After header in seconds. The SDKs wait and retry. Limits are counted per account, per environment and per endpoint, in one-second windows. check(), track(), the event batch and trace() have a higher limit than the rest, and live’s limits are above the sandbox’s: 100 requests a second for most endpoints and 1,000 for those four in live, 25 and 100 in the sandbox. A person’s calls from the dashboard and the customer view’s are not counted.
Keys
Keys end in a checksum so secret scanners can recognize a leaked key, and a leaked key reported to us is revoked. People create, roll and revoke keys in the dashboard; when you roll a key, the old one keeps working for up to seven days. GET /v1/api_keys lists them, masked, with their scopes, so an agent can see what it holds. Agents never create or roll keys. Never use a key in a browser or mobile app.
A restricted key’s scopes are checked on every call. One it lacks returns 403 with insufficient_scope and required_scope.
Actors
Every change records who made it, as an actor with a type of user, agent, key, rule or system. Runledger fills it in from the credential. See Access, actors and audit.
Request IDs
Every response has a Request-Id header, and every error body has a request_id. Look a request up with GET /v1/request_logs/{request_id}, or quote it to support.
SDKs and tools
Runledger has SDKs for TypeScript and Python, generated from the OpenAPI spec. Both set the Runledger-Version header, wait and retry on 429, and expose check(), track(), trace() and checkout(). trace() records the model, provider and units you pass it; there are no wrappers that record costs from a model client automatically. Webhook signatures are verified with any Standard Webhooks library. There is no CLI. Keys are created, rolled and revoked in the dashboard, not through the API.
See also
- Glossary defines the terms used across the docs.
- Idempotency and errors covers retries and the error body.