Developer Reference
Documentation
API reference for the RSoft Bank protocol. Read endpoints (explorer, status, history, rates) are public; borrowing endpoints (loan request, credit-line draw) require an API key. A2A uses agent signatures.
Quickstart - your agent's first loan in ~15 minutes
The full cycle: register an on-chain identity, borrow 5 USDC, repay it, and walk away with the thing that actually matters - verifiable on-chain credit history your agent can present to any lender that speaks ERC-8004.
1 · Wallet
An agentic wallet (Coinbase CDP recommended) with a few cents of ETH on Base for gas.
2 · Passport
A one-time ERC-8004 register() from the agent's own wallet - snippets on the Register page.
3 · Pilot API key
REST borrowing uses an API key during the pilot - ping @RSoft-Agentic-Bank to get one. A2A and MCP need no key.
# pip install cdp-sdk requests
# Env: CDP_API_KEY_ID / CDP_API_KEY_SECRET / CDP_WALLET_SECRET (your agent's
# CDP Server Wallet) + BANK_API_KEY (pilot key) + AGENT_WALLET (0x…)
import asyncio, os, time, uuid, requests
from cdp import CdpClient
from cdp.openapi_client.models.eip712_domain import EIP712Domain
from cdp.evm_transaction_types import TransactionRequestEIP1559
BANK = "https://rsoft-agentic-bank.com/api/v1"
KEY = {"X-API-Key": os.environ["BANK_API_KEY"]}
WALLET = os.environ["AGENT_WALLET"]
USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" # Base mainnet
VERIFYING = "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432" # EIP-712 domain
async def main():
async with CdpClient(api_key_id=os.environ["CDP_API_KEY_ID"],
api_key_secret=os.environ["CDP_API_KEY_SECRET"],
wallet_secret=os.environ["CDP_WALLET_SECRET"]) as cdp:
# 1) Sign the loan terms (EIP-712 LoanRequest) with the agent's wallet
amount, nonce, deadline = 5.0, str(uuid.uuid4()), int(time.time()) + 3600
domain = EIP712Domain(name="RSoft Agentic Bank", version="1",
chain_id=8453, verifying_contract=VERIFYING)
# NOTE: CDP requires the EIP712Domain entry inside `types` (ethers/viem omit it)
types = {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"}],
"LoanRequest": [
{"name": "agentWallet", "type": "address"},
{"name": "loanAmountUsdc6", "type": "uint256"},
{"name": "nonce", "type": "string"},
{"name": "deadline", "type": "uint256"}]}
message = {"agentWallet": WALLET, "loanAmountUsdc6": int(amount * 1e6),
"nonce": nonce, "deadline": deadline}
sig = await cdp.evm.sign_typed_data(address=WALLET, domain=domain,
types=types, primary_type="LoanRequest",
message=message)
# 2) Request the loan — the 5-agent pipeline runs and disburses USDC
r = requests.post(f"{BANK}/loan/request", headers=KEY, json={
"agent_wallet": WALLET, "loan_amount": amount,
"nonce": nonce, "deadline": deadline, "signature": sig}).json()
request_id = r["request_id"]
# 3) Poll until disbursed (public endpoint, no key)
while requests.get(f"{BANK}/loan/status/{request_id}").json()["status"] \
not in ("disbursed", "rejected"):
time.sleep(5)
# 4) Repay: quote is public; pay the EXACT amount to the treasury
info = requests.get(f"{BANK}/loan/repay-info/{WALLET}").json()
base6 = int(round(info["repayment_amount"] * 1e6))
data = ("0xa9059cbb" + info["pay_to"][2:].zfill(64)
+ hex(base6)[2:].zfill(64)) # ERC-20 transfer
tx = await cdp.evm.send_transaction(address=WALLET, network="base",
transaction=TransactionRequestEIP1559(to=USDC, data=data, value=0))
# 5) Report it (optional — unreported payments are auto-detected ~10 min)
requests.post(f"{BANK}/loan/repay", headers=KEY,
json={"request_id": request_id, "tx_hash": tx})
print("Loan repaid - your agent now has on-chain credit history ✓")
asyncio.run(main())What your agent earned: a repaid loan recorded in the bank's books and a positive ERC-8004 reputation mark signed by the bank's wallet - portable, verifiable, and impossible to self-fabricate. Each repayment also climbs the credit ladder: $5 → $10 (1 repaid) → $25 (3) → $50 (6) → $100 (10). Check any agent's standing on the Reputation page.
Getting Started
What is RSoft Bank?
RSoft Bank is a decentralized lending protocol designed for AI agents. Autonomous agents can request USDC loans that are evaluated through an automated multi-agent pipeline - from identity verification to on-chain settlement.
Base URL
https://rsoft-agentic-bank.comAll endpoints are prefixed with /api/v1 except health checks.
Authentication
All read endpoints listed here are public - no API key or wallet connection required. Responses are JSON format with ISO 8601 timestamps.
Agent quickstart · one prompt
Paste this into Claude Code, Cursor, Windsurf, Codex, OpenClaw or any agent that can read a URL. It loads the bank's skill manifest and walks the agent through setup, signing and repayment.
I want my AI agent to get credit from RSoft Bank. Read the docs at https://rsoft-agentic-bank.com/skills.md and then set it up.Or add the MCP server directly:
claude mcp add --transport http rsoft-bank https://7mavs5vu7ggbhtxvbavdgs26qa0cbawg.lambda-url.us-east-1.on.aws/mcpMachine-readable index for LLMs and crawlers: /llms.txt · /skills.md
How Agents Borrow
An agent can originate debt through four surfaces. REST, MCP and A2A are one-shot loans that share the same underwriting (caps, EIP-712 signature, replay-protected nonce); the credit line is revolving.
POST /api/v1/loan/requestDirect REST
Simplest path. Sign the loan terms and POST them. Pilot API key - see Quickstart.
Sign the EIP-712 LoanRequest (agentWallet, loanAmountUsdc6, nonce, deadline) and POST it. The bank runs the full 5-agent pipeline and disburses USDC to your wallet.
POST /api/v1/loan/request
X-API-Key: <bank_api_key>
{
"agent_wallet": "0x…",
"loan_amount": 5,
"nonce": "…",
"deadline": 1893456000,
"signature": "0x…" // EIP-712 LoanRequest
}tool: request_loanMCP tool
For autonomous agents that already use an MCP toolset. No API key needed.
Add the bank's MCP server to your agent's tools and call request_loan with your EIP-712 signature. Same underwriting and signature contract as REST - the MCP transports your signature, it never signs for you. See the MCP Server section for the full toolset.
// agent MCP config (Streamable HTTP)
mcpServers:
rsoft-bank:
url: https://7mavs5vu7ggbhtxvbavdgs26qa0cbawg.lambda-url.us-east-1.on.aws/mcp
// then your agent calls the tool
tool request_loan
args { "amount": 5, "agent_id": "0x…",
"signature": "0x…", "nonce": "…", "deadline": 1893456000 }SendMessage: negotiate_loan → request_loanAgent-to-Agent
For agents speaking the A2A 1.0 standard. Discover the agent card, delegate the task.
The bank is a standard A2A peer: discover it via its Agent Card, preview terms with negotiate_loan, sign them and execute with request_loan. See the A2A Protocol section for the full skill set.
// discovery
GET <a2a-server>/.well-known/agent-card.json
// delegate (JSON-RPC, header A2A-Version: 1.0)
POST <a2a-server>/
{ "jsonrpc": "2.0", "id": 1, "method": "SendMessage",
"params": { "message": { "role": "ROLE_USER", "parts": [
{ "data": { "skill": "negotiate_loan",
"agent_id": "0x…", "amount": 5 } } ]}}}POST /credit-lines/request → /{id}/drawRevolving credit
For agents with recurring capital needs.
Open an approved credit line once, then draw against it repeatedly during the window instead of requesting a new loan each time. Repay to restore available credit.
// open a line (once)
POST /api/v1/credit-lines/request
{ "agent_id": "0x…", "requested_limit": 50 }
// draw against it (repeat, owner-signed)
POST /api/v1/credit-lines/{line_id}/draw
{ "amount": 5, "signature": "0x…" }Repaying a Loan
Repayment is a plain USDC transfer to the treasury for the exact owed amount (principal + full-term interest - fixed at origination, so the quote never changes). Two steps, and the second one is optional.
/api/v1/loan/repay-info/{wallet}1 · Get the repayment quote
Public - no API key.
Returns exactly what the agent owes and where to send it.
GET /api/v1/loan/repay-info/0xYourAgentWallet
{
"request_id": "req_…",
"principal": 5.0,
"interest": 0.102739,
"repayment_amount": 5.102739,
"currency": "USDC",
"pay_to": "0x274C…74C5a" // bank treasury (Base)
}Send a USDC transfer from the agent's own wallet (the sender is verified - nobody can claim your payment) to pay_to for the exact repayment_amount.
/api/v1/loan/repay2 · Report the payment (optional fast path)
API key · settles instantly.
The bank verifies the transaction on-chain (token, recipient, sender, amount) before crediting - a tx hash can settle exactly one debt, ever.
POST /api/v1/loan/repay
X-API-Key: <bank_api_key>
{
"request_id": "req_…",
"tx_hash": "0x…" // your USDC transfer
}Paying alone is enough
If your agent dies between the transfer and the report, nothing bad happens: the bank sweeps incoming treasury transfers every ~10 minutes and credits any exact-amount payment from a borrower automatically. An agent that paid can never be marked in default. Every verified repayment updates the agent's standing (credit ladder) and posts a positive, bank-signed ERC-8004 reputation mark - the portable credit history other lenders can verify.
Sponsor Controls - a human behind the agent
An agent can bind one human sponsor who supervises its borrowing over WhatsApp (through RSoft MIA). The sponsor can pause, cap or revoke credit and approve requests above the agent's earned ceiling. The sponsor can never borrow, sign, or move the agent's funds - the agent's wallet keeps signing every loan. There is no web console: sponsor actions are WhatsApp commands, each confirmed with a 6-digit OTP.
Binding · two proofs
1 · The agent wallet signs an EIP-712 SponsorBinding(agentWallet, sponsorRef, nonce, deadline).
2 · The sponsor confirms via WhatsApp OTP through MIA (vincular agente 0x…).
One sponsor per agent. For phone sponsors sponsorRef is an opaque hash - the bank never sees the phone number.
What a sponsor can do
Pause / resume / revoke borrowing · set max_loan_amount and daily_draw_cap · enable draft mode · approve or reject drafts. Sponsor caps only tighten: the effective ceiling is ladder ∧ sponsor cap ∧ bank max.
Draft mode
When draft mode is on and the agent asks for more than its effective ceiling, POST /loan/request returns 202 instead of rejecting. The sponsor gets a WhatsApp message and replies aprobar <req_id> or rechazar <req_id> with an OTP. On approval the normal 5-agent pipeline resumes - Gatekeeper, Analyst, CFO, Settler and Auditor still apply. The sponsor lifts the earned ladder, never the bank's risk limits.
POST /api/v1/loan/request → 202 Accepted
{
"request_id": "req_…",
"status": "draft_pending_sponsor",
"requested": 50,
"ceiling": 25,
"expires_at": "2026-09-03T14:02:11Z", // 24h
"sponsor_notified": true
}The agent's signed request is consumed at intake (no replay): do not re-sign, poll GET /loan/status/{request_id} (or the MCP tool get_loan_status) until the status leaves draft_pending_sponsor. Drafts expire after 24h. The statuses draft_pending_sponsor, draft_rejected and draft_expired are never counted as outstanding debt.
Read the controls (public)
GET /api/v1/agents/0xYourAgentWallet/controls
{
"sponsor": { "bound": true, "kind": "phone", "status": "active" },
"controls": { "paused": false, "revoked": false,
"max_loan_amount": 20, "daily_draw_cap": 50,
"draft_mode_enabled": true, "version": 3 },
"effective": { "ladder_limit": 10, "ladder_level": 1, "global_max": 25,
"sponsor_cap": 20, "effective_ceiling": 10,
"daily_used": 0, "daily_remaining": 50 },
"pending_draft": null
}MCP: get_agent_controls(agent_id). get_loan_status(request_id) now includes amount, agent_wallet and a draft{…} block.
WhatsApp commands (via MIA)
vincular agente 0x… bind to an agent (after the agent signed SponsorBinding)
estado sponsor + controls + effective ceiling
pausar / reanudar pause / resume borrowing
límite 20 max_loan_amount (USDC)
límite diario 50 daily_draw_cap (USDC)
modo borrador on|off draft mode
revocar revoke borrowing
aprobar <req_id|último> approve a draft
rechazar <req_id|último> reject a draft
ayuda sponsor helpCommands are in Spanish. Every mutation asks for a 6-digit OTP.
Fail-closed
The sponsor check runs at intake and again in the Settler right before USDC moves. If the controls table is unreadable in real-money mode, origination is refused. Repayment is never blocked by sponsor controls.
Webhooks - loan events, pushed
Subscribe an HTTPS endpoint to loan lifecycle events instead of polling /loan/status. Deliveries are HMAC-signed. Delivery never gates a loan: a failing endpoint does not delay or block origination, disbursement or repayment.
Subscribe
POST /api/v1/webhooks
X-API-Key: <bank_api_key>
{
"url": "https://agent.example.com/hooks/rsoft", // https, no private hosts
"events": ["loan.disbursed", "loan.repaid", "loan.defaulted"],
"agent_wallet": "0x…" // optional: scope to one wallet
}
→ 201
{ "id": "whk_…", "secret": "…" } // the secret is returned ONCE - store itThe bank's API keys are shared, not per-agent, so a wallet-scoped subscription also requires that wallet's EIP-712 OwnerAction (action webhook_subscribe, resourceId = the url). Subscriptions for all agents need the bank admin key.
Events
loan.approved
loan.rejected
loan.disbursed
loan.repaid
loan.defaulted
loan.drafted
loan.draft_approved
loan.draft_rejected
loan.draft_expiredManage
GET /api/v1/webhooks?agent_wallet=0x…
DELETE /api/v1/webhooks/{id}
POST /api/v1/webhooks/{id}/ping
GET /api/v1/webhooks/{id}/deliveriesAll with X-API-Key. ping sends a test delivery; deliveries lists attempts and responses.
Verify the signature
X-RSoft-Timestamp: 1756821731 // unix seconds
X-RSoft-Signature: sha256=<hex> // HMAC_SHA256(secret, "{timestamp}.{raw_body}")
// Node
import { createHmac, timingSafeEqual } from "node:crypto";
const expected = "sha256=" + createHmac("sha256", SECRET)
.update(`${req.headers["x-rsoft-timestamp"]}.${rawBody}`).digest("hex");
const ok = timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-rsoft-signature"]));Sign over the raw body, not a re-serialized one. Reject stale timestamps. Retries: 3 attempts with backoff; 4xx responses (except 408 and 429) are not retried.
Agent Directory - the ERC-8004 registry, readable
Public, keyless reads of the ERC-8004 Identity and Reputation registries on Base mainnet, joined with the bank's own books. Look up any agent by token id or wallet, list recent registrations, or search. Identity Registry 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432, Reputation Registry 0x8004BAa17C55a88189AE136b182e5fdA19dE9b63.
GET /api/v1/agents/registry/58492
{
"token_id": 58492,
"exists": true,
"owner": "0x4cff…",
"agent_wallet": "0x4cff…",
"canonical_uri": "https://…/agent.json",
"client_count": 1,
"clients": ["0xB684898D3f4437D93848456141445c66Aa322B13"],
"known_issuers": [ ... ], // issuers the bank recognises by name
"trusted_feedback_count": 4,
"trusted_feedback_value": ...,
"chain_id": 8453,
"identity_registry": "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
"reputation_registry": "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63",
"bank": { ... } // the bank's own credit standing, if the wallet is a customer
}GET /api/v1/agents/registry/by-wallet/{wallet}
GET /api/v1/agents/registry/recent?limit=20
GET /api/v1/agents/registry/search?q=…Limitation (by design)
The IdentityRegistry is not enumerable on mainnet. recent and search are a bounded scan of recent Registered events (default 5000 blocks) plus the bank's own customer table - not the full census. Responses carry scanned_window and limitation so callers can see exactly what was covered. Lookups by token id or wallet are exact.
MCP tools · and free REST twins on the MCP host
get_agent(token_id | wallet) list_agents(limit) search_agents(q)
GET https://7mavs5vu7ggbhtxvbavdgs26qa0cbawg.lambda-url.us-east-1.on.aws/api/agents/{id|wallet}
GET https://7mavs5vu7ggbhtxvbavdgs26qa0cbawg.lambda-url.us-east-1.on.aws/api/agents/recent
GET https://7mavs5vu7ggbhtxvbavdgs26qa0cbawg.lambda-url.us-east-1.on.aws/api/agents/search?q=…MCP Server - the bank as a toolset
The bank speaks the Model Context Protocol. Point any MCP-capable agent (Claude, LangGraph, AgentKit, eliza, …) at the server below and the full credit cycle - creditworthiness, borrow, repay - becomes callable tools. Connecting and calling tools needs no API key; the loan request still requires your agent's own EIP-712 signature (the MCP transports it, it never signs for you). The complete cycle has been exercised end-to-end on Base mainnet with real USDC.
Endpoint (Streamable HTTP)
https://7mavs5vu7ggbhtxvbavdgs26qa0cbawg.lambda-url.us-east-1.on.aws/mcp
// agent MCP config
mcpServers:
rsoft-bank:
url: https://7mavs5vu7ggbhtxvbavdgs26qa0cbawg.lambda-url.us-east-1.on.aws/mcpTools
get_creditworthiness(agent_id)Credit score, history and outstanding debt for any agent. Use it before requesting - it tells you what the ladder will allow.
request_loan(amount, agent_id, signature, nonce, deadline)Originates the loan. Sign the EIP-712 LoanRequest struct with the borrower wallet (see Quickstart step 1 - same struct, same domain) and pass signature + nonce + deadline. Unsigned requests are rejected. On approval the bank disburses USDC on Base to your wallet.
get_repayment_info(agent_id)What you owe (principal + interest) and the treasury address to pay. Returns the request_id you'll need to confirm.
confirm_repayment(request_id, tx_hash)After sending the exact USDC amount on-chain, report the tx hash. The bank verifies it on Base and marks the loan repaid. Forgot to call it? The treasury sweep auto-credits exact payments within ~10 minutes.
get_trust_score(wallet)On-chain trust score (0-100) for ANY agent wallet, powered by RSoft Trust (beta) - vet a counterparty's ERC-8004 standing before trading or lending to it. See the Trust API section.
get_loan_status(request_id)Status of a loan request, including amount, agent_wallet and a draft{…} block when the request is waiting on a sponsor. Poll it after a 202 draft response.
get_agent_controls(agent_id)Sponsor binding, controls (paused, caps, draft mode) and the effective ceiling for an agent. Read it before requesting - it is the number the Gatekeeper will enforce. See Sponsor Controls.
get_agent(token_id | wallet)ERC-8004 directory entry: owner, wallet, URI, reputation clients and the bank's own standing for that wallet. See Agent Directory.
list_agents(limit)Recently registered agents - a bounded scan of recent Registered events plus the bank's customers, not the full census. The response says what window was scanned.
search_agents(q)Search the same bounded window plus the bank's customer table. Same limitation, same scanned_window field.
Full cycle via MCP (Python)
# pip install mcp
import asyncio
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
MCP_URL = "https://7mavs5vu7ggbhtxvbavdgs26qa0cbawg.lambda-url.us-east-1.on.aws/mcp"
async def main():
async with streamablehttp_client(MCP_URL) as (read, write, _):
async with ClientSession(read, write) as s:
await s.initialize()
# 1) Borrow - signature/nonce/deadline come from YOUR wallet
# signing the EIP-712 LoanRequest (see Quickstart step 1).
loan = await s.call_tool("request_loan", {
"amount": 5.0, "agent_id": "0xYourAgentWallet",
"signature": "0x…", "nonce": "…", "deadline": 1893456000,
})
# → { request_id, status: "initiated" } … disburses in seconds
# 2) Quote the debt
info = await s.call_tool("get_repayment_info",
{"agent_id": "0xYourAgentWallet"})
# → { request_id, repayment_amount, pay_to }
# 3) Send the EXACT repayment_amount in USDC to pay_to
# (your wallet stack does this - CDP, viem, web3.py, …)
# 4) Confirm
await s.call_tool("confirm_repayment", {
"request_id": "req_…", "tx_hash": "0x…",
})
# → { status: "repaid" } + positive ERC-8004 mark
asyncio.run(main())REST mirror - free reads & x402-paid intelligence
Agents without MCP support can use the same server over plain REST. Reads and repayment are free; underwriting intelligence is paid per request via x402 USDC micropayments - no account, no subscription, your agent pays the 402 challenge and gets the answer.
Free
GET /api/interest-rates current protocol rates
GET /api/creditworthiness/{agent} score + history for any agent
GET /api/repay-info/{agent} amount owed + treasury address
POST /api/repay report a repayment tx (money in is always free)
GET /api/agents/{id|wallet} ERC-8004 directory entry (see Agent Directory)
GET /api/agents/recent recently registered agents (bounded scan)
GET /api/agents/search?q= search the same bounded windowPaid (x402, USDC on Base)
GET /paid/interest-rates $0.001 rates snapshot
GET /paid/reputation/{agent} $0.001 ERC-8004 ReputationRegistry snapshot
POST /paid/credit-check $0.01 Bank Analyst (Kelly-AMM) assessment
POST /paid/risk-score $0.05 Gatekeeper + Analyst + Treasury caps
POST /paid/kya-verify $0.10 signed JWT KYA token (verifiable offline)
POST /paid/validation-attest $1.00 bank-signed ERC-8004 validation attestation
POST /paid/loans $0.01 loan request (same signature contract)A2A Protocol - the bank as a peer agent
The bank also speaks the A2A protocol (v1.0) - the Linux Foundation standard for agent-to-agent delegation. Any A2A client can discover the bank through its Agent Card and delegate banking tasks: preview terms, request a loan, check status, confirm repayment. Same underwriting, same EIP-712 signature contract as REST and MCP - the A2A surface transports your signature, it never signs for you.
Agent Card (discovery)
https://nng7khybjb4wlwe64r3eagbxhm0ihurx.lambda-url.us-east-1.on.aws/.well-known/agent-card.json8 skills advertised: get_interest_rates, get_creditworthiness, get_reputation, negotiate_loan, get_loan_status, request_loan, get_repayment_info, confirm_repayment.
Delegate a task (JSON-RPC)
Send a SendMessage request with a data part shaped {"skill": <name>, ...params}. The A2A-Version: 1.0 header is required.
POST https://nng7khybjb4wlwe64r3eagbxhm0ihurx.lambda-url.us-east-1.on.aws/
Content-Type: application/json
A2A-Version: 1.0
{
"jsonrpc": "2.0", "id": 1, "method": "SendMessage",
"params": { "message": { "role": "ROLE_USER", "parts": [
{ "data": { "skill": "negotiate_loan", "agent_id": "0xYourWallet", "amount": 10 } }
]}}
}negotiate_loan previews terms (credit analysis, no disbursement); sign the proposed terms and call request_loan to execute. Money-moving skills require the borrower's EIP-712 signature exactly like the Quickstart.
AgentKit Provider - the bank in your agent's toolbox
Building with Coinbase AgentKit? One package gives your agent the full credit cycle: rates, credit history, AgentTrust-8004 trust scores and real USDC loans on Base - signing the bank's EIP-712 LoanRequest natively with the agent's own wallet provider. The bank never sees a private key.
npm install rsoft-bank-agentkitimport { AgentKit } from "@coinbase/agentkit";
import { rsoftBankActionProvider } from "rsoft-bank-agentkit";
const agentKit = await AgentKit.from({
walletProvider, // any EVM wallet provider on Base mainnet
actionProviders: [
rsoftBankActionProvider({
apiKey: process.env.RSOFT_BANK_API_KEY, // loans; reads are free
}),
],
});6 actions: get_interest_rates, get_creditworthiness, get_trust_score, request_loan, get_repayment_info, confirm_repayment. Repay with AgentKit's native erc20 transfer + confirm_repayment. Source: github.com/rsoft-latam/rsoft-bank-agentkit · npm
OpenClaw Skill - the bank as installable commands
Running an OpenClaw agent? The official bank skill packages the whole cycle - check rates and credit, sign the loan request, borrow, and repay - as ready-to-run commands. It signs with a Coinbase CDP wallet (the key never leaves Coinbase's enclave) and reads its config from a file you control, so an agent can switch wallets by pointing at a different file. A live OpenClaw agent has borrowed and repaid a real loan through it, end to end.
Install
# install the official skill (Base mainnet, real USDC)
npx clawhub install rsoft-agentic-bank
# install its dependencies (Coinbase CDP SDK), once
cd <skill-dir> && npm install
# skill page: https://clawhub.ai/rsoft-latam/skills/rsoft-agentic-bankConfigure your CDP wallet
# a file only you can read — keep it OUT of synced folders
mkdir -p ~/.rsoft && cat > ~/.rsoft/wallet.env <<'EOF'
CDP_API_KEY_ID=your-cdp-api-key-id
CDP_API_KEY_SECRET=your-cdp-api-key-secret
CDP_WALLET_SECRET=your-cdp-wallet-secret
AGENT_WALLET=0xYourWalletAddress
BANK_API_KEY=your-pilot-api-key # for loan origination
EOF
chmod 600 ~/.rsoft/wallet.env
export WALLET_CONFIG_PATH=~/.rsoft/wallet.env🔒 CDP credentials control every wallet in that CDP project - use a project dedicated to this agent, never one holding funds you don't want it to touch.
Borrow & repay
node bin/address.js # your wallet address (verifies CDP access)
node bin/request-loan.js 5 # sign + request a 5 USDC loan (one shot)
node bin/repay.js # quote, pay the exact amount, confirm — one shotReal USDC on Base mainnet. The bank only originates loans signed by the borrowing wallet - same security contract as every other door into the bank.
Trust API - on-chain trust scoringBeta
RSoft Trust scores any agent wallet by what its ERC-8004 identity says on Base mainnet - powered by AgentTrust-8004, a model trained on a real census of 54,802 registered agents (published on Hugging Face). The bank itself logs this score inside every signed loan decision record, and the MCP server exposes it as get_trust_score. Free public reads while in beta.
Score a wallet
GET https://7pdor5bjoty7gyat56u6fgcrue0gbvnd.lambda-url.us-east-1.on.aws/score/0xYourWallet
{
"wallet": "0x6c37...ccda",
"registered": true,
"trust_score": 98, // 0-100, directional: identity age + reputation
"anomaly": false, // true = incoherent profile (e.g. reputation farming)
"reason": "within normal census distribution",
"features": { "identity_age_days": 201.9, "client_count": 3 }
}Unregistered wallets score 0 - no ERC-8004 identity means no anchor to score. First read for a wallet walks Base history (~40s); repeats are cached. Beta: model v0 uses the two features readable directly on-chain; richer features land with the event indexer.
RSoft Sentiment (ASI)
The first trust-weighted sentiment index for AI agents: reads what ERC-8004 agents DO on-chain (positions, capital, reputation) - not what humans say - and weights every agent's vote by its trust score. Anomalous profiles are excluded from the index entirely.
GET https://g3w2egj3mgmjezpzgtnzhkd5h40kqtxd.lambda-url.us-east-1.on.aws/api/v1/sentiment/asiTrust SDK - policies, gates and pricing
POST /evaluate turns the trust score into a decision: pick a policy (or compose your own gates), get a tier, pass/fail per gate, the anomaly flag and an optional price multiplier for the wallet. Gates come only from real on-chain signals - nothing self-reported. Keyless. Base URL https://7pdor5bjoty7gyat56u6fgcrue0gbvnd.lambda-url.us-east-1.on.aws.
Evaluate a wallet against a policy
POST /evaluate
{ "wallet": "0x…", "policy": "standard", "include_pricing": true, "base_price_usdc": 1.0 }
{
"wallet": "0x…",
"registered": true,
"trust_score": 82,
"trust_tier": "verified", // trusted | verified | limited | untrusted | blocked
"anomaly": false,
"policy": "standard", "operator": "AND",
"all_passed": true,
"gate_results": [
{ "gate": "registered", "passed": true, "value": true, "threshold": true },
{ "gate": "established", "passed": true, "value": 201.9, "threshold": 30 },
...
],
"pricing": { "price_multiplier": 0.77, "suggested_price_usdc": 0.77,
"suggested_interest_rate": ..., // indicative only
"bank_risk_tier_equivalent": "A", ... }
}Policies: quick, basic, standard, strict, financial, reputation, or custom {gates, operator: AND|OR|WEIGHTED, weights, threshold}. Gates: registered (ERC-8004 identity), established (identity ≥ 30 days), active (≥ 1 reputation client), coherent (no anomaly), score (≥ 50). Also GET /policies and GET /pricing?wallet=&base_price_usdc=.
Pricing
price_multiplier = 2.0 − 1.5 · trust_score / 100. A trusted wallet pays down to 0.5×, an unknown one 2.0×; anomalous or unregistered wallets are always 2.0×. The suggested_interest_rate is indicative only - the bank prices its own loans with its Kelly/AMM model, not with this number.
SDKs
TypeScript · npm rsoft-trust 0.1.0
npm install rsoft-trust
import { TrustClient } from "rsoft-trust";
const r = await new TrustClient().evaluate({ wallet, policy: "standard" });
if (!r.all_passed) throw new Error(r.trust_tier);
// Express: 403 with the gate breakdown when the policy fails
import { trustGate } from "rsoft-trust/express";
app.post("/pay",
trustGate({ policy: "standard", walletFrom: (req) => req.headers["x-wallet"] }),
handler);
// Edge / Workers / Next.js route handlers (WHATWG Request → Response)
import { trustGuard } from "rsoft-trust/fetch";
const g = await trustGuard(req, { policy: "standard" });
if (!g.ok) return g.response;Python · PyPI rsoft-trust 0.1.0
pip install "rsoft-trust[fastapi]"
from rsoft_trust import TrustClient
async with TrustClient() as trust:
r = await trust.evaluate(wallet, policy="standard")
# or: await trust.require(wallet, "standard") → raises TrustGateError
# FastAPI dependency (wallet from X-Wallet header or ?wallet=)
from rsoft_trust.fastapi import require_trust
@app.post("/pay", dependencies=[Depends(require_trust(policy="standard"))])
async def pay(): ...Source for both packages: github.com/rsoft-latam/rsoft-trust-api under packages/.
API Reference
Public GET endpoints for querying loan data, workflow status, and protocol information.
Loans
/api/v1/loan/status/{request_id}Get Loan Status
Retrieve the current status and details of a specific loan request.
Path Parameters
request_idstringThe unique loan request identifierResponse
{
"request_id": "req_abc123def456",
"status": "disbursed",
"agent_wallet": "0x1234...abcd",
"loan_amount": 1000,
"amount_approved": 1000,
"interest_rate": 0.085,
"term_days": 30,
"tx_hash": "0xabc...def",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:31:45Z"
}/api/v1/loan/workflow/{request_id}Get Workflow Steps
Retrieve the detailed execution status of each agent in the workflow pipeline. Includes timing, results, and errors for every step.
Path Parameters
request_idstringThe unique loan request identifierResponse
{
"request_id": "req_abc123def456",
"loan_status": "disbursed",
"workflow_status": "completed",
"total_duration_ms": 12450,
"current_step": null,
"steps": [
{
"step": "gatekeeper",
"step_order": 1,
"status": "completed",
"started_at": "2025-01-15T10:30:00Z",
"completed_at": "2025-01-15T10:30:02Z",
"duration_ms": 2100,
"result": { ... },
"error": null
}
],
"created_at": "2025-01-15T10:30:00Z",
"completed_at": "2025-01-15T10:31:45Z"
}/api/v1/loan/history/{wallet_address}Get Agent Loan History
Retrieve the loan history for a specific wallet address. Returns all past and current loan requests.
Path Parameters
wallet_addressstringThe agent's wallet addressQuery Parameters
limitintegerMax number of results (default: 10)Response
[
{
"request_id": "req_abc123def456",
"status": "repaid",
"agent_wallet": "0x1234...abcd",
"loan_amount": 1000,
"amount_approved": 1000,
"interest_rate": 0.085,
"term_days": 30,
"current_node": null,
"tx_hash": "0xabc...def",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-20T14:00:00Z"
}
]/api/v1/loan/explorerLoan Explorer
Public paginated endpoint to browse all protocol loans. Supports filtering by status. No authentication required.
Query Parameters
pageintegerPage number (default: 1)limitintegerItems per page (default: 20)statusstringFilter by loan statusResponse
{
"loans": [
{
"request_id": "req_abc123",
"agent_wallet": "0x1234...abcd",
"amount": 1000,
"currency": "USDC",
"status": "disbursed",
"interest_rate": 0.085,
"duration_days": 30,
"disbursement_tx_hash": "0xabc...def",
"created_at": "2025-01-15T10:30:00Z"
}
],
"total": 42,
"page": 1,
"limit": 20,
"total_pages": 3
}/api/v1/loan/active/{wallet_address}Get Active Loans
Retrieve all currently active loans for a wallet address. Includes outstanding balance summary.
Path Parameters
wallet_addressstringThe agent's wallet addressResponse
{
"active_loans": [
{
"request_id": "req_abc123def456",
"amount": 1000,
"interest_rate": 0.085,
"duration_days": 30,
"repayment_amount": 1085,
"status": "disbursed",
"disbursement_tx_hash": "0xabc...def",
"created_at": "2025-01-15T10:30:00Z"
}
],
"active_loans_count": 1,
"total_outstanding": 1085,
"wallet_address": "0x1234...abcd",
"agent_id": "agent-001"
}Agents & Rates
/api/v1/agents/{agent_id}/creditworthinessCheck Creditworthiness
Evaluate an agent's creditworthiness based on loan history and risk profile.
Path Parameters
agent_idstringThe unique agent identifierResponse
{
"agent_id": "agent-001",
"credit_score": 750,
"risk_tier": "low"
}/api/v1/interest-ratesInterest Rates
Retrieve current protocol interest rates. Rates are updated dynamically based on protocol utilization.
Response
{
"base_rate": 0.05,
"risk_tiers": {
"low": { "rate": "..." },
"medium": { "rate": "..." },
"high": { "rate": "..." }
},
"yield_strategies": {
"conservative": { "apy": "..." },
"balanced": { "apy": "..." },
"aggressive": { "apy": "..." },
"dynamic": { "apy": "..." }
},
"updated_at": "2025-01-15T00:00:00Z"
}/api/v1/agents/{wallet}/controlsSponsor Controls
Sponsor binding, sponsor-set controls and the effective borrowing ceiling for an agent. Public. See Sponsor Controls.
Path Parameters
walletstringThe agent's wallet addressResponse
{
"sponsor": { "bound": true, "kind": "phone", "status": "active" },
"controls": { "paused": false, "revoked": false, "max_loan_amount": 20,
"daily_draw_cap": 50, "draft_mode_enabled": true, "version": 3 },
"effective": { "ladder_limit": 10, "ladder_level": 1, "global_max": 25,
"sponsor_cap": 20, "effective_ceiling": 10,
"daily_used": 0, "daily_remaining": 50 },
"pending_draft": null
}Agent Directory (ERC-8004)
/api/v1/agents/registry/{token_id}Registry Entry by Token ID
Identity + reputation registry read for one ERC-8004 agent on Base mainnet, plus the bank's own credit standing if the wallet is a customer. Public.
Path Parameters
token_idintegerERC-8004 Identity Registry token idResponse
{
"token_id": 58492,
"exists": true,
"owner": "0x…",
"agent_wallet": "0x…",
"canonical_uri": "https://…",
"client_count": 1,
"clients": ["0x…"],
"known_issuers": [ ... ],
"trusted_feedback_count": 4,
"trusted_feedback_value": ...,
"chain_id": 8453,
"identity_registry": "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
"reputation_registry": "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63",
"bank": { ... }
}/api/v1/agents/registry/by-wallet/{wallet}Registry Entry by Wallet
Same payload as the token-id lookup, resolved from the agent wallet. Public.
Path Parameters
walletstringThe agent's wallet addressResponse
{ "token_id": 58492, "exists": true, "agent_wallet": "0x…", ... }/api/v1/agents/registry/recentRecently Registered Agents
Bounded scan of recent Registered events (default 5000 blocks) plus the bank's own customer table. The IdentityRegistry is not enumerable on mainnet, so this is not the full census; the response says what was scanned. Public.
Query Parameters
limitintegerMax number of agents to returnResponse
{
..., // the matched agents (same shape as a registry entry)
"scanned_window": { ... }, // blocks actually scanned
"limitation": "…" // states that the registry is not enumerable
}/api/v1/agents/registry/searchSearch Agents
Search within the same bounded window and the bank's customer table. Same limitation and scanned_window as recent. Public.
Query Parameters
qstringResponse
{
..., // the matched agents
"scanned_window": { ... },
"limitation": "…"
}Webhooks
All webhook endpoints require X-API-Key. Request shapes, events and the signature scheme are in the Webhooks section.
POST /api/v1/webhooks { url, events[], agent_wallet? } → { id, secret } (secret shown once)
GET /api/v1/webhooks?agent_wallet=0x… list subscriptions
DELETE /api/v1/webhooks/{id} remove a subscription
POST /api/v1/webhooks/{id}/ping send a test delivery
GET /api/v1/webhooks/{id}/deliveries delivery attempts and responsesHealth Checks
/healthHealth Check
Returns the overall health status of the API and its dependencies.
Response
{
"status": "healthy"
}/health/readyReadiness Check
Kubernetes-style readiness probe. Returns 200 when the service is ready to accept traffic.
Response
{
"status": "ready"
}/health/liveLiveness Check
Kubernetes-style liveness probe. Returns 200 as long as the service process is running.
Response
{
"status": "alive"
}Status Codes & Loan States
Loan Statuses
HTTP Status Codes
Request succeeded
Loan request parked as a draft pending sponsor approval (draft mode)
Invalid parameters or request body
Resource not found (invalid request_id, wallet, etc.)
Internal server error
Error Response Format
All error responses follow a consistent format with a detail field describing the error.
{
"detail": "Loan request not found: req_invalid_id"
}