Charge agents per call

Agents don't sign up for plans. They discover a tool, decide it's worth a fraction of a cent, and pay per call — if your API can take the money. Today that takes one decorator.

The monetization story for agent-facing APIs is currently a stack of workarounds: a billing proxy in front of the MCP server, a spreadsheet mapping tool names to prices, a cron job reconciling gateway logs against invoices. The price of an operation lives in three places, none of them the code that implements it.

AgentBack's answer follows the framework's one rule: the declaration lives on the boundary, and everything derives from it.

@price('$0.001')
@tool('get_forecast', {input: ForecastIn, output: ForecastOut})
async forecast(input: z.infer<typeof ForecastIn>) { … }

@price('$0.01')
@get('/premium', {response: Premium})
async premium() { … }

Two consumers, one declaration

Metering reads it first. With MeteringComponent installed, every call's usage event now carries cost and units alongside the principal, operation, latency, and status it already had. The usage log is billing-ready: StripeMeterSink streams those events to Stripe metered billing as they happen, and StripeUsageReporter replays a durable log in batch, idempotently, with each event's id as the Stripe identifier. No gateway, no reconciliation job — the same events that power your audit trail price your invoices.

The payment gate reads it second. installPriceGate(app, {rail}) refuses unpaid calls to priced operations. Unpriced routes and tools pass through untouched, so the gate is safe to install on a mixed surface.

app.component(MeteringComponent);   // order matters: metering wraps
installPriceGate(app, {rail});      // the gate, so refusals are logged
                                    // as payment_required — never billed

A refused call gets the framework's standard machine-actionable envelope, the same shape on a REST 402 and an MCP tool error:

{
  "error": {
    "statusCode": 402,
    "code": "payment_required",
    "message": "Payment required: 0.01 USD per call.",
    "retryable": true,
    "hint": "Pay per the attached 'challenge' (x402: retry with the X-PAYMENT header; MPP: open or top up a session), then retry the identical request.",
    "challenge": {
      "rail": "x402",
      "accepts": [{"maxAmountRequired": "10000", "asset": "0xUSDC", "…": "…"}]
    }
  }
}

The agent reads the challenge, pays through its wallet or session, and retries. A settled payment returns the receipt in the x-payment-response header. Note where the asked amount came from: the rail's requirements callback receives the @price spec in its payment context, so "0.01 USD" becomes "10000 USDC base units" in one place — there is no parallel price table to drift.

Rails are pluggable, and that's the point

Which payment rail wins for agents is genuinely unsettled. x402 has the protocol momentum and the crypto-native story; Stripe's metered billing and MPP sessions are where most real invoices get paid today. We don't think a framework should bet your revenue on that race. PaymentRail is one interface with one question — is this call paid for? — and x402, MPP sessions, and Stripe usage-log billing are adapters behind it. Pricing stays on the decorator either way. Swap the rail; the controllers don't know.

The honest caveat: framework-native pricing only gates and meters. Custody, settlement, refunds, tax — those stay with the processor and facilitator, where they belong. What the framework removes is the part that kept drifting: the mapping from "this operation" to "this price" to "this invoice line."

The runnable version is examples/hello-x402 — a paid route and a paid MCP tool behind a stand-in x402 facilitator, with the usage log printed on shutdown.