openapi: 3.1.0
info:
  title: InsumerAPI
  version: "1.0"
  description: >
    The Insumer Model is read-first blockchain verification infrastructure that returns ECDSA-signed, privacy-preserving booleans across 33 chains — without exposing wallet balances or requiring trust in the API provider.
    InsumerAPI is the programmatic interface for The Insumer Model — used by developers, AI agents, and platforms alike. One endpoint checks token balances, NFT ownership, and EAS attestations across 33 blockchains, returns ECDSA P-256 signed booleans, and never exposes wallet balances.
    Supports up to 10 conditions per request, each with its own chainId. Optional Merkle storage proofs (EIP-1186) for trustless verification.
    Pre-configured compliance templates for Coinbase Verifications (KYC, country, Coinbase One) on Base.
    Not a loyalty program. Not a reputation network. Not an identity system. Not a DeFi protocol. Not a payments processor. Not an oracle network.
    Used by AsterPay KYA (ERC-8183 agent trust scoring), Revettr (counterparty risk for x402), and SettlementWitness (pre/post transaction verification).
    4 agent SDKs: MCP server (npx -y mcp-server-insumer), LangChain (pip install langchain-insumer), ElizaOS (eliza-plugin-insumer), OpenAI GPT (GPT Store).
    Attestations independently verifiable with insumer-verify (npm install insumer-verify).
    Full guide: https://insumermodel.com/ai-agent-verification-api/
    Visual endpoint map: https://insumermodel.com/workbench/
  contact:
    email: contact@insumermodel.com
    url: https://insumermodel.com/developers/
  termsOfService: https://insumermodel.com/terms-of-service/
  license:
    name: Proprietary

servers:
  - url: https://api.insumermodel.com
    description: Production
  # Legacy endpoint removed — use api.insumermodel.com exclusively

security:
  - ApiKeyAuth: []

x-auth-note: "All endpoints require X-API-Key header. A 401 response with {ok: false, error: {code: 401, message: 'Invalid or missing API key.'}} is returned for any request with an invalid or missing key."

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: "API key in format: insr_live_ followed by 40 hex characters"

  schemas:
    ChainId:
      oneOf:
        - type: integer
          enum: [1, 56, 8453, 43114, 137, 42161, 10, 88888, 1868, 98866, 146, 100, 5000, 534352, 59144, 324, 81457, 167000, 2020, 42220, 1284, 1285, 88, 204, 480, 130, 57073, 1329, 80094, 33139]
        - type: string
          enum: ["solana", "xrpl", "bitcoin"]
      description: >
        EVM chain ID (integer), "solana" (string), "xrpl" (string), or "bitcoin" (string).
        Supported chains: Ethereum (1), BNB Chain (56), Base (8453), Avalanche (43114),
        Polygon (137), Arbitrum (42161), Optimism (10), Chiliz (88888), Soneium (1868),
        Plume (98866), Sonic (146), Gnosis (100), Mantle (5000), Scroll (534352),
        Linea (59144), zkSync Era (324), Blast (81457), Taiko (167000), Ronin (2020),
        Celo (42220), Moonbeam (1284), Moonriver (1285), Viction (88), opBNB (204),
        World Chain (480), Unichain (130), Ink (57073), Sei (1329), Berachain (80094),
        ApeChain (33139), Solana ("solana"), XRP Ledger ("xrpl"), Bitcoin ("bitcoin").

    OnboardingChainId:
      oneOf:
        - type: integer
          enum: [1, 56, 8453, 43114, 137, 42161, 10, 88888, 1868, 98866, 480, 146, 100, 5000, 534352, 59144, 324, 81457, 42220, 1284, 204, 130, 57073, 1329, 80094, 33139]
        - type: string
          enum: ["solana", "xrpl", "bitcoin"]
      description: >
        Chains supported for merchant token/NFT configuration (26 EVM chains + Solana + XRPL + Bitcoin).
        Excludes the 4 Covalent-only chains (Taiko, Ronin, Moonriver, Viction) which are available for verification queries only.
        Bitcoin only supports token_balance with contractAddress "native".

    PaymentChainId:
      oneOf:
        - type: integer
          enum: [1, 8453, 137, 42161, 10, 56, 43114]
        - type: string
          enum: ["solana", "bitcoin"]
      description: "Chains supported for crypto payments. EVM chains and Solana accept USDC and USDT. Bitcoin accepts BTC (converted to USD at market rate, requires 1 confirmation)."

    # Backwards-compatible alias
    UsdcChainId:
      $ref: "#/components/schemas/PaymentChainId"

    SuccessEnvelope:
      type: object
      required: [ok, data, meta]
      properties:
        ok:
          type: boolean
          const: true
        data:
          type: object
        meta:
          $ref: "#/components/schemas/Meta"

    ErrorEnvelope:
      type: object
      required: [ok, error, meta]
      properties:
        ok:
          type: boolean
          const: false
        error:
          type: object
          required: [code, message]
          properties:
            code:
              type: integer
            message:
              type: string
        meta:
          $ref: "#/components/schemas/Meta"

    RpcFailureEnvelope:
      type: object
      description: >
        Returned when one or more upstream data sources are
        unavailable after retries. No attestation is signed, no JWT issued, and no credits
        are charged. This is a retryable error — the caller should retry the same request
        after a short delay (e.g. 2-5 seconds). Do NOT treat this as a permanent verification
        failure.
      required: [ok, error, meta]
      properties:
        ok:
          type: boolean
          const: false
        error:
          type: object
          required: [code, message]
          properties:
            code:
              type: string
              enum: ["rpc_failure"]
            message:
              type: string
              example: "Unable to verify all conditions — data source unavailable after retries"
            failedConditions:
              type: array
              description: "Which data fetches failed. Useful for debugging which chain is down."
              items:
                type: object
                properties:
                  chainId:
                    type: string
                    description: "Chain ID that failed"
                  message:
                    type: string
                    description: "Error message from the failed fetch"
        meta:
          $ref: "#/components/schemas/Meta"

    Meta:
      type: object
      properties:
        version:
          type: string
          example: "1.0"
        timestamp:
          type: string
          format: date-time

    Condition:
      type: object
      required: [type]
      properties:
        type:
          type: string
          enum: [token_balance, nft_ownership, eas_attestation, farcaster_id]
        contractAddress:
          type: string
          description: "Token or NFT contract address (required for token_balance and nft_ownership). For XRPL: use \"native\" for XRP, or the issuer r-address for trust line tokens. For Bitcoin: must be \"native\"."
        chainId:
          $ref: "#/components/schemas/ChainId"
        threshold:
          type: number
          description: "Minimum balance required (for token_balance type). Must be > 0 when proof is merkle (use 0.000001 for prove-any-balance)."
        decimals:
          type: integer
          minimum: 0
          maximum: 77
          description: "Token decimals. Auto-detected from the contract if omitted."
        currency:
          type: string
          description: "XRPL currency code (required for XRPL trust line tokens, ignored for other chains). 3-char standard code (e.g. \"USD\") or token name (e.g. \"RLUSD\", auto hex-encoded to 40 chars)."
          example: "RLUSD"
        taxon:
          type: integer
          description: "XRPL NFToken taxon filter (optional, for nft_ownership on XRPL only). Filters NFTs by issuer + taxon."
        label:
          type: string
          maxLength: 100
          description: "Human-readable label for this condition"
        schemaId:
          type: string
          description: "EAS schema ID (bytes32 hex string). Required for eas_attestation unless template is provided."
          example: "0xf8b05c79f090979bf4a80270aba232dff11a10d9ca55c4f88de95317970f0de9"
        attester:
          type: string
          description: "Expected attester address (optional, for eas_attestation). If provided, only attestations from this address are accepted."
        indexer:
          type: string
          description: "EAS indexer contract address (optional, for eas_attestation). Used for efficient attestation lookup by wallet+schema."
        template:
          type: string
          enum: [coinbase_verified_account, coinbase_verified_country, coinbase_one, gitcoin_passport_score, gitcoin_passport_active]
          description: "Pre-configured compliance template name. Use instead of raw schemaId/attester/indexer. See GET /v1/compliance/templates for available templates."

    EvaluatedCondition:
      type: object
      description: "The exact condition logic that was evaluated, included for tamper-evidence. Callers can recompute conditionHash from this object to verify integrity."
      properties:
        type:
          type: string
          enum: [token_balance, nft_ownership, eas_attestation, farcaster_id]
        chainId:
          $ref: "#/components/schemas/ChainId"
        contractAddress:
          type: string
          description: "Contract address that was queried (token_balance and nft_ownership only). For XRPL: \"native\" or issuer r-address."
        operator:
          type: string
          enum: [gte, gt, valid, decoder, registered]
          description: "Comparison operator used: gte (>=) for token_balance, gt (>) for nft_ownership, valid for eas_attestation, decoder for Gitcoin Passport, registered for farcaster_id"
        threshold:
          type: number
          description: "Threshold value used in comparison (human-readable units for token_balance, 0 for nft_ownership). Not present for eas_attestation."
        decimals:
          type: integer
          nullable: true
          description: "Token decimals (present only for token_balance conditions). May be null for Solana, XRPL, and certain EVM chain conditions in attest when not provided in the request. Auto-detected for 26 of 30 EVM chains. Trust profiles always return a non-null value."
        currency:
          type: string
          description: "Normalized XRPL currency code (40-char hex for non-standard currencies). Present only for XRPL trust line conditions."
        taxon:
          type: integer
          description: "XRPL NFToken taxon filter. Present only for XRPL nft_ownership conditions when taxon was specified."
        schemaId:
          type: string
          description: "EAS schema ID (present only for eas_attestation conditions)"
        attester:
          type: string
          description: "Expected attester address (present only for eas_attestation conditions when attester was specified)"
        decoder:
          type: string
          description: "Decoder contract address (present only for decoder-based eas_attestation conditions like Gitcoin Passport)"

    AttestationResult:
      type: object
      properties:
        condition:
          type: integer
          description: "Index of the condition in the request array"
        label:
          type: string
        type:
          type: string
          enum: [token_balance, nft_ownership, eas_attestation, farcaster_id]
        chainId:
          $ref: "#/components/schemas/ChainId"
        met:
          type: boolean
        evaluatedCondition:
          $ref: "#/components/schemas/EvaluatedCondition"
        conditionHash:
          type: string
          description: "SHA-256 hash of the canonical (sorted-key) JSON of evaluatedCondition, prefixed with 0x. Callers can recompute this to verify the condition was not tampered with."
          example: "0x3a7f1b2c..."
        blockNumber:
          type: string
          description: "Hex block number at which the condition was evaluated. Present for 26 of 30 EVM chains, omitted for Taiko, Ronin, Moonriver, Viction, Solana, and XRPL."
          example: "0x12f4a80"
        blockTimestamp:
          type: string
          format: date-time
          description: "ISO 8601 timestamp of the block at which the condition was evaluated. Present for 26 of 30 EVM chains."
          example: "2026-02-26T12:34:56.000Z"
        ledgerIndex:
          type: integer
          description: "XRPL validated ledger index at which the condition was evaluated. Present only for XRPL conditions."
          example: 102575456
        ledgerHash:
          type: string
          description: "XRPL validated ledger hash for the ledger at which the condition was evaluated. Present only for XRPL conditions."
          example: "BB9023D447285923C36E3DF18EF0FC37A5A6A1113527FB5F6FCE3139AAD72388"
        trustLineState:
          type: object
          description: "Trust line state flags for XRPL trust line token conditions. Present only for non-native XRPL token_balance conditions."
          properties:
            frozen:
              type: boolean
              description: "Whether the trust line is frozen (issuer or holder freeze). A frozen line means the balance is not spendable."
        proof:
          description: "Present only when proof: \"merkle\" is requested"
          oneOf:
            - $ref: "#/components/schemas/MerkleProof"
            - $ref: "#/components/schemas/MerkleProofUnavailable"

    MerkleProof:
      type: object
      description: "EIP-1186 Merkle storage proof for trustless verification against block headers"
      properties:
        available:
          type: boolean
          const: true
        type:
          type: string
          const: "merkle"
        blockNumber:
          type: string
          description: "Hex block number at which the proof was generated"
          example: "0x12a05f200"
        mappingSlot:
          type: integer
          description: "ERC-20 balanceOf mapping slot in contract storage, resolved automatically by the API"
        storageKey:
          type: string
          description: "keccak256(abi.encode(address, uint256(slot))) storage key"
        accountProof:
          type: array
          items:
            type: string
          description: "Merkle proof nodes from state root to account"
        storageProof:
          type: array
          items:
            type: object
            properties:
              key:
                type: string
              value:
                type: string
                description: "Raw token balance (hex, before decimal division)"
              proof:
                type: array
                items:
                  type: string
          description: "Merkle proof nodes from storage root to balance slot"
        storageHash:
          type: string
          description: "Storage root hash of the contract account"

    MerkleProofUnavailable:
      type: object
      description: "Returned when Merkle proof is not available for a condition"
      properties:
        type:
          type: string
          const: "merkle"
        available:
          type: boolean
          const: false
        reason:
          type: string
          description: "Why the proof is unavailable (e.g. unsupported chain, Solana, nft_ownership, zero balance)"

    TrustCheck:
      type: object
      properties:
        label:
          type: string
          description: "Human-readable label (e.g. 'USDC on Ethereum')"
        chainId:
          $ref: "#/components/schemas/ChainId"
        met:
          type: boolean
          description: "Whether the wallet has a non-zero balance"
        evaluatedCondition:
          $ref: "#/components/schemas/EvaluatedCondition"
        conditionHash:
          type: string
          description: "SHA-256 of canonical evaluatedCondition JSON, 0x-prefixed"
        blockNumber:
          type: string
          description: "Hex block number (26 of 30 EVM chains)"
        blockTimestamp:
          type: string
          format: date-time
          description: "ISO 8601 block timestamp (26 of 30 EVM chains)"
        ledgerIndex:
          type: integer
          description: "XRPL validated ledger index (XRPL only)"
        ledgerHash:
          type: string
          description: "XRPL validated ledger hash (XRPL only)"
        trustLineState:
          type: object
          description: "Trust line state flags (XRPL trust line tokens only). Frozen lines fail attestation."
          properties:
            frozen:
              type: boolean
        proof:
          description: "EIP-1186 Merkle storage proof anchored to the block header, enabling trustless verification without re-querying the chain. Present only when proof: 'merkle' is requested."
          oneOf:
            - $ref: "#/components/schemas/MerkleProof"
            - $ref: "#/components/schemas/MerkleProofUnavailable"

    TrustDimension:
      type: object
      properties:
        checks:
          type: array
          items:
            $ref: "#/components/schemas/TrustCheck"
        passCount:
          type: integer
          description: "Number of checks where met is true"
        failCount:
          type: integer
          description: "Number of checks where met is false"
        total:
          type: integer
          description: "Total checks in this dimension"

    Attestation:
      type: object
      properties:
        id:
          type: string
          description: "Format: ATST- followed by 16 uppercase hex characters"
          example: "ATST-A7C3E1B2D4F56789"
        pass:
          type: boolean
          description: "true only when ALL conditions are met"
        results:
          type: array
          items:
            $ref: "#/components/schemas/AttestationResult"
        passCount:
          type: integer
        failCount:
          type: integer
        attestedAt:
          type: string
          format: date-time
        expiresAt:
          type: string
          format: date-time
          description: "Attestation expires after 30 minutes"

    Tier:
      type: object
      required: [name, threshold, discount]
      properties:
        name:
          type: string
          maxLength: 30
        threshold:
          type: number
          exclusiveMinimum: 0
        discount:
          type: integer
          minimum: 1
          maximum: 50

    TokenConfig:
      type: object
      required: [symbol, chainId, contractAddress, tiers]
      properties:
        symbol:
          type: string
          maxLength: 10
        chainId:
          $ref: "#/components/schemas/OnboardingChainId"
        contractAddress:
          type: string
        decimals:
          type: integer
          minimum: 0
          maximum: 18
          default: 18
        currency:
          type: string
          description: "XRPL trust line currency code (e.g. \"USD\", \"RLUSD\"). Required for XRPL trust line tokens."
        tiers:
          type: array
          items:
            $ref: "#/components/schemas/Tier"
          minItems: 1
          maxItems: 4

    NftConfig:
      type: object
      required: [name, contractAddress, chainId, discount]
      properties:
        name:
          type: string
          maxLength: 50
        contractAddress:
          type: string
        chainId:
          $ref: "#/components/schemas/OnboardingChainId"
        taxon:
          type: integer
          description: "XRPL NFToken taxon filter. Optional, for filtering NFTs by collection under the same issuer."
        discount:
          type: integer
          minimum: 1
          maximum: 50

    MerchantSummary:
      type: object
      properties:
        id:
          type: string
        companyName:
          type: string
        website:
          type: string
        location:
          type: string
        verified:
          type: boolean
        tokens:
          type: array
          items:
            type: object
            properties:
              symbol:
                type: string
              maxDiscount:
                type: integer
        nftCollections:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              discount:
                type: integer
        discountMode:
          type: string
          enum: [highest, stack]
        discountCap:
          type: integer

    MerchantDetail:
      type: object
      properties:
        id:
          type: string
        companyName:
          type: string
        website:
          type: string
        location:
          type: string
        verified:
          type: boolean
        verifiedDomain:
          type: string
        discountMode:
          type: string
          enum: [highest, stack, capped]
          description: "Effective discount mode. 'capped' is returned when discountMode is 'stack' and discountCap limits the total."
        discountCap:
          type: integer
        tokens:
          type: array
          items:
            type: object
        ownToken:
          type: object
          nullable: true
        partnerTokens:
          type: array
          items:
            type: object
        nftCollections:
          type: array
          items:
            type: object
        acceptsUsdc:
          type: boolean

    DiscountBreakdown:
      type: object
      properties:
        symbol:
          type: string
        tier:
          type: string
        discount:
          type: integer
        chain:
          type: string
          description: "Chain name (present only in GET /v1/discount/check responses, omitted in /v1/verify, /v1/acp/discount, /v1/ucp/discount)"

    ComplianceTemplate:
      type: object
      properties:
        provider:
          type: string
          description: "KYC provider name"
          example: "Coinbase"
        description:
          type: string
          description: "Human-readable description of what this template verifies"
          example: "Verified account (KYC completed)"
        chainId:
          type: integer
          description: "EVM chain where the attestation lives"
          example: 8453
        chainName:
          type: string
          description: "Human-readable chain name"
          example: "Base"

paths:
  /v1/jwks:
    get:
      operationId: getJwks
      summary: Get JWKS (public key set)
      description: >
        Returns the JSON Web Key Set containing InsumerAPI's ECDSA P-256 public signing key.
        Use this to verify attestation signatures without hardcoding the key.
        No authentication required. Cached for 24 hours.
      security: []
      responses:
        "200":
          description: JWKS document
          headers:
            Cache-Control:
              schema:
                type: string
                example: "public, max-age=86400"
          content:
            application/json:
              schema:
                type: object
                properties:
                  keys:
                    type: array
                    items:
                      type: object
                      properties:
                        kty:
                          type: string
                          example: "EC"
                        crv:
                          type: string
                          example: "P-256"
                        x:
                          type: string
                          description: "Base64url-encoded x coordinate"
                        y:
                          type: string
                          description: "Base64url-encoded y coordinate"
                        use:
                          type: string
                          example: "sig"
                        alg:
                          type: string
                          example: "ES256"
                        kid:
                          type: string
                          example: "insumer-attest-v1"

  /v1/compliance/templates:
    get:
      operationId: listComplianceTemplates
      summary: List compliance templates
      description: >
        Returns pre-configured compliance templates for common KYC/identity providers.
        Templates abstract away raw EAS schema IDs and attester addresses. Use a template
        name in POST /v1/attest conditions instead of specifying schemaId, attester, and
        indexer manually. No authentication required.
      tags: [On-Chain Verification]
      security: []
      responses:
        "200":
          description: Template catalog
          headers:
            Cache-Control:
              schema:
                type: string
                example: "public, max-age=3600"
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      templates:
                        type: object
                        additionalProperties:
                          $ref: "#/components/schemas/ComplianceTemplate"
                  meta:
                    $ref: "#/components/schemas/Meta"
              example:
                ok: true
                data:
                  templates:
                    coinbase_verified_account:
                      provider: "Coinbase"
                      description: "Coinbase Verified Account"
                      chainId: 8453
                      chainName: "Base"
                    coinbase_verified_country:
                      provider: "Coinbase"
                      description: "Coinbase Verified Country"
                      chainId: 8453
                      chainName: "Base"
                    coinbase_one:
                      provider: "Coinbase"
                      description: "Coinbase One Member"
                      chainId: 8453
                      chainName: "Base"
                    gitcoin_passport_score:
                      provider: "Gitcoin"
                      description: "Gitcoin Passport Score (≥20)"
                      chainId: 10
                      chainName: "Optimism"
                    gitcoin_passport_active:
                      provider: "Gitcoin"
                      description: "Gitcoin Passport Active"
                      chainId: 10
                      chainName: "Optimism"
                meta:
                  version: "1.0"
                  timestamp: "2026-02-27T12:00:00.000Z"

  /v1/attest:
    post:
      operationId: createAttestation
      summary: Create on-chain verification
      description: >
        Verify 1-10 on-chain conditions (token balances, NFT ownership, EAS attestations).
        Returns ECDSA-signed booleans, never raw balances. 1 credit standard, 2 with proof="merkle".
        EAS: use a compliance template or raw schemaId.
      tags: [On-Chain Verification]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [conditions]
              properties:
                wallet:
                  type: string
                  description: "EVM wallet address (0x...)"
                solanaWallet:
                  type: string
                  description: "Solana wallet address (base58)"
                xrplWallet:
                  type: string
                  description: "XRPL wallet address (r-address, 25-35 chars)"
                  example: "rN7n3473SaZBCG4dFL83w7p1W9cgPJqKro"
                bitcoinWallet:
                  type: string
                  description: "Bitcoin address (P2PKH, P2SH, bech32, or Taproot). Required if any condition uses chainId \"bitcoin\"."
                  example: "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"
                proof:
                  type: string
                  enum: ["merkle"]
                  description: >
                    Set to "merkle" to include EIP-1186 Merkle storage proofs in results.
                    Proofs are returned for token_balance conditions on 26 of 30 EVM chains.
                    Taiko, Ronin, Moonriver, Viction, Solana, XRPL, Bitcoin,
                    and nft_ownership conditions return proof.available: false. Costs 2 credits instead
                    of 1. Note: proof mode reveals the raw on-chain balance to the caller (standard
                    mode never does).
                format:
                  type: string
                  enum: ["jwt"]
                  description: >
                    Set to "jwt" to include a Wallet Auth by InsumerAPI token (ES256-signed JWT) in the response.
                    The JWT contains the attestation data as standard claims (iss, sub, jti, iat, exp)
                    plus pass, results, conditionHash, blockNumber, and blockTimestamp.
                    Verifiable by any standard JWT library using the JWKS at
                    https://insumermodel.com/.well-known/jwks.json. No additional cost.
                conditions:
                  type: array
                  items:
                    $ref: "#/components/schemas/Condition"
                  minItems: 1
                  maxItems: 10
            examples:
              standard:
                summary: Standard attestation (boolean only)
                value:
                  wallet: "0x1234567890abcdef1234567890abcdef12345678"
                  conditions:
                    - type: token_balance
                      contractAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
                      chainId: 1
                      threshold: 1000
                      decimals: 6
                      label: "USDC >= 1000"
                    - type: nft_ownership
                      contractAddress: "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D"
                      chainId: 1
                      label: "Bored Ape holder"
              eas_template:
                summary: EAS attestation via compliance template
                value:
                  wallet: "0x1234567890abcdef1234567890abcdef12345678"
                  conditions:
                    - type: eas_attestation
                      template: coinbase_verified_account
                      label: "Coinbase KYC verified"
              eas_raw:
                summary: EAS attestation with raw schema ID
                value:
                  wallet: "0x1234567890abcdef1234567890abcdef12345678"
                  conditions:
                    - type: eas_attestation
                      schemaId: "0xf8b05c79f090979bf4a80270aba232dff11a10d9ca55c4f88de95317970f0de9"
                      attester: "0x357458739F90461b99789350868CD7CF330Dd7EE"
                      indexer: "0x2c7eE1E5f416dfF40054c27A62f7B357C4E8619C"
                      chainId: 8453
                      label: "Coinbase Verified Account"
              xrpl:
                summary: XRPL attestation (XRP + RLUSD + USDC)
                value:
                  xrplWallet: "rN7n3473SaZBCG4dFL83w7p1W9cgPJqKro"
                  conditions:
                    - type: token_balance
                      contractAddress: "native"
                      chainId: "xrpl"
                      threshold: 100
                      label: "XRP >= 100"
                    - type: token_balance
                      contractAddress: "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De"
                      chainId: "xrpl"
                      currency: "RLUSD"
                      threshold: 50
                      label: "RLUSD >= 50"
                    - type: token_balance
                      contractAddress: "rGm7WCVp9gb4jZHWTEtGUr4dd74z2XuWhE"
                      chainId: "xrpl"
                      currency: "USDC"
                      threshold: 100
                      label: "USDC on XRPL >= 100"
              jwt_format:
                summary: Attestation with Wallet Auth token
                value:
                  wallet: "0x1234567890abcdef1234567890abcdef12345678"
                  format: "jwt"
                  conditions:
                    - type: token_balance
                      contractAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
                      chainId: 1
                      threshold: 1000
                      decimals: 6
                      label: "USDC >= 1000"
              merkle_proof:
                summary: Attestation with Merkle storage proofs
                value:
                  wallet: "0x1234567890abcdef1234567890abcdef12345678"
                  proof: "merkle"
                  conditions:
                    - type: token_balance
                      contractAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
                      chainId: 1
                      threshold: 1000
                      decimals: 6
                      label: "USDC >= 1000"
      responses:
        "200":
          description: Verification created
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      attestation:
                        $ref: "#/components/schemas/Attestation"
                      sig:
                        type: string
                        description: "ECDSA P-256 signature (base64, 88 chars). Verify with npm install insumer-verify."
                      kid:
                        type: string
                        description: "Key ID identifying which ECDSA key signed this attestation. Fetch the public key from GET /v1/jwks or https://insumermodel.com/.well-known/jwks.json"
                        example: "insumer-attest-v1"
                      jwt:
                        type: string
                        description: "Wallet Auth by InsumerAPI token — ES256 JWT (only present when format: \"jwt\" is requested). Verifiable with any standard JWT library using the JWKS endpoint."
                  meta:
                    type: object
                    properties:
                      creditsRemaining:
                        type: integer
                      creditsCharged:
                        type: integer
                        description: "1 for standard, 2 for proof: merkle"
                      version:
                        type: string
                      timestamp:
                        type: string
                        format: date-time
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "402":
          description: Insufficient verification credits
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "503":
          description: >
            Data source unavailable — one or more RPC/API calls failed after retries.
            No attestation signed, no credits charged. Retryable after a short delay.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RpcFailureEnvelope"

  /v1/trust:
    post:
      operationId: walletTrust
      summary: Wallet trust fact profile
      description: >
        ECDSA-signed wallet trust profile: 36 base checks across 4 dimensions (stablecoins — USDC and USDT, governance, NFTs, staking),
        plus optional Solana USDC and XRPL stablecoin checks (up to 39 total).
        Returns per-check booleans and overall summary. 3 credits standard, 6 with proof="merkle".
      tags: [On-Chain Verification]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [wallet]
              properties:
                wallet:
                  type: string
                  pattern: "^0x[a-fA-F0-9]{40}$"
                  description: "EVM wallet address to profile (required)"
                  example: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
                solanaWallet:
                  type: string
                  description: "Optional Solana wallet address (base58). If provided, adds Solana USDC check."
                xrplWallet:
                  type: string
                  description: "Optional XRPL wallet address (r-address). If provided, adds XRPL stablecoin checks (RLUSD + USDC)."
                bitcoinWallet:
                  type: string
                  description: "Optional Bitcoin address. If provided, adds Bitcoin Holdings dimension (native BTC balance check)."
                proof:
                  type: string
                  enum: ["merkle"]
                  description: "Set to 'merkle' for EIP-1186 Merkle storage proofs on stablecoin/governance checks. 6 credits."
      responses:
        "200":
          description: Trust fact profile generated
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      trust:
                        type: object
                        properties:
                          id:
                            type: string
                            description: "Trust profile ID (format: TRST-XXXXX)"
                            example: "TRST-A1B2C"
                          wallet:
                            type: string
                          conditionSetVersion:
                            type: string
                            description: "Version of the curated condition set"
                            example: "v1"
                          dimensions:
                            type: object
                            description: "Checks organized by dimension"
                            properties:
                              stablecoins:
                                $ref: "#/components/schemas/TrustDimension"
                              governance:
                                $ref: "#/components/schemas/TrustDimension"
                              nfts:
                                $ref: "#/components/schemas/TrustDimension"
                              staking:
                                $ref: "#/components/schemas/TrustDimension"
                              solana:
                                description: "Present only when solanaWallet is provided"
                                $ref: "#/components/schemas/TrustDimension"
                              xrpl:
                                description: "Present only when xrplWallet is provided"
                                $ref: "#/components/schemas/TrustDimension"
                          summary:
                            type: object
                            properties:
                              totalChecks:
                                type: integer
                              totalPassed:
                                type: integer
                              totalFailed:
                                type: integer
                              dimensionsWithActivity:
                                type: integer
                              dimensionsChecked:
                                type: integer
                          profiledAt:
                            type: string
                            format: date-time
                          expiresAt:
                            type: string
                            format: date-time
                      sig:
                        type: string
                        description: "ECDSA P-256 signature over the trust object (base64 P1363)"
                      kid:
                        type: string
                        description: "Key ID for JWKS lookup"
                        example: "insumer-attest-v1"
                  meta:
                    type: object
                    properties:
                      creditsRemaining:
                        type: integer
                      creditsCharged:
                        type: integer
                        description: "3 for standard, 6 for proof: merkle"
                      version:
                        type: string
                      timestamp:
                        type: string
              example:
                ok: true
                data:
                  trust:
                    id: "TRST-A1B2C"
                    wallet: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
                    conditionSetVersion: "v1"
                    dimensions:
                      stablecoins:
                        checks:
                          - label: "USDC on Ethereum"
                            chainId: 1
                            met: true
                            evaluatedCondition:
                              type: "token_balance"
                              chainId: 1
                              contractAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
                              operator: "gt"
                              threshold: 0
                              decimals: 6
                            conditionHash: "0x3a7f..."
                            blockNumber: "0x12f4a80"
                            blockTimestamp: "2026-02-26T12:34:56.000Z"
                        passCount: 3
                        failCount: 4
                        total: 7
                      governance:
                        checks: []
                        passCount: 2
                        failCount: 2
                        total: 4
                      nfts:
                        checks: []
                        passCount: 0
                        failCount: 3
                        total: 3
                      staking:
                        checks: []
                        passCount: 1
                        failCount: 2
                        total: 3
                    summary:
                      totalChecks: 17
                      totalPassed: 6
                      totalFailed: 11
                      dimensionsWithActivity: 3
                      dimensionsChecked: 4
                    profiledAt: "2026-02-26T12:34:57.000Z"
                    expiresAt: "2026-02-26T13:04:57.000Z"
                  sig: "MEYCIQDx...base64..."
                  kid: "insumer-attest-v1"
                meta:
                  creditsRemaining: 47
                  creditsCharged: 3
                  version: "1.0"
                  timestamp: "2026-02-26T12:34:57.000Z"
        "400":
          description: Bad request (missing wallet)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "402":
          description: Insufficient credits
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "503":
          description: >
            Data source unavailable — one or more RPC/API calls failed after retries.
            No trust profile signed, no credits charged. Retryable after a short delay.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RpcFailureEnvelope"

  /v1/trust/batch:
    post:
      operationId: batchWalletTrust
      summary: Batch wallet trust profiles
      description: >
        Batch trust profiles for up to 10 wallets. 5-8x faster than sequential calls.
        Each wallet gets an independent ECDSA-signed profile. Partial success supported.
        3 credits/wallet standard, 6 with proof="merkle".
      tags: [On-Chain Verification]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [wallets]
              properties:
                wallets:
                  type: array
                  minItems: 1
                  maxItems: 10
                  description: "1-10 wallet entries to profile"
                  items:
                    type: object
                    required: [wallet]
                    properties:
                      wallet:
                        type: string
                        pattern: "^0x[a-fA-F0-9]{40}$"
                        description: "EVM wallet address (required)"
                      solanaWallet:
                        type: string
                        description: "Solana wallet address (base58). Adds Solana USDC check for this wallet."
                      xrplWallet:
                        type: string
                        description: "XRPL wallet address (r-address). Adds XRPL stablecoin checks (RLUSD + USDC) for this wallet."
                      bitcoinWallet:
                        type: string
                        description: "Bitcoin address. Adds Bitcoin Holdings dimension (native BTC balance check) for this wallet."
                proof:
                  type: string
                  enum: ["merkle"]
                  description: "Set to 'merkle' for EIP-1186 Merkle storage proofs on all wallets. 6 credits/wallet."
            example:
              wallets:
                - wallet: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
                - wallet: "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"
                  solanaWallet: "5v9CXTpN3WHbM2jAYty88qDGz7P4yuMv8fnuPRXBPmiB"
      responses:
        "200":
          description: Batch trust profiles (may include partial failures)
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      results:
                        type: array
                        description: "One entry per wallet — either a trust profile or an error"
                        items:
                          oneOf:
                            - type: object
                              properties:
                                trust:
                                  type: object
                                  description: "Full trust profile (same schema as POST /v1/trust)"
                                  properties:
                                    id:
                                      type: string
                                    wallet:
                                      type: string
                                    conditionSetVersion:
                                      type: string
                                    dimensions:
                                      type: object
                                    summary:
                                      type: object
                                    profiledAt:
                                      type: string
                                    expiresAt:
                                      type: string
                                sig:
                                  type: string
                                  description: "ECDSA P-256 signature over this wallet's trust object"
                                kid:
                                  type: string
                            - type: object
                              properties:
                                error:
                                  type: object
                                  properties:
                                    wallet:
                                      type: string
                                    message:
                                      type: string
                      summary:
                        type: object
                        properties:
                          requested:
                            type: integer
                          succeeded:
                            type: integer
                          failed:
                            type: integer
                  meta:
                    type: object
                    properties:
                      creditsCharged:
                        type: integer
                        description: "Total credits charged (successCount × creditsPerWallet)"
                      creditsRemaining:
                        type: integer
                      version:
                        type: string
                      timestamp:
                        type: string
              example:
                ok: true
                data:
                  results:
                    - trust:
                        id: "TRST-A1B2C"
                        wallet: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
                        conditionSetVersion: "v1"
                        dimensions:
                          stablecoins: { passCount: 3, failCount: 4, total: 7 }
                          governance: { passCount: 2, failCount: 2, total: 4 }
                          nfts: { passCount: 0, failCount: 3, total: 3 }
                          staking: { passCount: 1, failCount: 2, total: 3 }
                        summary: { totalChecks: 26, totalPassed: 6, totalFailed: 20, dimensionsWithActivity: 3, dimensionsChecked: 4 }
                        profiledAt: "2026-02-26T12:34:57.000Z"
                        expiresAt: "2026-02-26T13:04:57.000Z"
                      sig: "MEYCIQDx...base64..."
                      kid: "insumer-attest-v1"
                    - trust:
                        id: "TRST-D4E5F"
                        wallet: "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"
                        conditionSetVersion: "v1"
                        dimensions:
                          stablecoins: { passCount: 1, failCount: 6, total: 7 }
                          governance: { passCount: 0, failCount: 4, total: 4 }
                          nfts: { passCount: 0, failCount: 3, total: 3 }
                          staking: { passCount: 0, failCount: 3, total: 3 }
                          solana: { passCount: 1, failCount: 0, total: 1 }
                        summary: { totalChecks: 18, totalPassed: 2, totalFailed: 16, dimensionsWithActivity: 2, dimensionsChecked: 5 }
                      sig: "MEUCIQC...base64..."
                      kid: "insumer-attest-v1"
                  summary: { requested: 2, succeeded: 2, failed: 0 }
                meta: { creditsCharged: 6, creditsRemaining: 44, version: "1.0" }
        "400":
          description: Bad request (invalid wallets array)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "402":
          description: Insufficient credits for entire batch
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/credits:
    get:
      operationId: getCredits
      summary: Check verification credit balance
      description: "Returns the verification credit balance, tier, and daily rate limit for the authenticated API key."
      tags: [On-Chain Verification]
      responses:
        "200":
          description: Credit balance
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      apiKeyCredits:
                        type: integer
                      tier:
                        type: string
                        enum: [free, pro, enterprise]
                      dailyLimit:
                        type: integer
                  meta:
                    $ref: "#/components/schemas/Meta"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/keys/buy:
    post:
      operationId: buyKey
      summary: Buy a new API key with USDC, USDT, or BTC (no auth required)
      description: >
        Agent-friendly key purchase: send USDC, USDT, or BTC to the platform wallet, then call this endpoint
        with the transaction hash to receive a new API key with credits. No email or prior
        authentication needed — the sender wallet address from the transaction becomes the key's
        identity. One key per wallet address. Use POST /v1/credits/buy to top up an existing key.
        Volume discounts: $5–$99 = 25 credits/$1 ($0.04/call),
        $100–$499 = 33 credits/$1 ($0.03/call, 25% off), $500+ = 50 credits/$1 ($0.02/call, 50% off).
        USDC and USDT accepted on EVM chains and Solana (auto-detected from transaction).
        BTC accepted on Bitcoin (converted to USD at market rate, requires 1 confirmation).
        Crypto sent on unsupported chains cannot be recovered. All purchases are final and non-refundable.
      tags: [Agent Onboarding]
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [txHash, chainId, appName]
              properties:
                txHash:
                  type: string
                  description: "Transaction hash proving payment"
                chainId:
                  $ref: "#/components/schemas/PaymentChainId"
                amount:
                  type: number
                  minimum: 5
                  description: "Stablecoin amount sent (min 5). Not required for BTC — USD value is derived from the on-chain BTC amount at market rate."
                appName:
                  type: string
                  maxLength: 100
                  description: "Name for this API key (e.g. your agent or app name)"
      responses:
        "200":
          description: API key created with credits
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      success:
                        type: boolean
                      key:
                        type: string
                        description: "Your new API key (store securely — shown only once)"
                      name:
                        type: string
                      tier:
                        type: string
                      dailyLimit:
                        type: integer
                      creditsAdded:
                        type: integer
                      totalCredits:
                        type: integer
                      usdcPaid:
                        type: string
                      effectiveRate:
                        type: string
                      chainName:
                        type: string
                      registeredWallet:
                        type: string
                        description: "Wallet address registered to this key (extracted from transaction)"
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "409":
          description: Key already exists for this wallet, or transaction already used
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "422":
          description: On-chain verification failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/credits/buy:
    post:
      operationId: buyCredits
      summary: Buy verification credits with USDC, USDT, or BTC
      description: >
        Purchase verification credits by sending USDC, USDT, or BTC to the platform wallet and providing
        the transaction hash. Volume discounts: $5–$99 = 25 credits/$1 ($0.04/call),
        $100–$499 = 33 credits/$1 ($0.03/call, 25% off), $500+ = 50 credits/$1 ($0.02/call, 50% off).
        USDC and USDT accepted on EVM chains and Solana (auto-detected from transaction).
        BTC accepted on Bitcoin (converted to USD at market rate, requires 1 confirmation).
        Crypto sent on unsupported chains cannot be recovered. All purchases are final and non-refundable.
        Sender verification: the first purchase registers the sender wallet address to the API key.
        Subsequent purchases must come from the same sender wallet.
        To change the registered wallet, include `"updateWallet": true` — the verified transfer
        from the new address proves ownership.
      tags: [On-Chain Verification]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [txHash, chainId]
              properties:
                txHash:
                  type: string
                  description: "Transaction hash proving payment"
                chainId:
                  $ref: "#/components/schemas/PaymentChainId"
                amount:
                  type: number
                  minimum: 5
                  description: "Stablecoin amount sent (min 5). Not required for BTC."
                updateWallet:
                  type: boolean
                  default: false
                  description: "Set true to replace the registered sender wallet with this transaction's sender"
      responses:
        "200":
          description: Credits purchased
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      creditsAdded:
                        type: integer
                      totalCredits:
                        type: integer
                      usdcPaid:
                        type: string
                      chainName:
                        type: string
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "409":
          description: Transaction already used
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "422":
          description: On-chain verification failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/merchants:
    get:
      operationId: listMerchants
      summary: List merchants in public directory
      description: "Browse merchants with optional filters. Returns public directory listings."
      tags: [Discovery]
      security: []
      parameters:
        - name: token
          in: query
          schema:
            type: string
          description: "Filter by accepted token symbol (e.g. UNI)"
        - name: verified
          in: query
          schema:
            type: string
            enum: ["true", "false"]
          description: "Filter by domain verification status"
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            maximum: 200
          description: "Results per page"
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
          description: "Pagination offset"
      responses:
        "200":
          description: Merchant list
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/MerchantSummary"
                  meta:
                    type: object
                    properties:
                      total:
                        type: integer
                      limit:
                        type: integer
                      offset:
                        type: integer
                      version:
                        type: string
                      timestamp:
                        type: string
                        format: date-time
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

    post:
      operationId: createMerchant
      summary: Create a new merchant
      description: >
        Create a merchant programmatically. Receives 100 free verification credits.
        The API key that creates the merchant owns it. Max 10 merchants per API key.
      tags: [Agent Onboarding]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [companyName, companyId]
              properties:
                companyName:
                  type: string
                  maxLength: 100
                  description: "Display name"
                companyId:
                  type: string
                  minLength: 2
                  maxLength: 50
                  pattern: "^[a-zA-Z0-9_-]+$"
                  description: "Unique ID (alphanumeric, dash, underscore)"
                location:
                  type: string
                  maxLength: 200
                  description: "City or region"
            example:
              companyName: "Acme Coffee"
              companyId: "ACME-COFFEE"
              location: "New York, NY"
      responses:
        "201":
          description: Merchant created
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      companyName:
                        type: string
                      credits:
                        type: integer
                        example: 100
                      apiAccessEnabled:
                        type: boolean
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "409":
          description: Merchant ID already exists
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: Max 10 merchants per API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/merchants/{id}:
    get:
      operationId: getMerchant
      summary: Get merchant details
      description: "Returns full public merchant profile including token tiers and NFT collections."
      tags: [Discovery]
      security: []
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Merchant ID"
      responses:
        "200":
          description: Merchant details
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    $ref: "#/components/schemas/MerchantDetail"
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Invalid merchant ID format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/tokens:
    get:
      operationId: listTokens
      summary: List registered tokens and NFTs
      description: "Returns all tokens and NFT collections in the registry, with optional filters."
      tags: [Discovery]
      security: []
      parameters:
        - name: chain
          in: query
          schema:
            $ref: "#/components/schemas/ChainId"
          description: "Filter by chain ID"
        - name: symbol
          in: query
          schema:
            type: string
          description: "Filter by token symbol"
        - name: type
          in: query
          schema:
            type: string
            enum: [token, nft]
          description: "Filter by asset type"
      responses:
        "200":
          description: Token and NFT registry
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      tokens:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            symbol:
                              type: string
                            name:
                              type: string
                            chainId:
                              $ref: "#/components/schemas/ChainId"
                            chainName:
                              type: string
                            contractAddress:
                              type: string
                            decimals:
                              type: integer
                            logo:
                              type: string
                      nfts:
                        type: array
                        items:
                          type: object
                          properties:
                            id:
                              type: string
                            name:
                              type: string
                            contractAddress:
                              type: string
                            chainId:
                              $ref: "#/components/schemas/ChainId"
                            chainName:
                              type: string
                            image:
                              type: string
                            standard:
                              type: string
                  meta:
                    type: object
                    properties:
                      tokenCount:
                        type: integer
                      nftCount:
                        type: integer
                      version:
                        type: string
                      timestamp:
                        type: string
                        format: date-time
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/discount/check:
    get:
      operationId: checkDiscount
      summary: Calculate discount for wallet at merchant
      description: >
        Checks on-chain balances server-side and calculates the total discount.
        Free to call — does not consume merchant credits. Returns tier and discount
        per token, but never raw balance amounts.
      tags: [Discount Verification]
      security: []
      parameters:
        - name: wallet
          in: query
          schema:
            type: string
          description: "EVM wallet address (0x...)"
        - name: solanaWallet
          in: query
          schema:
            type: string
          description: "Solana wallet address (base58)"
        - name: xrplWallet
          in: query
          schema:
            type: string
          description: "XRPL wallet address (r-address)"
        - name: merchant
          in: query
          required: true
          schema:
            type: string
          description: "Merchant ID"
      responses:
        "200":
          description: Discount calculation
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      eligible:
                        type: boolean
                      totalDiscount:
                        type: integer
                      discountMode:
                        type: string
                        enum: [highest, stack, capped]
                      breakdown:
                        type: array
                        items:
                          $ref: "#/components/schemas/DiscountBreakdown"
                      merchantId:
                        type: string
                      merchantName:
                        type: string
                      chainsChecked:
                        type: array
                        items:
                          type: string
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Missing or invalid parameters
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/verify:
    post:
      operationId: createVerification
      summary: Create signed discount code
      description: >
        Creates a signed discount verification code (INSR-XXXXX) valid for 30 minutes.
        Consumes 1 merchant credit. If merchant has Stripe Connect, a coupon is auto-created.
      tags: [Discount Verification]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [merchantId]
              properties:
                merchantId:
                  type: string
                wallet:
                  type: string
                  description: "EVM wallet address"
                solanaWallet:
                  type: string
                  description: "Solana wallet address"
                xrplWallet:
                  type: string
                  description: "XRPL wallet address (r-address)"
      responses:
        "200":
          description: Verification code created
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      verified:
                        type: boolean
                      totalDiscount:
                        type: integer
                      code:
                        type: string
                        description: "Format: INSR-XXXXX"
                        example: "INSR-A7K3M"
                      expiresAt:
                        type: string
                        format: date-time
                      breakdown:
                        type: array
                        items:
                          $ref: "#/components/schemas/DiscountBreakdown"
                      stripeCodeCreated:
                        type: boolean
                      cloverDiscountName:
                        type: string
                        nullable: true
                      merchantName:
                        type: string
                      sig:
                        type: string
                        description: "ECDSA P-256 signature"
                      usdcPayment:
                        type: object
                        nullable: true
                        description: "Present when merchant has USDC payments enabled"
                        properties:
                          evmAddress:
                            type: string
                          preferredChainId:
                            type: integer
                          preferredChainName:
                            type: string
                          usdcContract:
                            type: string
                          solanaAddress:
                            type: string
                            nullable: true
                          supportedChains:
                            type: array
                            items:
                              type: object
                              properties:
                                chainId:
                                  type: integer
                                name:
                                  type: string
                                usdcContract:
                                  type: string
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "402":
          description: No merchant credits
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: Merchant has not enabled API access
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "503":
          description: >
            Data source unavailable — one or more RPC/API calls failed after retries.
            No discount code signed, no credits charged. Retryable after a short delay.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RpcFailureEnvelope"

  /v1/payment/confirm:
    post:
      operationId: confirmPayment
      summary: Verify USDC payment for discount code
      description: >
        After /v1/verify, confirm that a USDC payment was made on-chain.
        The server checks the transaction receipt to verify USDC arrived at the merchant address.
      tags: [Discount Verification]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [code, txHash, chainId, amount]
              properties:
                code:
                  type: string
                  description: "Verification code (e.g. INSR-A7K3M)"
                txHash:
                  type: string
                  description: "On-chain transaction hash or Solana signature"
                chainId:
                  $ref: "#/components/schemas/UsdcChainId"
                amount:
                  oneOf:
                    - type: string
                    - type: number
                  description: "USDC amount sent"
      responses:
        "200":
          description: Payment confirmed
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      confirmed:
                        type: boolean
                      code:
                        type: string
                      txHash:
                        type: string
                      chainId:
                        $ref: "#/components/schemas/UsdcChainId"
                      chainName:
                        type: string
                      amountVerified:
                        type: string
                      confirmedAt:
                        type: string
                        format: date-time
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: Merchant USDC payments not enabled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Code or merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "409":
          description: Payment already confirmed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "410":
          description: Code expired
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "422":
          description: On-chain verification failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/merchants/{id}/tokens:
    put:
      operationId: updateMerchantTokens
      summary: Configure merchant token tiers
      description: >
        Set up the merchant's own token and/or partner tokens with discount tiers.
        Max 8 tokens total (own + partner). Must be called by the API key that created the merchant.
      tags: [Agent Onboarding]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Merchant ID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ownToken:
                  oneOf:
                    - $ref: "#/components/schemas/TokenConfig"
                    - type: "null"
                  description: "Merchant's own token config, or null to disable"
                partnerTokens:
                  type: array
                  items:
                    $ref: "#/components/schemas/TokenConfig"
                  description: "Partner token configs"
            example:
              ownToken:
                symbol: "ACME"
                chainId: 8453
                contractAddress: "0x1234567890abcdef1234567890abcdef12345678"
                decimals: 18
                tiers:
                  - name: "Bronze"
                    threshold: 100
                    discount: 5
                  - name: "Silver"
                    threshold: 1000
                    discount: 10
                  - name: "Gold"
                    threshold: 10000
                    discount: 15
              partnerTokens: []
      responses:
        "200":
          description: Tokens configured
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      ownToken:
                        type: object
                        nullable: true
                        properties:
                          symbol:
                            type: string
                          tiersConfigured:
                            type: integer
                      partnerTokens:
                        type: array
                        items:
                          type: object
                      totalTokens:
                        type: integer
                      maxTokens:
                        type: integer
                        example: 8
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: API key does not own this merchant
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/merchants/{id}/nfts:
    put:
      operationId: updateMerchantNfts
      summary: Configure NFT collections
      description: >
        Set NFT collections that grant discounts. Max 4 collections.
        Must be called by the API key that created the merchant.
      tags: [Agent Onboarding]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Merchant ID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [nftCollections]
              properties:
                nftCollections:
                  type: array
                  items:
                    $ref: "#/components/schemas/NftConfig"
                  maxItems: 4
            example:
              nftCollections:
                - name: "Acme Founders"
                  contractAddress: "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D"
                  chainId: 1
                  discount: 15
      responses:
        "200":
          description: NFTs configured
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      nftCollections:
                        type: array
                        items:
                          type: object
                          properties:
                            name:
                              type: string
                            discount:
                              type: integer
                            chainId:
                              $ref: "#/components/schemas/ChainId"
                      totalCollections:
                        type: integer
                      maxCollections:
                        type: integer
                        example: 4
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: API key does not own this merchant
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/merchants/{id}/settings:
    put:
      operationId: updateMerchantSettings
      summary: Update merchant settings
      description: >
        Update discount stacking mode, cap, and USDC payment settings.
        All fields optional. Must be called by the API key that created the merchant.
      tags: [Agent Onboarding]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Merchant ID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                discountMode:
                  type: string
                  enum: [highest, stack]
                  description: "'highest' uses best single discount, 'stack' sums all"
                discountCap:
                  type: integer
                  minimum: 1
                  maximum: 100
                  description: "Maximum total discount percentage"
                usdcPayment:
                  oneOf:
                    - type: object
                      properties:
                        enabled:
                          type: boolean
                        evmAddress:
                          type: string
                          description: "EVM wallet for USDC (0x + 40 hex)"
                        solanaAddress:
                          type: string
                          description: "Solana wallet for USDC (base58)"
                        preferredChainId:
                          $ref: "#/components/schemas/UsdcChainId"
                    - type: "null"
                  description: "USDC payment config, or null to disable"
      responses:
        "200":
          description: Settings updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      discountMode:
                        type: string
                      discountCap:
                        type: integer
                      usdcPayment:
                        type: object
                        nullable: true
                        properties:
                          enabled:
                            type: boolean
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Validation error or no fields provided
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: API key does not own this merchant
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/merchants/{id}/credits:
    post:
      operationId: buyMerchantCredits
      summary: Buy merchant verification credits with USDC
      description: >
        Purchase merchant verification credits (consumed by /v1/verify).
        Volume discounts: $5–$99 USDC = 25 credits/$1 ($0.04/call),
        $100–$499 = 33 credits/$1 ($0.03/call), $500+ = 50 credits/$1 ($0.02/call).
        Send USDC to: EVM 0xAd982CB19aCCa2923Df8F687C0614a7700255a23 or
        Solana 6a1mLjefhvSJX1sEX8PTnionbE9DqoYjU6F6bNkT4Ydr.
        Supported chains: Ethereum, Base, Polygon, Arbitrum, Optimism, BNB Chain, Avalanche, Solana.
        USDC sent on unsupported chains cannot be recovered. All purchases are final and non-refundable.
        Sender verification: the first purchase registers the sender wallet address to the API key.
        Subsequent purchases must come from the same sender wallet.
        To change the registered wallet, include `"updateWallet": true` — the verified USDC transfer
        from the new address proves ownership.
      tags: [Agent Onboarding]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Merchant ID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [txHash, chainId, amount]
              properties:
                txHash:
                  type: string
                chainId:
                  $ref: "#/components/schemas/UsdcChainId"
                amount:
                  type: number
                  minimum: 5
                updateWallet:
                  type: boolean
                  default: false
                  description: "Set true to replace the registered sender wallet with this transaction's sender"
      responses:
        "200":
          description: Credits purchased
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      creditsAdded:
                        type: integer
                      totalCredits:
                        type: integer
                      usdcPaid:
                        type: string
                      chainName:
                        type: string
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: API key does not own this merchant
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "409":
          description: Transaction already used
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "422":
          description: On-chain verification failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/merchants/{id}/directory:
    post:
      operationId: publishToDirectory
      summary: Publish merchant to public directory
      description: >
        Publish (or refresh) the merchant's listing in the public directory.
        No request body needed. Call again after updating tokens/settings.
      tags: [Agent Onboarding]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Merchant ID"
      responses:
        "200":
          description: Published
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      published:
                        type: boolean
                      tokensListed:
                        type: integer
                      nftCollectionsListed:
                        type: integer
                  meta:
                    $ref: "#/components/schemas/Meta"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: API key does not own this merchant
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/merchants/{id}/status:
    get:
      operationId: getMerchantStatus
      summary: Get full private merchant details
      description: >
        Returns full details including credits, token configs, directory status,
        verification status, and USDC settings. Only accessible by the API key that created the merchant.
      tags: [Agent Onboarding]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Merchant ID"
      responses:
        "200":
          description: Full merchant status
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      id:
                        type: string
                      companyName:
                        type: string
                      location:
                        type: string
                      credits:
                        type: integer
                      apiAccessEnabled:
                        type: boolean
                      discountMode:
                        type: string
                        enum: [highest, stack]
                      discountCap:
                        type: integer
                      listedInDirectory:
                        type: boolean
                      ownToken:
                        type: object
                        nullable: true
                      partnerTokens:
                        type: array
                        items:
                          type: object
                      nftCollections:
                        type: array
                        items:
                          type: object
                      usdcPayment:
                        type: object
                        nullable: true
                        properties:
                          enabled:
                            type: boolean
                          evmAddress:
                            type: string
                          solanaAddress:
                            type: string
                            nullable: true
                      verification:
                        type: object
                        description: "Domain verification status (token is never exposed here)"
                        properties:
                          status:
                            type: string
                            enum: [unverified, pending, verified]
                          domain:
                            type: string
                            nullable: true
                          method:
                            type: string
                            nullable: true
                            enum: [dns, meta, file]
                          verifiedAt:
                            type: string
                            format: date-time
                            nullable: true
                      createdAt:
                        type: object
                        description: "Firestore timestamp object"
                        properties:
                          _seconds:
                            type: integer
                          _nanoseconds:
                            type: integer
                  meta:
                    $ref: "#/components/schemas/Meta"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: API key does not own this merchant
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/merchants/{id}/domain-verification:
    post:
      operationId: requestDomainVerification
      summary: Request domain verification token
      description: >
        Generates a verification token for proving domain ownership. The token can be placed
        via DNS TXT record, HTML meta tag, or a verification file. Each POST regenerates the
        token, invalidating any previous one. Only the API key that created the merchant can call this.
      tags: [Agent Onboarding]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Merchant ID"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [domain]
              properties:
                domain:
                  type: string
                  description: "Domain to verify (e.g. example.com). No protocol prefix."
            example:
              domain: "acme-coffee.com"
      responses:
        "200":
          description: Verification token generated
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      domain:
                        type: string
                      token:
                        type: string
                        description: "Verification token (insumer-XXXXXXXXXXXXXXXX)"
                      methods:
                        type: object
                        properties:
                          dns:
                            type: object
                            properties:
                              type:
                                type: string
                                example: "TXT"
                              host:
                                type: string
                              value:
                                type: string
                              instructions:
                                type: string
                          meta:
                            type: object
                            properties:
                              tag:
                                type: string
                              instructions:
                                type: string
                          file:
                            type: object
                            properties:
                              path:
                                type: string
                              content:
                                type: string
                              instructions:
                                type: string
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Invalid domain format or missing domain
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: API key does not own this merchant
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

    put:
      operationId: verifyDomain
      summary: Trigger domain verification check
      description: >
        Checks all three verification methods (DNS TXT, meta tag, file) for the previously
        requested domain. Rate limited to 5 attempts per hour per merchant. If already verified,
        returns the existing verification result. Only the API key that created the merchant can call this.
      tags: [Agent Onboarding]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: "Merchant ID"
      responses:
        "200":
          description: Verification result
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      verified:
                        type: boolean
                      domain:
                        type: string
                      method:
                        type: string
                        enum: [dns, meta, file]
                        description: "Present only when verified is true"
                      verifiedAt:
                        type: string
                        format: date-time
                        description: "Present only when verified is true"
                      message:
                        type: string
                        description: "Present only when verified is false"
                      attemptsRemaining:
                        type: integer
                        description: "Present only when verified is false"
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: No verification pending
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: API key does not own this merchant
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "429":
          description: Rate limit exceeded (5 attempts per hour)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

  /v1/acp/discount:
    post:
      operationId: acpDiscount
      summary: ACP-format discount eligibility check
      description: >
        ACP discount check. Same verification as POST /v1/verify, wrapped in OpenAI/Stripe Agentic Commerce Protocol
        format (coupon objects, allocations, applied/rejected arrays). 1 merchant credit. Source: "acp".
      tags: [Commerce Protocol Integration]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [merchantId]
              properties:
                merchantId:
                  type: string
                  description: "Merchant identifier"
                wallet:
                  type: string
                  description: "EVM wallet address (0x...)"
                solanaWallet:
                  type: string
                  description: "Solana wallet address"
                xrplWallet:
                  type: string
                  description: "XRPL wallet address (r-address)"
                items:
                  type: array
                  description: "Optional line items for per-item cent-amount allocations"
                  items:
                    type: object
                    properties:
                      path:
                        type: string
                        description: "JSONPath reference to the line item"
                        example: "$.line_items[0]"
                      amount:
                        type: integer
                        description: "Item price in cents"
                        example: 2500
      responses:
        "200":
          description: ACP-format discount response
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      protocol:
                        type: string
                        const: "acp"
                      version:
                        type: string
                        example: "2026-01-30"
                      discounts:
                        type: object
                        properties:
                          codes:
                            type: array
                            items:
                              type: string
                          applied:
                            type: array
                            items:
                              type: object
                              properties:
                                id:
                                  type: string
                                code:
                                  type: string
                                coupon:
                                  type: object
                                  properties:
                                    id:
                                      type: string
                                    name:
                                      type: string
                                    percent_off:
                                      type: integer
                                amount:
                                  type: integer
                                  nullable: true
                                automatic:
                                  type: boolean
                                method:
                                  type: string
                                priority:
                                  type: integer
                                allocations:
                                  type: array
                                  items:
                                    type: object
                                    properties:
                                      path:
                                        type: string
                                      amount:
                                        type: integer
                                start:
                                  type: string
                                  format: date-time
                                end:
                                  type: string
                                  format: date-time
                          rejected:
                            type: array
                            items:
                              type: object
                              properties:
                                code:
                                  type: string
                                reason:
                                  type: string
                      verification:
                        type: object
                        properties:
                          code:
                            type: string
                          expiresAt:
                            type: string
                            format: date-time
                          breakdown:
                            type: array
                            items:
                              $ref: "#/components/schemas/DiscountBreakdown"
                          sig:
                            type: string
                          kid:
                            type: string
                  meta:
                    type: object
                    properties:
                      creditsRemaining:
                        type: integer
                      creditsCharged:
                        type: integer
                        description: "Always 1 for ACP discount checks"
                      version:
                        type: string
                      timestamp:
                        type: string
                        format: date-time
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "402":
          description: No merchant credits
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "503":
          description: >
            Data source unavailable — retryable after a short delay.
            No discount signed, no credits charged.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RpcFailureEnvelope"

  /v1/ucp/discount:
    post:
      operationId: ucpDiscount
      summary: UCP-format discount eligibility check
      description: >
        UCP discount check. Same verification as POST /v1/verify, wrapped in Google Universal Commerce Protocol
        format (title, extension field, no start/end dates). 1 merchant credit. Source: "ucp".
      tags: [Commerce Protocol Integration]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [merchantId]
              properties:
                merchantId:
                  type: string
                  description: "Merchant identifier"
                wallet:
                  type: string
                  description: "EVM wallet address (0x...)"
                solanaWallet:
                  type: string
                  description: "Solana wallet address"
                xrplWallet:
                  type: string
                  description: "XRPL wallet address (r-address)"
                items:
                  type: array
                  description: "Optional line items for per-item cent-amount allocations"
                  items:
                    type: object
                    properties:
                      path:
                        type: string
                        description: "JSONPath reference to the line item"
                        example: "$.line_items[0]"
                      amount:
                        type: integer
                        description: "Item price in cents"
                        example: 2500
      responses:
        "200":
          description: UCP-format discount response
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    type: object
                    properties:
                      protocol:
                        type: string
                        const: "ucp"
                      version:
                        type: string
                        example: "2026-01-11"
                      extension:
                        type: string
                        const: "dev.ucp.shopping.discount"
                      discounts:
                        type: object
                        properties:
                          codes:
                            type: array
                            items:
                              type: string
                          applied:
                            type: array
                            items:
                              type: object
                              properties:
                                code:
                                  type: string
                                title:
                                  type: string
                                amount:
                                  type: integer
                                  nullable: true
                                automatic:
                                  type: boolean
                                method:
                                  type: string
                                priority:
                                  type: integer
                                allocations:
                                  type: array
                                  items:
                                    type: object
                                    properties:
                                      path:
                                        type: string
                                      amount:
                                        type: integer
                      verification:
                        type: object
                        properties:
                          code:
                            type: string
                          expiresAt:
                            type: string
                            format: date-time
                          sig:
                            type: string
                          kid:
                            type: string
                  meta:
                    type: object
                    properties:
                      creditsRemaining:
                        type: integer
                      creditsCharged:
                        type: integer
                        description: "Always 1 for UCP discount checks"
                      version:
                        type: string
                      timestamp:
                        type: string
                        format: date-time
        "400":
          description: Validation error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "401":
          description: Invalid or missing API key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "402":
          description: No merchant credits
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Merchant not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "503":
          description: >
            Data source unavailable — retryable after a short delay.
            No discount signed, no credits charged.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RpcFailureEnvelope"

  /v1/codes/{code}:
    get:
      operationId: validateCode
      summary: Validate INSR-XXXXX discount code
      description: >
        Validates a discount code for merchant backends during ACP/UCP checkout flows.
        No authentication required. Does not expose wallet address, token breakdown, or analytics.
        Returns only what the merchant needs to apply the discount.
      tags: [Commerce Protocol Integration]
      security: []
      parameters:
        - name: code
          in: path
          required: true
          schema:
            type: string
            pattern: "^INSR-[A-Z0-9]{5}$"
          description: "Discount code in INSR-XXXXX format"
          example: "INSR-A7K3M"
      responses:
        "200":
          description: Code validation result
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    const: true
                  data:
                    oneOf:
                      - type: object
                        description: Valid code
                        properties:
                          valid:
                            type: boolean
                            const: true
                          code:
                            type: string
                          merchantId:
                            type: string
                          discountPercent:
                            type: integer
                          expiresAt:
                            type: string
                            format: date-time
                          createdAt:
                            type: string
                            format: date-time
                      - type: object
                        description: Invalid code
                        properties:
                          valid:
                            type: boolean
                            const: false
                          code:
                            type: string
                          reason:
                            type: string
                            enum: [expired, already_used, not_found]
                  meta:
                    $ref: "#/components/schemas/Meta"
        "400":
          description: Invalid code format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"

tags:
  - name: On-Chain Verification
    description: Privacy-preserving on-chain verification. Returns only boolean results, never balance amounts.
  - name: Discovery
    description: Browse merchants, tokens, and NFT collections in the public directory.
  - name: Discount Verification
    description: Calculate discounts, create verification codes, and confirm USDC payments.
  - name: Commerce Protocol Integration
    description: ACP (OpenAI/Stripe) and UCP (Google) compatible discount endpoints for AI agent commerce flows. Code validation for merchant backends.
  - name: Agent Onboarding
    description: Create and manage merchants programmatically. The API key that creates a merchant owns it.
