{
  "openapi": "3.1.0",
  "info": {
    "title": "Spritefield Asset API",
    "version": "1.1.0",
    "description": "Discover royalty-free game textures and 3D models and resolve original files. The hosted site is public with no application API key; self-hosted copies may have different access settings. If hosting protections challenge automated requests, use the browser WebMCP tools. Preview images may be material renders, not texture maps. Inspect each result's license and attribution requirements."
  },
  "servers": [
    {
      "url": "/",
      "description": "Current Spritefield deployment"
    }
  ],
  "paths": {
    "/api/textures": {
      "get": {
        "operationId": "findTextures",
        "summary": "Search for textures",
        "description": "Uses weighted full-text relevance, related terms, and spelling correction. If no terms match, returns browsing suggestions with search.mode=discovery. Pagination is finite and never repeats assets.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Describe a material, color, or surface.",
            "schema": {
              "type": "string",
              "maxLength": 120,
              "default": ""
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Number of results per page.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque nextCursor from the preceding response. Keep the same query.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Texture results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TextureSearch"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Catalog unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/textures/{id}": {
      "get": {
        "operationId": "getTextureFiles",
        "summary": "Resolve original texture files",
        "description": "Poly Haven returns PBR map channels at the selected resolution. Other providers return the original image or archive and may not offer resolution selection. Preferred format can fall back per map; each file reports the actual format. Does not download the files.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Exact id returned by findTextures.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_-]{0,159}$"
            }
          },
          {
            "name": "resolution",
            "in": "query",
            "required": false,
            "description": "Preferred PBR map resolution.",
            "schema": {
              "type": "string",
              "enum": [
                "1k",
                "2k",
                "4k",
                "8k",
                "16k"
              ],
              "default": "1k"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "Preferred map file format.",
            "schema": {
              "type": "string",
              "enum": [
                "jpg",
                "png",
                "exr"
              ],
              "default": "jpg"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Original file URLs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TextureDetails"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Texture not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Requested resolution unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Original provider unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Catalog unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/models": {
      "get": {
        "operationId": "findModels",
        "summary": "Search for 3D models and model packs",
        "description": "Returns individual models and packs separately using isPack. assetCount is the creator-advertised count for a pack. Cursor pagination and discovery fallback follow texture-search rules.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Describe a material, color, or surface.",
            "schema": {
              "type": "string",
              "maxLength": 120,
              "default": ""
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Number of results per page.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque nextCursor from the preceding response. Keep the same query.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Model results",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelSearch"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Catalog unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/models/{id}": {
      "get": {
        "operationId": "getModelFiles",
        "summary": "Resolve a model and all required companion files",
        "description": "Poly Haven models include their mesh plus every companion resource with relative paths. Preserve those paths when downloading. Resolution describes textures, not mesh detail. Other providers return a complete model-pack ZIP; resolution and format selection do not change that archive.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Exact id returned by findModels.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9_-]{0,159}$"
            }
          },
          {
            "name": "resolution",
            "in": "query",
            "required": false,
            "description": "Texture resolution for an individual model.",
            "schema": {
              "type": "string",
              "enum": [
                "1k",
                "2k",
                "4k",
                "8k",
                "16k"
              ],
              "default": "1k"
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": false,
            "description": "Use a format advertised by the model result.",
            "schema": {
              "type": "string",
              "enum": [
                "gltf",
                "blend",
                "fbx",
                "usd",
                "zip"
              ],
              "default": "gltf"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Original model files",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelDetails"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters or cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Texture not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Requested resolution unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Original provider unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Catalog unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "License": {
        "type": "object",
        "required": [
          "id",
          "url",
          "attributionRequired",
          "attribution"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "attributionRequired": {
            "type": "boolean"
          },
          "attribution": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Texture": {
        "type": "object",
        "required": [
          "id",
          "name",
          "provider",
          "creator",
          "tags",
          "previewUrl",
          "sourceUrl",
          "license",
          "dimensions",
          "detailUrl"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "creator": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "previewUrl": {
            "type": "string",
            "format": "uri"
          },
          "sourceUrl": {
            "type": "string",
            "format": "uri"
          },
          "license": {
            "$ref": "#/components/schemas/License"
          },
          "dimensions": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "number"
            },
            "minItems": 2,
            "maxItems": 2
          },
          "detailUrl": {
            "type": "string",
            "description": "Relative API URL for the texture."
          },
          "isPack": {
            "type": "boolean"
          },
          "assetCount": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Creator-advertised count for a pack."
          }
        }
      },
      "TextureSearch": {
        "type": "object",
        "required": [
          "query",
          "total",
          "catalogTotal",
          "count",
          "nextCursor",
          "search",
          "textures"
        ],
        "properties": {
          "query": {
            "type": "string"
          },
          "total": {
            "type": "integer",
            "description": "Result count, or full texture count in discovery mode."
          },
          "catalogTotal": {
            "type": "integer"
          },
          "count": {
            "type": "integer"
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "search": {
            "type": "object",
            "required": [
              "mode",
              "relatedTerms"
            ],
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "ranked",
                  "discovery"
                ]
              },
              "relatedTerms": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "textures": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Texture"
            }
          }
        }
      },
      "TextureFile": {
        "type": "object",
        "required": [
          "role",
          "url",
          "format",
          "resolution"
        ],
        "properties": {
          "role": {
            "type": "string",
            "description": "base_color, normal_opengl, normal_directx, roughness, displacement, ambient_occlusion, another available map role, or original."
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "format": {
            "type": "string"
          },
          "resolution": {
            "type": "string"
          },
          "bytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "md5": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "TextureDetails": {
        "type": "object",
        "required": [
          "texture",
          "requestedResolution",
          "files",
          "availableResolutions",
          "note"
        ],
        "properties": {
          "texture": {
            "$ref": "#/components/schemas/Texture"
          },
          "requestedResolution": {
            "type": "string"
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TextureFile"
            }
          },
          "availableResolutions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "note": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "availableResolutions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sourceUrl": {
            "type": "string"
          },
          "availableFormats": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Model": {
        "type": "object",
        "required": [
          "id",
          "name",
          "provider",
          "creator",
          "tags",
          "previewUrl",
          "sourceUrl",
          "license",
          "detailUrl",
          "formats",
          "polycount",
          "isPack",
          "assetCount"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "creator": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "previewUrl": {
            "type": "string",
            "format": "uri"
          },
          "sourceUrl": {
            "type": "string",
            "format": "uri"
          },
          "license": {
            "$ref": "#/components/schemas/License"
          },
          "detailUrl": {
            "type": "string",
            "description": "Relative API URL for the texture."
          },
          "isPack": {
            "type": "boolean"
          },
          "assetCount": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Creator-advertised count in a complete pack; not independently counted."
          },
          "formats": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "polycount": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "ModelSearch": {
        "type": "object",
        "required": [
          "query",
          "total",
          "catalogTotal",
          "count",
          "nextCursor",
          "search",
          "models"
        ],
        "properties": {
          "query": {
            "type": "string"
          },
          "total": {
            "type": "integer",
            "description": "Result count, or full texture count in discovery mode."
          },
          "catalogTotal": {
            "type": "integer"
          },
          "count": {
            "type": "integer"
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          },
          "search": {
            "type": "object",
            "required": [
              "mode",
              "relatedTerms"
            ],
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "ranked",
                  "discovery"
                ]
              },
              "relatedTerms": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "models": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Model"
            }
          }
        }
      },
      "ModelFile": {
        "type": "object",
        "required": [
          "role",
          "url",
          "format",
          "resolution",
          "path"
        ],
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "model",
              "resource",
              "archive"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "format": {
            "type": "string"
          },
          "resolution": {
            "type": "string"
          },
          "bytes": {
            "type": [
              "integer",
              "null"
            ]
          },
          "md5": {
            "type": [
              "string",
              "null"
            ]
          },
          "path": {
            "type": "string",
            "description": "Relative file path. Preserve folders so the model can resolve its companion geometry and textures."
          }
        }
      },
      "ModelDetails": {
        "type": "object",
        "required": [
          "model",
          "requestedResolution",
          "selectedFormat",
          "availableFormats",
          "availableResolutions",
          "files",
          "note"
        ],
        "properties": {
          "model": {
            "$ref": "#/components/schemas/Model"
          },
          "requestedResolution": {
            "type": "string"
          },
          "selectedFormat": {
            "type": "string"
          },
          "availableFormats": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "availableResolutions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ModelFile"
            }
          },
          "note": {
            "type": "string"
          }
        }
      }
    }
  }
}