Leash
not deployed

Money stays in fiat rails. Spending authority is enforced on-chain.

An AI agent with a payment method can be talked into paying the wrong merchant. Leash puts the boundary somewhere the agent cannot reach and the operator cannot quietly edit.

Both need a deployed contract · none on Base Sepolia yet

Leash eye illustration tracking the cursor

Four payment attempts. One got through.

Every rejection below is a real transaction that reverted on chain. Each one produced a receipt, emitted no AuthorizationGranted event, and left the amount already spent untouched. A backend watching for confirmed authorisations sees nothing to settle.

Rp52.000to Rock Burger

On the allowlist, inside the ceiling, before expiry.

AuthorizationGranted logs: 1 · settled once
Authorized
Rp50.000to Evil Store

The agent was told to pay a merchant the owner never authorised.

AuthorizationGranted logs: 0 · settlement eligibility: none
RefusedTargetNotAllowed
Rp500.000to Rock Burger

On the allowlist, and Rp492.000 past what the mandate has left.

AuthorizationGranted logs: 0 · settlement eligibility: none
RefusedAmountExceedsCap
Rp1.000to Rock Burger

A payment that would have passed an hour earlier.

AuthorizationGranted logs: 0 · settlement eligibility: none
RefusedRevoked

Transaction hashes for this run are reproducible local-chain evidence, not a public testnet claim. See docs/VERIFICATION.md.

The contract can refuse in eight ways. It cannot be talked out of any of them.

This is the whole refusal vocabulary. Nothing in this interface invents a reason a payment failed — it prints the error the contract raised.

TargetNotAllowed
This merchant is not on the mandate's allowlist. Register a new mandate that includes it. An existing allowlist cannot be extended.
AmountExceedsCap
The amount is larger than the mandate's remaining balance. Spend within the remaining balance, or register a new mandate with a higher ceiling.
Revoked
This mandate was revoked. Revocation is permanent. Nothing restores it. Register a new mandate to authorise spending again.
Expired
The mandate's expiry has passed. Register a new mandate with a future expiry.
NotOwner
The signer is not authorised for this action. Payments must come from the session key; revocation must come from the owner.
ZeroAmount
The amount is zero. Enter an amount above zero.
InvalidMandate
No mandate exists for this identifier, or its terms are invalid. Check the mandate ID, or register the mandate first.
MandateAlreadyExists
A mandate with this identifier is already registered. Mandate identifiers are permanent. Use a different one.

Authorisation is not settlement.

The contract decides whether a payment is eligible. A backend polls confirmed blocks, finds the authorisation event, and only then issues a card. There is no endpoint an agent can call to ask for money directly — the confirmed event is the only trigger that exists.

That boundary is what makes a reverted transaction final. No event, no settlement, nothing to dispute later.

Mock BaaS · no card, no money
Backend output, valid payment
Backend output, all three refusals

Try to make it pay the wrong merchant.

The agent on the next page runs on a real model with a real session key. It can be talked into anything. The contract is the part that cannot.

Open the agent console

Console opens once a contract is deployed on Base Sepolia