Developer & Wallet API

Zero-trust, on-chain settlement oracle endpoints for Stellar wallets, routers, aggregators, and DeFi protocols.

1. Pre-Flight Anchor Health Check

Stellar wallets (Vibrant, LOBSTR, Freighter) query this endpoint prior to executing SEP-24 deposits or SEP-31 cross-border remittances. If healthy is false or grade is below C, wallets should warn users or route to an alternate anchor rail.

GET /api/v1/anchors/{domain}/health-check
// Click "Run Live Check" above to test against the live ledger

2. Dynamic SVG Status Badges

Embed auto-updating reliability badges on anchor documentation, GitHub repositories, status pages, or ecosystem directories.

GET /api/v1/badges/{domain}.svg

Live Rendered SVG Badge:

Live Badge

3. Cross-Asset Corridors Matrix (SEP-38 & Path Payments)

Query aggregated cross-border path payment flows, volume metrics, and source-to-destination asset liquidity pairs.

GET /api/v1/corridors
// Click above to fetch cross-asset corridors

3b. Cross-Chain Settlement Evidence (STP)

Per-anchor settlement evidence across Stellar, EVM/CCTP, Tron and Solana, with an evidence tier attached to every figure — PROVEN (read off the Stellar ledger), ATTESTED (a signed cross-chain artifact such as a Circle CCTP burn plus its Iris attestation), or DERIVED (a visible transfer bound to an off-chain proof of a custodial fiat leg). Tiers are never inflated and never blended into a single number. See MULTICHAIN.md for the schema, and /cross-chain.html for the rendered view.

GET /api/v1/cross-chain.json

Regenerated by scripts/cross-chain-scan.ts on the scheduled scan (cron asks hourly; GitHub runs it best-effort, measured median ~2.8h). Each anchor carries its tier mix, a per-chain state (observed / empty / unresolved / failed — kept distinct, because "no address curated for this anchor on Base" is not the same claim as "it settled nothing there"), per-asset volumes, and sample STP attestations with a reproducible SHA-256 digest.

3c. Anchor Records, History and Attribution

Three static artifacts, regenerated by the same scheduled job and served without a database in the path. Between them they answer the questions a due-diligence process actually asks: is this account really this business's, what has it done, and what did it look like ninety days ago.

GET /api/v1/anchors/{domain}/profile.json

One anchor's complete record: every declared account with its current state, how that attribution was established, per-account observation history, and the caveats that apply. Rendered for humans at /anchor/{domain}.

GET /api/v1/history.json

The network time series — one observation per scan of how many tracked accounts were settling, slow, dark, or without payment history. Points are appended, never rewritten. A scan that failed verification leaves a gap rather than an interpolated value, because a missing hour is honest and an invented one is not.

GET /api/v1/verify.json

Which on-chain accounts each anchor declares as its own, and on what basis. Attribution comes from the anchor's own SEP-1 stellar.toml; an account cited there only as a currency issuer is included solely when that issuer account's own on-chain home_domain confirms the claim. Every scan is additionally checked for an account claimed by two anchors before it can be published — a check added after a shared stablecoin issuer was found credited to a single named business.

3d. Evidence — check the figures instead of trusting them

Every scan commits the exact per-account records its published numbers derive from to a Merkle root, and emits an inclusion proof per account. You can recompute a leaf from the response you were served and confirm it reaches the root — so the question stops being "do you believe Landfall's score" and becomes "does the arithmetic hold".

GET /api/v1/evidence.json

The dataset root for the current scan, the record count, and the methodology id — a hash over every parameter that changes the numbers without the ledger changing (record cap, dust floor, refund window and tolerance, liveness thresholds). When that id moves, the method moved, so you can tell "the anchor changed" from "they changed how they measure".

GET /api/v1/evidence/{account}.json

One account's record, its index, and the sibling path proving it sits under the dataset root. Verification is four steps and needs only SHA-256 — canonicalise the record per RFC 8785, hash the leaf as SHA-256(0x00 || namespace || 0x00 || bytes), walk the path with SHA-256(0x01 || left || right), compare to the root. The steps ship in the response.

What this proves, and what it does not. It proves the record you were served is the one committed in that scan, and that the dataset is reproducible. It does not, by itself, prove tamper-evidence: the root travels in the same document it authenticates, so whoever serves that document could recompute both. A root becomes evidence against its own publisher only once it is committed somewhere the publisher cannot rewrite — the Stellar ledger, or a third party's retained copy. Every artifact says so in those words, because publishing a root while implying more than it carries would be worse than publishing none.

3e. Declared vs Observed Capability, and Routing

A stellar.toml saying TRANSFER_SERVER_SEP0024 = "https://…" is a claim that an anchor supports hosted deposit and withdrawal. It is not a claim that the endpoint answers. Directories publish the claim; wallets integrate against the claim; nothing in the ecosystem checks it, and the claim can have been false for a year.

GET /api/v1/capabilities.json

Every SEP endpoint each tracked anchor declares, and whether it answered when asked — with latency and the reason for any failure. One unauthenticated GET per declared endpoint, to the public /info path each SEP defines. SEP-10 is judged on answering a bare challenge request at all, since HTTP 400 is the correct response there and scoring it as a failure would punish correct behaviour.

GET /api/v1/routes.json

The question a wallet actually has: "my user is in Nigeria and wants naira — who can do that, and which of them is working right now?" Joins three things that previously had to be joined by the caller: the currencies an anchor declares, whether its endpoints answered, and whether it has settled on-chain lately. Routes are ordered by evidence — endpoints answering plus a recent settlement outranks endpoints answering plus a long silence, which outranks a declared corridor with a dead API. Each route carries that evidence inline, so a consumer who disagrees with the ordering can impose its own.

These are observations, not endorsements. Nothing here says an anchor is solvent, licensed, honest, or safe to use — only that its endpoints answered a probe and its accounts moved value on a public ledger. A failed probe means an endpoint did not answer correctly from one vantage point at one moment; geo-blocking, rate limiting and a deploy in progress all look identical from here. A single probe is an observation, not a verdict on an operator.

3f. Drift, Corridors and the Audit Trail

GET /api/v1/toml-drift.json

A stellar.toml is mutable and unsigned. An anchor can rotate its declared accounts, repoint a transfer server, or change the issuer behind a currency code at any moment — and every wallet reading SEP-1 will follow, because following is the point of the standard. Nothing else records that it changed. This keeps a snapshot per anchor and reports the difference, with a Horizon lookup on any newly declared account so an account with years of history and one created yesterday can be told apart. A diff cannot distinguish a routine rotation from a hostile one and never claims to; severity reflects what a change could cost someone who follows it.

GET /api/v1/asset-health.json

Settlement per asset instead of per anchor. An anchor can be settling hourly in one currency and dark in another, and a single per-anchor verdict hides it — zeam.money is currently settling in ZARZ and GBPZ while USDZ, EURZ and USDC are dark. Counts, volumes and counterparty spread are measured; per-asset liveness is inferred from the account carrying the asset, because the scan timestamps activity per account rather than per asset, and every liveness object says so in its basis field.

GET /api/v1/scan-verification.json

The result of every pre-publication check, written whether it passed or failed, naming the rule that tripped and the accounts involved. Blocked runs matter most: the site keeps serving the previous figures, so without this the only public evidence that anything happened is an hour of unchanged numbers. Withholding an update is itself a claim about the data, and it should be as inspectable as the data would have been.

3g. Recipient Confirmation

A DERIVED-tier transfer (Tron, Solana) is a real on-chain event whose fiat leg — a bank deposit, a mobile money credit — happens off-chain, invisible to anything here. This is the weakest of three ways to close that gap, and the only one that needs no anchor cooperation: the person who was supposed to receive the money says whether it arrived. See FIAT_CONFIRMATION.md for what it can and cannot prove — short version: nobody is authenticated, only a recipient's report can ever bind, and each transfer can be confirmed exactly once.

POST /api/v1/fiat-confirmations

Submits a claim: { chain, reference, respondent, outcome, reportedAmount?, note? }. Returns eligible immediately for the two rules that don't depend on the transfer's own timestamp — a sender's own report, or a "not received" outcome, can never bind, and this says so right away rather than accepting the submission silently. The timing rules are checked later, when the next cross-chain scan processes the transfer. 409 if this transfer already has a confirmation — there is no update path, by design.

GET /api/v1/fiat-confirmations/{chain}/{reference}

Reads back what was submitted for one transfer, if anything.

3h. Live FX Quotes

Route Scout's exchange-rate column used to come from a constant typed into the source once and never revisited. This asks each anchor's own SEP-38 quote server for a live indicative price — the same fix already applied to the fee column via SEP-24 /info. Sampled at $100 once an hour: an indicative quote can differ at execution time and can vary with amount, so this is a representative sample, not a firm price.

GET /api/v1/anchor-quotes.json

Per anchor, per corridor: ok with the sampled price when the anchor runs SEP-38 and its own /info lists that corridor; otherwise no-sep38, unreachable, unsupported-corridor, or unsupported-asset, each with a reason. As of this writing, most tracked anchors fall into the first category — SEP-38 adoption among them is genuinely close to zero, which this endpoint states rather than papering over with the catalogue figure and no disclosure. Route Scout falls back to the catalogue spread exactly where this reports anything other than ok, and the card says which one it used.

3i. Trust Check

Counterparty risk signals for an arbitrary Stellar address, computed live from Horizon — no DATABASE_URL required, since this route never touches this project's own Postgres. See /trust-check.html for the rendered form and packages/trust-check/src/analyze.ts for the exact, reproducible arithmetic behind every score. Deliberately no external fraud database, scam list, or reputation feed — none exists that this project can independently verify, and inventing one would be exactly the failure mode Landfall exists to catch in other tools. Every flag is a ledger fact with a stated detail, never presented as an accusation.

GET /api/v1/trust-check?address={G...|txHash}

Accepts a Stellar public key directly, or a transaction hash (resolved to its source account). Returns observed account age — a lower bound only, since Horizon does not retain full history forever — counterparty concentration, a pass-through/fast-forwarding signal, a transparent 0–100 score built from fixed, documented per-flag deductions, a risk level, and a confidence rating that overrides the score when there simply isn't enough observed history to say anything ("unknown" beats a number computed from three payments).

// Click "Run Live Check" above to test against the live ledger

3j. Intent & Route Engine

State the outcome you want — "deliver ₦500,000", or "send $1,000" — and get back every candidate route ranked, plus an executable plan for the winner: the ordered steps, each naming who performs it. Landfall never appears as the actor on a step that moves value, because it holds no keys, no funds and no custody. See VERIFIED_ROUTES.md for why ranking is lexicographic (grade first, then liquidity, then price) rather than a blended score.

POST /api/v1/intent

Body: { from, to, basis: "send"|"receive", amount, midRate, candidates[], sortBy?, minGrade?, requirePricedTerms? }.

What the caller supplies, and what it cannot. Commercial terms — rate spread, fees, payout speed — come from the caller, because they are each anchor's own published figures, not Landfall's; the tracked anchors' current ones are republished at /api/v1/anchor-fees.json and /api/v1/anchor-quotes.json for exactly this. The reliability grade is overwritten from Landfall's ledger scan on every request, whatever the caller sent. A route-ranking API where the ranked party supplies its own score is not a ranking API — that asymmetry is the point of the endpoint. midRate is required rather than defaulted: Landfall carries no FX feed and will not invent one.

3k. Fraud Reports

Lets someone who was affected file a report about an address — and lets the reported party respond. This is the module Landfall spent a long time deliberately not faking: Trust Check ships with a line saying no external fraud feed is used "because none exist here that this project can independently verify." This is that feed, built so the promise still holds.

A report is a claim, never a finding. Every report must cite a transaction hash, and Landfall verifies against the ledger that the transaction exists and involves the reported address before storing anything — a report failing that check is rejected outright, not kept at low weight. What Landfall verifies is the transaction; what it cannot verify, and never claims to, is what the parties agreed. Report volume is never scored, ranked, or blended into any Landfall figure, and there is no "confirmed" status in the schema because nothing here could establish one. The reported party's response travels in the same payload as the accusation — see DISPUTES.md.

POST /api/v1/fraud-reports

Body: { subject, evidenceTxHash, category, note, reporterAddress? }. Categories: did_not_receive, wrong_amount, impersonation, unauthorized_debit, other. Returns 400 if the cited transaction does not exist or does not involve the subject; 409 if you have already filed on that transaction.

GET /api/v1/fraud-reports/{subject}

Reports filed about one address, with the disclaimer attached to the payload rather than left to the client to remember. Withdrawn and rejected reports are excluded. Zero reports is reported as absence of evidence, explicitly not as a clean record.

3i. Trend History

Every scan since 12 August has appended a per-account observation, and until now nothing read it back. This does: state changes actually observed, how long each account has been in its current state, and whether its payment counters are still rising.

GET /api/v1/trends.json

Per account: current run (state, observations, days, and everChanged — which separates "dark for 26 days" from "dark for as long as anyone has been looking"), every observed transition with its date, and a volume direction derived from the delta in cumulative counters rather than their size.

It does not forecast. Across every account and every stored scan, zero transitions into the dark state have been observed — every dark account was already dark when first seen. A "this anchor is about to go dark" warning needs examples of anchors going dark, and this dataset contains none. What is reported is what happened.

4. AI Agent Integration (Model Context Protocol / MCP)

Empower AI agents (Claude, Cursor, Antigravity) to reason about live Stellar settlement health using the official MCP server.

Add Landfall to your Claude Desktop or Cursor configuration:

{
  "mcpServers": {
    "landfall": {
      "command": "node",
      "args": ["scripts/mcp/server.mjs"],
      "env": {
        "LANDFALL_API": "https://landfall-chi.vercel.app"
      }
    }
  }
}