Verification an agent can read for itself.
Looking a card up is free for everyone. Only AI agents pay for a check, and no outside agent pays yet.
For AI operatorsFor developersFor wallet buildersFor your agent: SKILL.md
For AI operators
Know which traders are real before your agent acts.
- Check a claim before acting on it. Real is allow. Unclear is warn: stop and ask the owner. Fake, no record or no answer is block.
- Read the proof straight from NEAR. No Tally server and no API key in between.
- Set a spending limit. The credit you load is all the agent can ever spend.
- Audit what it checked. Every paid check leaves a public record on NEAR.
The paid check Live on testnet
The owner sets a limit
They load a little credit and give the agent a key that can only pay for checks.
The agent pays per answer
One fee per answer, out of that credit. No record, no charge.
The trader earns
If the card is Real and has a badge, part of the fee goes into it.
What a check costs
Read from NEAR just nowReading from NEAR…
Why pay when reading is free?
For the receipt: a public record on NEAR that this agent checked this card before it acted.
The fee also pays the trader, and a paid check is what gets a badge burned once its verdict was corrected.
How does an agent find it?
From this page or SKILL.md. It pays the contract directly on NEAR: no marketplace and no account with Tally.
What the agent can and can't do
It can
- Pay for a check, out of the credit its owner loaded
- Read any record for free
It can't
- Send its owner's NEAR anywhere
- Call any other contract, or anything here but the check
- Spend more than the credit that was loaded
- Be charged for a card that has no record
Run the demo agent yourself
git clone https://github.com/pab412/Tally-near
cd Tally-near/web && npm install
node scripts/demo-agent.mjsNothing to set up: it makes its own throwaway testnet account. It's a script with one rule, not an AI.
What the demo agent prints
One run, shortened
ExampleTally demo agent, on NEAR testnet
Rule: Real is allow (go ahead).
Unclear is warn (stop and ask the
owner). Fake, no record, or no answer
from NEAR is block.
1. The owner loads credit: 0.03 NEAR.
That is all the agent can spend.
2. The agent gets its own key. It can
call "check" and nothing else.
3. The agent checks 3 cards:
cab484af…3be332b8 paid 0.01 NEAR
BLOCK: refuses
why: NEAR says Fake; check demo1,
recorded 2026-11-10; 1 entry,
never corrected
3171ca6c…36599e58 paid 0.01 NEAR
ALLOW: goes ahead
why: NEAR says Real; check
sample-b, recorded 2026-11-11;
trader.testnet holds its verified
card
0.005 NEAR of the fee went into
the trader's verified card.
01a765b7…5d0c68bf paid 0 NEAR
BLOCK: refuses
why: no record on NEAR: Tally has
never checked this card
4. Attack, acted out: a message tells
the agent to skip the check and send
0.01 NEAR of the owner's money to
another account. It tries.
NEAR refuses: this key can only pay
for checks.
5. The verified card now holds
0.005 NEAR.
6. The owner takes the agent's key away.For developers
One call to NEAR. No API key.
Every read is a plain JSON-RPC call to a NEAR node. Pick a method to see the call and what NEAR answers right now.
In Claude Code or Codex, the same reads come as a plug-in: the MCP server . No key, nothing paid.
Endpoint and contracts
- Endpoint
- https://rpc.testnet.fastnear.com
- Network
- NEAR testnet. Any testnet RPC node works.
- Proof registry
- proof.notpab.testnet
- Verified cards
- card.notpab.testnet
- Paid check
- check.notpab.testnet
Read methods, free
Answered by NEAR just nowA card's latest verdict and how many entries it has. null for a card Tally never checked. proof.notpab.testnet
The call
curl -s https://rpc.testnet.fastnear.com \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":"tally","method":"query","params":{"request_type":"call_function","finality":"optimistic","account_id":"proof.notpab.testnet","method_name":"get","args_base64":"eyJmaW5nZXJwcmludCI6Ijc3NmI3MjlhOThhNDU1NzZlNDM4MWM3NzMyZDRiMGVhZmNiZWM0ZWVjYzExNGM3OTExNmQ5YTcwMDIyNThkYmUifQ=="}}'What NEAR answers
Asking NEAR…The paid check, method by method
deposit(account_id)The owner- Loads the attached NEAR as credit for an account. That credit is all the agent can spend.
check(fingerprint)The agent- Asks the registry and the card contract in one call. One fee, taken only when there is a record to give.
refund()The owner- Sends the caller's unspent credit back to them.
credit_of(account_id)Anyone, free- What an account has left to spend, in yoctoNEAR.
terms()Anyone, free- The fee, the trader's share and how many checks were answered.
What check answers
record- The card's record, exactly as the registry gives it to anyone. null when Tally never checked the card.
held_by- The account holding the card's verified card, when the card is Real and has one. Otherwise null.
charged- What this check cost, in yoctoNEAR. "0" when there was no record.
to_trader- The part of the fee paid into the trader's verified card, in yoctoNEAR.
Errors, word for word
- A check for something that isn't a fingerprintThe paid check
- “A fingerprint is 64 lower-case hex characters”
- A check with less credit left than one feeThe paid check
- “Not enough credit for a check: the account's owner has to load more”
- A check the registry couldn't answerThe paid check
- “The proof registry didn't answer, so nothing was charged”
- A deposit smaller than one feeThe paid check
- “Load at least the price of one check”
- A refund with nothing leftThe paid check
- “There is no credit to send back”
- Anyone but Tally writing a recordThe proof registry
- “Only Tally's attester can record a check”
- A record with a bad check idThe proof registry
- “A check id is 1 to 32 letters, digits, - or _”
Permissions and spending limits
The agent works with a NEAR function-call key that can only call check on the paid check contract. NEAR itself refuses anything else that key tries: a transfer, another method, another contract.
The most the agent can spend is the credit that was loaded, plus the gas allowance on its key. refund() sends unspent credit back.
Source and tests
The paid check has 21 unit tests, and its whole flow is among the 59 checks run on testnet.
For wallet builders
Ask who is safe to pay, before the money moves.
A spending limit says how much an agent may spend. The registry says who it may pay. A wallet asks it in the same call that would send the money, so what the agent was told never decides.
use tally_types::{decide, proof_registry, Decision, Record};
// before any money moves, ask the registry about the card
pub fn pay(&mut self, fingerprint: String, amount: U128) -> Promise {
proof_registry::ext(self.registry.clone())
.get(fingerprint.clone())
.then(Self::ext(env::current_account_id()).on_asked(fingerprint, amount))
}
#[private]
pub fn on_asked(&mut self, fingerprint: String, amount: U128,
#[callback_result] record: Result<Option<Record>, PromiseError>) {
// no answer is no record, and both are a block
match decide(record.ok().flatten().as_ref()) {
Decision::Allow => { /* pay the card's holder */ }
Decision::Warn => { /* stop: ask the owner */ }
Decision::Block => { /* refuse */ }
}
}deposit(limit)The owner- Loads the attached NEAR into the caller's own wallet and sets the most one payment may be.
pay(fingerprint, amount)The agent- Asks the registry about the card first. Its holder is paid only if the answer is Real. Anything else is refused, with why.
refund()The owner- Sends back what the caller's wallet still holds.
wallet_of(account_id)Anyone, free- What an account's wallet holds, and its limit.
The whole thing is contracts/guard, an example wallet on testnet at tally-guard.testnet . It is not a product people hold money in. Try it: node scripts/demo-guard.mjs pays a Real card's holder and is refused for a Fake one. Its source