---
name: lumi-api
description: Pay-per-call reads from Lumi Trades, paid in XRP or RLUSD or USDC on the XRP Ledger, or USDC on Base, or USDC on Stellar via x402. Lumi Trust (should I pay this x402 URL?, vendor reports); market data (daily market brief, BTC + macro backdrop, yen carry-trade watch, Fear & Greed sentiment, funding, events calendar, daily policy brief); and Lumi Market Ideas (signals for 51 top coins, scan, lite reads, support/resistance levels, paper-trade ideas and history; Paper-trade analysis, not investment advice. Lumi does not trade real money.).
---

# Lumi API (agent guide)

Base URL: https://api.lumitrades.io
Network: `xrpl:0` (mainnet). Assets: XRP or RLUSD (Ripple USD, issuer `rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De`, currency `524C555344000000000000000000000000000000`) or USDC (Circle USDC, issuer `rGm7WCVp9gb4jZHWTEtGUr4dd74z2XuWhE`, currency `5553444300000000000000000000000000000000`). Pay to: `rM8kmmnz5qPMg7V3r7SHYXgDGvm8PhzLvM`.
Also: `eip155:8453` (Base). Asset: USDC `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`. Pay to: `0x5E617bac19912e6d212CB303ae84655E9E203532`. Facilitator: https://api.cdp.coinbase.com/platform/v2/x402 (Coinbase CDP; fallback https://facilitator.payai.network).
Also: `stellar:pubnet` (Stellar). Asset: USDC `CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75` (Circle, 7 decimals). Pay to: `GCKTH26VNR4H4WLCXS5DX6563XRQQXWP5QKQWFD6CHOGTZJ2N5GKBU2L`. Facilitator: https://channels.openzeppelin.com/x402 (OpenZeppelin, fees sponsored).
Protocol: [x402](https://xrpl.org/docs/agents/agentic-payments-x402) v2, `exact` scheme (one presigned XRPL Payment per call, or one EIP-3009 USDC authorization on Base, or one signed USDC transfer on Stellar).
No API key, no account, no sign-up for the per-call endpoints (the optional trust data feed uses keys, see below).
A2A agent card: https://api.lumitrades.io/.well-known/agent-card.json. MCP: https://api.lumitrades.io/mcp.

## What you can buy

Three groups: **Lumi Trust**, **market data** and **Lumi Market Ideas** (OpenAPI tags `trust`, `market data`, `Lumi Market Ideas`).

### Lumi Trust (x402 pay-checks and vendor reports)

| Endpoint | Price | What you get |
|---|---|---|
| `GET /v1/trust/check` | 0.004 USDC on Base or 0.0032 XRP (3200 drops) or 0.004 RLUSD or 0.004 USDC or 0.004 USDC on Stellar | Use this before your agent pays an unfamiliar x402 URL: allow / warn / block with coded reasons, from Lumi's daily CDP Bazaar index, free 402-quote checks and Lumi's own test-buys (Base USDC for Bazaar listings; XRPL only for a small curated vendor list, counted separately). Query: url (required), payTo and amount (optional: the payTo address and USD price you were quoted, to catch address or price mismatches). block = a concrete mismatch or a paid call that returned nothing; warn = not enough evidence; allow = listing and recent checks agree. Links the free lookup and, for curated vendors, the $0.02 full report. Observations, not a guarantee. Not charged on bad input. |
| `GET /v1/trust/vendors/{id}/report` | 0.02 USDC on Base or 0.0157 XRP (15700 drops) or 0.02 RLUSD or 0.02 USDC or 0.02 USDC on Stellar | Use this when you need evidence before paying an x402 vendor on Lumi's trust list: the full observation history. Every free 402-quote check (90-day retention) with status, quoted price and latency; every paid test-buy with amount, ledger tx hash, explorer link and delivered yes/no; latency stats (min/p50/p90/p95/max) and the 7-day trend; uptime % over 24h/7d/30d/90d and a daily series; per-endpoint quoted-price history; verified agent reports; vendor replies; score components, sample sizes and confidence labels. Automated observations, not a rating. Not sold (503, not charged) when the vendor has no observation in the last 48 h. The free summary is GET /v1/trust/vendors/{id}. |

### Market data

| Endpoint | Price | What you get |
|---|---|---|
| `GET /v1/market` | 0.02 USDC on Base or 0.0157 XRP (15700 drops) or 0.02 RLUSD or 0.02 USDC or 0.02 USDC on Stellar | Use this when you need the whole crypto backdrop in one call: BTC levels/scenario levels/signal, macro (USD/JPY note, total cap, dominance, Fear & Greed, BTC perp funding/OI) and breadth across tracked coins. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/brief` | 0.02 USDC on Base or 0.0157 XRP (15700 drops) or 0.02 RLUSD or 0.02 USDC or 0.02 USDC on Stellar | Use this when you need a ready-to-read daily crypto brief (the same read as Lumi's newsletter): BTC price/levels/signal, Fear & Greed, funding, top/bottom movers, key levels, yen watch, events in the next 48h, open paper trades and sourced news. JSON with a ready-to-use `markdown` field; add ?format=md (or Accept: text/markdown) for text/markdown. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/macro/yen` | 0.01 USDC on Base or 0.0079 XRP (7900 drops) or 0.01 RLUSD or 0.01 USDC or 0.01 USDC on Stellar | Use this when you need to check whether yen carry-trade stress could spill into crypto: USD/JPY and Nikkei 225 (price, 1d/5d change, ranges), a rule-based carry-trade risk level (low/watch/elevated) around the 155 USD/JPY line with reasons, BTC context, upcoming Japan events (BOJ, CPI) and sources. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/sentiment` | 0.005 USDC on Base or 0.004 XRP (4000 drops) or 0.005 RLUSD or 0.005 USDC or 0.005 USDC on Stellar | Use this when you need a quick read of crowd positioning: Fear & Greed (value, band, day-over-day change), BTC perp funding rate with a plain-English read + open interest, total market cap/volume/dominance, and breadth/signal-label counts across tracked coins. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/events` | 0.005 USDC on Base or 0.004 XRP (4000 drops) or 0.005 RLUSD or 0.005 USDC or 0.005 USDC on Stellar | Use this when you need to know what scheduled events could move crypto soon: timed releases/decisions (UTC + US Central, with why it matters) and dated per-coin catalysts (Fed, BOJ, CPI, network upgrades, conferences). Every row is sourced. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/funding` | 0.003 USDC on Base or 0.0024 XRP (2400 drops) or 0.003 RLUSD or 0.003 USDC or 0.003 USDC on Stellar | Use this when you need derivatives positioning: the current perpetual funding rate for every top-50 coin listed on OKX (rate, 8h-normalised, annualised, next funding time, plain-English read), BTC read, median and the most positive/negative. Refreshed every 15 minutes; positive = longs pay shorts. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/fear-greed` | 0.003 USDC on Base or 0.0024 XRP (2400 drops) or 0.003 RLUSD or 0.003 USDC or 0.003 USDC on Stellar | Use this when you need crypto sentiment: the CoinMarketCap Fear & Greed index now, yesterday, last week and last month, the change vs each, yearly high/low and a 30-day daily series. Refreshed every 30 minutes. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/events/next48h` | 0.003 USDC on Base or 0.0024 XRP (2400 drops) or 0.003 RLUSD or 0.003 USDC or 0.003 USDC on Stellar | Use this when you need to know what could move crypto in the next 48 hours: scheduled macro releases and decisions with exact UTC and US Central times, why each matters and its source, plus dated per-coin catalysts. An empty window is a valid answer. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/institutional` | 0.1 USDC on Base or 0.0782 XRP (78200 drops) or 0.1 RLUSD or 0.1 USDC or 0.1 USDC on Stellar | Use this when you need a sourced daily summary of what central banks, standard-setters, regulators, banks and tokenization firms are doing in digital assets and stablecoins, and which tracked networks each move touches. Every item has a source URL, date, why-it-matters, impact and confidence, and a link_type that separates confirmed links from indirect-alignment analysis. Also: big picture, per-coin map, cross-item connections, no-news notes and dated what-to-watch. JSON with a `markdown` field; ?format=md returns text/markdown. Neutral and apolitical; not financial advice. Not charged on bad input or stale data (every response carries as_of and stale_after). |

### Lumi Market Ideas

Paper-trade analysis, not investment advice. Lumi does not trade real money. Responses also carry `product_line: "Lumi Market Ideas"`, `notice` and the `X-Lumi-Product-Line` / `X-Lumi-Notice` headers.

| Endpoint | Price | What you get |
|---|---|---|
| `GET /v1/signal/{coin}` | 0.01 USDC on Base or 0.0079 XRP (7900 drops) or 0.01 RLUSD or 0.01 USDC or 0.01 USDC on Stellar | Lumi Market Ideas: Paper-trade analysis, not investment advice. Lumi does not trade real money. Use this when you need a fast, mechanical buy/sell read on one coin before acting: overall label + score, per-timeframe (1h/4h/1d) labels and an indicator summary (RSI, MACD, EMA stack, Bollinger, StochRSI, volume). 50 top coins (GET /v1/coins), computed live from public exchange candles; 1h refreshed about every 13 minutes. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/levels/{coin}` | 0.01 USDC on Base or 0.0079 XRP (7900 drops) or 0.01 RLUSD or 0.01 USDC or 0.01 USDC on Stellar | Lumi Market Ideas: Paper-trade analysis, not investment advice. Lumi does not trade real money. Use this when you need to know where price may stall or turn for one coin: support/resistance levels, bias, rule-based 7-day bull/base/bear scenario levels (not forecasts), commentary, catalysts and cited sources. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/scan` | 0.02 USDC on Base or 0.0157 XRP (15700 drops) or 0.02 RLUSD or 0.02 USDC or 0.02 USDC on Stellar | Lumi Market Ideas: Paper-trade analysis, not investment advice. Lumi does not trade real money. Use this when you need to screen the whole market in one call instead of 50: label, score, 1h/4h/1d labels, 1h RSI, price and 24h change for every top-50 coin (GET /v1/coins), strongest first, plus label counts and breadth. Computed live; 1h refreshed about every 13 minutes; freshness fields reflect the oldest coin included. Not sold when fewer than 40 coins are fresh. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/lite/{coin}` | 0.004 USDC on Base or 0.0032 XRP (3200 drops) or 0.004 RLUSD or 0.004 USDC or 0.004 USDC on Stellar | Lumi Market Ideas: Paper-trade analysis, not investment advice. Lumi does not trade real money. Use this when you only need the headline read on one coin, cheaply: overall label and score, 1h/4h/1d labels and price with 24h change. Same live computation as /v1/signal/{coin}, which adds the full indicator breakdown. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/slate` | 0.02 USDC on Base or 0.0157 XRP (15700 drops) or 0.02 RLUSD or 0.02 USDC or 0.02 USDC on Stellar | Lumi Market Ideas: Paper-trade analysis, not investment advice. Lumi does not trade real money. Use this when you need current, concrete trade setups to evaluate (paper trades, not advice): direction, entry/entry zone, stop, targets, leverage, conviction, plus 'no setup' notes. Not charged on bad input or stale data (every response carries as_of and stale_after). |
| `GET /v1/trades` | 0.02 USDC on Base or 0.0157 XRP (15700 drops) or 0.02 RLUSD or 0.02 USDC or 0.02 USDC on Stellar | Lumi Market Ideas: Paper-trade analysis, not investment advice. Lumi does not trade real money. Use this when you need the full list of Lumi's paper-trade ideas (not advice): every idea with entry, stop, targets, leverage and reasoning. Not charged on bad input or stale data (every response carries as_of and stale_after). |

`{coin}` is a ticker or CoinGecko id, case-insensitive.
- `/v1/signal/{coin}` (51 coins, computed live from public exchange candles; 1h/4h refreshed about every 13 minutes, 1d every few hours): HBAR, XLM, XRP, ZBCN, AXL, XDC, BTC, ETH, SOL, DOGE, ADA, LINK, SUI, SHX, BNB, TRX, ZEC, HYPE, XMR, NEAR, BCH, LTC, UNI, AVAX, TAO, SHIB, CRO, QNT, ENA, AAVE, PUMP, WLD, ONDO, DOT, PEPE, MNT, ASTER, SKY, MORPHO, ICP, ARB, ETC, KAS, ALGO, POL, ATOM, RENDER, FIL, INJ, APT, OP.
- `/v1/levels/{coin}` (Lumi's watchlist and majors): HBAR, XLM, XRP, ZBCN, AXL, XDC, BTC, ETH, SOL, DOGE, ADA, LINK, SUI, SHX.
Unknown coins (or levels for an uncovered coin) return 404 **before** any payment is requested.

Which one to call (Lumi Trust):
- About to pay some x402 URL? `GET /v1/trust/check?url=<resource>&payTo=<address in its quote>&amount=<USD>` returns `allow` / `warn` / `block`
  with coded reasons (payTo or price mismatch vs the listing, never observed, failed quotes, slow, young domain, paid but no data).
- Checking any x402 seller before you pay it? See the lookup and vendor report below.

Which one to call (market data):
- Need "what's going on in crypto today" in one call? `GET /v1/brief` (add `?format=md` for ready-to-post markdown).
- Macro risk from Japan / the yen carry trade? `GET /v1/macro/yen`.
- Mood/positioning only? `GET /v1/sentiment`. What's scheduled? `GET /v1/events`.
- Just one number, cheaply? `GET /v1/fear-greed` (CoinMarketCap Fear & Greed + history), `GET /v1/funding` (perp funding for the top coins on OKX),
  `GET /v1/events/next48h` (only the next 48 hours).
- What are central banks, regulators and big banks doing, and what does it mean for XRP, XLM, XDC, QNT, AXL, HBAR? `GET /v1/institutional`
  (daily, sourced; each link labelled `confirmed`, `indirect_alignment` (Lumi's analysis) or `context`; `?format=md` for markdown).

Which one to call (Lumi Market Ideas; Paper-trade analysis, not investment advice. Lumi does not trade real money.):
- Screening the whole market? `GET /v1/scan`: all 51 coins' labels, scores, 1h RSI and price in one call (cheaper than 51 signal calls).
- Only the headline for one coin? `GET /v1/lite/{coin}` (label, score, 1h/4h/1d labels, price); `/v1/signal/{coin}` adds every indicator.
- Every past paper-trade idea as a raw list? `GET /v1/trades`. Current ideas only? `GET /v1/slate`.
- One coin? `GET /v1/signal/{coin}` or `/v1/lite/{coin}` (any of 51) + `GET /v1/levels/{coin}` (watchlist and majors).

Lumi Trust lookups:
- Checking any x402 seller before you pay it? `GET /v1/trust/vendors?url=<resource>` (or `?domain=`, `?payTo=`) is free and covers the whole CDP Bazaar catalog (answer-only; tx evidence is in the paid `/v1/trust/check`, $0.004). For curated vendors, `GET /v1/trust/vendors/{id}` is the free summary; `GET /v1/trust/vendors/{id}/report` ($0.02)
  is the full observation history (every quote check and paid test-buy with tx hashes, latency stats and trend, uptime %, verified reports, replies).
  Unknown vendor: 404; no observation in the last 48 h: 503. Both before any 402, never charged.

Fields that are not available at request time are omitted, never filled with guesses.

**Not charged on bad input or stale data.** Unknown coins, bad parameters or unfilled path templates get a 4xx before any quote.
Every paid response carries `as_of` (when the data was produced), `data_age_seconds`, `next_update` (expected next refresh)
and `stale_after`. Past `stale_after` the endpoint answers 503 "Not charged." instead of a quote, and a payment that arrives
after it is refused before settlement.

**You only pay when you get data.** Before a price is even quoted, the API loads and checks every input the endpoint needs
(present, non-empty, recently refreshed). If anything is missing, empty or stale you get `503` with
`{ "error": "stale_data", "data_age_seconds", "retry_after_seconds", "note": "Not charged." }` (`data_unavailable` when an input is missing) and **no 402 quote**, so no payment can be signed or settled. The response body is built *before* the payment is settled and is
served right after a validated `tesSUCCESS`: a settled payment always returns the data you paid for.

Free: `GET /`, `GET /health`, `GET /skill.md`, `GET /v1/coins`, `GET /v1/samples`, `GET /v1/trust/vendors` (`?url=|domain=|payTo=` lookup), `GET /v1/trust/catalog`, `GET /v1/trust/vendors/{id}`, `GET /v1/trust/methodology`, `GET /badge/{key}.svg|.json`, `GET /trust/v/{key}`, `GET|POST /v1/trust/receipts/verify`, `GET /.well-known/x402`, `GET /.well-known/agent-card.json`, `GET /.well-known/mcp/server-card.json`, `GET /.well-known/agents.json`, `GET /openapi.json`, `POST /mcp`, `POST /a2a`.

Every paid response is JSON with `kind`, `timestamp`, `source: "lumitrades.io"`, `as_of`, `data_age_seconds`, `next_update`, `stale_after`, a `payment` block (network, tx hash, explorer link) and a `disclaimer`. All timestamps are ISO 8601 UTC.
lumitrades.io publishes market data, signals and analysis four times a day (6 AM, 12 PM, 6 PM and 11 PM US Central); the policy brief (`/v1/institutional`) once a day (not sold if older than 36 h).

## Free trial: 3 calls per new wallet (Base or XRPL)

Every paid endpoint (including `/v1/institutional`) can be tried for free, 3 calls per wallet, using the
x402 **sign-in-with-x** extension (CAIP-122). Nothing is charged and no funds move. It works with a Base wallet or an XRP Ledger
wallet; it is one trial per wallet (one shared ledger and the same caps for both chains, not 3 per chain).

1. Send the request with header `X-Lumi-Trial: 1`. The 402 then also carries `extensions["sign-in-with-x"]` = `{ info: { domain, uri, version: "1", nonce, issuedAt, expirationTime, statement, resources: [uri] }, supportedChains: [{"chainId":"eip155:8453","type":"eip191"},{"chainId":"xrpl:0","type":"xrpl"}] }`.
   The challenge is one-time, bound to that exact URL and valid for 5 minutes.
2. Sign it with your wallet (proof of control; not a payment):
   - **Base** (`type: "eip191"`): sign the EIP-4361 message for those fields with an EOA on Base (EIP-191 `personal_sign`). The wallet must hold at least 0.01 USDC on Base (checked on-chain, nothing is moved).
   - **XRP Ledger** (`type: "xrpl"`, `chainId: "xrpl:0"`): build the same CAIP-122 message, with first line `<domain> wants you to sign in with your XRP Ledger account:`, your r-address on the second line and `Chain ID: 0`, then sign its UTF-8 bytes with ripple-keypairs: `sign(utf8 hex, privateKey)`. Add `publicKey` (hex) to the payload. The key must be the account's master key (not disabled) or its current RegularKey, and the account must be activated (checked on the validated ledger, nothing is moved). XRP, RLUSD and USDC payers all qualify.
3. Retry the same URL with header `SIGN-IN-WITH-X: base64(JSON{ domain, address, statement, uri, version, chainId, type, nonce, issuedAt, expirationTime, resources, signature[, publicKey] })`.
4. You get the normal body plus `access: { mode: "trial", charged: false, wallet, trial_calls_used, trial_calls_remaining }`. If the trial is not granted
   (used up, 3 per IP per day, bad or reused signature, unfunded wallet) you get the normal 402 with `trial: { granted: false, reason }`: just pay.

Without that header the 402 is a plain x402 payment quote (with a top-level `trial` hint), so normal payers and discovery scanners see prices only.
A `PAYMENT-SIGNATURE` header always takes precedence. REST only (not MCP/A2A).

Base, with the official client:

```ts
import { wrapFetchWithSIWx } from "@x402/extensions/sign-in-with-x";
import { privateKeyToAccount } from "viem/accounts";
const tryFree = wrapFetchWithSIWx(fetch, privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`));
const r = await tryFree("https://api.lumitrades.io/v1/sentiment", { headers: { "X-Lumi-Trial": "1" } }); // 200 with access.mode "trial" (first 3 calls), else the 402 to pay
```

XRP Ledger:

```ts
// npm i xrpl ripple-keypairs   (3 free calls per new wallet; nothing is charged)
import { Wallet } from "xrpl";
import { sign } from "ripple-keypairs";

const wallet = Wallet.fromSeed(process.env.XRPL_SEED);
const url = "https://api.lumitrades.io/v1/sentiment";
const quote = await (await fetch(url, { headers: { "X-Lumi-Trial": "1" } })).json();
const ext = quote.extensions["sign-in-with-x"], i = ext.info;
const chain = ext.supportedChains.find((c) => c.type === "xrpl"); // { chainId: "xrpl:0", type: "xrpl" }
const message = [
  `${i.domain} wants you to sign in with your XRP Ledger account:`, wallet.classicAddress, "", i.statement, "",
  `URI: ${i.uri}`, `Version: ${i.version}`, `Chain ID: ${chain.chainId.split(":")[1]}`, `Nonce: ${i.nonce}`,
  `Issued At: ${i.issuedAt}`, `Expiration Time: ${i.expirationTime}`, "Resources:", ...i.resources.map((r) => `- ${r}`),
].join("\n");
const signature = sign(Buffer.from(message, "utf8").toString("hex").toUpperCase(), wallet.privateKey);
const payload = { ...i, address: wallet.classicAddress, chainId: chain.chainId, type: "xrpl", signature, publicKey: wallet.publicKey };
const res = await fetch(url, { headers: { "SIGN-IN-WITH-X": Buffer.from(JSON.stringify(payload)).toString("base64") } });
const body = await res.json(); // body.access = { mode: "trial", charged: false, trial_calls_remaining, ... }
```

## How payment works

`accepts[]` order: USDC on Base (accepts[0]), XRP (accepts[1]), RLUSD on the XRP Ledger (accepts[2]), USDC on the XRP Ledger (accepts[3]), USDC on Stellar (accepts[4]). Choose by `network` + `asset`, not by position; all options share one invoice that can be paid once.
Most x402 clients (`@x402/fetch` + `@x402/evm`) simply take `accepts[0]`, USDC on Base: see "Paying with USDC" below. The XRPL flow:

1. `GET https://api.lumitrades.io/v1/signal/XRP` with no payment -> **HTTP 402**. The body (and the base64 `PAYMENT-REQUIRED` header) is an x402 v2 `PaymentRequired`:
   `accepts[1] = { scheme: "exact", network: "xrpl:0", asset: "XRP", amount: "<drops>", payTo: "rM8kmmnz5qPMg7V3r7SHYXgDGvm8PhzLvM", maxTimeoutSeconds: 300, extra: { invoiceId, sourceTag: 804681468 } }`
   `accepts[2]` is the same quote in **RLUSD** (Ripple USD): `{ asset: "524C555344000000000000000000000000000000", amount: "<value, e.g. 0.01>", extra: { issuer: "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De", invoiceId, sourceTag, symbol: "RLUSD" } }`.
   `accepts[3]` is the same quote in **USDC** (Circle USDC): `{ asset: "5553444300000000000000000000000000000000", amount: "<value, e.g. 0.01>", extra: { issuer: "rGm7WCVp9gb4jZHWTEtGUr4dd74z2XuWhE", invoiceId, sourceTag, symbol: "USDC" } }`.
   Pick one option and echo it back unchanged as `accepted` (all options share one invoice; it can be paid once). To pay in a stablecoin your wallet needs a
   trust line to that issuer and a balance; the Payment's `Amount` is `{ currency, issuer, value }` and `SendMax` equals `Amount` (no conversion).
2. Sign (do not submit) an XRPL `Payment` for exactly `amount` drops to `payTo`, with `SourceTag`, a `LastLedgerSequence`,
   and the invoice bound via a Memo (`MemoData = hex(utf8(invoiceId))`) and/or `InvoiceID = SHA-256(invoiceId)`.
3. Retry the same request with header `PAYMENT-SIGNATURE: base64(JSON{ x402Version: 2, accepted: <accepts[1]>, payload: { signedTxBlob, invoiceId } })`.
4. The server has the facilitator (https://xrpl-facilitator-mainnet.t54.ai) verify and submit your payment. On a validated `tesSUCCESS` you get **200**
   with the data and a `PAYMENT-RESPONSE` header: base64 `{ success, transaction, network, payer }`.

Quotes expire after 300s and each invoice can be paid once. If you get 402 again, read `error` and pay the fresh quote.
**Idempotent retries:** send `Idempotency-Key: <16-128 chars of A-Z a-z 0-9 _ ->` (or the x402 `payment-identifier` extension id) with your payment.
Retrying with the same key and the same payment returns the cached response (header `Idempotent-Replayed: true`) and never settles again;
the same key with a different payment gets 409 `idempotency_key_reused`, not charged.
503 with `retryable: true, settlementAttempted: false` is safe to retry with the same header. 409 `settlement_status_unknown`: look up your tx on the ledger before paying again.

The x402-xrpl SDKs (T54, the reference XRPL implementation) do all of this for you.

### Paying with USDC on Base (accepts[0])

The same 402 lists `accepts[0]` = `{ scheme: "exact", network: "eip155:8453", asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", amount: "<atomic USDC, 6 decimals: 10000 = 0.01>", payTo: "0x5E617bac19912e6d212CB303ae84655E9E203532", maxTimeoutSeconds: 300, extra: { name: "USD Coin", version: "2", invoiceId, symbol: "USDC", decimals: 6 } }`.
Sign an EIP-3009 `transferWithAuthorization` for exactly `amount` to `payTo` and retry with `PAYMENT-SIGNATURE: base64(JSON{ x402Version: 2, accepted: <that entry, unchanged>, payload: { signature, authorization: { from, to, value, validAfter, validBefore, nonce } } })`.
Lumi's facilitator (Coinbase CDP, fallback https://facilitator.payai.network) verifies it and submits it on Base, paying the gas: your wallet needs USDC only, no ETH. Same rules: one invoice, one payment, never charged when data is stale.
The official x402 client packages (`@x402/fetch` + `@x402/evm`) do this for you; USDC on Base is the first option, so they pick it by default.

### Paying with USDC on Stellar (accepts[4])

The same 402 lists `{ scheme: "exact", network: "stellar:pubnet", asset: "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75", amount: "<atomic USDC, 7 decimals: 100000 = 0.01>", payTo: "GCKTH26VNR4H4WLCXS5DX6563XRQQXWP5QKQWFD6CHOGTZJ2N5GKBU2L", maxTimeoutSeconds: 300, extra: { areFeesSponsored: true, invoiceId, symbol: "USDC", decimals: 7 } }`.
Sign a SEP-41 `transfer(from, payTo, amount)` on the USDC contract (auth entry only) and retry with `PAYMENT-SIGNATURE: base64(JSON{ x402Version: 2, accepted: <that entry, unchanged>, payload: { transaction: "<XDR>" } })`.
Lumi's facilitator (https://channels.openzeppelin.com/x402, OpenZeppelin Channels) verifies it, sponsors the fee and submits it: your account needs a USDC trustline and balance, no extra XLM. Same rules: one invoice, one payment, never charged when data is stale.
`@x402/fetch` + `@x402/stellar` (`createEd25519Signer`, `ExactStellarScheme`, network `stellar:pubnet`) do this for you; register only the Stellar scheme so the client picks this option.


## Example: TypeScript / Node (npm `x402-xrpl`)

```ts
import { Wallet } from "xrpl";
import { x402Fetch, decodePaymentResponseHeader } from "x402-xrpl";

const fetchPaid = x402Fetch({
  wallet: Wallet.fromSeed(process.env.XRPL_SEED!),   // your agent's wallet
  network: "xrpl:0",
  wsUrl: "wss://xrplcluster.com",
  maxValue: "78200",                            // spend cap per call, in drops
});

const res = await fetchPaid("https://api.lumitrades.io/v1/signal/XRP");
console.log(res.status, await res.json());
const receipt = res.headers.get("PAYMENT-RESPONSE");
if (receipt) console.log(decodePaymentResponseHeader(receipt)); // { success, transaction, ... }
```

Pay in a stablecoin instead (wallet must hold it on a trust line to the issuer):

```ts
const STABLE = { RLUSD: { currency: "524C555344000000000000000000000000000000", issuer: "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De" }, USDC: { currency: "5553444300000000000000000000000000000000", issuer: "rGm7WCVp9gb4jZHWTEtGUr4dd74z2XuWhE" } };
const want = STABLE.RLUSD;
const fetchStable = x402Fetch({
  wallet: Wallet.fromSeed(process.env.XRPL_SEED!),
  network: "xrpl:0",
  wsUrl: "wss://xrplcluster.com",
  // pick the stablecoin option and cap it at 0.05 per call
  paymentRequirementsSelector: (accepts) => {
    const r = accepts.find((a: any) => a.asset === want.currency && a.extra?.issuer === want.issuer);
    if (!r || Number(r.amount) > 0.05) throw new Error("no acceptable stablecoin quote");
    return r;
  },
});
```
Note: the SDK's numeric `maxValue` compares raw `amount` strings across assets (drops for XRP, a decimal value for stablecoins), so use a selector like the one above when paying in a stablecoin.


## Example: USDC on Base (npm `@x402/fetch`, `@x402/evm`, `viem`)

```ts
import { wrapFetchWithPayment, x402Client, decodePaymentResponseHeader } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

const client = new x402Client();
registerExactEvmScheme(client, { signer: privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`) });
const fetchPaid = wrapFetchWithPayment(fetch, client);   // picks the eip155:8453 USDC option

const res = await fetchPaid("https://api.lumitrades.io/v1/brief");
console.log(res.status, await res.json());
console.log(decodePaymentResponseHeader(res.headers.get("PAYMENT-RESPONSE")!)); // { success, transaction, network, payer }
```

## Example: Coinbase AgentKit (direct URL, USDC on Base)

AgentKit 0.10.4's `discover_x402_services` currently hides x402 v2 sellers (it reads `metadata.description`, the Bazaar returns `description`),
so give your agent the URL directly. `make_http_request` gets the 402 and keeps the eip155:8453 option; `retry_http_request_with_x402` pays it.

```ts
// npm i @coinbase/agentkit viem
import { AgentKit, ViemWalletProvider, x402ActionProvider } from "@coinbase/agentkit";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";

const walletProvider = new ViemWalletProvider(createWalletClient({
  account: privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`), chain: base, transport: http(),
}));
const agentkit = await AgentKit.from({ walletProvider, actionProviders: [x402ActionProvider()] });
const act = (n: string) => agentkit.getActions().find((a) => a.name.endsWith(n))!;

const url = "https://api.lumitrades.io/v1/signal/XRP";
const quote = JSON.parse(await act("make_http_request").invoke({ url }));        // status: error_402_payment_required
const o = quote.acceptablePaymentOptions.find((x: any) => x.network === "eip155:8453"); // amount "10000" = $0.01
const paid = JSON.parse(await act("retry_http_request_with_x402").invoke({
  url, method: "GET",
  selectedPaymentOption: { scheme: o.scheme, network: o.network, asset: o.asset, amount: o.amount, payTo: o.payTo },
}));
console.log(paid.data, paid.details?.paymentProof);
```
In an LLM agent with AgentKit tools, the prompt "call https://api.lumitrades.io/v1/signal/XRP and pay the Base USDC option" does the same.
Full runnable version: https://github.com/cypher-osiris/lumi-api/blob/main/examples/agentkit-direct-url.ts

## Example: Python (PyPI `x402-xrpl`)

```python
import os
from xrpl.wallet import Wallet
from x402_xrpl.clients import x402_requests, decode_payment_response

session = x402_requests(
    Wallet.from_seed(os.environ["XRPL_SEED"]),
    rpc_url="https://xrplcluster.com/",
    network_filter="xrpl:0",
    scheme_filter="exact",
)
r = session.get("https://api.lumitrades.io/v1/market", timeout=180)
print(r.status_code, r.json()["macro"]["yen_note"])
print(decode_payment_response(r.headers["PAYMENT-RESPONSE"]))
```

## MCP

- **Remote MCP server:** `https://api.lumitrades.io/mcp` (Streamable HTTP, stateless; protocol 2026-07-28 and 2025-03-26 .. 2025-11-25).
  Add it as a remote MCP connector URL. Free tools: `lumi_catalog`, `lumi_coins`, `lumi_samples`, `lumi_quote`, `lumi_vendor_scores`. Paid tools
  return the price and how to pay, since a remote connector has no wallet:
  Lumi Trust `lumi_trust_check`, `lumi_vendor_report`; market data `lumi_brief`, `lumi_market`, `lumi_macro_yen`, `lumi_sentiment`, `lumi_funding`,
  `lumi_fear_greed`, `lumi_events`, `lumi_events_48h`, `lumi_institutional_brief`; Lumi Market Ideas (Paper-trade analysis, not investment advice. Lumi does not trade real money.) `lumi_signal`, `lumi_lite`,
  `lumi_scan`, `lumi_levels`, `lumi_slate`, `lumi_trades`.
- **Local MCP server that pays for you:** `npx -y lumi-trades-mcp` with `XRPL_SEED` (XRP/RLUSD/USDC on the XRP Ledger) and/or
  `EVM_PRIVATE_KEY` (USDC on Base, gasless) set (npm `lumi-trades-mcp`, MCP Registry `io.github.cypher-osiris/lumi-trades-mcp`).
  Same tools; each paid call is an x402 payment from your wallet within a per-call cap (`LUMI_MAX_USD_PER_CALL`, default 0.30, which covers every endpoint) and a session budget.

```json
{ "mcpServers": { "lumi": { "command": "npx", "args": ["-y", "lumi-trades-mcp"], "env": { "EVM_PRIVATE_KEY": "0x...", "LUMI_MAX_USD_PER_CALL": "0.30" } } } }
```

## A2A

- **Agent card:** `https://api.lumitrades.io/.well-known/agent-card.json` (alias `/.well-known/agent.json`), A2A 1.0 and 0.3: `supportedInterfaces` lists both (protocolVersion 1.0 and 0.3), and the top-level 0.3 fields say `protocolVersion: "0.3.0"`, so 0.3-only registries show 0.3.0.
- **JSON-RPC endpoint:** `POST https://api.lumitrades.io/a2a` with `SendMessage` (A2A 1.0) or `message/send` (0.3). Send text such as "list coins", "catalog" or
  "signal XRP", or a data part `{ "skill": "signal", "coin": "XRP" }`. Free skills (`catalog`, `coins`, `quote`, `vendor_trust`) answer inline. Paid skills answer with
  the live x402 terms (every network option) and the REST URL to pay at; nothing is charged over A2A.

## x402 vendor observations (free)

Lumi publishes its automated observations of other x402 sellers on the XRP Ledger: an unpaid 402-quote check of each listed endpoint every 30 minutes,
and small paid test-buys from Lumi's own wallets, each with the amount, ledger tx hash, HTTP status and whether the response contained data.
Test-buys are capped and disclosed: test-buys share a hard cap of about $0.50 a day (0.30 RLUSD + 0.10 XRP for curated XRPL vendors, plus up to 0.15 USDC for Stellar sellers) (not every vendor is bought every day), Base catalog re-buys are capped at $0.10 per day, and no listing priced above $0.05 is bought. The real cadence per vendor is in the coverage figures of /v1/trust/vendors.
Scores are observations with sample sizes, not ratings; few samples are marked low confidence; vendors are listed in allowlist order, not ranked.

- Lookup before you pay: `GET https://api.lumitrades.io/v1/trust/vendors?url=<resource>` | `?domain=` | `?payTo=` (full CDP Bazaar catalog, free quote checks on a rotating schedule = "quote-verified, low confidence"; not every listing is checked yet, the response's `coverage` line says "N resources across M domains indexed, X quote-verified, Y purchase-verified on Base (USDC); separately, Z XRPL endpoints from curated vendors purchase-verified"; on-chain test-buys = "purchase-verified", counted per chain: Base USDC buys of Bazaar listings vs XRPL buys of a small curated list (`purchase_verified_by_chain`); no match = `{observed:false}`, not a negative signal). Free lookups are answer-only (tier, last quote check, test-buy count; no tx hashes) and rate-limited per client; evidence is in the paid `/v1/trust/check` ($0.004) and `/v1/trust/vendors/{id}/report` ($0.02). Browse a teaser (top 100 most-used, 25 per page, 40 requests/day): `GET https://api.lumitrades.io/v1/trust/catalog?page=1`.
- `GET https://api.lumitrades.io/v1/trust/vendors`, `GET https://api.lumitrades.io/v1/trust/vendors/{id}`, method: `GET https://api.lumitrades.io/v1/trust/methodology`. MCP: `lumi_vendor_scores`. A2A: `vendor_trust`.
- **"Tested by Lumi" badge (free, for sellers):** `GET https://api.lumitrades.io/badge/{key}.svg` (and `.json`), key = your Lumi vendor id or the exact host of your x402 service. Shows Lumi's latest stored paid test-buy: delivered (green), not delivered (red), inconclusive (gray, Lumi-side, not counted against you) or not yet tested (neutral), with the date (MM-DD-YYYY, UTC) and chain (XRPL, Base or Stellar). Links to the public result page `https://api.lumitrades.io/trust/v/{key}` (dated tests, how Lumi tests, right to reply via hello@lumitrades.io). Factual test evidence, not an endorsement or guarantee; paying Lumi never changes a result. Cached 1 hour. Embed (Markdown/README): `[![Tested by Lumi](https://api.lumitrades.io/badge/{key}.svg)](https://api.lumitrades.io/trust/v/{key})`; HTML: `<a href="https://api.lumitrades.io/trust/v/{key}"><img src="https://api.lumitrades.io/badge/{key}.svg" alt="Tested by Lumi" height="20"></a>`. Docs: `GET https://api.lumitrades.io/badge`.
- **Paid detailed report ($0.02 per call, x402 on every rail):** `GET https://api.lumitrades.io/v1/trust/vendors/{id}/report`: full observation history (every free quote
  check over 90 days, every paid test-buy with tx hash and delivered yes/no), latency min/p50/p90/p95/max and 7-day trend, uptime % for 24h/7d/30d/90d and a
  daily series, quoted-price history, verified agent reports, vendor replies, sample sizes and confidence labels. Not sold (503, not charged) if Lumi has no
  observation of the vendor in the last 48 h. MCP: `lumi_vendor_report`. A2A: `vendor_report`.
- **Paid pay-check ($0.004 per call):** `GET https://api.lumitrades.io/v1/trust/check?url=<resource>[&payTo=<address>][&amount=<USD>]`: one verdict, `allow`, `warn` or `block`,
  with coded reasons (`pay_to_mismatch`, `price_above_listing`, `paid_no_data`, `not_observed`, `unreliable_quotes`, `slow`, `young_domain` ...), a confidence label,
  and links to the free lookup and (for curated vendors) the $0.02 report. A bad or missing `url` gets 400 before any quote. MCP: `lumi_trust_check`. A2A: `trust_check`.
- **Report a call you paid for:** `POST https://api.lumitrades.io/v1/reports` with `{vendor_id, network, tx_hash, reporter, outcome, signature}`, where `signature` signs
  `lumi-report:<network>:<tx_hash>` with the paying key. Only reports with an on-ledger payment and a valid signature are shown or scored.
- **Private tips:** `POST https://api.lumitrades.io/v1/tips` `{text, coins?, source_urls?}`. Stored privately; never published or returned.
- **Vendors can reply:** sign `lumi-reply:<vendor_id>:<sha256 of text>` with a listed pay-to key and `POST https://api.lumitrades.io/v1/trust/replies`, or email hello@lumitrades.io.

## For x402 sellers: monitoring (opt-in, seller-funded)

Docs: `GET https://api.lumitrades.io/v1/monitor`. Plans, paid via x402 (XRPL XRP/RLUSD/USDC or Base USDC), no auto-renew:
- **Monitor** $9/30 days: a test-buy of 1 endpoint per term (one recheck after a failure) + alerts.
- **Monitor Plus** $29/30 days: weekly test-buys of up to 20 endpoints + alerts.
- **Audit** $99 one-off: a test-buy of every listed endpoint, full report signed by Lumi's XRPL wallet.

Steps: `GET /v1/monitor/challenge?pay_to=<payTo>` → sign it with the payTo wallet (XRPL: ripple-keypairs; Base: EIP-191) → `POST /v1/monitor/enroll` with endpoints and a webhook URL and/or email → pay `GET /v1/monitor/subscribe/{monitor|monitor_plus|audit}?enrollment=<id>`. Each test-buy is capped at $0.1; endpoints priced above it are rejected (the plan would be unprofitable). Alerts: paid but no/invalid data, 5xx/timeouts, payTo change, price change, schema change, recovery. Paying never changes a score; seller-funded tests are labelled; findings can be replied to, not removed; the badge (`/v1/monitor/badge/<id>.svg`) is removed automatically on failure or lapse; corrections: `/v1/monitor/corrections`.

## For facilitators, wallets and frameworks: trust data feed

Docs: `GET https://api.lumitrades.io/v1/feed`; licence: `https://api.lumitrades.io/v1/feed/license` (no resale of raw data, attribution "Trust data: Lumi (lumitrades.io)", vet402 data CC BY).
- Free key: 1000 lookups/month (`GET /v1/feed/challenge?wallet=`, sign, `POST /v1/feed/keys`). Answer-only.
- Metered: $1.5 per 1,000 lookups (`GET /v1/feed/credits?key_id=<id>`, x402).
- **Feed** $99/30 days: 100,000 lookups, full evidence + receipts, delta feed. **Feed Pro** $199/30 days: 500,000 lookups, licensed full snapshot export, status-change webhooks. (`GET /v1/feed/subscribe/{feed|feed_pro}[?key_id=]`.)
- `POST /v1/feed/lookup` {items:[{url}|{payTo}]} with `Authorization: Bearer <key>`; `GET /v1/feed/delta?since=`; `GET /v1/feed/snapshot?cursor=`; `GET /v1/feed/usage`.

## Rules of use

- Analytics and education only. Not financial advice. Trade ideas are hypothetical paper trades for illustration, not orders or signals to copy. Crypto is volatile and leverage can magnify losses. Do your own research.
- Trade ideas are a public paper-trading slate. Do not present them as personalised advice.
- Cite "Lumi Trades (lumitrades.io)" when you surface this data to people.
