Skip to navigation

Access, actors and audit

Scoped keys, who made each change, and the audit log.

A coding agent that configures billing needs enough access to do the job and not enough to cause damage. Runledger gives it scoped credentials, records every change with who made it, and keeps a log a person can read.

Keys and scopes

A secret key (rl_sk_...) has full access to its environment. A restricted key (rl_rk_...) carries a list of scopes, an optional expires_at, and an agent flag that marks a key issued to an agent. OAuth sessions from the MCP server carry the same scopes.

ScopeAllows
config:readRead credit types, outcome definitions, plans, endpoints, providers, routing and notification rules, and the presets. Also the provider price catalog, cost models, rate cards, price rules and margin guardrails, and running a simulation
config:writeApply configuration in the sandbox. Never given to an agent in live mode
config:proposePropose live configuration for a person to approve
events:read, events:writeRead events, preview and send them
customers:read, customers:writeRead customers. Write is for creating test customers in the sandbox
webhooks:writeCreate and update endpoints, and send test events in the sandbox
providers:read, providers:connectRead provider connections. Start one: test mode connects directly in the sandbox, and connecting in live isn’t open yet. Also remove a sandbox one. Only an Admin removes a live provider, and an agent key is refused. When live connections open, an Admin confirms the first live provider in the dashboard, and an agent can’t
test_helpers:writeTest clocks, scenarios, simulated payments and the sandbox reset, in the sandbox
audit:readRead the audit log, the request logs and the webhook event log
charges:read, disputes:readRead charges and their reversals, and read disputes
usage:readRead a customer’s usage summary
spend_limits:read, spend_limits:writeRead spend limits. Write sets a limit for one customer, the way your backend or your customer would. Operator limits are configuration and need config:write or a proposal
auto_top_up:writeSet, change or remove a customer’s auto top-up
customer_sessions:writeOpen a customer view for a customer. Granted one key at a time
charges:write, disputes:resolveConfirm or reverse a charge, and open or decide a dispute. Never in a preset: an Admin grants them one at a time

An agent key that holds spend_limits:write or auto_top_up:write can create a customer’s limit or lower it in live mode, and is refused with permission_denied when it tries to raise or remove one. Agent keys can’t create customer sessions. These are the only live data writes an agent key can hold, and neither is in a preset.

A call outside a key’s scopes returns 403 with code: insufficient_scope and required_scope naming what it needed. An MCP client can answer that by asking the person to widen the grant, which is the step-up flow the MCP authorization spec describes.

GET /v1/api_keys lists keys, masked: name, type, prefix, scopes, agent, expiry and last use, never the secret. It lets an agent see what it holds. Agents never create, roll or revoke keys, and never receive live provider credentials. People do that in the dashboard: an Admin for live keys, and an Admin or a Collaborator for sandbox keys.

Who did it

Every change records an actor: {type, id, name, on_behalf_of, client}.

typeMeans
userA signed-in person, in the dashboard. A person at Runledger support acting in your account is a user whose name is Runledger support
agentAn agent, through an OAuth session or an agent key. client names the software, such as the MCP client, and on_behalf_of the person it acts for
keyAny other API key
ruleA reversal rule
systemRunledger itself, such as a clock closing, or a customer’s own change on the customer view, named customer_session with the session’s ID

Runledger fills the actor in from the credential, and a client can’t claim to be someone else. An API key always acts as itself, a secret key included, and the dashboard calls the API as the signed-in person, so approving a proposal or removing a live provider needs an Admin signed in to the dashboard. A request that sends a Runledger-Actor header is refused with 400 parameter_unknown. actor appears on request logs, reversals, dispute decisions, credit grants and voids, outcome definition versions, plans, webhook endpoints, payment providers, routing and notification rules, and config proposals. Request logs filter by actor_type, actor and key, so you can list everything an agent did.

Request logs

GET /v1/request_logs lists the requests made with your keys in the last 30 days, with the status, the error code, the idempotency key, the actor and the bodies. Secrets are redacted before a request is stored: a webhook signing secret and every link that grants access (a checkout page, a customer view link, an export download, an invoice’s payment page, a provider’s connect page) read [redacted]. Other fields are kept as sent. A configuration write sent with Runledger-Dry-Run: true is logged like any other request, and appears tagged Dry run, so you can tell a check from a change. The dry run writes nothing. When the same change is later made for real, that request is logged on its own, and the audit event it creates has dry_run_preceded: true.

The audit log

GET /v1/audit_events records what changed, who changed it, and through which surface (dashboard, api or mcp). Each event has the action, the resource and its version, the field changes, the request_id, and dry_run_preceded, which is true when the same change was dry-run or validated first. The dry run itself is in the request logs, tagged Dry run. A change made by approving a config proposal also carries approved_by, the signed-in Admin who approved it and when. actor stays the one who made the request, such as the agent or the Collaborator that proposed it. An Admin can approve any proposal, including one for notification or routing rules. Events are kept for at least 13 months, and the log needs the audit:read scope.

Besides configuration changes, these write an audit event with the actor:

ActionWritten when
catalog.price_change_heldA person holds a catalog price change for a price rule in the dashboard review, so customer prices stay where they are. actor is who held it, and the resource is the price change (catp_...)
auto_top_up.createdA customer’s auto top-up rule is set for the first time, by the customer, an operator or an agent
auto_top_up.updatedAn existing rule changes: its threshold, target_balance, monthly_limit or payment method
auto_top_up.turned_offA rule is turned off by the customer, an operator or an agent, which removes it: DELETE /v1/customers/{customer_id}/auto_top_up, after which a retrieve is 404. A rule that stops by itself after three failed payments stays, with status: disabled, sends the auto_top_up.disabled webhook and is recorded with the system as actor

The changes on an auto top-up event show the fields before and after, so you can see who raised a monthly_limit. Holding a price change writes no configuration, which is why it has an event of its own. Choosing follow instead, or editing the rule, is an ordinary price rule change and is recorded as one. Filter by actor_type, actor, resource, source and time.

See also