Docs / Guides
Getting started for agents
The buyer SDK wraps x402’s fetch. Before paying it verifies the seller’s signed offer, reads the service’s on-chain state, stake and terms, and applies your policy. After paying it verifies the receipt against what you actually sent and received, evaluates every predicate locally, and files a claim in one call.
1. Install
npm install @lopw/buyer-sdk # access for approved integratorsYour agent needs a Stellar key (S...) with USDC on the same network as the contracts it talks to. The same key pays via x402 and signs claim transactions.
2. Create a buyer with a policy
The policy is what your agent insists on before it pays. If any check fails, the x402 payment is aborted before money moves. Here: at least 100 USDC of stake, the service must offer P6 (oracle deviation), and a single receipt must be able to pay out at least 1 USDC.
import { LopwBuyer, FileReceiptStore } from "@lopw/buyer-sdk";
const agent = new LopwBuyer({
secret: process.env.AGENT_SECRET, // Stellar secret key; pays via x402 and files claims
policy: { minStake: 100n * 10_000_000n, requiredPredicates: [6], minMaxClaim: 10_000_000n },
store: new FileReceiptStore("./receipts.json"), // receipts survive restarts until their claim windows close
});BuyerPolicy field | Type | Meaning |
|---|---|---|
minStake | bigint | Refuse services whose stake is below this (asset base units) |
requiredPredicates | number[] | Refuse unless the warranty covers all of these predicate IDs |
requireWarranty | boolean | Refuse sellers that offer no LOPW warranty at all. Default true |
minMaxClaim | bigint | Refuse offers whose single-receipt cover (max_claim) is below this |
LopwBuyerConfig field | Meaning |
|---|---|
secret | Stellar secret key that pays and files claims (required) |
binding | A StellarBinding; defaults to testnet. Use new StellarBinding(getNetwork()) to follow LOPW_NETWORK |
policy | The BuyerPolicy above |
store | Where receipts are kept: FileReceiptStore(path) or MemoryReceiptStore (default). Any ReceiptStore with put, list, hasPaidOffer works |
fetch | A custom fetch implementation, if needed |
3. Fetch a paid resource
const { response, body, warranty } = await agent.fetch("https://fx.example.com/v1/quote?pair=EUR-USD");
// response: the HTTP Response body: Buffer warranty: WarrantyRecord | undefinedBefore paying, the SDK decodes the offer from extensions.lopw and refuses to pay, throwing WarrantyRefused through x402’s abort hook, when:
- the offer names a registry other than the binding’s (offer names an unknown registry)
valid_untilhas passed (offer expired)- the store already holds a receipt for this
offer_nonce(offer already paid) - the offer’s
priceorassetdiffer from the x402 payment requirements (offer does not match the payment requested) - the service is not
Active(service is Suspended, and so on) offer.terms_hashis not the service’s current terms- stake or
max_claimare below the policy minimums - the offer signature does not verify against the key at
offer.key_version - the terms lack a required predicate (warranty lacks predicate P6)
After paying, it reads the receipt from PAYMENT-RESPONSE and checks the signature, that the receipt cites this offer, names your address as buyer and the settlement transaction as payment, and that request_hash, response_hash, response_bytes and status match what was sent and received. Then it evaluates every predicate in the terms, reading the oracle for P6.
WarrantyRecord field | Meaning |
|---|---|
offer, receipt | The signed offer and receipt, decoded |
duplicate | A second receipt for the same payment, if the seller sent one (P1 evidence) |
terms, service | The terms paid under and the service’s state at payment time |
problems | Receipt checks that failed. Non-empty means the response is untrusted and not claimable |
breaches | Predicate IDs the receipt proves were breached |
claimableUntil | Unix seconds after which no claim is possible: served_at + claim_window |
4. Claim when a promise is broken
if (warranty?.breaches.length) {
const result = await agent.claim(warranty); // uses the first breach unless you pass a predicate ID
// result: { outcome: { paid: 50000000n } | { rejected: "not_proven" | "not_verifiable" }, transaction: "…" }
}A proven breach pays your agent from the seller’s stake in the same transaction: min(max_claim, stake left). The SDK waits one ledger if needed so the claim is never rejected for landing in a ledger closed before served_at. Contract errors read as names (WindowClosed, AlreadyClaimed, SlashRateLimited) rather than numbers.
What your agent gets
A paid call either matches what the seller promised, or produces evidence that pays out automatically. A service whose stake falls below its minimum is suspended, and agents with a policy stop paying it at once.
Next: tune the policy, file a claim, or read the predicates your agent evaluates.