Why a bare charge does not work
The obvious way to charge an agent is to have it sign a payment for a fixed amount and transfer it. That is what the standard x402exact scheme does with EIP-3009: it transfers exactly the signed value. But it cannot do two things Utter needs:
- Meter. The final price often depends on the response (its size, the compute it took). A bare transfer fixes the amount before the handler runs.
- Gate on the result. A bare transfer settles regardless of whether the answer was any good. The agent pays for a malfunction.
The flow
1
402 with a quote
A call without an
X-PAYMENT header returns HTTP 402 and an accepts body describing the utter-escrow scheme: the asset (USDC), the escrow contract, the payTo (the resource id), the cap (maxAmountRequired), the pricing, and the EIP-712 domain to sign under. See the call endpoint reference.2
Sign a capped authorization
The agent signs a
DebitAuthorization for a cap, under the UtterEscrow domain, and sends it back base64-encoded in the X-PAYMENT header. The signed cap is a ceiling, not the price.3
Verify reserves the cap
The gate calls the facilitator’s
/verify, which recovers the signer, checks the buyer has enough un-reserved deposit balance, checks the nonce is unused, and reserves the cap. Only now, with funds locked, does the handler run. This is the rule that removes the free-compute vector.4
Handler runs under a timeout
The untrusted handler runs in its sandbox, bounded by the timeout the buyer signed for. The gate clones the response body so it never consumes the client’s stream.
5
Classify the response (the gate)
The gate classifies the body as
success, declared_error, or malfunction. This branch is the gate.6
Settle, release, or fail closed
On
success, the gate computes min(metered, cap) and calls /settle, which debits the buyer and splits the amount inline. On a declared_error it releases with no charge (or charges a declared error price, never more than the cap). On a malfunction or a timeout it releases the reservation, records a strike, and returns an error. It never debits on anything but a success.Three outcomes, three behaviors
The gate distinguishes three things that a naive charge would conflate. This distinction is the wrongful-strike guard: a bad answer from the endpoint is the creator’s fault, but a bad input from the buyer is not.Success -> charge
Success -> charge
The response validates against the endpoint’s declared success schema. The buyer is charged
min(computed, cap), split 70/30 between the creator and the platform, and a receipt is returned in the X-PAYMENT-RESPONSE header.Declared error -> no strike
Declared error -> no strike
The response validates against the endpoint’s declared error schema. This is a well-formed “your input was bad” answer, not a malfunction. The reservation is released with no strike against the creator. Depending on the endpoint’s error policy the call is free or charged a small declared-error price, never more than the cap.
Malfunction or timeout -> no charge, strike
Malfunction or timeout -> no charge, strike
The response is neither a valid success nor a valid declared error, or the handler timed out or threw. The reservation is released, nothing is charged, and a strike is recorded against the creator. Five consecutive strike-worthy failures deactivate the endpoint. See reputation and strikes.
Exactly once
A network retry must never double charge and never re-sign. Settlement is idempotent, keyed by the payment nonce (which is the idempotency key):- The facilitator caches
(idemKey -> result)and short-circuits a repeated/settleto the cached receipt. - The escrow contract flips a single-use nonce on-chain, so a retry after a crash reverts instead of debiting twice, and the receipt is rebuilt from the on-chain event.
- A buyer that lost the response calls
GET /results/:idemKeyto recover the receipt without paying again.
Why the split is inline
The 70/30 creator/platform split happens inside the same on-chain debit that charges the buyer, not as a later payout job. ThePaymentEscrow.debit call credits the creator’s and the treasury’s internal balances in one transaction and emits a Debited event carrying both legs. There is no separate settlement step that could drift, fail, or be skipped. The creator withdraws their accumulated balance whenever they want.
The gate never runs a handler against an unreserved authorization, and it never debits on anything but a validated success. If you remember one thing about Utter, remember that.