Authorize agent spend before the provider call.

Reserve against the customer's budget, run the work, then settle what it actually cost and release the rest. A denied run makes zero provider calls and still leaves a receipt that says why.

free during early access · no card at signup

Settles to your own Stripe. The money never touches Unprice.

AGPL-3.0 open source · EU-hosted · two calls to integrate · nothing to deploy

The reservationbudget authorized · before provider
Requestreservations.reserve
Price2,050 tokens × $0.002 = $4.10
Budget check$5.90

does the balance cover this $4.10 request?

allow · within budget

allowed: true

cost $4.10 · covered by balance

deny · over budget

LIMIT_EXCEEDED

needs $4.10 · balance $1.80

Authorize first. Settle what the run actually cost.follow the full path ↓
01The status quo

The provider was already paid before anything checked.

Your Redis counter is not a budget. It records the spend after the provider call already made it. When a customer disputes the invoice, engineering reconstructs the charge from logs.

The DIY stackone agent run · traced
cron reset-usagelast success · 3d ago
POST /v1/run200 · work executed
INCR usage:acme-corp4,101 → 4,102
provider cost$0.48 · already spent
38 more log lines · unindexed
30 days later · invoice line$1,204.00
support · ticket #4812reply due · 24h

"Why were we charged $1,204? We set a $500 budget."

assignedengineering
plan versionunknown
budget checknever ran
ledgerno entry
evidencereconstructed by hand
02The money path

The authorization becomes the receipt.

An allow reserves against the customer's budget before the agent runs. After the work completes, Unprice settles actual usage, captures the ledger movement, and writes the invoice line. A deny makes zero provider calls: no work, no ledger entry, no invoice line, no charge.

The money path3 requests · one budget
Requestusage.consume

the agent run asks before it spends

Metertokens used · 2,050
Accessincluded in plan · yes
Pricing rule$0.002 / token · pro@v3
Budget check$10.00

does the balance cover this $4.10 request?

allow · within budget

accepted: true

cost $4.10 · covered by balance

Walletreserve −$4.10
Ledgercapture · balanced
Invoiceline explained
invoice line · explain$4.10

2,050 tokens × $0.002 · pro@v3

reserve → capture · balanced

Paymentyour own Stripe

the money never touches Unprice

deny · over budget

LIMIT_EXCEEDED

needs $4.10 · balance $1.80

Walletuntouched
Ledgerno entry
Invoiceno line
invoice line

zero provider calls · no cost to explain

reason → your app · limit exceeded

Paymentno charge

Every step is in the public SDK. Reserve for variable-cost agent work, or authorize known usage in one call. Use TypeScript, REST, or curl.

03The receipts

Read the proof.

Read the concurrency test, run the benchmark against your deployment, and inspect the authorization source. If a deny leaks a provider call or the ledger does not balance, the source and tests will show it.

A recorded walkthrough is coming. Until it does, the receipts below are the ones you can check yourself. You do not need an account or a demo call.

AI chatbotreserve before generation · settle actual token usage on finishRead the integration
concurrency5 concurrent over-limit writes → 2 accepted, 3 rejectedRead the test
latencyk6 harness · run it against your own deploymentRun the benchmark
the money pathAGPL-3.0 · the decision, the ledger, and the wallet in the openRead the source
04First integration

Reserve the customer's budget before the model spends yours.

Reserve a ceiling against the customer's budget before generateText runs. If the reservation is denied, the provider is never called. After the run, settle actual token usage and release the rest.

reserve → generate → settle
1import { generateText } from "ai"
2import { Unprice } from "@unprice/api"
3
4const unprice = new Unprice({ token: process.env.UNPRICE_TOKEN })
5
6// 1. Reserve the most this model call may cost.
7const { result: reservation, error } = await unprice.reservations.reserve({
8 customerId,
9 maximumAmountMinor: 10, // $0.10 ceiling, held against the customer's budget
10 idempotencyKey: messageId,
11})
12
13// Hard deny. Return here and the provider is never called.
14if (error) {
15 return new Response("Customer budget unavailable", { status: 402 })
16}
17if (!reservation.allowed) {
18 return new Response("Customer budget unavailable", { status: 402 })
19}
20
21// 2. Spend only after the reservation succeeds.
22const generation = await generateText({
23 model,
24 prompt,
25 maxOutputTokens: 2_000,
26})
27
28// 3. Capture actual usage and release the unused amount.
29const settlement = await reservation.settle({
30 featureSlug: "ai-output-tokens",
31 eventSlug: "completions",
32 id: messageId,
33 properties: {
34 inputTokens: generation.usage.inputTokens,
35 outputTokens: generation.usage.outputTokens,
36 },
37})
38
39if (settlement.error) throw settlement.error
40
41return Response.json({ text: generation.text })

Use the smallest operation that matches the work. A reservation fits variable-cost agent calls. These two paths cover simpler cases.

usage.consumeknown cost · authorize and consume in one atomic call
access.checkshadow only · read-only and reserves nothing
This is the same authorization pattern used by the working open-source AI chatbot.Read the chatbot integration
  1. 01

    Reserve

    Authorize the maximum cost against the customer's budget before the provider call.

    customerfunds the work
    held$0.10 maximum
    deniedno provider call
  2. 02

    Run

    Start the agent only after Unprice confirms the reservation.

    provideryour model account
    limit2,000 output tokens
    executoryour application
  3. 03

    Settle

    Report actual token usage. Unprice captures what the run cost, releases the rest, and closes the reservation.

    capturedactual usage
    releasedunused hold
    evidenceusage → invoice
05The questions

Asked before you integrate.

Unprice is for products where one customer request can start an agent run or paid workflow that spends real money before anyone checks. If your product only charges per seat, Stripe Billing is enough. If you want one aggregate cap on your model-provider bill rather than a budget per customer, use an AI gateway.

Stripe today · not tax, accounting, or revenue recognition

Why not just Stripe?
Stripe captures payment after the work is done, so it cannot stop the work. Keep it. Unprice runs earlier: it authorizes the spend before the provider call, then hands Stripe an invoice line that can be explained. The full argument
Why not an AI gateway?
A gateway sits inside the provider call and caps your total provider bill. Unprice sits before it and authorizes one run against one customer's budget. A denied run never reaches the gateway. Some products want both.
Why not Redis?
You can build the reservation in Redis. A Lua script that holds an amount and releases the remainder is about fifty lines, and it will be correct. The reservation was never the hard part. The trail is: which plan version was in force, what rate applied, which grant the hold came from, how the settled amount became a ledger entry that balances, and which invoice line it landed on. That is what gets rebuilt by hand every time support asks why a customer was charged. Unprice keeps it on one path, with the reservation attached. Read the source
Where does this run?
On the hosted cloud: install the SDK, call the API, nothing to deploy. It runs in EU regions, and per-customer state — wallet reservations, run budgets, idempotency keys — is pinned to Cloudflare's EU jurisdiction, so it is never placed outside it. To run Unprice yourself, deploy the open-source runtime to your own Cloudflare account, and your data lives wherever you put it. Either way, payments settle in your own Stripe account.
Does Unprice hold the money?
No. Your app asks before paid work runs and gets an allow or deny with evidence. Stripe captures payment in your own account or through Stripe Connect, and the built-in Sandbox provider lets you test the path without a processor. Unprice records the decision, the ledger movement, and the receipt. The money never touches Unprice.
What does a deny look like to my user?
Whatever you decide to show. A deny is a business result, not an outage: the call returns 200 with allowed set to false and a machine-readable reason such as LIMIT_EXCEEDED. Your app can explain the limit and offer an upgrade.
How much latency does the check add?
One request, ahead of a provider call that costs orders of magnitude more. A warm check is a cached read plus one Durable Object read. Invoicing, analytics, and ledger work run off the request path. Latency depends on where your traffic runs, so the repo ships a k6 harness — point it at your deployment and read the percentiles. Run the benchmark
What if Unprice is down?
The check returns an explicit error and your code picks the fallback: fail open and log it, or fail closed for expensive actions. Caches can serve stale answers while they revalidate, and shadow mode blocks nothing during adoption.
06The offer

Put one agent action on a budget in one afternoon.

Bring the agent run or paid workflow that burns margin when a customer triggers it. That is the only prerequisite. Read the argument in the manifesto.

one plan version · one customer signup · one reserved agent run

Sandbox first

Test the reservation with Unprice's built-in Sandbox before it touches production. If the result does not match your model, remove it. No provider traffic, payment processor, or contract changed.

I am taking ten design partners and I onboard each one myself. Email me your action and I'll reply with the first step, even if no slot is left.

Seb, founder of Unprice · [email protected]