Teppi
Avant de payer un endpoint x402 ou un serveur MCP, lisez ce que ce paiement a livré : vérifié, signé, reproductible.
Serveur MCP hébergé
npx add-mcp 'https://api.teppi.xyz/mcp'S’installe dans Claude Code, Codex, Cursor et plus
Documentation
Check before you pay, in the client you already use
Version 2026.09.4.
Every snippet below asks the Teppi record one question at the moment a 402 arrives, before a payment is signed: what paying this endpoint, and whoever it pays, has actually delivered. The answer is signed, free, needs no key, and is cached for a minute. A url the record has never seen is answered UNRATED, never as bad, and is put in line for a free handshake.
Nothing here sends your request body, your wallet or your payment anywhere. The one thing that leaves is the url you are about to pay, and the payTo address from its 402 when you pass it.
The official x402 client
@x402/core's client calls onBeforePaymentCreation before it signs anything. Returning
{ abort: true, reason } stops the payment. teppiHook is that hook, and it reads the url and
the payTo from the 402 itself.
npm install teppi-client
import { x402Client } from '@x402/core/client';
import { teppiHook } from 'teppi-client';
const client = new x402Client()
.register('eip155:*', evmScheme)
.onBeforePaymentCreation(teppiHook({ minBand: 'C' }));
@x402/fetch and @x402/axios wrap this same client, so the hook covers them too: build the
client as above and hand it to the wrapper you already use.
Any client that takes a fetch
beforeYouPay wraps a fetch. On a 402 it asks the record first; when the policy refuses, the
402 becomes a 403 that says why, before anything is signed. It works for any 402, x402 or MPP,
since it reads only the status and the url.
import { beforeYouPay } from 'teppi-client';
const guarded = beforeYouPay(fetch, { minBand: 'C', unrated: 'allow' });
The default policy refuses a listing the record shows failing before payment, and a band below
the one you name. UNRATED is paid by default, since most of the market has no paid record yet.
Pass policy for your own rule; it receives the whole answer.
Paid only on delivery
Where the answer carries buy, the same call can be bought through Teppi: the seller is paid by
us, the answer is checked, and you are charged only once it passed. The price is the seller's plus
a 10% fee, at least $0.002.
import { beforeYouPay } from 'teppi-client';
const guarded = beforeYouPay(fetch, { payOnDelivery: true, publish: true });
publish is off unless you set it. Set, the call you bought goes into the public record, bytes
and all, under one opaque name for your wallet. It counts toward what the next buyer reads about
that seller, and it can pull a seller's published figures down but never push them up, so a
seller buying its own endpoint gains nothing by it. Left off, nothing you sent is published: only
salted commitments that you alone can open.
From a total of $0.05, and up to $1.00, the same 402 can also offer auth-capture: your money
waits in the audited Base escrow, captured for Teppi's treasury only once the answer passed and
handed back otherwise, and you can take it back yourself after 30 minutes whatever Teppi does. The
official client pays it with AuthCaptureEvmScheme, and since it prefers paying after the fact
whenever both are offered, preferEscrow keeps the escrow terms alone:
import { x402Client } from '@x402/core/client';
import { AuthCaptureEvmScheme } from '@x402/evm/auth-capture/client';
import { preferEscrow } from 'teppi-client';
const client = new x402Client()
.register('eip155:8453', new AuthCaptureEvmScheme(signer))
.registerPolicy(preferEscrow);
One GET, from any language
curl -sS 'https://api.teppi.xyz/v1/check?url=https%3A%2F%2Fseller.example%2Fv1%2Fprice&pay_to=0x2a462db85807f0ff497ddd15f5978317d079a3a1'
import requests
answer = requests.get(
"https://api.teppi.xyz/v1/check",
params={"url": url, "pay_to": pay_to},
timeout=5,
).json()
if answer["defects"]:
raise RuntimeError(answer["defects"][0]["says"])
Read band with tier beside it: a letter is only ever given on a tier somebody paid for, and
UNRATED means unknown, not bad. defects says what the listing gets wrong before any payment,
payee what the record knows about whoever gets paid, and buy how to pay only on delivery.
As a tool your agent can call
The same answer is the check_grade tool on Teppi's MCP server, with search_capabilities to
find an alternative that has been paid and checked.
claude mcp add --transport http teppi https://api.teppi.xyz/mcp
{ "mcpServers": { "teppi": { "url": "https://api.teppi.xyz/mcp" } } }
The second is for a client that reads a json config, such as Cursor's .cursor/mcp.json. A
skill file that makes an agent ask before it pays is at https://api.teppi.xyz/docs/skill.
Your whole fleet at once
Send every paid endpoint your agents call, up to 200, and get each one looked up the way a check is, then added up by the address each one pays. One address often stands behind many origins, so a list that looks spread out can be one operator.
curl -sS https://api.teppi.xyz/v1/exposure \
-H 'content-type: application/json' \
-d '{"dependencies":[{"url":"https://seller.example/v1/price","calls_per_month":3000},"https://other.example/v1/lookup"]}'
Each line carries a standing: took_the_money (a paid call settled and nothing came back),
listing_mismatch, lettered, paid_no_letter, handshake_only or not_in_record. The
last two mean unknown, and unknown is never counted as a failure: the spend totals keep
monthly_spend_not_yet_measured apart from monthly_spend_where_the_record_shows_a_failure. Pass
the pay_to from a 402 and pay_to_seen_before says whether this seller has been seen paying
that address. The answer is signed like a check, and the same report is the check_exposure tool.
Told when a card falls
Register an https receiver and the endpoints to watch. Before anything is kept, the receiver is posted a challenge and must echo it, so nobody can point notices at a url that did not ask. The answer carries a token, shown once, that reads or stops the watcher.
curl -sS https://api.teppi.xyz/v1/watchers \
-H 'content-type: application/json' \
-d '{"url":"https://you.example/teppi","watch":["https://seller.example/v1/price"]}'
The receiver answers the challenge and takes only notices that verify against the key that signs the cards:
import { noticeReceiver } from 'teppi-client';
export const POST = noticeReceiver(async (notice) => {
console.log(notice.capability_id, notice.change, notice.from, '->', notice.to);
});
A notice names the card it is about, so nothing has to be taken from the notice itself. Read a
watcher back with GET /v1/watchers/{watcher_id} and stop it with POST /v1/watchers/stop,
each with the token as a bearer.
Checking that Teppi said it
Every answer is signed by the key the record publishes for checks, and never by the key that signs scorecards.
import { check, publishedKeys, verify } from 'teppi-client';
const answer = await check('https://seller.example/v1/price');
const result = await verify(answer, await publishedKeys());
Changelog
2026.09.4
| Change | Reason |
|---|---|
Paying in escrow, with AuthCaptureEvmScheme and preferEscrow | A buyer's money can now wait in the audited escrow until the answer passed, and the official client picks paying after the fact unless told otherwise |
2026.09.3
| Change | Reason |
|---|---|
Your whole fleet at once (POST /v1/exposure), and told when a card falls (POST /v1/watchers with noticeReceiver) | A fleet checked one url at a time never sees that many of its dependencies pay one address, and notices existed with no way to ask for them |
2026.09.2
| Change | Reason |
|---|---|
publish: true beside payOnDelivery, and what publishing does and does not do | A buyer who wanted its call to count had to set a header by hand on the paid retry, which a wrapper hides |
2026.09.1
| Change | Reason |
|---|---|
| First version: the official x402 client, any fetch, paid only on delivery, one GET from any language, MCP, and checking the signature | Every piece existed, and nothing said where it goes in the client a developer already has open |