Skip to content

A mandate layer for AI agents on NEAR

Spend by mandate. Prove every payment.

Give your AI agent a budget, not your wallet. It can pay only the payee you named, up to the limits you signed. A NEAR contract refuses everything else, and every decision goes into a signed ledger you can publish.

For teams whose AI agent pays supplier invoices, and for developers who want to give an agent money without the keys. Testnet today, and one small vault, v0.probarion.near, with a payment on mainnet (opens in a new tab) and a refusal on mainnet (opens in a new tab). You need no account to read the chain or run the verifier.

Real transactions on NEAR testnet, with test tokens, each sent by its vault's agent key. On the Canary our own fake invoice fooled the model into proposing 50 test USDC. Code refused it: it found no amount it could read with certainty, and the Canary's own rule refuses every attempt. The Canary then sent it to the contract unfiltered, on purpose, and the contract refused it.

The problem

Giving an AI agent a wallet means trusting a prompt with money.

A model can be talked into anything by the invoice or web page it reads. Probarion takes the rule out of the prompt and puts it in a NEAR contract.

An agent with its own wallet key

  • ✕Can send any amount to any address the prompt names.
  • ✕A hijacked invoice changes the payee and nobody notices.
  • ✕Refusals leave no record. You learn of a bad payment afterwards.

An agent with a Probarion mandate

  • ✓Its key can call one method, spend, and only inside your caps, uses and expiry.
  • ✓spend has no recipient field. The payee comes from the mandate you signed.
  • ✓Every decision, refusals included, is signed into a ledger the verifier checks.
0

full-access keys on the vault. A public RPC call shows it.

1

method the agent's key can call: spend.

0

recipient arguments on spend. The payee comes from the mandate.

9

checks anyone can run. Live, they read the chain. Offline, they check a snapshot against itself, so take the snapshot yourself.

Try it, live on NEAR testnet

Ask the contract. It answers, not us.

Pick what the agent tries. This page sends it to the vault's check_spend view, the same check spend runs before it pays, and shows the contract's answer word for word. No wallet or account needed: it reads NEAR testnet, whatever network this page is on.

What the agent tries

Start from a scenario, or change the fields yourself.

USDC
Payee
probarion-payee.testnetFrom the mandate. The agent cannot set it.

The contract's answer

Recorded answer from 2026-11-09, block 276860836. Raw: {"ok":true,"code":null,"would_hold":false}

Would pay

Inside the mandate. 2 USDC would go to probarion-payee.testnet, the payee the owner signed, whatever the prompt said.

check_spend({
  request: {
    mandate_id: 1,
    amount: "2000000",  // 2 USDC
    invoice_hash, ref, decision_hash,
    recipient  // no such field
  },
  signer_pk: "ed25519:5errZX…"  // the agent key
})

Method: check_spend (a view: nothing is signed and nothing is paid)The vault on the explorer (opens in a new tab)

Mandate 1, as the chain has it

The owner signed these limits in their own wallet. The agent cannot change them.

payee
probarion-payee.testnet
max_per_payment
5 USDC
max_total
20 USDC (19 left)
uses
1 of 5
approval_above
5 USDC
expires
2026-12-01
agent_pk
ed25519:5errZXjDMX…
status
active

Why not just a spending limit?

A cap stops big payments. It does not stop the wrong ones.

You can write a contract with a limit in an afternoon. These are the parts that take longer, and where Probarion stands on each.

QuestionA limit contract you writeProbarion
Who picks the payee?Usually the caller, so the agent.The mandate. spend has no recipient argument. On testnet
What can the agent's key do?Often a full-access key, or a key that can call anything.A function-call key that can call spend and nothing else. Every owner method refuses any caller but the owner's account. On testnet
A cross-chain payee?The agent passes an address to a bridge.The vault builds the Intents withdrawal memo from the mandate. On testnet
Big payments?Refused, or allowed.Held above a limit you set until a person approves. On testnet
A record of what was refused?Failed transactions, if anyone looks.Each decision is signed into a hash-chained ledger before money moves, refusals too. 0.1.1 on npm
Can someone check it without trusting you?They have to read your code.An open verifier runs 9 checks, on a snapshot or against the chain. Shipped, on npm
Is the deployed code the code they read?Hard to tell.Reproducible builds, with code hashes recorded in the repository. Shipped
Is the model's advice tied to this payment?No model, or one nobody can check.A NEAR AI Cloud TEE attestation bound by a nonce to one ledger position. Checked online on testnet, image not pinned

How it compares with NEAR Agent API, x402 and AP2, sources read 2026-11-11, including what Probarion lacks (opens in a new tab)

What happens to one invoice

The owner signs the rules once. When an invoice arrives, your agent asks to pay it. It never holds the money or types an address. The model may advise, code decides, and the contract has the last word.

1

The owner signs a mandate

From their own wallet: who gets paid, how much per payment and in total, how many times, until when, and above what amount a person must approve.

An example mandate
payeeprobarion-payee.testnet
max_per_payment5 USDC
max_total20 USDC
max_uses5
approval_above5 USDC
2

A private model proposes, if there is one

It runs in a TEE on NEAR AI Cloud. Its attestation is bound by a nonce to one position in one vault's ledger. It only gives advice: it cannot widen a mandate.

TodayOn testnet, the Canary's ledger of 2026-11-10, published in the repository, holds two attested decisions that pass A1 to A7 offline and, on 2026-11-11, O1 and O2 online. The testnet demo vault's ledger holds five more, by Payables, which pass A1 to A7 offline; four of them, entries 10 to 13, also passed O1 and O2 online on 2026-11-12. That day the code allowed the fourth and the fifth, and the vault paid each invoice, 2.00 test USDC. The TEE measurement is not pinned, so this does not show which model image ran, and A8 was not run on the Canary's two; on the demo vault's five it passed on 2026-11-12, with the invoices committed in the repository.
What the proposal is bound to
actionpay
mandate_id1
noncenext ledger position + vault
signatureprovider_tee
3

Code decides, and signs before anything moves

Deterministic rules run first and cannot be removed. The SDK signs the decision into the ledger before anything is sent, and it records refusals too.

decide(), built-in rules include
mandate.none
mandate.payee_mismatch
mandate.over_total
invoice.duplicate
vault.daily_backstop
4

The contract decides last

Even if every line of off-chain code were wrong, the vault refuses a payment outside its mandate. Above the approval limit, the spend waits for an approver's own wallet. The agent's key cannot approve anything, not even its own payment.

Try it yourself above.
check_spend on mandate 1, at most 5 USDC, valid until 2026-12-01
2 USDCok: true
6 USDCok: false, OverPerPayment
5

Check a published record

With a ledger key, the SDK anchors the ledger root on the vault, and the owner can publish the ledger on a proof page. The verifier ties each spend in a snapshot to exactly one decision, with no server and no account. Offline, those spends come from the file. A live run reads them from the chain.

TodayThese commands run on a test fixture: this site serves an exact copy of the file in the repository. The testnet demo vault's proof page runs the same checks on its own ledger and chain data.
In an empty folder, with Node 24 or later. In Windows PowerShell 5.1, type curl.exe for curl.
$ curl -O https://probarion.xyz/samples/valid-snapshot.json
$ npx probarion verify --offline valid-snapshot.json
9 passed, 0 failed, 0 unproven
RESULT: PASS

Four invoices on the testnet demo vault

Invoices we sent to our own demo vault on NEAR testnet.

Its mandate pays probarion-payee.testnet, at most 5 test USDC a payment, and holds anything above 3 for an approver. Whatever an invoice says, the money can only go to that payee. No model read these invoices: the code read each amount, and nothing was sent until the owner confirmed it in their wallet.

Its proof page runs checks a to i on this ledger and the chain. Open the proof page →

Plug in your agent

Your agent keeps its brain. Your vault holds the money.

You keep your model and your prompts. The owner creates a vault and a mandate from their own wallet, gives the agent a key that can only call spend, and the agent pays through the SDK.

TypeScript SDK 0.1.1 on npm

One call runs the cycle: code decides, the contract checks and pays, the ledger records it.

Developer quickstart

CLI 0.1.1 on npm

verify checks a vault or a snapshot. Other commands print owner actions for the owner's wallet to sign.

Install

Web console Tried on testnet

Create a vault, its keys and mandates, pay invoices with Payables and publish a proof page. Sign in with a NEAR wallet.

Open console

MCP server 0.1.1 on npm, run by a real MCP client on testnet

For agents that speak MCP: a small set of tools over the same SDK.

MCP server README (opens in a new tab)
agent.ts@probarion/sdk0.1.1 on npm
import { Probarion } from "@probarion/sdk";

const probarion = new Probarion({
  network: "testnet",
  vault: "acme.vaults.probarion.testnet",
  agentKey: process.env.PROBARION_AGENT_KEY, // function-call key: can only call `spend`
  ledgerKey: process.env.PROBARION_LEDGER_KEY, // function-call key: can only call `anchor`
  model: null, // or NEAR AI Cloud, for attested advice
});

const outcome = await probarion.process({
  text: invoiceText, // untrusted: it is shown to the model as data and nothing else
  ref: "INV-2026-001", // your reference; the vault accepts each ref once
  facts, // invoice_hash, amount, po_ref, purpose_hash, payee_hint
});

console.log(outcome.status, outcome.code, outcome.spendId, outcome.txHash);

outcome.status is one of: refused_by_code · refused_by_contract · held · paid · burned · failed · unconfirmed. What each status means

On testnet, the factory is vaults.probarion.testnet and the demo vault is demo.vaults.probarion.testnet. On mainnet there is one vault, v0.probarion.near, and no factory yet.

Teams paying invoices with an agentAccounts payable in USDC, where a person signs the limits once and approves only the big ones.

Payee fixed by the mandate : INV-2002 asked to pay new-account.testnet. Code refused it with mandate.payee_mismatch, and nothing was sent.

Live on testnetINV-2002 in the demo vault's ledger, entry 3 (opens in a new tab)

Pay per API callAn agent pays an endpoint's 402 through the MCP server, under a mandate with a set number of uses. Our own 402 format, not x402.

20 uses of 0.01 test USDC : 20 calls paid, 0.01 test USDC each. The 21st was refused by code with mandate.uses_exhausted, before any transaction.

0.1.1 on npm, run by a real MCP client on testnetThe 402 example's own agent on testnet, 2026-11-09 (opens in a new tab)

Cross-chain payoutsPay a Base or Solana address through NEAR Intents, with the address fixed by the mandate. On testnet a stand-in contract takes the place of NEAR Intents, so the burn is recorded and nothing leaves NEAR. No payout through NEAR Intents has run on mainnet yet.

Address fixed by the mandate : On testnet the vault built the memo WITHDRAW_TO:0x1111…1111 from the mandate and burned 1 test USDC.

On testnetThe testnet burn for a Base address, tx 3u4ffrwp…dcvYe (opens in a new tab)

The agent cannot name a payee.

A forged invoice or a hijacked prompt can ask to pay anyone. spend has no field to put an address in, so the money can only go to a payee you signed. For a cross-chain payee, the vault builds the withdrawal memo itself, from the mandate.

SpendRequest {mandate_idamountinvoice_hashrefdecision_hashrecipientdoes not exist}

The console

Screenshots from the testnet demo vault, taken while a fifth invoice went through it: INV-2004, 4 test USDC, above the mandate's 3 USDC approval limit.

The vault overview in the console: Held for approval (1), spend 3, 4 USDC to probarion-payee.testnet under mandate 1. Only the vault's approvers can approve or reject it.

Held for an approver

The owner confirmed the amount in their wallet. Above 3 USDC, the vault holds the spend until an approver signs, and it can only pay probarion-payee.testnet.

Testnet console, 2026-11-09, vault demo.vaults.probarion.testnet · Held 9qUtxTgu…699d (opens in a new tab)

The invoice page in Payables: the owner's NEP-413 confirm, then three columns. Model wanted: rules only. Code allowed: each rule passed. Contract did: held for approval, spend 3.

The invoice's page

The owner's signed confirm, every code rule the invoice passed, and what the contract did with it, side by side.

Testnet console, 2026-11-09, vault demo.vaults.probarion.testnet

The vault overview after the approval: nothing held, and three paid spends of 4, 4 and 2 USDC, all to probarion-payee.testnet.

Paid after approval

The approver approved spend 3 from their own key, and the vault paid. Each spend went to the mandate's payee.

Testnet console, 2026-11-09, vault demo.vaults.probarion.testnet · Approved EYPyrd9h…r972 (opens in a new tab)

The proof page lists this vault's spends and runs checks a to i on its ledger and the chain. Open the proof page

Change one field. Watch it break.

This is the real verifier from @probarion/core. It runs in your browser on a committed test fixture: five entries signed with test keys, not a live vault. It shows one thing: the verifier catches an edit that was not re-signed. Nothing leaves the page.

Signed ledger, test fixturePASS. As recorded.
The five entries of the signed test ledger
SeqKindOutcomeMandateAmount, USDC
0DecisionAllow125.000000
1PaymentPaid125.000000
2DecisionAllow240.000000
3PaymentWithdrawal to Base240.000000
4DecisionRefuse: payee mismatch125.000000
PASSAll 9 offline checks pass.
  • aSignaturespassEvery entry is signed by the vault's ledger key of its time.
  • bHash linkspassEach entry carries the hash of the one before, so an edit breaks the chain.
  • cAnchorspassThe ledger matches the roots anchored on the vault.
  • dA decision for every spendpassEvery payment on chain matches exactly one decision in the ledger.
  • eAttestationspassModel advice, when used, is signed and tied to this exact place in the ledger.The hardware quote and the request are not checked here.
  • fLocked vaultpassNo one holds a full-access key to the vault, and it runs a known build.
  • gIntents keyspassNo key is registered for the vault in NEAR Intents, so only the vault contract can move its balance there.
  • hBurn memospassEach withdrawal to another chain names the address in the mandate and the spend's amount.
  • iFactory versionpassIf the vault came from a factory, it is Probarion's, and its version is one the factory lists.

Recorded result. Press a button to run the verifier yourself.

Read it on chain

A smoke run of the contract alone on NEAR testnet, with test tokens, on 2026-11-01.

Paid a NEAR account

1 test USDC to probarion-payee.testnet. Events spend_submitted, ft_transfer, spend_paid.

3DFV2thB…Fy9cX (opens in a new tab)

Withdrawal for a Base address

The vault burned 1 test USDC with the memo WITHDRAW_TO:0x1111…1111, which it built from the mandate. On testnet nothing leaves NEAR.

3u4ffrwp…dcvYe (opens in a new tab)

Refused a mandate that does not exist

The contract answered PROBARION:NoMandate. Nothing was paid.

AkjyVPmb…dzgY (opens in a new tab)

What works today, and what does not

Vault contractOn testnet
Offline verifier, checks a to iShipped, on npm
Reproducible buildsShipped
Vault factoryOn testnet
Web console: vault, keys, faucet, NEAR USDC top-up, mandate, Payables, Agent (MCP) tab, Freeze, approvals, Reject, revoke, withdraw, key removal, proof pageTried on testnet
Web console: email alerts, Base top-upBuilt, not tried yet
Public proof pageLive for the testnet demo vault
TypeScript SDK and CLI0.1.1 on npm
Payables agentLive on testnet
Live attested model runChecked online on testnet, image not pinned
Canary public challengeLive on testnet
MCP server0.1.1 on npm, run by a real MCP client on testnet
Mainnet vaultOn mainnet: 1 payment, 1 refusal, no Base payout yet

The numbers, transactions and status rows on this page are checked against the repository by tests (apps/web/test/site-facts.test.ts).

Known gaps

  • No third party has audited this code. The threat model (opens in a new tab) says what verify does not prove yet.
  • v0.probarion.testnet, the vault of the 2026-11-01 smoke run, sent its spends with the agent key alone, without a ledger, so a live verify --vault on it fails checks d and h. It also runs a build from before the 2026-11-03 set_ledger_key fix. The vaults the testnet factory creates, the demo vault among them, run the fixed build recorded in contracts/code-hashes.json.

Built on NEAR, with NEAR AI Cloud

Where each payment is made, and the optional model that can advise it. No partnership or endorsement is implied.

Ships through

Also used: Intel TDX and NVIDIA NRAS attestation, which verify --online checks (O1 and O2). NEAR Intents payouts are built and tested against a testnet stand-in; none has run through NEAR Intents yet.

NEAR, NEAR Intents, NEAR AI Cloud, USDC, Base and Solana are trademarks of their respective owners. They are named to say what Probarion is built on or is designed to pay through, not to imply an endorsement. npm is a registered trademark of npm, Inc. Model Context Protocol and MCP are trademarks of LF Projects, LLC. GitHub and the Invertocat logo are trademarks of GitHub, Inc. Chrome and Chrome Web Store are trademarks of Google LLC. Intel is a trademark of Intel Corporation or its subsidiaries. NVIDIA is a trademark of NVIDIA Corporation.

Don't trust us. Run the verifier.

Five minutes from a clone: build, verify a snapshot, then point it at a vault on testnet.

A live run reads the chain itself: verify --vault. On demo.vaults.probarion.testnet it passes checks a to i.

Put your agent behind a mandate