Skip to navigation

Test in the sandbox

Run a month of settlement in a few seconds.

Every account has a sandbox. Sandbox keys (rl_sk_test_...) see only sandbox objects, nothing in the sandbox moves money, and a payment provider connects at once. In the sandbox stripe is Runledger’s own Stripe sandbox, shared by every account, or a fake when the deployment has no Stripe key, so test payments don’t appear in your own Stripe dashboard. The sandbox is where a coding agent is allowed to write: with the right scopes it applies configuration directly, and each write is recorded as made by that agent.

Test clocks

Settlement runs on clocks: confirmation windows, reversal horizons, dispute deadlines, hold and grant expiry, billing periods and invoice finalization. A test clock lets you move time forward and watch all of it happen.

  1. Create a clock with POST /v1/test_clocks and a frozen_time.
  2. Create customers with test_clock set to its ID. Everything time-based for those customers follows the clock.
  3. Advance it with POST /v1/test_clocks/{id}/advance. Clocks only move forward, up to 90 days per call. Everything that falls due runs in order, the clock is advancing until it finishes, and then test_clock.ready fires.
  4. Delete it when you’re done, which deletes its customers too. Clocks are deleted automatically after 30 days.

Windows, spend limit periods, credit expiry and auto top-up retries all follow the clock too, so a week of usage windows runs in one call.

Use a separate clock for each scenario, so test runs don’t interfere with each other. Each account has one sandbox, so a separate sandbox for continuous integration is a second account.

Start clean and check your work

  • POST /v1/sandbox/reset deletes the sandbox’s customers, events, test clocks and request logs so a test run can start from nothing. It works only with a sandbox key. The dashboard asks a person to type a confirmation first; the API takes confirm: true.
  • POST /v1/integration_checks reviews the sandbox’s recent traffic against the going-live checklist and returns a pass, warn, fail or unknown result for each item, with the evidence and the fix. It is the same result as the dashboard’s Get started page and the MCP check_integration tool.

Simulate the payment provider

EndpointSimulates
POST /v1/test_helpers/checkout_sessions/{id}/completeThe customer paying, or failing to pay, on the hosted page
POST /v1/test_helpers/invoices/{id}/simulate_paymentThe provider collecting, or failing to collect, an invoice
POST /v1/test_helpers/auto_top_ups/{id}/resolveAn auto top-up purchase succeeding, or its payment failing

All of them send the same webhooks as the real thing. They work only with sandbox keys; a live key gets 403.

Two scenarios worth running

Two agents, one balance

Give a prepaid customer 100 credits, then fire two check() calls at once, each holding 60. One is allowed and one is denied with insufficient_balance. Track the allowed one and the balance ends at 40. Checks and holds explains why.

The unhappy customer

  1. Set up the chat_resolution definition and postpaid plan from the quickstart, on a clock. The definition’s dispute_settings.decision_window of P7D sets the deadline below.
  2. Track resolution.chat with reference: "conv_9001". The charge is pending.
  3. Track csat.submitted with reference: "conv_9001" and properties: {"score": 1}. The low_csat rule opens a dispute; the charge is disputed and won’t confirm.
  4. Advance the clock six days. dispute.decision_due fires.
  5. Either resolve the dispute with uphold, and the charge confirms, or let the deadline pass, and the default reverse applies: the dispute shows decided_by: "default" and the charge is reversed. Resolving a dispute needs disputes:resolve, which the key from Get started doesn’t carry, so use a secret key from API keys.
  6. Advance to the end of the month. The finalized invoice has no line for the reversed charge.

What the sandbox doesn’t simulate

  • Payment provider behavior beyond success and failure, such as card authentication steps and bank-specific declines.
  • Bank debits and PayTo, which Runledger doesn’t support.
  • Load. Sandbox rate limits are lower, so don’t load test against it.

Each account has one sandbox, which comes with the account; a second sandbox is a second account.

See also