Consent
Who agreed to a charge when an agent is the one calling, and which of those agreements a merchant can actually enforce.
When a person buys something, one agreement covers everything: they saw the price and they paid it. When an agent calls a paid API, that single agreement splits into three, and they are easy to confuse.
| Agreement | Who makes it | Can the merchant see it? |
|---|---|---|
| The money may be spent | The payer, when they signed the proof or funded the credential | Yes — it is the proof, and its limit |
| This call is worth paying for | The client, against a budget its owner set | No |
| A person agreed to this amount | Whoever is at the client, if anyone asks | Only if the merchant asks |
Tollstile is the merchant's side of this. It can hold the first agreement to its terms and it can ask for the third. It has no view of the second, and it never holds the wallet.
What the merchant enforces
- The server decides the price. Amount, asset, network, and recipient come from the route and the signed quote — never from the client's payload.
- A quote is bound to the request it priced. A cheap quote cannot pay for a bigger request.
- A proof is spent once. Replay is refused at the ledger, not in memory. See Authorizations and charges.
- A maximum, then what was used.
upTo()authorizes a ceiling and settles the amount the handler reports; the payer is never charged past what they authorized. - Caps per payer. Spend limits refuse calls over a rate or an amount, whatever the payer's credential allows.
- A retry is not a second charge. Idempotency answers a repeat from the ledger.
None of that requires a person to be present. It is what makes an unattended agent safe to charge.
Asking a person
A client approves the tool, not the amount — and "always allow" removes even that. So a paid call can be charged repeatedly without anyone seeing a price.
Over MCP, a server can ask. paidTool(..., { approval }) puts the amount in front of the person at the client, after the payment is verified and reserved and before the handler runs, and charges only if they accept. Anything else releases the reservation and charges nothing.
What clients do today, measured rather than assumed. A server learns what a client can do from its initialize, and the answers are not what the published matrices say: Claude Code 2.1.186 declares elicitation: { form: {} } — asked to approve $0.25, it showed the person an Accept / Decline dialog, and the charge settled only after they accepted. It declares no url, so it cannot be sent to a page. OpenAI's codex-mcp-client declares both, and does open the page. Claude Desktop declares neither. So a merchant who needs the person to see something should assume the plainest channel: the text the model reads. The demo publishes what every client that connects to it declared, at /api/clients.
Ask for what is worth interrupting someone over. above keeps small calls quiet; a maximum a person approves once, through upTo(), beats a question per call.
HTTP has no equivalent today. A 402 goes to the agent, which is the party that pays; there is no channel from the merchant to the person behind it. A payer who wants a human in that loop puts them in the wallet, not in the API.
What Tollstile does not know
- Whether a person exists behind a call at all.
- Whether the client's owner set a budget, or what it is.
- Whether a client that declares it can ask a person will really show them anything.
- On a stateless HTTP server, whether a person answered: the answer arrives on a later request that the server which asked is no longer waiting for. Such a server refuses the charge rather than guessing.
A merchant who needs the charge to be approved must require the approval and refuse without it. Anything softer is a charge nobody agreed to.