Charge anything. See everything.

Any pricing model you can describe. Every charge, retry and cent accounted for — through the processor you already use.

Early access · every workspace starts in test mode — no processor or card required

roi

Overview

Test environment · no production data or money

Billed this period
$182,940
USD · 412 invoices
Recovered
$21,406
68% of at-risk
At risk
$9,880
17 invoices retrying
Pending settlement
$3,120
ACH · oldest 2d
InvoiceCustomerAmountStatus
inv_8fa21cNorthwind Trading
3rd attempt · stripe
$4,820.00recovered
inv_71b0e4Corvus Analytics
next in 3d · adyen
$1,240.00retrying
inv_5c93aaMeridian Health
1st attempt · stripe
$18,600.00paid
inv_2ad7f1Ashgrove Labs
ACH · settling
$960.00pending
inv_44e0b8Pallas Robotics
hard decline · 04
$7,315.00failed

Bill an agent product

Tokens, credits, per-action. Input and output priced apart, per model.

See every attempt

Full charge and retry history per invoice. The trail is the record.

Keep your processor

Your merchant agreement. Your rates. Your vault. No re-tokenising.

Bring the processor you already have a merchant agreement with

Stripebuilt in
Windcavebuilt in
NMIbuilt in
Fluidpaybuilt in
Cybersourcebuilt in
Dejavoobuilt in
Your ownone interface

Your processor should be a choice, not a dependency.

Most billing platforms quietly become the payment layer too. Separate the decision from the execution and it stays yours.

Billing decides. Payments execute.

Split them and either can change without a migration.

A decline is a routing problem.

One vault means every retry hits the same wall.

Scope not taken is scope not audited.

References only. No card data, no PCI.

01Agent & token billing

Know which model is losing you money

Price input and output separately, per model, at sub-cent precision. Provider cost rates against the same cells — so margin is per model, not a blended average hiding the loss-maker.

See the billing engine

ai_tokens · rate card

model × token_type · per 1M tokens
cost coverage 82%
ModelTypePriceProvider costMargin
gpt-4.1input$3.00$1.5050%
gpt-4.1output$15.00$7.5050%
opus-4.8input$3.00$5.00−67%
opus-4.8output$15.00$25.00−67%
(catch-all)$4.00untracked0%
Blended margin reads 41% — but one model is underwater on every call. Untracked cost reports 0% margin, never 100%.

02Billing engine

Meter, rate, invoice, recognise

Usage becomes tiers, tiers become invoices, invoices become collectable. One workflow.

Every pricing model

Graduated tier · API calls

Period 2026-06-01 → 2026-06-30 · 1,000,000 units metered
First 100,000$0.0000$0.00
Next 400,000$0.0012$480.00
Next 500,000$0.0008$400.00
Invoice inv_9c04de$880.00

03Dunning engine

From brittle to unmissable

A durable workflow, not a cron job. Next Tuesday's retry still fires after a deploy, a restart, or an outage.

How the engine works

inv_8fa21c · Northwind Trading

$4,820.00
Dunning workflow · run 01JQ8F…7K2 · started 11 days ago
1
stripesoft_declinecode 51 · insufficient funds
day 0
durable timer · 3 days — survives deploys, restarts and outages
2
stripesoft_declinecode 51 · insufficient funds
day 3
durable timer · 7 days — retry routed to a second processor
adyensucceededcode 00 · psp_ref AX7K92MQ
day 10
Recovered on attempt 3, across two processors+$4,820.00

04Connector framework

One port, every processor

The engine never branches on a vendor name. Cross-processor retry is a routing decision.

Read about the seam
engine/internal/connector/connector.go
// One port. Every processor implements it — pre-built or your own.
// The engine never branches on "if stripe".
type Connector interface {
	Name() string
	Charge(ctx, Instruction) (Result, error)
	Refund(ctx, RefundRequest) (Result, error)
}

// An Instruction carries PSP-minted references only. Never a PAN.
type Instruction struct {
	CustomerRef  string  // cus_… minted by the merchant's PSP
	MethodRef    string  // pm_…  minted by the merchant's PSP
	AmountMinor  int64   // integer minor units — no floats, ever
	Currency     string
}

// Cross-PSP retry is just a different connector name on a later attempt.
registry.Register("stripe", stripe.New(creds))
registry.Register("adyen",  adyen.New(creds))
registry.Register("acme",   yourOwnConnector)

05The boundary

Out of the money path

We see references, never card numbers. Funds settle straight from your PSP to your bank.

Talk through your setup
roi — the billing brain
Decides what to charge and when to retry. Holds the ledger, the plan, the attempt history.
charge instruction · references only
no card data crosses this line
Your PSP account
Holds the card, owns the vault, moves the money. Your merchant agreement, your rates.
funds settle directly to you
Your bank account
roi is never a party to the funds flow.

Every model, one engine.

Graduated tiers
Each tier priced at its own rate.
Volume tiers
Whole quantity at the tier reached.
Matrix pricing
Rate by dimension — model, region, SKU.
Package pricing
Blocks of N, partial block rounds up.
Per unit
Sub-cent rates in millionths.
Flat fee
Platform fees, alongside anything else.
Prepaid credits
Blocks drawn down in expiry order.
Minimums
Commit, then true up as its own line.
Price ramps
Year one, year two, year three.
Seats
Peak in period, or seats at close.
Allowance + overage
First tier free, the rest metered.
Tier flat fees
A fee attached to a tier itself.

Combine them in one plan. Simulate a change against a customer's real history — or usage that doesn't exist yet — before you ship it.

Built like money software.

Durable by construction

The attempt history is the audit trail.

WorkflowSubscriptionWorkflow
History events1,284
Periods billed36
ContinueAsNew×3
Replay determinismverified

Integer money math

Minor units end to end. No floats.

amountMinor int64 = 88000
// $880.00 USD, exact

// never:
// amount float64 = 880.00

Test and live, truly apart

A rehearsal can't be mistaken for production.

Testrehearsals · no money
Liveproduction billing
Separate keys, separate ledgers, separate connections.

Own the billing. Keep the processor.