{
  "openapi": "3.1.0",
  "info": {
    "title": "GDTF Stage Studio – fixture library API (read-only)",
    "version": "2026-10-13",
    "summary": "Public read-only catalog API of the GDTF Stage Studio lighting fixture library (SOH Sound & Lights, Prague).",
    "description": "Catalog of 10500+ lighting fixtures with GDTF profiles on gdtf-lights.com. The API only reads: search and list fixtures, fixture detail with DMX modes and download links (GDTF, GDTF Share, DMX channel list, Avolites Titan .d4 where available), lookup by names and IDs. Nothing is ordered or saved through the API – a link that opens fixtures in \"My list\" is `https://gdtf-lights.com/?cart=ID:COUNT,ID:COUNT&clear=2` (IDs without spaces). The site publishes no prices or stock: `pr` and `prWeb` are null and `stock` is 0. The MVR scene file is generated by the separate MVR API (`/mvr-generate.php`, see llms.txt). A read-only MCP server (Streamable HTTP, JSON-RPC 2.0 via POST) runs at `https://gdtf-lights.com/catalog-api.php?action=mcp` with the tools search_fixtures, get_fixture, get_gdtf_download and prepare_mvr_export. Every action is also available as `/catalog-api.php?action=<action>` (by-ids = by_ids); both addresses return the same. Limit: 10 requests per second per IP (burst 60), otherwise HTTP 429.",
    "contact": {
      "name": "SOH Sound & Lights s.r.o.",
      "url": "https://gdtf-lights.com/contact"
    }
  },
  "externalDocs": {
    "description": "Rules for AI, link format for \"My list\" and the MVR workflow",
    "url": "https://gdtf-lights.com/llms.txt"
  },
  "servers": [
    {
      "url": "https://gdtf-lights.com",
      "description": "gdtf-lights.com (English; ?lang=cs|de|es switches the catalog language)"
    }
  ],
  "tags": [
    {
      "name": "katalog",
      "description": "Fixture catalog (read-only)"
    }
  ],
  "paths": {
    "/api/catalog/status": {
      "get": {
        "tags": [
          "katalog"
        ],
        "operationId": "getStatus",
        "summary": "Catalog status (number of fixtures)",
        "responses": {
          "200": {
            "description": "Catalog database status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/catalog/bootstrap": {
      "get": {
        "tags": [
          "katalog"
        ],
        "operationId": "getBootstrap",
        "summary": "Categories, catalog settings and the first page of the listing",
        "parameters": [
          {
            "$ref": "#/components/parameters/lang"
          },
          {
            "name": "category",
            "in": "query",
            "description": "Category ID (from the categories field); default all",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Number of items on the first page",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Categories and the first page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BootstrapResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/catalog/list": {
      "get": {
        "tags": [
          "katalog"
        ],
        "operationId": "listProducts",
        "summary": "Search and list fixtures with filters",
        "description": "Full-text `q` searches the name, ID, description, tags and parameters. Filters (facets) take values exactly as in the `facets` field of the response; separate several values with a comma.",
        "parameters": [
          {
            "$ref": "#/components/parameters/lang"
          },
          {
            "name": "q",
            "in": "query",
            "description": "Search text, e.g. \"Robin Esprite\" or \"LED PAR RGBW\"",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "Category ID, default `all`",
            "schema": {
              "type": "string",
              "default": "all"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "default",
                "name",
                "manufacturer",
                "power",
                "weight",
                "dmx"
              ],
              "default": "default"
            }
          },
          {
            "name": "dir",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 24
            }
          },
          {
            "name": "view",
            "in": "query",
            "description": "`card` = lightweight items without the full description (fullDe) and search text",
            "schema": {
              "type": "string",
              "enum": [
                "card"
              ]
            }
          },
          {
            "name": "domain",
            "in": "query",
            "description": "Equipment type (values from facets.domain)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "manufacturer",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lightColor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lightSource",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "beamAngle",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ipRating",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "speakerSize",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dmxChannels",
            "in": "query",
            "description": "Channel count of a DMX mode, e.g. `8ch`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of the listing, facets and category counts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/catalog/product": {
      "get": {
        "tags": [
          "katalog"
        ],
        "operationId": "getProduct",
        "summary": "Fixture detail by slug or ID",
        "parameters": [
          {
            "$ref": "#/components/parameters/lang"
          },
          {
            "name": "slug",
            "in": "query",
            "description": "Slug from the fixture page URL (/product/<slug>)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "id",
            "in": "query",
            "description": "Fixture ID (field id), e.g. `ROBE-119729`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Fixture and up to 8 related fixtures",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/catalog/lookup": {
      "get": {
        "tags": [
          "katalog"
        ],
        "operationId": "lookupProducts",
        "summary": "Look up fixtures by names (tolerant to case and punctuation)",
        "parameters": [
          {
            "$ref": "#/components/parameters/lang"
          },
          {
            "name": "needles",
            "in": "query",
            "description": "Names or IDs to look up: a JSON array or a comma-separated list",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "description": "One name to look up (instead of needles)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Found fixtures and a map from search term to ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LookupResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    },
    "/api/catalog/by-ids": {
      "get": {
        "tags": [
          "katalog"
        ],
        "operationId": "getProductsByIds",
        "summary": "Fixtures by IDs (also in the compact form without spaces and punctuation)",
        "parameters": [
          {
            "$ref": "#/components/parameters/lang"
          },
          {
            "name": "ids",
            "in": "query",
            "required": true,
            "description": "Comma-separated IDs, e.g. `ROBE-119729,ROBE-119733`",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Found fixtures (unknown IDs are skipped)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ItemsResponse"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "lang": {
        "name": "lang",
        "in": "query",
        "description": "Catalog language; default English",
        "schema": {
          "type": "string",
          "enum": [
            "cs",
            "en",
            "de",
            "es"
          ]
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request (unknown action, invalid UTF-8)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Fixture not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded (nginx response, not JSON)",
        "content": {
          "text/html": {
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "ServerError": {
        "description": "Server error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "success",
          "error"
        ],
        "properties": {
          "success": {
            "const": false
          },
          "error": {
            "type": "string"
          }
        }
      },
      "StatusResponse": {
        "type": "object",
        "required": [
          "success",
          "products"
        ],
        "properties": {
          "success": {
            "const": true
          },
          "products": {
            "type": "integer",
            "description": "Number of fixtures in the catalog of the language"
          },
          "source": {
            "type": "string"
          },
          "db": {
            "type": "string"
          }
        }
      },
      "Category": {
        "type": "object",
        "required": [
          "id",
          "label"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "`all` = all categories"
          },
          "label": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          }
        }
      },
      "FacetValue": {
        "type": "object",
        "required": [
          "value",
          "count"
        ],
        "properties": {
          "value": {
            "type": "string"
          },
          "label": {
            "type": "string",
            "description": "Only in facets.domain"
          },
          "count": {
            "type": "integer"
          }
        }
      },
      "Facets": {
        "type": "object",
        "additionalProperties": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/FacetValue"
          }
        },
        "properties": {
          "domain": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FacetValue"
            }
          },
          "manufacturer": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FacetValue"
            }
          },
          "lightColor": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FacetValue"
            }
          },
          "lightSource": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FacetValue"
            }
          },
          "beamAngle": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FacetValue"
            }
          },
          "ipRating": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FacetValue"
            }
          },
          "speakerSize": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FacetValue"
            }
          },
          "dmxChannels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FacetValue"
            }
          }
        }
      },
      "DmxMode": {
        "type": "object",
        "required": [
          "name",
          "channels"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "DMX mode name exactly as in the GDTF file"
          },
          "channels": {
            "type": "integer",
            "minimum": 0
          },
          "default": {
            "const": true,
            "description": "Only on the default mode"
          },
          "breaks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "break": {
                  "type": "integer"
                },
                "channels": {
                  "type": "integer"
                }
              }
            }
          },
          "description": {
            "type": "string"
          },
          "grandMA2_url": {
            "type": "string",
            "description": "grandMA2 library for this mode (relative URL)"
          }
        }
      },
      "Product": {
        "type": "object",
        "required": [
          "id",
          "n",
          "url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable fixture ID; use it in the ?cart= link without spaces"
          },
          "n": {
            "type": "string",
            "description": "Display name"
          },
          "rawName": {
            "type": "string"
          },
          "fullName": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "description": "Relative URL of the fixture page"
          },
          "cat": {
            "type": "string",
            "description": "Category ID"
          },
          "i": {
            "type": "string",
            "description": "Icon (emoji)"
          },
          "img": {
            "type": "string",
            "description": "Relative image URL"
          },
          "imgW": {
            "type": "integer"
          },
          "imgH": {
            "type": "integer"
          },
          "pr": {
            "type": [
              "number",
              "null"
            ],
            "description": "Not published on gdtf-lights.com (null)"
          },
          "prWeb": {
            "type": [
              "number",
              "null"
            ],
            "description": "Not published on gdtf-lights.com (null)"
          },
          "sp": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tags"
          },
          "de": {
            "type": "string",
            "description": "Short description"
          },
          "fullDe": {
            "type": "string",
            "description": "Full description (missing in view=card)"
          },
          "dt": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Parameters (keys in the catalog language)"
          },
          "ai": {
            "type": "string",
            "description": "Prompt basis for the AI visualization"
          },
          "manualUrl": {
            "type": "string"
          },
          "videoUrl": {
            "type": "string"
          },
          "fixtureGdtf": {
            "type": "string",
            "description": "Relative URL of the GDTF file (download)"
          },
          "fixtureGdtfShare": {
            "type": "string",
            "description": "Fixture on GDTF Share"
          },
          "fixtureOfl": {
            "type": "string",
            "description": "Open Fixture Library link, usually empty"
          },
          "fixtureTxt": {
            "type": "string",
            "description": "Relative URL of the tab-separated DMX channel list by mode"
          },
          "fixtureTitan": {
            "type": "string",
            "description": "Avolites Titan personality (.d4), only for SOH Stage Lights library fixtures; otherwise empty"
          },
          "fixtureMa2": {
            "type": [
              "object",
              "array"
            ],
            "description": "Legacy format of grandMA2 libraries {mode: url}; the new one is in modes"
          },
          "modes": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/DmxMode"
            },
            "description": "DMX modes in GDTF order; null = fixture without mode data"
          },
          "gdtfRid": {
            "type": "string",
            "description": "GDTF Share revision ID"
          },
          "manufacturer": {
            "type": "string",
            "description": "Manufacturer"
          },
          "lightColor": {
            "type": "string"
          },
          "beamAngle": {
            "type": "string"
          },
          "lightSource": {
            "type": "string"
          },
          "ipRating": {
            "type": "string"
          },
          "speakerSizes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "searchText": {
            "type": "string"
          },
          "dmxModes": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "DMX channel counts of the modes (for the filter)"
          },
          "stock": {
            "type": "integer",
            "description": "Not used on gdtf-lights.com (0)"
          },
          "powerWatts": {
            "type": "integer"
          },
          "updatedAt": {
            "type": "string",
            "description": "Date of the last content change (ISO 8601)"
          },
          "lite": {
            "const": 1,
            "description": "Only in view=card"
          }
        }
      },
      "ProductList": {
        "type": "object",
        "required": [
          "items",
          "total"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Product"
            }
          },
          "total": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "remaining": {
            "type": "integer"
          },
          "category": {
            "type": "string"
          },
          "sort": {
            "type": "string"
          },
          "dir": {
            "type": "string"
          },
          "q": {
            "type": "string"
          },
          "filters": {
            "type": [
              "object",
              "array"
            ]
          }
        }
      },
      "ListResponse": {
        "type": "object",
        "required": [
          "success",
          "list"
        ],
        "properties": {
          "success": {
            "const": true
          },
          "list": {
            "$ref": "#/components/schemas/ProductList"
          },
          "facets": {
            "$ref": "#/components/schemas/Facets"
          },
          "category_counts": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "catalog": {
            "type": "object",
            "properties": {
              "product_url_prefix": {
                "type": "string"
              },
              "page_size": {
                "type": "integer"
              }
            }
          }
        }
      },
      "BootstrapResponse": {
        "type": "object",
        "required": [
          "success",
          "categories",
          "list"
        ],
        "properties": {
          "success": {
            "const": true
          },
          "categories": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Category"
            }
          },
          "category_gradients": {
            "type": [
              "object",
              "array"
            ]
          },
          "category_counts": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "settings": {
            "type": [
              "object",
              "array"
            ]
          },
          "catalog_info": {
            "type": [
              "object",
              "array"
            ]
          },
          "catalog": {
            "type": "object",
            "properties": {
              "initial_category": {
                "type": "string"
              },
              "page_size": {
                "type": "integer"
              },
              "product_url_prefix": {
                "type": "string"
              }
            }
          },
          "list": {
            "$ref": "#/components/schemas/ProductList"
          },
          "facets": {
            "$ref": "#/components/schemas/Facets"
          }
        }
      },
      "ProductResponse": {
        "type": "object",
        "required": [
          "success",
          "product"
        ],
        "properties": {
          "success": {
            "const": true
          },
          "product": {
            "$ref": "#/components/schemas/Product"
          },
          "related": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Product"
            }
          }
        }
      },
      "LookupResponse": {
        "type": "object",
        "required": [
          "success",
          "items",
          "matches"
        ],
        "properties": {
          "success": {
            "const": true
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Product"
            }
          },
          "matches": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Search term → ID of the found fixture"
          }
        }
      },
      "ItemsResponse": {
        "type": "object",
        "required": [
          "success",
          "items"
        ],
        "properties": {
          "success": {
            "const": true
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Product"
            }
          }
        }
      }
    }
  }
}
