Build credit for AI agents on Base.
Tessera is a USDC credit protocol for AI agents. Settlement history goes in, programmable credit lines come out. This page is the engineering surface: how the underwriter scores agents, how to integrate the SDK, and how every product on the platform fits together.
Live on Base mainnet · v0.0.1 SDK · subgraph synced
What is Tessera
Tessera is the first credit card built for AI agents. At its core it’s a USDC line of credit on Base, with limits underwritten on-chain from the agent’s settlement history. Your agent settles invoices through the protocol, that history becomes its credit file, and the limit it can draw against scales with the volume and reliability of its settled work.
The protocol is permissionless on the borrower side — the underwriter is the chain. No KYB, no application, no human in the loop. Default and the limit tightens; pay back on time and it grows. All visible on-chain.
Quickstart
Three lines of config gets you on the rails. The SDK ships with all addresses, subgraph URL, and link generators preset for Base mainnet.
npm install @tessera/sdk viemimport { Tessera, TESSERA_BASE_MAINNET } from "@tessera/sdk";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.AGENT_PK as `0x${string}`),
chain: base,
transport: http(),
});
const tessera = new Tessera({
...TESSERA_BASE_MAINNET,
walletClient: wallet,
});
// Read your agent's current credit picture
const profile = await tessera.getAgentProfile(wallet.account.address);
console.log("Lifetime settled:", profile.settledVolume);
console.log("Repayment rate:", profile.repayRate);
console.log("Computed limit:", profile.creditLimit); // Phase 1Need a richer starter? github.com/cmxdev1/Tessera/tree/main/examples has working agents for each common flow.
The product suite
Tessera is a protocol of four live surfaces and three planned ones. Everything reads from the same on-chain state.
Tessera Credit
USDC credit line for AI agents. Limits underwritten on-chain from settlement history.
Lender Vault
ERC-4626 vault. Deposit USDC, fund agent draws, earn the spread linearly toward maturity.
Tessera Pay
Shareable USDC pay-me links. The on-ramp that builds credit history one settlement at a time.
Public Ledger
Every invoice, draw, and repayment on the protocol. Filterable, expandable, on-chain receipts.
Agent Dashboard
Bill another agent. Repay invoices billed to you. One screen for both sides of agent settlement.
Escrow
Deliverable-locked payments for one-off agent hires. Buyer locks, seller delivers, release on confirmation.
Reputation credentials
Portable on-chain credentials built from settlement history. Citable across protocols.
MCP server
Drop-in MCP integration for Claude, Cursor, and other agent frameworks. Wraps the SDK.
Credit underwriting
Limits are computed from on-chain inputs only. No human in the loop, no application, no quarterly review. The formula is public and runs against the Tessera settlement subgraph. Limits update as new invoices settle.
function computeCreditLimit(profile: AgentProfile): number {
const { settledVolume, avgInvoice, repayRate } = profile;
const raw =
settledVolume * 0.40 + // 40% of lifetime volume
avgInvoice * 8 + // 8x the average ticket
repayRate * 5_000; // up to $5k for perfect repayment
return Math.min(50_000, raw); // $50k initial cap
}Signal weights — what the underwriter actually reads
| Signal | Source | Weight | Why it matters |
|---|---|---|---|
| settledVolume | subgraph | × 0.40 | Bigger track record → bigger line. Linear. |
| avgInvoice | derived | × 8 | Bigger tickets → bigger working-capital ceiling. |
| repayRate | subgraph | × $5,000 | Reliability premium. 100% on-time → +$5k. |
| cap | protocol | $50,000 | Initial ceiling, lifts as vault TVL and underwriting data deepen. |
What happens when an agent defaults
A missed repayment is recorded on-chain and permanently dents the agent’s repayment rate — the same number the underwriter reads for every future draw. Lenders take the principal loss (no insurance pool in v0). Default events are public on the protocol ledger and shown on the agent’s profile page (both surfaces unlock in a future phase).
Settlement protocol
The substrate beneath the credit product. Three primitives feed the same on-chain state: invoice (retainer / project work), escrow (deliverable-locked one-offs, Phase 1), and reputation (the credential you build by using either).
Invoice lifecycle
- 01createInvoiceOriginator calls the SDK with the buyer’s address, face value in USDC, maturity timestamp, and discount in basis points. An invoice record mints on-chain.
- 02fundInvoiceAny lender in the vault can fund the invoice in a single transaction. The discounted advance (e.g. 97% of face at 3% discount) flows directly to the originator’s wallet.
- 03Linear accrualSpread accrues to vault NAV linearly toward maturity. Lenders hold
tsUSDCv1ERC-4626 shares; NAV ticks up automatically — no claim transactions. - 04repayInvoiceOn or before maturity, the buyer agent calls
repayInvoicewith face value in USDC. Funds flow back into vault NAV. Repayment rate ticks up; credit limit recomputes.
SDK reference
@tessera/sdk is a TypeScript client for any Node.js or Edge runtime. Accepts any viem WalletClient — bring your own signer.
New here? Start at the SDK quickstart at tesseracredit.com/sdk — installation, capabilities overview, and a 30-second integration walkthrough. This section is the full method reference.
On-chain writes
// Originator side
await tessera.createInvoice({
buyer: "0x…",
faceValueUsdc: 1000n * 10n ** 6n,
maturityUnix: Math.floor(Date.now() / 1000) + 14 * 86400,
discountBps: 300, // 3%
});
// Buyer side
await tessera.repayInvoice(invoiceId);
// Vault side
await tessera.deposit(amountUsdc);
await tessera.redeem(shares);
// Phase 1: credit-line operations (simulated today)
await tessera.drawCredit(amountUsdc);
await tessera.repayCredit(amountUsdc);Reads (subgraph)
const profile = await tessera.getAgentProfile(address);
// { settledVolume, avgInvoice, repayRate, monthsOnBase,
// settledInvoices, computedCreditLimit }
const score = await tessera.getReputationScore(address);
// 0..100, weighted blend of repayRate, age, volume
const recent = await tessera.recentInvoices({
agent: address,
limit: 25,
});Helpers
// Link generators (use anywhere, no wallet needed)
tessera.getPayLink(invoiceId); // /pay/<id>
tessera.getProfileLink(address); // /a/<address>
tessera.getPayMeLink(address); // /pay-me/<address>
// Async wait helpers — for agent automation
await tessera.waitForFunded(invoiceId, { timeoutMs: 60_000 });
await tessera.waitForRepaid(invoiceId, { timeoutMs: 60_000 });
await tessera.waitForDraw(drawId, { timeoutMs: 60_000 });signInWithTessera and verifyTesseraSession — a SIWE-compatible auth primitive for dapps that want to ID their agent users in one call. See Sign in with Tessera below or try the live demo at /signin.Sign in with Tessera
A SIWE-compatible auth primitive. One SDK call gets a dapp cryptographic proof an agent owns the wallet (standard EIP-4361 SIWE message) plus their full Tessera profile — ecosystem, score, credit tier, history. Drop-in identity for any dapp that wants to ID their agent users and price them by creditworthiness.
Try it live at tesseracredit.com/signin — connect a wallet, sign once, watch the session JSON + verify response render side-by-side.
1. Client — request a signed session
import { signInWithTessera } from "@tessera/sdk";
import { useWalletClient } from "wagmi";
const { data: walletClient } = useWalletClient();
const session = await signInWithTessera(walletClient, {
statement: "Sign in to MyAgentApp to verify your Tessera identity.",
});
// session = {
// address, // lowercased wallet
// domain, uri, chainId,
// nonce, // anti-replay
// issuedAt, expiresAt, // ISO 8601
// message, // EIP-4361 SIWE message — drop-in for any SIWE consumer
// signature, // standard ECDSA hex
// profile: {
// ecosystem, // "virtuals" | "openclaw" | "cdp" | "generic" | "new" | "unknown"
// ecosystemLabel,
// score?, // 0–100 (Tessera Score, Phase 0 live)
// tier?, // New / Bronze / Silver / Gold / Platinum / Diamond
// percentile?, // 0–99 rank against the broader agent population
// creditLineEstimate?, // USDC whole dollars (Phase 0: capped at $25k)
// hasPayHistory?, // Phase 1+
// hasSettlements?, // Phase 1+
// },
// }2. Server — verify the session
Two options. Either verify locally with the SDK (zero network calls, pure crypto), or POST to the hosted endpoint if your runtime can’t bundle viem.
// Option A — verify with the SDK (recommended)
import { verifyTesseraSession } from "@tessera/sdk";
export async function POST(req: Request) {
const session = await req.json();
const result = await verifyTesseraSession(session, {
expectedDomain: "myagentapp.com", // pin to your domain
maxAgeSeconds: 3600, // reject sessions older than 1h
});
if (!result.valid) {
return new Response(`unauthorized: ${result.reason}`, { status: 401 });
}
// result.address is the verified wallet — trust it.
await mySession.create({ user: result.address });
return new Response("ok");
}// Option B — POST to the Tessera hosted endpoint
const res = await fetch("https://www.tesseracredit.com/api/auth/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
session,
options: { expectedDomain: "myagentapp.com", maxAgeSeconds: 3600 },
}),
});
const { valid, address, reason } = await res.json();
if (!valid) throw new Error(`unauthorized: ${reason}`);Verification reasons
When valid: false, the response carries a machine-readable reason:
session_expired— current time is pastexpiresAt.session_too_old— exceeded the caller’smaxAgeSeconds.domain_mismatch— session was issued for a different domain than expected.invalid_signature— signature does not recover tosession.address.signature_verification_error— verification threw (malformed signature or RPC failure for EIP-1271 smart accounts).
x402 payments
Tessera ships paid endpoints under the x402 Payment Protocol — the HTTP-native standard for agent-to-API micropayments on stablecoins. Agents pay per-call in USDC on Base; we return the same data the free endpoints serve. The paid path is built for x402-native integrators that want a wallet-native consumption flow.
Live endpoints — what’s on the rails today
Two endpoints are live on Base mainnet, both implementing the full x402 v2 wire format so any v2-aware client or discovery indexer can consume them.
GET /api/x402/score/[address]— paid Score lookup, $0.001 USDC. Same response shape as the free/api/score/[address].GET /api/x402/agent/[address]— full agent profile in one call (Score + tier + percentile + credit-line estimate + five-input breakdown + Agent Directory record if listed), $0.002 USDC. One call replaces three.
The free /api/score and /api/agent endpoints stay free for SDK consumers; these paid paths are for agents that natively speak x402 and want a wallet-native consumption flow.
The 402 response (what discovery sees)
Tessera implements x402 v2 — the latest spec with CAIP-2 network identifiers and the renamed PAYMENT-SIGNATURE header. Required by modern x402 discovery indexers.
{
"x402Version": 2,
"error": "PAYMENT-SIGNATURE header is required",
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x...your-x402-recipient",
"maxAmountRequired": "1000",
"resource": "https://www.tesseracredit.com/api/x402/score/0xabc...",
"description": "Tessera Score for any Base wallet — 0-100 credit score, tier, percentile, credit-line estimate, full input breakdown.",
"mimeType": "application/json",
"maxTimeoutSeconds": 60,
"extra": { "name": "USD Coin", "version": "2" }
}]
}Calling it as an x402 client
Use the v2 @x402/fetch (or @x402/axios) wrapper — it intercepts 402 responses, signs an EIP-3009 payment authorization with your agent’s wallet, and retries with the PAYMENT-SIGNATURE header automatically.
import { wrapFetchWithPayment } from "@x402/fetch";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.AGENT_PK as `0x${string}`);
const fetchWithPayment = wrapFetchWithPayment(fetch, account);
// One call. The wrapper handles the 402 + payment + retry under the hood.
const res = await fetchWithPayment(
"https://www.tesseracredit.com/api/x402/score/0xd8dA6BF2..."
);
const { score, tier, percentile, creditLineEstimate, paidVia } =
await res.json();
// paidVia: "x402" confirms the response came from the paid path.Settlement model
Payment verification + settlement run through the public x402 Foundation facilitator on Base mainnet. The flow:
- Client signs an EIP-3009
TransferWithAuthorizationfor the requested amount, recipient, and network (eip155:8453for Base mainnet, CAIP-2 format). - Server forwards the signed authorization to the facilitator, which verifies the signature and submits the USDC transfer on-chain.
- Settlement only happens AFTER the route handler returns successfully — so clients aren’t charged for indexer failures or invalid input.
- Response includes a
PAYMENT-RESPONSEheader confirming settlement.
/api/score/[address] endpoint covers SDK consumers + dapp gating. The x402 variant exists so agents paying-as-they-go via the standard have a native way to consume Tessera Score, and so Tessera surfaces on every x402 discovery service. Revenue from paid lookups will eventually route to a FeeTreasury that buys + burns $TESSERA.Privacy extension — encrypted responses (Phase 1, live)
Both paid endpoints support an opt-in Tessera Encrypted Response extension. The agent provides an ephemeral X25519 public key in a request header; the server encrypts the response body to that pubkey via ECIES (X25519 + HKDF + AES-256-GCM) before returning. Once sent, the server cannot decrypt its own output — only the agent’s wallet can.
The SDK ships wrapFetchWithEncryption — wrap any fetch (including wrapFetchWithPayment) and the encryption is invisible. Payment-outer, encryption- inner composition; your downstream code is unchanged.
import { wrapFetchWithPayment } from "@x402/fetch";
import { wrapFetchWithEncryption } from "@tessera/sdk";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.AGENT_PK as `0x${string}`);
const fetchPaying = wrapFetchWithPayment(fetch, account);
const fetchEncrypted = wrapFetchWithEncryption(fetchPaying);
// One call — payment + encryption both invisible.
const res = await fetchEncrypted(
"https://www.tesseracredit.com/api/x402/score/0xd8dA6BF2..."
);
const { score, tier } = await res.json(); // already decryptedWant the raw wire format? See it in flight on /privacy/x402 — three panes showing your agent keypair, the encrypted envelope on the wire, and the cleartext after local decrypt. Or roll your own without the SDK:
# 1. Agent generates X25519 keypair locally (any X25519 lib)
# 2. Sends pubkey (base64url, 43 chars) in the request header
curl https://www.tesseracredit.com/api/x402/score/0xabc... \
-H "PAYMENT-SIGNATURE: <signed-payment>" \
-H "X-Tessera-Encrypt-Pubkey: <base64url X25519 pubkey>"
# Response:
# X-Tessera-Encrypted: true
# Content-Type: application/json
# { "v": 1,
# "scheme": "ecies-x25519-aes256gcm",
# "ephemeralPubkey": "<server's per-request pubkey>",
# "iv": "<base64url, 12 bytes>",
# "ciphertext": "<base64url, body + AES-GCM auth tag>" }
#
# 3. Derive shared secret via X25519 with server's ephemeralPubkey
# Run HKDF-SHA256 with salt = ephemeralPubkey || ourPubkey,
# info = "tessera-pay-memo/v1/aes256gcm". Decrypt via AES-256-GCM.Backwards compatible — clients that don’t send the header get the same cleartext response as before. The 402 discovery response itself is never encrypted (clients need to read accepts[] to construct a payment).
Roadmap — more paid endpoints
Score + Agent are the first two. Building next:
POST /api/x402/attest— Tessera-signed attestation of a derived claim (e.g. “score ≥ 60”, “tier ≥ Gold”, “has zero defaults”). The agent attaches this attestation to other dapps without re-revealing their wallet history. Foundational primitive for Phase 5 credit- gated privacy.POST /api/x402/score/batch— N addresses per request, priced per-address. For underwriters + dapp gating systems that need bulk lookup.GET /api/x402/receipts/[txHash]— third-party verification of any Tessera Pay receipt. Compliance + audit use cases.- Pay-on-credit (Phase 1+): agents with high-enough Tessera Score can call paid endpoints on credit — Tessera underwrites; agent settles weekly. The credit identity layer becomes the credit substrate of x402 itself.
Subgraph
Every counter on every product page is derived from the Tessera subgraph. Public endpoint, no auth required.
https://api.studio.thegraph.com/query/1749787/tessera-base/v0.0.1Useful queries
// Lifetime profile for a single agent
const QUERY_AGENT_PROFILE = `
query AgentProfile($id: ID!) {
agent(id: $id) {
id
settledInvoiceCount
settledVolume
repayedOnTime
repayedLate
defaulted
firstInvoiceAt
}
}
`;
// Recent invoices, paginated
const QUERY_RECENT_INVOICES = `
query Recent($limit: Int!, $skip: Int!) {
invoices(
first: $limit, skip: $skip,
orderBy: createdAt, orderDirection: desc
) {
id faceValueUsdc status
originator { id } buyer { id }
createdAt maturityAt repaidAt
}
}
`;Vault mechanics
The lender side. Capital flows from depositors into approved agent draws and invoices; spread accrues back to depositor share NAV.
- ERC-4626 standard. Share token is
tsUSDCv1. Deposit USDC, receive shares; redeem shares, get USDC back at current NAV. - Linear accrual. No claim transactions. NAV ticks up automatically as funded paper accrues toward maturity.
- Withdrawal queue. If liquidity is fully deployed, redemption requests sit in an FIFO queue and clear as positions mature. No lockup — queue is the backstop, not a fixed term.
- Manual underwriting (v0). Each invoice is approved by the protocol operator (a Gnosis Safe on Base) before lenders can fund it. Programmatic underwriting based on the formula above is a Phase 1 contract upgrade.
- 25-30% target APY. Reflects the rate the market needs to clear at while the protocol is new and lenders are taking real risk on novel agent counterparties. Expect normalization toward 15-20% as repayment data accumulates.
Roadmap
- Phase 0 · live
Simulated credit demo
The credit playground is live at /demo so you can preview the full experience. Surrounding products (vault, pay-me, agent dashboard, public ledger) are parked in 'Building' status and unlock product-by-product.
- Phase 1
Real credit draws + programmatic underwriting
drawCredit / repayCredit on a live contract. The formula above replaces manual underwriting for sub-cap draws. Escrow product ships.
- Phase 1.2
Multi-tenor pools + reputation credentials
Separate vault pools by tenor (7/14/30/60 day) so lenders can pick duration risk. Portable reputation credential surfaces as a standalone primitive.
- Phase 2
MCP + the broader integration surface
Native MCP server for Claude / Cursor / etc. Multi-chain expansion if the unit economics hold. Insurance vault as a separate yield product.
Resources
- GitHub
Monorepo: contracts, SDK, subgraph, frontend
- Settlement vault
0x4ead…cd52 · Base mainnet · source verified
- Tessera token
0xF5Ba…e439 · Base mainnet · ERC-20
- Subgraph
Public read endpoint
- X
Updates, launch posts, release notes
- Telegram
Realtime channel, community Q&A
- SDK quickstart
Install, capabilities, and a 30-second integration walkthrough
- Credit demo
Full simulated playground — try the experience
- Sign in with Tessera
Live SIWE round-trip — connect, sign, verify in-browser
Docs reflect Phase 0 (settlement live, credit simulated). Real credit draws launch with Phase 1. Last updated May 29, 2026.