Spend and limits
See what your account spends per user, key and agent, cap each of them, and charge your users for theirs.
Platform usage is charged to your NoClick account at what each service costs NoClick, nothing added, and reported in US dollars. It's paid from your account's balance, where funds you add carry your plan's fee (15% on Free, 5% on Member). Everything is attributed to the key, the user and the agent it was for, so you can see exactly where the money goes and put a ceiling on each.
What costs money
| Type | What it covers |
|---|---|
| Model tokens | every agent turn and its model calls |
| Tool runs | integration operations, including the ones on NoClick's key, and the search and fetch capabilities |
| Sandboxes | each second a sandbox runs, including the ones behind the code capability and the one an agent on a coding harness runs in |
| Emails | request emails to people and inbox replies |
| Phone calls | each call's minutes |
| AI | tokens, images and minutes of audio through AI models (an agent model's turns are the agent's) |
| Storage | what buckets store, by the GB-month; requests and downloads are free |
| Volumes | what volumes store, by the GB-month |
| Apps | requests, compute time and builds of deployed apps; what an app spends on AI, agents and its data shows under those types, attributed to the app |
| Databases | compute time (Postgres), rows read and written (SQLite), and storage, for databases |
| Browser minutes | each minute a browser runs, plus its model's tokens |
| App email | each message an app sends |
| Domains | domains you buy and renew |
Phone numbers are billed monthly on top.
An agent on a coding harness (claude_code, codex, opencode, hermes, openclaw) thinks with its model connection's key or plan, so its model tokens are the provider's to bill, not NoClick's. What NoClick bills is its sandbox's running time, from the turn that starts it through about a minute after each reply, even when the key is yours: 1 core and 4,096 MB at a sandbox's price, about $0.24 an hour without a region. That time counts toward the agent's budget and its user's and key's caps like any other spend. A turn needs a little balance to start its sandbox, and fails with noclick.PaymentRequired without it.
| Type | What it covers |
|---|---|
| Dicts | what dicts store, and their reads and writes |
| Queues | messages put on queues |
| Card numbers | $0.10 per card number issued; what a card pays comes from your company's card balance, limited by each card's own limit |
See what you've spent
import noclick
month = noclick.usage(group_by="user")
print(month.total_usd)
for group in month.groups:
print(group.id, group.usd, group.events)
month["user_42"].usd # one user, by id: 0.0 if they spent nothing
month.get("user_42") # the group, or None if they spent nothing
support = noclick.Agent.from_name("support")
noclick.usage(group_by="user", agent=support) # what one agent cost, per usergroup_by is "user", "key", "agent" or "project" (each app's spend), and agent narrows it to one agent. Inside as_user, noclick.usage() covers only that user.
For a chart, get daily lines:
series = noclick.Usage.series(group_by="type") # this month, one line per type above
for line in series.series:
print(line.label, line.total_usd, [point.usd for point in line.points])group_by can also be "user", "agent" or "key", and from_ and to pick another window of up to 92 days.
Cap it
A cap for each kind of spender, each per calendar month:
| Cap | Set it with |
|---|---|
| A key's spend | the key's settings in the console |
| A user's spend | spend_cap_usd_per_month on the user |
| An agent's budget | budget_usd_per_month on the agent; a sub-agent's comes out of its parent's |
| An app's budget | budget_usd_per_month on the app |
Once any cap that applies to a call is used up, the call fails with noclick.SpendCapExceeded before doing any work:
try:
agent.message("Summarize my week", user="user_42")
except noclick.SpendCapExceeded as err:
print(err.scope, err.spent, err.cap) # "user", 20.13, 20.0Caps are checked before work starts, so the call that crosses a cap still finishes; the next one is refused. Caps reset at the start of each month.
The first refusal sends your webhooks a spend.cap_reached event naming the cap's scope and id, once a month for each cap (again if you raise it and it's reached again), and NoClick emails your account's owner, at most once a day while it refuses work. Your users never hear from NoClick about money: the event is how your code decides whether and how to tell them, say by pausing their spending with your own message.
noclick.Usage.limits() lists every capped key, user and agent with what it has spent this month, and every app with a budget in projects.
When an agent changes a cap
Your own server, with a secret key, changes any cap either way. An agent connected through MCP, your NoClick assistant included, may only tighten one by itself: lowering it, or setting one where there's none, goes straight through. That holds for an agent's budget_usd_per_month and its turn_limits.usd, a user's spend_cap_usd_per_month, the budget_usd_per_month of an app or a workspace, a channel's budget_usd, and your account's monthly cap.
Raising or removing one waits for your account's owner. NoClick sends them a link on the app they use (WhatsApp, iMessage or email) that names the cap and what it goes from and to. They approve or decline it with one tap, without signing in, within 24 hours, and the link works once. Nothing changes until they approve; the agent's call answers approval_required with sent_to naming where the link went, and no link of its own.
Your account's monthly cap
Above all of these, your account can have a cap of its own: what it may spend in each billing period, counted as the Usage page totals it. Once the period's spend reaches it, every call that costs money fails with noclick.SpendCapExceeded (its scope is "account") until the period resets. What the account already has keeps renewing (numbers, domains, storage), and apps keep serving pages until the period reaches twice the cap. noclick.Usage.limits() shows it under account: cap_usd, and where one is set spent_usd and resets_at.
The account's owner sets, raises or removes it in Settings → Plan and billing in the NoClick app. Besides the owner, only the account's own assistant can change it, with PUT /v1/usage/limits/account and {"cap_usd": 50} (null removes it): it lowers the cap, or sets one where there's none, at once, and a raise or a removal waits for the owner's approval as above. Your keys, your apps' code and your other agents are refused, whatever they hold.
When an app reaches a cap
A deployed app spends on its own, from its hosting to its AI calls and the agents it messages or made, what it buys (a number, a domain, each renewal too) and what bills after its request (its numbers' calls and texts, its automations, MCP servers, channels, machines and cards), all billed to your account and attributed to the app and its user. It counts toward the user's cap and the app's own budget_usd_per_month, which every app made through the API has ($10 unless its maker sets another), and toward the budget of the app whose code made it, if any. When either is used up, or your balance is, the app keeps serving pages and reading its data, while its paid calls (writes, app.ai, agent turns) fail with noclick.SpendCapExceeded or noclick.PaymentRequired naming the app and its email waits to go out (held); only at twice its budget do its pages pause:
app = noclick.Project.current()
try:
reply = app.ai.chat("Summarize today's bookings.")
except noclick.SpendCapExceeded as err:
print(err.scope, err.project, err.spent, err.cap) # project booking 25.04 25.0Deploys are refused the same way, NoClick emails your account's owner once a day until it lifts, and a spend.cap_reached event names the app and its user, so your own code decides whether and how to tell your customer. When it runs out lists which calls keep working and which fail.
When the account runs out
Usage is charged to your account's balance, in dollars: what your plan includes each month, plus any funds you add. When it's used up, calls that spend fail with noclick.PaymentRequired. Its balance_usd is what's left and required_usd what the call needed, and its offers are one-tap links to add funds or change plan, each a payment operation that finishes when it's paid (see Errors). The console's Usage page shows the balance and when your plan's amount renews.
Add funds
To add funds before that, or for any amount, the account's owner (signed in) and the account's own assistant make the same link: POST /v1/account/payment_links with {"amount_usd": 35} (whole dollars, $10 to $1,000), or {"kind": "plan", "plan": "member"} for a plan. It answers at once, without charging anything:
| Field | What it is |
|---|---|
url | The link: it opens checkout for this account, with no sign-in. |
amount_usd | What the balance gets. |
fee_usd | Your plan's fee on it. |
price_usd | What checkout charges: the amount and the fee. |
operation_id | A payment operation that succeeds once it's paid, or expires unpaid after 48 hours. |
Asking for the same purchase again while its link is open answers that link. Your keys, your apps' code and your other agents are refused, whatever they hold.
Pay from an agent's wallet
An agent whose wallet grants Stripe Shared Payment Tokens (spt_..., from Link's agent wallet) adds funds itself, with no link: its person approves the amount in the wallet, and the token pays. Two protocols take it, at the same price as a link (whole dollars, $10 to $1,000, your plan's fee on top) into the same balance, and the callers a link allows are the ones allowed here, an unclaimed sign-up's own key included. Either refuses a token approved for less than the total. Where NoClick doesn't take wallet payments yet, every route below answers 409 not_available; a payment link works there.
Agentic Commerce Protocol (version 2026-04-17). Create a checkout, sending an Idempotency-Key on each POST:
POST /v1/checkout_sessions
API-Version: 2026-04-17
{"line_items": [{"id": "account_funds_usd", "unit_amount": 2500}], "currency": "usd"}unit_amount is the cents to add. The checkout's totals add the fee, and its payment handler stripe_spt names the Stripe profile to issue the token to (config.merchant_id). Approve a spend request for the total there, then pay it:
POST /v1/checkout_sessions/{id}/complete
{"payment_data": {"handler_id": "stripe_spt",
"instrument": {"type": "card", "credential": {"type": "spt", "token": "spt_..."}}}}| Answer | What it means |
|---|---|
200, completed | Paid: its order is the deposit, and the balance has the funds. |
200, ready_for_payment | Declined: its error message says why, nothing was charged, and another token may pay it. |
202, complete_in_progress | Stripe hasn't said: read it (GET /v1/checkout_sessions/{id}) or complete it again. It never pays twice. |
A checkout expires unpaid after an hour; POST /v1/checkout_sessions/{id}/cancel cancels one sooner. Its errors take ACP's shape, {"type", "code", "message"}.
Machine Payments Protocol. POST /v1/account/funds with {"amount_usd": 25} answers 402 with a WWW-Authenticate: Payment challenge (method="stripe", intent="charge") whose request is the total in cents and the profile to issue the token to (methodDetails.networkId). Send the same request again with the credential in Payment-Authorization, since your key stays in Authorization. 201 with a Payment-Receipt adds the funds; the same credential again answers 200 and adds nothing more. A credential that doesn't pay answers 402 with a new challenge, and a challenge expires after 15 minutes.
Charging your users
NoClick bills your account, never your users: everything done for a user, from an agent's turns to a domain registered in their name, is charged to you, in dollars. What you charge them, and how, is yours to decide: a subscription, monthly credits with top-ups, paying for exactly what they're short, a team's shared pool, an invoice at the end of the month. That logic stays in your own billing system, where it already lives, and NoClick gives it three things:
- The usage, as it happens: a
usage.recordedevent for each step of spend for a user, within seconds. - A switch:
user.pause_spending()stops a user's spending and tells them what to do;user.resume_spending()lets them go on. - A ceiling: a user's spend cap, which holds whatever your billing does.
Usage, as it happens
Subscribe a webhook to usage.recorded, and each step of spend for one of your users reaches your server a few seconds after it settles: an agent's turn when it ends, an AI request, a tool run, a call when it ends, each stretch of a running sandbox or machine as it's billed, an email, a text, a purchase or a renewal, and, early the next day, what their storage held that day.
event = noclick.Webhook.verify(request.get_data(), request.headers, os.environ["NOCLICK_WEBHOOK_SECRET"])
if event.type == "usage.recorded":
usage = event.data
billing.record(customer=usage["user_id"], usd=usage["usd"], at=usage["occurred_at"], key=event.id)Each event's data is one usage record:
| Field | What it is |
|---|---|
user_id | Your id for the user it was for. |
usd | What NoClick charges you for it, in dollars, to 6 decimal places. |
kind | What it was: agent.turn, ai.request, tool.run, sandbox.time, phone.call, email.send, text.message, purchase, renewal or storage.day. |
lines | What it's made of, each with its type (as in What costs money), usd, quantity and unit: token, run, second, email, text (a text's segments), gb_hour or item (a purchase or a renewal). |
agent_id, agent, conversation, operation_id | The agent, conversation and turn it was for, when it was one. |
resource | What a span, a purchase or a renewal was about, like {"object": "phone_number", "id": "..."}. |
started_at, occurred_at | When it began and when it ended; the same for anything instantaneous. |
corrects | The record a correction amends, else None. |
That's what any meter API asks for: whose usage, how much, when, and a key that makes a second delivery harmless. Use event.id as the key (the record's own id is the same), since the same event can arrive twice. A turn's record comes a few seconds after the turn ends, so that what it charged after its reply is in it; an agent's step outside any turn (a call's) is a record of its own. Charge by usd with your margin on top, or by a line's quantity (a call's seconds, say) if that's how you price. Spend that costs your account nothing, like model tokens on a user's own key, sends no record.
Records are never edited. If NoClick has to amend one (a turn charged for something after its record went), the correction arrives as a record of its own, with corrects set to the record's id and a usd that may be negative: net it in your ledger, or send it as an adjustment if your meter takes them. The records add up to what noclick.usage(group_by="user") reports for the same period once they've all settled (a day's storage arrives early the next day), so you can reconcile your totals against it, and replay any your server missed from the event log, which keeps 30 days.
Pause a user's spending
When your billing says a user can't go on (their balance is low, a payment failed, a trial ended), pause them with what they can do about it, and resume them once they've paid:
user = noclick.User.from_id("user_42")
user.pause_spending(message="You've used your credits.", action=("Top up", top_up_url("user_42")))
# later, from your payment webhook:
user.resume_spending()While a user is paused, nothing that costs money starts for them. Your code's calls raise noclick.SpendingPaused; a message they send an agent gets your message and link back, without the agent running, and waits to run until you resume; a call hears your message. What they already have keeps running. Pause a user's spending has the details for each case.
Where the top-up link comes from
The action you pause with is your own page, so the money goes to your account and you decide what to offer. Three common ways, simplest first:
- A payment link from your payment processor, with the user's id on it, like
https://pay.example.com/l/abc?client_reference_id=user_42. Its "paid" webhook names the user, so you credit them and resume. - Your billing page, where the user signs in. Simple for people on your web app, slower for someone answering from their email or phone.
- A stable link on your own domain that makes a checkout when it's opened, like
https://acme.com/top-up/user_42?sig=.... Use this when the amount varies (topping up exactly what's missing) or the message may be read days later: a checkout session made ahead of time can expire before anyone opens it. Sign the link, so nobody can make one for another user.
How far past their balance a user can go
Your billing hears of each step seconds after it settles, and pausing stops new work at once, so what a user can spend past the point you'd have stopped them is the work in flight when you pause, which finishes: a model call in progress, the reply a call is speaking. Pause at a margin (when a dollar's worth is left rather than none), and keep a spend cap as the ceiling no delay can cross.
Plan features
What a user may do at all (web search only on your Pro plan, say) belongs in your policy, not their balance: a deny rule with a condition on the user, and a message that tells them how to get it.
noclick.Policy.add(
noclick.Rule("search", when='user.metadata.plan == "free"',
message="Web search comes with Pro: https://acme.com/upgrade?user={{user.id}}"),
to="deny",
)Keep metadata.plan in step from your billing with user.update(metadata=...). A refused call comes back to the agent with your message, and the agent tells the user. A purchase you'd rather judge case by case goes in require_approval: it waits until a person, or your own code with request.approve(), answers.
Without any of this
noclick.usage(group_by="user")gives what each user cost you this month, to bill them on at the end of it, andgroup_by="project"what each of their apps did.- An app takes its own users' payments with
app.payments, into a payments account its owner claims.