Schemas and shapes

The exact shapes the API returns, rendered here from the same generators the routes call. Amounts are decimal strings in base units with the token and its decimals beside them, never floats. Every list is the same paged envelope. Nothing on this page was typed by hand.

The 402 payment challenge

What an unpaid request to a paid endpoint returns. The amount and the v1 maxAmountRequired carry the same value so a client reading either spelling finds it. Built live by lib/b402.ts for the rebalancing agent.

HTTP 402, body
{
  "x402Version": 2,
  "accepts": [
    {
      "scheme": "eip3009",
      "network": "eip155:56",
      "asset": "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d",
      "maxAmountRequired": "20000000000000000",
      "amount": "20000000000000000",
      "payTo": "0xcd10D44703D6989290E0A8219f345fA0a5BF4c64",
      "resource": "https://muster.zkasuran.dev/api/agent/rebalancing/jobs",
      "description": "Reads a live PancakeSwap v3 pool and reports whether a range is still in range, how far the price is from each bound and whether to hold, widen or recentre.",
      "mimeType": "application/json",
      "maxTimeoutSeconds": 300,
      "extra": {
        "name": "World Liberty Financial USD",
        "version": "1",
        "decimals": 18
      }
    }
  ],
  "error": "PAYMENT-SIGNATURE header is required"
}

The x402 transport envelope

x402 v2 also carries the 402 as a PAYMENT-REQUIRED response header, base64 of the same JSON, so a client that reads the header rather than the body reads the same fields. These are the real headers the route sets.

response headers
{
  "PAYMENT-REQUIRED": "eyJ4NDAyVmVyc2lvbiI6MiwiYWNjZXB0cyI6W3sic2NoZW1lIjoiZWlwMzAwOSIsIm5ldHdvcmsiOiJlaXAxNTU6NTYiLCJhc3NldCI6IjB4OGQwRDAwMEVlNDQ5NDhGQzk4YzlCOThBNEZBNDkyMTQ3NmYwOEIwZCIsIm1heEFtb3VudFJlcXVpcmVkIjoiMjAwMDAwMDAwMDAwMDAwMDAiLCJhbW91bnQiOiIyMDAwMDAwMDAwMDAwMDAwMCIsInBheVRvIjoiMHhjZDEwRDQ0NzAzRDY5ODkyOTBFMEE4MjE5ZjM0NWZBMGE1QkY0YzY0IiwicmVzb3VyY2UiOiJodHRwczovL211c3Rlci56a2FzdXJhbi5kZXYvYXBpL2FnZW50L3JlYmFsYW5jaW5nL2pvYnMiLCJkZXNjcmlwdGlvbiI6IlJlYWRzIGEgbGl2ZSBQYW5jYWtlU3dhcCB2MyBwb29sIGFuZCByZXBvcnRzIHdoZXRoZXIgYSByYW5nZSBpcyBzdGlsbCBpbiByYW5nZSwgaG93IGZhciB0aGUgcHJpY2UgaXMgZnJvbSBlYWNoIGJvdW5kIGFuZCB3aGV0aGVyIHRvIGhvbGQsIHdpZGVuIG9yIHJlY2VudHJlLiIsIm1pbWVUeXBlIjoiYXBwbGljYXRpb24vanNvbiIsIm1heFRpbWVvdXRTZWNvbmRzIjozMDAsImV4dHJhIjp7Im5hbWUiOiJXb3JsZCBMaWJlcnR5IEZpbmFuY2lhbCBVU0QiLCJ2ZXJzaW9uIjoiMSIsImRlY2ltYWxzIjoxOH19XSwiZXJyb3IiOiJQQVlNRU5ULVNJR05BVFVSRSBoZWFkZXIgaXMgcmVxdWlyZWQifQ==",
  "cache-control": "no-store"
}

The preview contract

A GET with ?preview=1 returns this free, so a buyer reads what they are buying before they pay. It carries no computed result. From lib/preview.ts, the same definition the route serves.

GET /api/agent/health-factor?preview=1
{
  "agent": "Venus Health Factor Watch",
  "shelf": "health-factor",
  "summary": "Reads a borrower position on Venus and returns the health factor, the weighted collateral behind it and the uniform price fall that would liquidate it.",
  "price": {
    "base": "20000000000000000",
    "decimals": 18,
    "token": "USD1",
    "asset": "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d",
    "human": "0.02 USD1",
    "scheme": "eip3009",
    "network": "eip155:56"
  },
  "inputs": [
    {
      "name": "account",
      "required": true,
      "description": "the borrower address to inspect",
      "example": "0xed87331DcAe2ed002c42EdD102fEf91bd2BdB0bE"
    }
  ],
  "outputs": [
    "healthFactor, or null when the account carries no borrow",
    "totalCollateralUsd weighted by each market liquidation threshold",
    "per-market rows with the threshold used, so the weighting reconciles",
    "priceDropToLiquidationPct",
    "the block every number was read at"
  ],
  "reads": [
    "Venus Comptroller getAssetsIn and getAccountLiquidity",
    "per vToken exchangeRateStored, balanceOfUnderlying and borrowBalanceStored",
    "the Venus oracle price per underlying"
  ],
  "operator": "Muster, first party. Disclosed on every row that renders.",
  "endpoint": "/api/agent/health-factor"
}

The paged list envelope

Every list under /v1 is returned in one envelope, with pageSize capped at 100. Each item carries a derived firstParty boolean, so the first-party label travels with the data and not only with the page. The wrapper, from lib/v1.ts:

{ items, page, pageSize, total, totalPages }
{
  "items": [],
  "page": 1,
  "pageSize": 50,
  "total": 0,
  "totalPages": 0
}

The per-item schemas are in the OpenAPI document at /v1/openapi.json. Try /v1/listings?hireable=1 for a populated envelope.

The constants file

Every constant a builder needs, machine readable at /constants.json. Generated from the code's own tables, deterministic, so it can be diffed against the published prose. Read decimals() at runtime rather than trusting the table, which the token docs call the source of truth.

GET /constants.json
{
  "$comment": "Machine-readable constants for Muster on BNB Smart Chain. Generated from lib/constants.ts and lib/fee.ts. Every address and decimal was read from chain, see docs/01-GROUND-TRUTH.md. Read decimals() at runtime rather than trusting this table, which the token docs call the source of truth.",
  "license": "LicenseRef-zkasuran-SAND-1.0",
  "chain": {
    "id": 56,
    "caip2": "eip155:56",
    "name": "BNB Smart Chain",
    "testnetId": 97,
    "testnetCaip2": "eip155:97"
  },
  "registry": {
    "identity": "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
    "reputation": "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63",
    "identityCounterSlot": "0xa040f782729de4970518741823ec1276cbcd41a0c7493f62d173341566a04e00"
  },
  "tokens": {
    "U": {
      "address": "0xcE24439F2D9C6a2289F741120FE202248B666666",
      "symbol": "U",
      "decimals": 18,
      "eip3009": true,
      "permit": true,
      "b402": [
        "eip3009",
        "permit2-exact",
        "permit2-upto"
      ]
    },
    "USD1": {
      "address": "0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d",
      "symbol": "USD1",
      "decimals": 18,
      "eip3009": true,
      "permit": true,
      "b402": [
        "eip3009",
        "permit2-exact",
        "permit2-upto"
      ],
      "isDefaultQuote": true
    },
    "USDT": {
      "address": "0x55d398326f99059fF775485246999027B3197955",
      "symbol": "USDT",
      "decimals": 18,
      "eip3009": false,
      "permit": false,
      "b402": [
        "permit2-exact",
        "permit2-upto"
      ]
    },
    "USDC": {
      "address": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
      "symbol": "USDC",
      "decimals": 18,
      "eip3009": false,
      "permit": false,
      "b402": [
        "permit2-exact",
        "permit2-upto"
      ]
    }
  },
  "eip712": {
    "domainTypehash": "0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f",
    "transferWithAuthorizationTypehash": "0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267",
    "receiveWithAuthorizationTypehash": "0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8",
    "domains": {
      "USD1": {
        "name": "World Liberty Financial USD",
        "version": "1"
      },
      "FDUSD": {
        "name": "First Digital USD",
        "version": "1"
      }
    }
  },
  "permit2": {
    "address": "0x000000000022D473030F116dDEE9F6B43aC78BA3",
    "domainSeparatorBsc": "0x4142cc3c823f819c467fa4437d637fe20589a31dfcd1da2ff22292c9ed9344e7",
    "primaryType": "PermitWitnessTransferFrom"
  },
  "b402": {
    "bazaarBase": "https://www.binance.com/bapi/ramp/v1/public/ramp/b402",
    "x402Version": 2,
    "mainnetAccess": "on-request"
  },
  "shelves": [
    "rebalancing",
    "grid-trading",
    "yield",
    "health-factor"
  ],
  "fee": {
    "basisPoints": 200,
    "floorBase": "10000000000000000",
    "minBrokeredBase": "100000000000000000",
    "decimals": 18,
    "deploymentTakesFee": false
  }
}