{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "GRC Marketplace read API",
    "version": "grc-marketplace-index/v1",
    "description": "Implemented read-only routes. Loopback server is a development fixture, not an assigned public host. All JSON identifiers/amounts stay decimal strings. CORS does not authenticate callers. No purchase/login/webhook endpoints are implemented."
  },
  "servers": [
    {
      "url": "http://127.0.0.1:8800",
      "description": "Local operator/dev fixture only; public deployments use configured HTTPS."
    }
  ],
  "security": [],
  "paths": {
    "/healthz": {
      "get": {
        "operationId": "readReadiness",
        "summary": "Read truthful indexing readiness and checkpoint.",
        "responses": {
          "200": {
            "description": "Ready and caught up to confirmed target.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "503": {
            "description": "Syncing or validation unavailable; progress is returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "403": {
            "description": "Origin not allowed or invalid preflight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Per-socket-peer request quota or bucket capacity exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string"
                },
                "description": "60 seconds for an exhausted existing peer bucket; capacity rejection may omit this header."
              }
            }
          }
        }
      }
    },
    "/v1/info": {
      "get": {
        "operationId": "readMarketplaceIdentity",
        "summary": "Read deployment identity and current checkpoint only when ready.",
        "responses": {
          "200": {
            "description": "Identity and confirmed checkpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Identity"
                }
              }
            }
          },
          "400": {
            "description": "Invalid, duplicate or unsupported query/address/value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Origin not allowed or invalid preflight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown route after readiness verification.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Per-socket-peer request quota or bucket capacity exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string"
                },
                "description": "60 seconds for an exhausted existing peer bucket; capacity rejection may omit this header."
              }
            }
          },
          "503": {
            "description": "INDEX_NOT_READY while RPC validation, catch-up or reorg processing is incomplete.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/listings": {
      "get": {
        "operationId": "listMarketplaceOffers",
        "summary": "Read sorted listings with an exclusive listing-ID cursor.",
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "description": "Exclusive decimal ID cursor. Pass previous next unchanged; stop when next is null.",
            "schema": {
              "type": "string",
              "pattern": "^(0|[1-9][0-9]{0,77})$",
              "description": "Canonical unsigned decimal uint256; retain as a string.",
              "default": "0"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 24
            }
          },
          {
            "name": "active",
            "in": "query",
            "description": "false includes closed, expired and otherwise unavailable listings.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page with independently observed checkpoint and next cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListingsPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid, duplicate or unsupported query/address/value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Origin not allowed or invalid preflight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown route after readiness verification.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Per-socket-peer request quota or bucket capacity exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string"
                },
                "description": "60 seconds for an exhausted existing peer bucket; capacity rejection may omit this header."
              }
            }
          },
          "503": {
            "description": "INDEX_NOT_READY while RPC validation, catch-up or reorg processing is incomplete.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/inventory/{account}": {
      "get": {
        "operationId": "readNftInventory",
        "summary": "Read positive balances; this does not authenticate ownership of the account.",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^0x[0-9a-fA-F]{40}$"
            }
          },
          {
            "name": "after",
            "in": "query",
            "description": "Exclusive decimal ID cursor. Pass previous next unchanged; stop when next is null.",
            "schema": {
              "type": "string",
              "pattern": "^(0|[1-9][0-9]{0,77})$",
              "description": "Canonical unsigned decimal uint256; retain as a string.",
              "default": "0"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 24
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sorted token-ID page with positive quantities and next cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InventoryPage"
                }
              }
            }
          },
          "400": {
            "description": "Invalid, duplicate or unsupported query/address/value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Origin not allowed or invalid preflight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown route after readiness verification.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Per-socket-peer request quota or bucket capacity exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string"
                },
                "description": "60 seconds for an exhausted existing peer bucket; capacity rejection may omit this header."
              }
            }
          },
          "503": {
            "description": "INDEX_NOT_READY while RPC validation, catch-up or reorg processing is incomplete.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Identity": {
        "type": "object",
        "required": [
          "schema",
          "chainId",
          "itemsAddress",
          "marketplaceAddress",
          "startBlock",
          "anchorHash",
          "confirmations",
          "indexedBlock",
          "indexedBlockHash",
          "indexedTimestamp",
          "confirmedBlock",
          "blocksBehind",
          "caughtUp",
          "networkIdentity"
        ],
        "properties": {
          "schema": {
            "const": "grc-marketplace-index/v1"
          },
          "chainId": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,77})$",
            "description": "Canonical unsigned decimal uint256; retain as a string."
          },
          "itemsAddress": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$"
          },
          "marketplaceAddress": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$"
          },
          "startBlock": {
            "type": "integer",
            "minimum": 1
          },
          "anchorHash": {
            "type": "string",
            "pattern": "^0x[0-9a-f]{64}$"
          },
          "confirmations": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "indexedBlock": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 1
              },
              {
                "type": "null"
              }
            ]
          },
          "indexedBlockHash": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^0x[0-9a-f]{64}$"
              },
              {
                "type": "null"
              }
            ]
          },
          "indexedTimestamp": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          },
          "confirmedBlock": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          },
          "blocksBehind": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ]
          },
          "caughtUp": {
            "type": "boolean"
          },
          "networkIdentity": {
            "$ref": "#/components/schemas/NetworkIdentity"
          }
        }
      },
      "Item": {
        "type": "object",
        "required": [
          "tokenId",
          "collectionId",
          "itemId",
          "maxSupply",
          "supply",
          "uri"
        ],
        "properties": {
          "tokenId": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,77})$",
            "description": "Canonical unsigned decimal uint256; retain as a string."
          },
          "collectionId": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,77})$",
            "description": "Canonical unsigned decimal uint256; retain as a string."
          },
          "itemId": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,77})$",
            "description": "Canonical unsigned decimal uint256; retain as a string."
          },
          "maxSupply": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,77})$",
            "description": "Canonical unsigned decimal uint256; retain as a string."
          },
          "supply": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,77})$",
            "description": "Canonical unsigned decimal uint256; retain as a string."
          },
          "uri": {
            "type": "string",
            "maxLength": 4096,
            "description": "ERC-1155 template already expanded to a 64-character lowercase hexadecimal token identifier; content is not certified by the read API."
          }
        }
      },
      "Listing": {
        "type": "object",
        "required": [
          "listingId",
          "seller",
          "tokenId",
          "remainingQuantity",
          "availableQuantity",
          "unitPriceWei",
          "expiresAt",
          "status",
          "item"
        ],
        "properties": {
          "listingId": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,77})$",
            "description": "Canonical unsigned decimal uint256; retain as a string."
          },
          "tokenId": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,77})$",
            "description": "Canonical unsigned decimal uint256; retain as a string."
          },
          "remainingQuantity": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,77})$",
            "description": "Canonical unsigned decimal uint256; retain as a string."
          },
          "availableQuantity": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,77})$",
            "description": "Canonical unsigned decimal uint256; retain as a string."
          },
          "unitPriceWei": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,77})$",
            "description": "Canonical unsigned decimal uint256; retain as a string."
          },
          "expiresAt": {
            "type": "string",
            "pattern": "^(0|[1-9][0-9]{0,77})$",
            "description": "Canonical unsigned decimal uint256; retain as a string."
          },
          "seller": {
            "type": "string",
            "pattern": "^0x[0-9a-fA-F]{40}$"
          },
          "status": {
            "enum": [
              "active",
              "closed",
              "expired",
              "paused",
              "unapproved",
              "unavailable"
            ]
          },
          "item": {
            "$ref": "#/components/schemas/Item"
          }
        }
      },
      "Health": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Identity"
          },
          {
            "type": "object",
            "required": [
              "status"
            ],
            "properties": {
              "status": {
                "enum": [
                  "ok",
                  "syncing"
                ]
              }
            }
          }
        ]
      },
      "ListingsPage": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Identity"
          },
          {
            "type": "object",
            "required": [
              "listings",
              "next"
            ],
            "properties": {
              "listings": {
                "type": "array",
                "maxItems": 100,
                "items": {
                  "$ref": "#/components/schemas/Listing"
                }
              },
              "next": {
                "anyOf": [
                  {
                    "type": "string",
                    "pattern": "^(0|[1-9][0-9]{0,77})$",
                    "description": "Canonical unsigned decimal uint256; retain as a string."
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            }
          }
        ]
      },
      "InventoryPage": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Identity"
          },
          {
            "type": "object",
            "required": [
              "account",
              "items",
              "next"
            ],
            "properties": {
              "account": {
                "type": "string",
                "pattern": "^0x[0-9a-fA-F]{40}$"
              },
              "items": {
                "type": "array",
                "maxItems": 100,
                "items": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Item"
                    },
                    {
                      "type": "object",
                      "required": [
                        "quantity"
                      ],
                      "properties": {
                        "quantity": {
                          "type": "string",
                          "pattern": "^(0|[1-9][0-9]{0,77})$",
                          "description": "Canonical unsigned decimal uint256; retain as a string."
                        }
                      }
                    }
                  ]
                }
              },
              "next": {
                "anyOf": [
                  {
                    "type": "string",
                    "pattern": "^(0|[1-9][0-9]{0,77})$",
                    "description": "Canonical unsigned decimal uint256; retain as a string."
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            }
          }
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "enum": [
              "ORIGIN_NOT_ALLOWED",
              "INVALID_PREFLIGHT",
              "METHOD_NOT_ALLOWED",
              "RATE_LIMITED",
              "INDEX_NOT_READY",
              "INVALID_QUERY",
              "NOT_FOUND"
            ]
          }
        }
      },
      "NetworkIdentity": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schema",
          "evmChainId",
          "cosmosChainId",
          "genesisFileSha256",
          "hybridGenesisFingerprint",
          "evmAnchor"
        ],
        "description": "Independently approved metadata; clients compare it with local configuration. EVM reads prove neither the file SHA nor Hybrid fingerprint. uint256 upper bounds are enforced by runtime.",
        "properties": {
          "schema": {
            "const": "grc-network-identity/v1"
          },
          "evmChainId": {
            "type": "string",
            "pattern": "^[1-9][0-9]{0,77}$"
          },
          "cosmosChainId": {
            "type": "string",
            "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
          },
          "genesisFileSha256": {
            "type": "string",
            "pattern": "^(?!0{64}$)[0-9a-f]{64}$"
          },
          "hybridGenesisFingerprint": {
            "type": "string",
            "pattern": "^0x(?!0{64}$)[0-9a-f]{64}$"
          },
          "evmAnchor": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "number",
              "hash"
            ],
            "properties": {
              "number": {
                "type": "string",
                "pattern": "^[1-9][0-9]{0,77}$"
              },
              "hash": {
                "type": "string",
                "pattern": "^0x(?!0{64}$)[0-9a-f]{64}$"
              }
            }
          }
        }
      }
    }
  },
  "x-operational-semantics": {
    "cache": "no-store",
    "cors": "Exact configured Origin allowlist; requests without Origin are permitted. GET preflight: OPTIONS with Origin and Access-Control-Request-Method: GET returns204; otherwise403.",
    "rateLimit": "300 GET requests per socket peer per 60 seconds; bounded process-local buckets; restart resets them. Configure real-client limits at the public proxy.",
    "unsupportedMethods": "Methods other than GET/OPTIONS return405 METHOD_NOT_ALLOWED.",
    "pagination": "Pages are not a frozen snapshot; compare checkpoint hashes and restart a traversal when consistency is required.",
    "readiness": "503 precedes route/query processing while the confirmed checkpoint is unverified or behind."
  }
}
