# Kertivo Check integration examples Kertivo Check verifies Base mainnet USDC payments (x402 v2, scheme exact, network eip155:8453, payTo 0x509E55c4F898B43C856Fb0E51048B1b7bFBC9eDE). POST /check costs $0.01 USDC per call. Try the free demo first; it returns the same shape as a paid /check. ## 1. Free demo (no wallet) curl -s -X POST https://www.kertivo.com/demo ## 2. See the price handshake (no wallet, nothing is charged) curl -si -X POST https://www.kertivo.com/check -H 'content-type: application/json' -d '{"txHash":"0x<64 hex>"}' Expect HTTP 402 with a PAYMENT-REQUIRED header (base64 JSON) and a JSON body listing accepts[]: scheme exact, network eip155:8453, asset USDC, amount 10000 (atomic, 6 decimals), payTo 0x509E55c4F898B43C856Fb0E51048B1b7bFBC9eDE. An x402 client signs an EIP-3009 USDC authorization and retries with a PAYMENT-SIGNATURE header; you pay only when the call succeeds. ## 3. Pay and verify from JavaScript // npm i @x402/fetch @x402/evm viem (Node 18+, ESM: save as pay-check.mjs) import { wrapFetchWithPaymentFromConfig, decodePaymentResponseHeader } from "@x402/fetch"; import { ExactEvmScheme } from "@x402/evm"; import { privateKeyToAccount } from "viem/accounts"; const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY); // a Base wallet holding a little USDC const payFetch = wrapFetchWithPaymentFromConfig(fetch, { schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }], // Base mainnet only }); // Verify any Base USDC payment by tx hash (yours or someone else's). export async function paycheck(txHash, expectedPayTo, expectedAmount) { const r = await payFetch("https://www.kertivo.com/check", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ txHash, expectedPayTo, expectedAmount }), }); if (!r.ok) throw new Error("kertivo-check " + r.status + ": " + (await r.text())); return r.json(); // { settlement, funding, summary, receipt, ... } } // After paying ANOTHER x402 seller: take the tx hash from its PAYMENT-RESPONSE header and verify it. export async function verifyLastPayment(sellerResponse, expectedPayTo, expectedAmount) { const h = sellerResponse.headers.get("PAYMENT-RESPONSE") || sellerResponse.headers.get("X-PAYMENT-RESPONSE"); if (!h) throw new Error("no payment response header (seller did not settle, or the call was free)"); const settle = decodePaymentResponseHeader(h); // { success, transaction, network, payer } if (!settle.success || !settle.transaction) throw new Error("seller reported settlement failure"); return paycheck(settle.transaction, expectedPayTo, expectedAmount); } // Example: did my $0.01 payment to seller 0xSeller actually settle, and who funded the payer? // const out = await paycheck("0x", "0xSellerPayTo", "0.01"); // console.log(out.summary, out.settlement.verdict, out.funding.verdict); // If the retry still returns 402 with an error like "invalid_payload ... execution reverted" or "insufficient_funds", // the wallet doesn't hold enough USDC on Base mainnet (nothing was charged). ## 4. Other paid routes (same client, just change URL and body) - POST https://www.kertivo.com/address/check ($0.001): {"from":"0xYou","to":"0xRecipient"} before sending USDC (address-poisoning check, recent window); add "contacts":["0xFriend",...] (up to 50) to also catch lookalikes of your address book offline - POST https://www.kertivo.com/address/check/full ($0.005): same body, full transfer history - POST https://www.kertivo.com/trust/lookup ($0.002): {"url":"https://seller.example/route"} Kertivo Index grade, evidence and claims - POST https://www.kertivo.com/check/deep ($0.05): same body as /check, full funding history + recheckable sha256 receipt ## 5. Paid but got nothing back? (free) curl -s -X POST https://www.kertivo.com/claim -H 'content-type: application/json' -d '{"endpoint":"https://seller.example/route","txHash":"0x<64 hex>","httpStatusAfterPay":500}' Returns a public, hash-recheckable claim (payment verified on Base, recipient matched against the Kertivo Index). Evidence only: no custody, no arbitration. ## 6. Verify a signed response (free) Every 200 JSON from /check, /check/deep, /address/check, /address/check/full, paid /trust/lookup, a JSON claim record (GET /claim/:id) and the free /demo carries `attestation`: an EIP-191 signature by our response-signing key (not our payTo; it holds no funds). Keep the whole body and anyone can later prove what our check saw. Signer and rules: https://www.kertivo.com/.well-known/attestation // npm i viem (Node 18+, ESM: save as verify-kertivo.mjs). Free: no wallet, no payment. import { createHash } from "node:crypto"; import { recoverMessageAddress } from "viem"; // Same canonical JSON we sign: object keys sorted recursively, no whitespace. const canonical = (v) => Array.isArray(v) ? "[" + v.map(canonical).join(",") + "]" : v && typeof v === "object" ? "{" + Object.keys(v).sort().map((k) => JSON.stringify(k) + ":" + canonical(v[k])).join(",") + "}" : JSON.stringify(v ?? null); // Check a saved Kertivo response body (the whole JSON, as received). Returns { ok, signer, reasons }. // publishedSigner: pass the signer you pinned earlier; if omitted it is read from https://www.kertivo.com/.well-known/attestation export async function verifyKertivo(body, publishedSigner) { const a = body && body.attestation; if (!a || typeof a !== "object") return { ok: false, reasons: ["no attestation"] }; if (!publishedSigner) publishedSigner = (await (await fetch("https://www.kertivo.com/.well-known/attestation")).json()).signer; const reasons = []; const { attestation, ...rest } = body; const sha256 = "0x" + createHash("sha256").update(canonical(rest)).digest("hex"); if (sha256 !== a.sha256) reasons.push("body hash mismatch (body was changed after signing)"); const message = ["x402-paycheck signed response v1", "origin: " + a.origin, "route: " + a.route, "issuedAt: " + a.issuedAt, "sha256: " + a.sha256, "statement: " + a.statement].join("\n"); if (message !== a.message) reasons.push("message does not match attestation fields"); let signer; try { signer = await recoverMessageAddress({ message: a.message, signature: a.signature }); } catch { reasons.push("bad signature"); } if (signer && (!publishedSigner || signer.toLowerCase() !== String(publishedSigner).toLowerCase())) reasons.push("not signed by the published Kertivo key"); return { ok: reasons.length === 0, signer, reasons }; } // Example, $0: const body = await (await fetch("https://www.kertivo.com/demo", { method: "POST" })).json(); // console.log(await verifyKertivo(body)); // { ok: true, signer: "0x...", reasons: [] } // Edit any field of body and run it again: ok becomes false with "body hash mismatch". More: https://www.kertivo.com/llms.txt and https://www.kertivo.com/openapi.json