Skip to navigation

Going live

What to set up before Runledger carries real billing.

This page is the plan to follow to go live. The runbook, which starts at Make your integration production-ready, has the code and the full steps.

Going live means five things: your agents keep working when Runledger is slow or down, no event is lost or billed twice, someone decides every dispute, you’ve compared Runledger’s invoices with your own before a customer sees one, and the live configuration and payment providers are connected and reviewed by a person. An account starts in the sandbox, and live keys and live configuration work from the start. Live takes no payments until every row of the checklist below passes and Runledger has reviewed the account. An Admin then connects and confirms the first live payment provider; connecting a live provider isn’t open yet.

Before you start

  • An integration that works in the sandbox. The quickstart builds one.
  • Someone who can approve live configuration: an Admin in the dashboard. A Collaborator proposes live changes, and an Admin approves them.

Keep checks off the critical path

check() runs before the work starts, so your users wait for it. Runledger is hosted in Australia, so from North America or Europe every call makes a long network round trip. Three habits keep it out of the way:

  • Check once per run. Call check() when a conversation or run starts, and hold its whole budget. One hold covers the run, and a long run can extend it.
  • Start the work and the check together. A voice agent can greet the caller while the check runs, then act only if it comes back denied.
  • Fail open when Runledger doesn’t answer. Give check() a short timeout and no retries. If it times out, let the work run and record it with track() afterwards. Recording finished work never fails for lack of balance, so the cost of an outage is a prepaid balance that can go below zero until it ends.

Send events through your queue

Put track() on the job queue you already run, and build each idempotency_key from your own IDs, such as conv_4821-resolved. Every retry of the job then lands on the same event, for up to 35 days, so nothing is billed twice. Set timestamp to when the event happened: events can be dated up to 35 days back, so a job that waits out an outage is still billed in the right period. Send the warnings that track() returns to someone who can act on them; no_rate_for_event and reference_not_found usually mean an event name or a reference doesn’t match your setup.

Decide who handles disputes

When nobody decides a dispute by its deadline, its default_decision applies, and that’s reverse unless you change it. Before you go live, send dispute.decision_due (it fires 24 hours before each deadline) to the people who will decide, give them a list of open disputes with the evidence, and set the default to uphold if your contracts say a charge stands unless it’s reversed. A notification rule on dispute.decision_due emails them without any code on your side. A dispute that is still open when a charge’s reversal horizon passes holds the charge’s lock until it’s decided, so a dispute nobody sees also keeps revenue from locking.

Plan the move from your current billing

Runledger runs your plans, subscriptions and invoices, and your payment providers still collect the money. Settlement runs on Runledger’s own plans and invoices, so it doesn’t sit on top of a billing system you already use.

Move customers at the start of a billing period. Your old system bills everything dated before that boundary, and Runledger bills everything from it. Unused prepaid credits move as paid credit grants, valued at what the customer paid, and each subscription is created at the boundary, since a subscription starts when it is created. Runledger can’t use a card your provider already holds, so customers who pay by card save it again on a hosted checkout page before the boundary, at whichever of your connected payment providers routing picks for them. A customer stays with that provider afterwards. Charges your old system made stay there: if a customer comes back about an earlier conversation, credit it the way you do today.

Compare before you switch

  1. Backtest. Replay a few months of your own history in the sandbox on a test clock, and compare the invoices with the ones you sent.
  2. Shadow run. Send your live traffic to the sandbox for one billing period while your current billing keeps invoicing, with check() denials logged and not enforced.
  3. Cut over at a period boundary. Keep your old billing set up, and switched off, until the first Runledger invoices are paid.

Checklist

ItemDone when
EmailThe Admin’s email address is verified. Sandbox work doesn’t need it, and live can’t open without it
KeysRestricted live keys (rl_rk_live_...) are in your secret store and used only from your servers
check()It runs once per run, alongside the work, with a short timeout, and fails open when Runledger doesn’t answer
track()It runs from your queue, with idempotency keys built from your IDs and the real timestamp
Warningstrack() warnings reach someone who can fix the event names, references or plans behind them
WebhooksA live endpoint verifies signatures, drops duplicates on webhook-id and fetches each object before acting
Disputesdispute.decision_due reaches the people who decide, and each outcome definition has the default_decision your contracts call for
EvidenceOutcome events carry a transcript link and a factual description, with no personal data
Payment providersA provider is connected in the sandbox and a test payment through it has succeeded, such as a checkout completed with a test card. After the review, each provider you’ll use is connected in live by a person, with currencies and countries set, an Admin confirms the first one, and routing sends each new customer where you expect (POST /v1/payment_providers/resolve shows which); connecting in live isn’t open yet
ReconciliationSomeone is alerted on reconciliation.breaks_found, and a reconciliation runs for each provider
Top-up chargebacksIf you take card top-ups, a notification rule on topup_chargeback.threshold_exceeded has a threshold you chose, and someone owns the response
Notificationsdispute.decision_due, reconciliation.breaks_found and proposal.pending reach the people who act on them
Agent accessAgent keys carry only the scopes they need and an expiry, none holds charges:write or disputes:resolve, and a person reviews every proposed live change
Repeated errorsAt most 5% of the sandbox’s requests in the last seven days failed. Above that the row warns, and above 20% it fails, with the commonest error codes
Backtest and shadow runRunledger’s invoices match yours, or every difference is explained
CutoverOld subscriptions end at the boundary, and each event goes to the system its timestamp belongs to
Reviewed by RunledgerRunledger has reviewed the account and approved live access. The row reads In review by Runledger until it does

POST /v1/integration_checks reviews your recent sandbox traffic for what a live integration needs and returns a pass, warn, fail or unknown result for each item, with the fix. The dashboard’s Get started page and the developer MCP server’s check_integration tool show the same result. A row the data shows passes on its own, and a row you can’t attest has to pass. Some rows read unknown until there is something to read: Agent access until an agent key exists, and Evidence until an outcome event has been sent. An Admin asks for Runledger’s review from Get started once every other row is done.

Next steps