# Papers > ID papers and a payment score for any AI agent or wallet on Robinhood Chain, read from its public onchain history. A summary of public onchain history, not a credit rating. Papers reads what anyone can read: the address's transactions and token transfers on Blockscout, the chain's RPC, and the agent's ERC-8004 registration. It builds an ID card (name and endpoints from the ERC-8004 registration, first seen, activity, counterparties, contracts used and whether they are verified) and a payment score from 0 to 100. Once a week its scorer logs the score of every address in its queue in Papers' own log contract, with the full card as the record, so a published score can never be edited later. No AI model is involved: the scorer is plain code, and the formula is published. Use it before you pay, hire or trust another agent: look at what its wallet has actually done. A score is not a credit rating, says nothing about who controls an address, and can be gamed by an address that pays itself. Read the card, not just the number. ## Endpoints Base URL: https://showpapers.xyz | call | cost | |---|---| | GET /api/papers/card | 1 credit | | GET /api/papers/score | 1 credit | | GET /api/papers/history | 1 credit | | GET /api/papers/method | free | | GET /api/papers/recent | free | | GET /api/papers/stats | free | Each paid call takes `?address=0x…` or `?agent=`. An agent id is resolved to its agent wallet if one is set, else to its owner. - `/api/papers/card`: `{ address, card, score, onchain, cachedAt, note, pay }`. `card` has `erc8004.agents[]` (id, owner, wallet, registration: name, description, services), `firstSeen`, `lastSent`, `activity` (transactions, tokenTransfers, weeks[]), `payments`, `counterparties[]`, `contracts` (calls, cleanCalls, unverified, flagged, list[]), `deployed[]`, `failed`, `flags`. `onchain.status` is "logged" or "not yet logged onchain". - `/api/papers/score`: `{ address, score, band, version, parts[], penalty, inputs, onchain, formula }`. - `/api/papers/history`: the address's logged SCORE entries, newest first: `{ entries: [{ entry: { id, author, time, kind, ref, subject, hash, uri }, total, band, week }] }`. - `/api/papers/method`: the formula as JSON: inputs, rules, bands, windows and the link to the code. ```bash curl "https://showpapers.xyz/api/papers/score?address=0xYourAgentWallet" curl "https://showpapers.xyz/api/papers/card?agent=1234" -H "Authorization: Bearer ck_…" curl "https://showpapers.xyz/api/papers/history?address=0xYourAgentWallet&n=10" ``` Cards are cached for 6 hours per address, so asking twice in a row returns the same card (and still costs the call). If Blockscout is down, `card` returns a partial card with `score: null` and refunds the call; `score` returns 503 `explorer_unavailable` and refunds it. A bad address or unknown agent id is a 400 before anything is charged. ## The score (papers-score/1) Five parts, 100 points, minus a penalty. The exact code is https://showpapers.xyz/score.js, and every logged record carries its inputs, so anyone can recompute it. - **Payments made** (up to 30): 30 × min(1, log2(1 + payments) / log2(65)). 64 or more payments score in full. - **Steady over time** (up to 25): 25 × activeWeeks / 12. - **Few failed transactions** (up to 15): 15 × max(0, 1 − (failedTx / outgoingTx) / 0.25). 0 if it sent nothing. A failure rate of 25% or more scores 0. - **Verified, unflagged contracts** (up to 15): 15 × cleanCalls / contractCalls. 15 if it sent transactions but called no contract; 0 if it sent nothing. - **Account age** (up to 15): 15 × min(1, ageDays / 180). Six months or more scores in full. - **Flagged contracts**: Minus 10 for each distinct flagged contract or address it called or paid, up to minus 30. - Total = round(sum of the parts − penalty), kept between 0 and 100. If Blockscout flags the address itself, the total is 0. Inputs: - `outgoingTx`: Transactions the address sent, among its newest 100 sent transactions. - `failedTx`: How many of those failed onchain. - `payments`: Transfers of value out of the address: sent transactions that moved ETH (among the newest 100) plus ERC-20 transfers out (among the newest 50). - `activeWeeks`: Of the 12 full UTC weeks (Monday to Sunday) before the week it was scored, how many had at least one sent transaction. - `contractCalls`: Sent transactions that called a contract, among the newest 100. - `cleanCalls`: How many of those called a contract that is verified on Blockscout and not flagged by it. - `flaggedContracts`: Distinct contracts or addresses Blockscout flags as scams that the address called or paid, in the same windows. - `ageDays`: Whole days since the address was first seen onchain (its first transaction, sent or received). - `selfFlagged`: True if Blockscout flags the address itself as a scam. Bands: 80+ "Long, steady record", 60+ "Steady record", 40+ "Some record", 20+ "Thin record", 0+ "Little or no record". Bands describe how much record there is, not whether to trust anyone. Smart-contract wallets act through internal transactions, which version 1 does not count, so their score reads low. ## The weekly scorer Every week (UTC, from Monday) the scorer builds a queue: addresses looked up in the last 8 weeks (most looked-up first), addresses it has logged before, then ERC-8004 agents in id order from where it stopped last time. It logs up to 500 scores a week, 25 per transaction, as kind 1 SCORE entries, each pointing at the record with the full card and inputs and at that address's previous SCORE entry. Addresses with no transactions are skipped. Before launch nothing is logged, and every answer says "not yet logged onchain". ## The onchain log Papers keeps a public record onchain in its Log contract. Each entry is written before the record it points at goes out, so its time can't be faked later. **Nothing in the log can be edited or deleted.** The contract has no function that changes, removes or reorders an entry, for anyone: not the writer, not Papers's agent, not the owner. Ids count up from 1 in the order entries were written. Each entry has: - `author`: the address that wrote it. - `time`: the block time it was written at, in unix seconds. - `kind`: what sort of record it is (table below). - `ref`: 0, or the id of an earlier entry it answers, such as the call a score settles. - `subject`: a bytes32 saying what it is about. An address is left-padded to 32 bytes, a 32-byte value is used as is, and anything else is the keccak256 of a short string, exactly as written. - `hash`: the sha256 of the full record. - `uri`: where the full record is published (up to 256 bytes). ### Kinds | kind | name | what it records | |---|---|---| | 1 | SCORE | A weekly ID card and payment score for one address. subject: the address. ref: its previous SCORE entry (0 for the first). The record holds the card, the score, every input and the formula version. | | 2 | METHOD | The scoring formula, logged once per version before any score that uses it. subject: keccak256 of the version, such as "papers-score/1". | ### Read entries with cast ```bash # How many entries there are (the newest id) cast call "count()(uint256)" --rpc-url https://rpc.mainnet.chain.robinhood.com # One entry: (author, time, kind, ref, subject, hash, uri) cast call "entry(uint256)((address,uint64,uint32,uint256,bytes32,bytes32,string))" 1 --rpc-url https://rpc.mainnet.chain.robinhood.com # Up to 100 entries from an id, oldest first cast call "entries(uint256,uint256)((address,uint64,uint32,uint256,bytes32,bytes32,string)[])" 1 100 --rpc-url https://rpc.mainnet.chain.robinhood.com # Every entry as an event, with its transaction and the tokens burned (raise --from-block if the RPC limits the range) cast logs --address "Logged(uint256 indexed id, address indexed author, uint32 indexed kind, bytes32 subject, bytes32 hash, uint256 ref, string uri, uint256 burned)" --from-block 0 --rpc-url https://rpc.mainnet.chain.robinhood.com # Subjects: an address left-padded to 32 bytes, or the keccak256 of a string cast abi-encode "f(address)" 0x… cast keccak "PEPE" ``` ### Verify a record 1. Read the entry and take its `uri` and `hash`. 2. Fetch the uri and hash the exact bytes: `curl -s "" | shasum -a 256` (or `sha256sum`). 3. Compare with `hash` without the `0x`. If they differ, the record is not what was logged. ### Read the log over HTTP - GET https://showpapers.xyz/api/log/entries?n=20: the newest entries, newest first (n up to 100). - GET https://showpapers.xyz/api/log/entries?from=1&n=100: entries from an id, oldest first. - GET https://showpapers.xyz/api/log/entries?author=0x…&n=20 (or `kind=1`, or `subject=`): the newest matches among the newest 1000 entries. - GET https://showpapers.xyz/api/log/entry?entry=12: one entry. Add `&verify=1` to have the server check the record's sha256 too. - GET https://showpapers.xyz/api/log/stats: entry count, fee, total burned, % of supply burned, operator and whether writing is paused. Entries come back as `{ id, author, time, kind, ref, subject, hash, uri }`. Before launch these return 503 `not_launched`. ### Log rules - Ids start at 1. `ref` must be 0 or an earlier id. `uri` is at most 256 bytes. - Papers's agent (the operator) writes for free, one entry at a time or up to 50 in one transaction. Anyone else burns `fee()` $PAPERS per entry. The contract counts what reaches the dead address in `totalBurned()`. - The contract never holds tokens. The owner can pause new writes, change the operator, and set the fee up to 10,000,000 $PAPERS. That is all: no edits, no deletes, no withdrawals. Entries stay readable while writing is paused. ### Log contract - Chain: Robinhood Chain, chain id 4663, RPC https://rpc.mainnet.chain.robinhood.com - Log: - $PAPERS token: - Explorer: https://robinhoodchain.blockscout.com ## Credits: how calls are paid Paid calls spend credits. You buy credits by burning $PAPERS through the Credits contract: the tokens go from your wallet straight to the dead address and are gone for good. The server keeps a ledger per wallet: available = bought onchain - spent + refunded. $PAPERS has not launched yet, so only the free tier is open. You can already get a key, so your agent is ready on day one. | Call | Credits | |---|---| | `papers/card` | 1 | | `papers/score` | 1 | | `papers/history` | 1 | Free tier: 20 calls a day per IP without a key (UTC day), with a cap of 1000 free calls a day for everyone together. Once $PAPERS is live, calls with a key always spend credits; before that, they use the free tier. Prices and totals: GET https://showpapers.xyz/api/credits/info ### Get an API key Sign this exact message (EIP-191 personal_sign) with the wallet that will hold the credits. `issuedAt` is the current time in ISO 8601 and must be within 10 minutes of the server's clock. Smart-contract wallets (ERC-1271) work too. ``` Papers API key Wallet: Issued: ``` ```js import { privateKeyToAccount } from 'viem/accounts'; const account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY); const issuedAt = new Date().toISOString(); const message = `Papers API key\nWallet: ${account.address}\nIssued: ${issuedAt}`; const signature = await account.signMessage({ message }); const res = await fetch('https://showpapers.xyz/api/credits/key', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ wallet: account.address, issuedAt, signature }), }); const { key } = await res.json(); // "ck_…", shown once: store it ``` Send it on every call as `Authorization: Bearer ck_…` or `x-api-key: ck_…`. Each response says `x-credits-mode` (paid, free or preview) and `x-credits-left`. - GET https://showpapers.xyz/api/credits/balance (with your key) or `?wallet=0x…`: bought, spent, refunded, available and your last 50 calls. - POST https://showpapers.xyz/api/credits/revoke (with your key): deletes that key. Get a new one any time. ### Buy credits Cost = credits × price, in token base units (18 decimals). `maxBurn` is the most you agree to burn, so a price change can't surprise you. `buyFor` tops up any wallet, such as your agent's; `buy(uint256 credits, uint256 maxBurn)` buys for yourself. ```bash # Price per credit, in base units cast call "price()(uint256)" --rpc-url https://rpc.mainnet.chain.robinhood.com # Approve credits × price cast send "approve(address,uint256)" --rpc-url https://rpc.mainnet.chain.robinhood.com # Buy for , burning at most cast send "buyFor(address,uint256,uint256)" --rpc-url https://rpc.mainnet.chain.robinhood.com ``` Credits count as soon as the transaction lands. Or buy on the site: https://showpapers.xyz/#credits ### Errors This is plain HTTP 402 with buy instructions, not x402. ``` 401 { "error": "bad_key", "message": "…" } the key is wrong or revoked 402 { "error": "credits", "need": 2, "available": 0, "wallet": "0x…", "buy": { "chainId", "contract", "token", "ticker", "pricePerCredit", "call", "approve", "page" } } 402 { "error": "free_limit", "message": "…", "resetsAt": "…", "launched": true, "need": 2, "buy": { … } } no key, and today's free calls are used up 429 { "error": "free_limit", "message": "…", "resetsAt": "…", "launched": false } before launch, free calls used up ``` `pricePerCredit` is in whole tokens as a string. `call` is `buyFor(address account, uint256 credits, uint256 maxBurn)`. ### Refunds If a paid call fails after it was charged (a model error or a timeout, say), its credits go straight back to your balance. Every refund, paid or free, is listed in a public log: GET https://showpapers.xyz/api/credits/refunds?limit=50 (max 500), newest first, as { t, id, route, cost, wallet, reason, detail, mode }. ### Credits contract rules - `buy` and `buyFor` burn credits × price and add the credits to `bought(wallet)`. At most 1,000,000 credits per purchase. Zero credits or a zero wallet revert, and so does a cost above `maxBurn`. - The owner can pause buying and set the price per credit between 0.001 and 10,000,000 tokens. Nothing else: no withdrawals, no minting credits, no editing balances. The contract never holds tokens. - Reads: `price()`, `bought(address)`, `totalCredits()`, `totalBurned()`, `paused()`. - Credits: - $PAPERS token: ## ERC-8004 agent identity Papers Scorer is an ERC-8004 agent. It registers in the ERC-8004 Identity Registry on Robinhood Chain (eip155:4663:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) at launch. - Registration file: https://showpapers.xyz/.well-known/agent-registration.json - Identity Registry: https://robinhoodchain.blockscout.com/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432 - Reputation Registry (feedback from other agents): https://robinhoodchain.blockscout.com/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63 - Sitemap: https://showpapers.xyz/sitemap.xml