{
  "openapi": "3.1.0",
  "info": {
    "title": "ApproSearch — API de configuración",
    "version": "1.0.0",
    "description": "API para revisar y modificar la configuración del buscador de una tienda: pesos de relevancia, sinónimos, reglas, filtros y widget.\n\nAutenticación: API key con scope `config:read` (lecturas) o `config:write` (lecturas y escrituras), en la cabecera `X-API-Key` o como `Authorization: Bearer <key>`. Las keys se crean en el panel (API Keys → preset «Agente IA»).\n\nEmpieza siempre por `GET /me` para conocer la tienda y los permisos de la key. Antes de guardar cambios de relevancia, pruébalos con `POST /stores/{id}/relevance/test`.\n\nGuía completa para agentes (semántica de cada peso y ajuste, flujo de trabajo, errores): https://api.approsearch.com/api/v1/docs/agent.md\nServidor MCP (mismas operaciones como herramientas): POST https://api.approsearch.com/mcp"
  },
  "servers": [
    {
      "url": "https://api.approsearch.com/api/v1"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    },
    {
      "BearerApiKey": []
    }
  ],
  "tags": [
    {
      "name": "Identidad"
    },
    {
      "name": "Tiendas"
    },
    {
      "name": "Relevancia",
      "description": "Pesos por campo y ajustes del motor. Ver la guía para el significado de cada valor."
    },
    {
      "name": "Sinónimos",
      "description": "Se aplican al guardar (sin reindexar). La caché de búsqueda del widget tarda hasta 3 minutos en reflejarlos; el simulador es inmediato."
    },
    {
      "name": "Reglas",
      "description": "BOOST, PIN, HIDE, REDIRECT, BANNER, FILTER con trigger por consulta."
    },
    {
      "name": "Filtros"
    },
    {
      "name": "Widget"
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "tags": [
          "Identidad"
        ],
        "summary": "Quién soy: cuenta, tienda(s), scopes y último uso de la key",
        "responses": {
          "200": {
            "description": "Identidad de la key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores": {
      "get": {
        "tags": [
          "Tiendas"
        ],
        "summary": "Lista las tiendas de la cuenta",
        "responses": {
          "200": {
            "description": "Tiendas.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{id}": {
      "get": {
        "tags": [
          "Tiendas"
        ],
        "summary": "Detalle de una tienda (índices, contadores, widget)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "responses": {
          "200": {
            "description": "Tienda.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Tiendas"
        ],
        "summary": "Modifica nombre o URL de desarrollo",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Por API key solo se admiten `name` y `devUrl`. Cualquier otro campo (url, status, platform, config…) devuelve 403.",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                  },
                  "devUrl": {
                    "type": "string",
                    "description": "URL del entorno de desarrollo o cadena vacía para limpiarla."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tienda actualizada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{id}/relevance": {
      "get": {
        "tags": [
          "Relevancia"
        ],
        "summary": "Pesos y ajustes efectivos",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "responses": {
          "200": {
            "description": "Relevancia.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "description": "Configuración efectiva de relevancia (valores por defecto fusionados con los guardados).",
                      "properties": {
                        "boosts": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "number"
                          },
                          "description": "Peso por campo. Campos: name, name.autocomplete, name.partial, reference, ean13, upc, mpn, combinationReferences, combinationEans, combinationMpns, categories.name, tags, attributes.value, features.value, description, short_description, manufacturer, supplier, ai_description_manual, ai_description."
                        },
                        "searchSettings": {
                          "type": "object",
                          "description": "Ajustes del motor. Valores por defecto: {\"fuzziness\":\"AUTO\",\"minMatchShort\":\"75%\",\"minMatchLong\":\"30%\",\"longQueryThreshold\":5,\"phraseBoost\":8,\"exactRefBoost\":20,\"crossFieldsBoost\":3,\"stockBoost\":1.5,\"salesBoost\":0.5,\"ratingBoost\":0.3,\"discountBoost\":1.1,\"hideOutOfStock\":false,\"exactReferenceSearch\":true,\"referenceCaseSensitive\":false,\"fuzzyPrefixLength\":2,\"scoreCutoff\":0,\"referenceMode\":\"off\"}"
                        }
                      }
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Relevancia"
        ],
        "summary": "Guarda pesos y/o ajustes",
        "description": "AVISO: `boosts` REEMPLAZA el mapa completo de pesos (envía todos los campos que quieras conservar; lo que falte vuelve al valor por defecto). `searchSettings` se FUSIONA clave a clave. Invalida la caché de búsqueda de la tienda.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "boosts": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "number",
                      "minimum": 0,
                      "maximum": 200
                    }
                  },
                  "searchSettings": {
                    "type": "object",
                    "properties": {
                      "fuzziness": {
                        "type": "string",
                        "enum": [
                          "AUTO",
                          "0",
                          "1",
                          "2"
                        ]
                      },
                      "minMatchShort": {
                        "type": "string"
                      },
                      "minMatchLong": {
                        "type": "string"
                      },
                      "longQueryThreshold": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 20
                      },
                      "phraseBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 300
                      },
                      "exactRefBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 500
                      },
                      "crossFieldsBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 200
                      },
                      "stockBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 50
                      },
                      "salesBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 50
                      },
                      "ratingBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 50
                      },
                      "discountBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 50
                      },
                      "hideOutOfStock": {
                        "type": "boolean"
                      },
                      "exactReferenceSearch": {
                        "type": "boolean"
                      },
                      "referenceCaseSensitive": {
                        "type": "boolean"
                      },
                      "fuzzyPrefixLength": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 2
                      },
                      "scoreCutoff": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100
                      },
                      "referenceMode": {
                        "type": "string",
                        "enum": [
                          "off",
                          "priority",
                          "strict"
                        ]
                      }
                    },
                    "additionalProperties": false
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Relevancia resultante.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "description": "Configuración efectiva de relevancia (valores por defecto fusionados con los guardados).",
                      "properties": {
                        "boosts": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "number"
                          },
                          "description": "Peso por campo. Campos: name, name.autocomplete, name.partial, reference, ean13, upc, mpn, combinationReferences, combinationEans, combinationMpns, categories.name, tags, attributes.value, features.value, description, short_description, manufacturer, supplier, ai_description_manual, ai_description."
                        },
                        "searchSettings": {
                          "type": "object",
                          "description": "Ajustes del motor. Valores por defecto: {\"fuzziness\":\"AUTO\",\"minMatchShort\":\"75%\",\"minMatchLong\":\"30%\",\"longQueryThreshold\":5,\"phraseBoost\":8,\"exactRefBoost\":20,\"crossFieldsBoost\":3,\"stockBoost\":1.5,\"salesBoost\":0.5,\"ratingBoost\":0.3,\"discountBoost\":1.1,\"hideOutOfStock\":false,\"exactReferenceSearch\":true,\"referenceCaseSensitive\":false,\"fuzzyPrefixLength\":2,\"scoreCutoff\":0,\"referenceMode\":\"off\"}"
                        }
                      }
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{id}/relevance/test": {
      "post": {
        "tags": [
          "Relevancia"
        ],
        "summary": "Simulador: prueba una consulta con pesos provisionales SIN guardar",
        "description": "Devuelve el ranking y el desglose de scoring de cada producto con los `boosts`/`searchSettings` indicados (si se omiten, usa los guardados). Úsalo antes y después de cada cambio de relevancia. `applyRules` aplica también las reglas activas.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "q": {
                    "type": "string",
                    "minLength": 1
                  },
                  "lang": {
                    "type": "string"
                  },
                  "limit": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 500
                  },
                  "boosts": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "number",
                      "minimum": 0,
                      "maximum": 200
                    }
                  },
                  "searchSettings": {
                    "type": "object",
                    "properties": {
                      "fuzziness": {
                        "type": "string",
                        "enum": [
                          "AUTO",
                          "0",
                          "1",
                          "2"
                        ]
                      },
                      "minMatchShort": {
                        "type": "string"
                      },
                      "minMatchLong": {
                        "type": "string"
                      },
                      "longQueryThreshold": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 20
                      },
                      "phraseBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 300
                      },
                      "exactRefBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 500
                      },
                      "crossFieldsBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 200
                      },
                      "stockBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 50
                      },
                      "salesBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 50
                      },
                      "ratingBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 50
                      },
                      "discountBoost": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 50
                      },
                      "hideOutOfStock": {
                        "type": "boolean"
                      },
                      "exactReferenceSearch": {
                        "type": "boolean"
                      },
                      "referenceCaseSensitive": {
                        "type": "boolean"
                      },
                      "fuzzyPrefixLength": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 2
                      },
                      "scoreCutoff": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 100
                      },
                      "referenceMode": {
                        "type": "string",
                        "enum": [
                          "off",
                          "priority",
                          "strict"
                        ]
                      }
                    },
                    "additionalProperties": false
                  },
                  "applyRules": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "q"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resultados con desglose.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{id}/widget": {
      "get": {
        "tags": [
          "Widget"
        ],
        "summary": "Configuración del widget",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "responses": {
          "200": {
            "description": "Widget.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Widget"
        ],
        "summary": "Modifica la configuración del widget (sin customJS/customCSS)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Configuración visual y funcional del widget. `features` se FUSIONA con lo guardado; el resto de campos se reemplazan. Por API key NO se admiten `customJS` ni `customCSS` (403): solo desde el panel.",
                "properties": {
                  "theme": {
                    "type": "string"
                  },
                  "layout": {
                    "type": "string",
                    "enum": [
                      "grid",
                      "list"
                    ]
                  },
                  "position": {
                    "type": "string"
                  },
                  "colors": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "typography": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "features": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Interruptores del widget (displayMode, filters, showBrand, minChars, livePricing, themeCards…)."
                  },
                  "defaultSearch": {
                    "type": "object",
                    "additionalProperties": true
                  },
                  "translations": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Textos por idioma: { es: { clave: texto } }."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Widget actualizado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/synonyms": {
      "get": {
        "tags": [
          "Sinónimos"
        ],
        "summary": "Lista sinónimos",
        "description": "Sin `page`, `limit`, `q`, `type` ni `active` devuelve la lista completa. Con cualquiera de ellos devuelve una página y `meta` (`page`, `limit`, `total`, `totalPages`).",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 5
            },
            "description": "Filtra por idioma ISO (p. ej. `es`). Sin él devuelve todos los idiomas."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Página (por defecto 1)."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            },
            "description": "Sinónimos por página (por defecto 20)."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Texto contenido en algún término o en el término de entrada (sin distinguir mayúsculas)."
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "BIDIRECTIONAL",
                "UNIDIRECTIONAL"
              ]
            }
          },
          {
            "name": "active",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sinónimos.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Sinónimos"
        ],
        "summary": "Crea un grupo de sinónimos",
        "description": "BIDIRECTIONAL: 2+ términos equivalentes en `terms`. UNIDIRECTIONAL: `input` (lo que escribe el cliente) → `terms` (a qué equivale). Los sinónimos casan formas EXACTAS (tras minúsculas y sin acentos): lista las variantes (singular, plural) que quieras cubrir.",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "language": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 5,
                    "default": "es"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "BIDIRECTIONAL",
                      "UNIDIRECTIONAL"
                    ],
                    "default": "BIDIRECTIONAL"
                  },
                  "terms": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1
                    },
                    "minItems": 1
                  },
                  "input": {
                    "type": "string"
                  }
                },
                "required": [
                  "terms"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sinónimo creado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/synonyms/{synId}": {
      "put": {
        "tags": [
          "Sinónimos"
        ],
        "summary": "Modifica un sinónimo",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          },
          {
            "name": "synId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID del sinónimo"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "BIDIRECTIONAL",
                      "UNIDIRECTIONAL"
                    ]
                  },
                  "terms": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1
                    },
                    "minItems": 1
                  },
                  "input": {
                    "type": "string"
                  },
                  "active": {
                    "type": "boolean"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sinónimo actualizado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Sinónimos"
        ],
        "summary": "Elimina un sinónimo",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          },
          {
            "name": "synId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID del sinónimo"
          }
        ],
        "responses": {
          "200": {
            "description": "Sinónimo eliminado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/synonyms/import": {
      "post": {
        "tags": [
          "Sinónimos"
        ],
        "summary": "Importa sinónimos desde CSV (una línea por grupo, términos separados por coma)",
        "description": "Omite los grupos duplicados e informa de creados y omitidos.",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "language": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 5,
                    "default": "es"
                  },
                  "csv": {
                    "type": "string",
                    "minLength": 1
                  }
                },
                "required": [
                  "csv"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Resultado de la importación.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/rules": {
      "get": {
        "tags": [
          "Reglas"
        ],
        "summary": "Lista reglas",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 5
            },
            "description": "Filtra por idioma ISO (p. ej. `es`). Sin él devuelve todos los idiomas."
          }
        ],
        "responses": {
          "200": {
            "description": "Reglas.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Reglas"
        ],
        "summary": "Crea una regla",
        "description": "El `trigger` define cuándo se aplica (consulta + tipo de coincidencia). `actions` depende de `type`: BOOST → `boost[]` (modos term/value/range); PIN → `pin[]` (IDs de producto); HIDE → `hide[]`; REDIRECT → `redirect` (URL); BANNER → `banner`; FILTER → `filter`. Un boost en modo `value` exige un campo numérico del índice (la API lo comprueba).",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "language": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 5,
                    "default": "es"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "BOOST",
                      "PIN",
                      "HIDE",
                      "REDIRECT",
                      "BANNER",
                      "FILTER"
                    ]
                  },
                  "trigger": {
                    "type": "object",
                    "properties": {
                      "query": {
                        "type": "string",
                        "minLength": 1
                      },
                      "match": {
                        "type": "string",
                        "enum": [
                          "exact",
                          "contains",
                          "starts_with",
                          "regex"
                        ],
                        "default": "contains"
                      }
                    },
                    "required": [
                      "query"
                    ],
                    "additionalProperties": false
                  },
                  "actions": {
                    "type": "object",
                    "properties": {
                      "boost": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "field": {
                              "type": "string",
                              "minLength": 1
                            },
                            "mode": {
                              "type": "string",
                              "enum": [
                                "term",
                                "value",
                                "range"
                              ],
                              "default": "term"
                            },
                            "value": {
                              "type": "string"
                            },
                            "weight": {
                              "type": "number",
                              "minimum": 0.1,
                              "maximum": 100
                            },
                            "factor": {
                              "type": "number",
                              "minimum": 0.001,
                              "maximum": 100
                            },
                            "modifier": {
                              "type": "string",
                              "enum": [
                                "none",
                                "log1p",
                                "ln1p",
                                "sqrt"
                              ]
                            },
                            "missing": {
                              "type": "number"
                            },
                            "min": {
                              "type": "number"
                            },
                            "max": {
                              "type": "number"
                            }
                          },
                          "required": [
                            "field"
                          ],
                          "additionalProperties": false
                        }
                      },
                      "pin": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "hide": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "redirect": {
                        "type": "string"
                      },
                      "banner": {
                        "type": "object",
                        "properties": {
                          "imageUrl": {
                            "type": "string"
                          },
                          "link": {
                            "type": "string"
                          },
                          "position": {
                            "type": "string",
                            "enum": [
                              "top",
                              "bottom",
                              "sidebar"
                            ],
                            "default": "top"
                          }
                        },
                        "required": [
                          "imageUrl",
                          "link"
                        ],
                        "additionalProperties": false
                      },
                      "filter": {
                        "type": "object",
                        "additionalProperties": {
                          "anyOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          ]
                        }
                      }
                    },
                    "additionalProperties": false
                  },
                  "priority": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000,
                    "default": 0
                  },
                  "startDate": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "endDate": {
                    "type": "string",
                    "format": "date-time"
                  }
                },
                "required": [
                  "name",
                  "type",
                  "trigger",
                  "actions"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Regla creada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/rules/{ruleId}": {
      "get": {
        "tags": [
          "Reglas"
        ],
        "summary": "Detalle de una regla",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          },
          {
            "name": "ruleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la regla"
          }
        ],
        "responses": {
          "200": {
            "description": "Regla.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Reglas"
        ],
        "summary": "Modifica una regla",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          },
          {
            "name": "ruleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la regla"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "BOOST",
                      "PIN",
                      "HIDE",
                      "REDIRECT",
                      "BANNER",
                      "FILTER"
                    ]
                  },
                  "trigger": {
                    "type": "object",
                    "properties": {
                      "query": {
                        "type": "string",
                        "minLength": 1
                      },
                      "match": {
                        "type": "string",
                        "enum": [
                          "exact",
                          "contains",
                          "starts_with",
                          "regex"
                        ],
                        "default": "contains"
                      }
                    },
                    "required": [
                      "query"
                    ],
                    "additionalProperties": false
                  },
                  "actions": {
                    "type": "object",
                    "properties": {
                      "boost": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "field": {
                              "type": "string",
                              "minLength": 1
                            },
                            "mode": {
                              "type": "string",
                              "enum": [
                                "term",
                                "value",
                                "range"
                              ],
                              "default": "term"
                            },
                            "value": {
                              "type": "string"
                            },
                            "weight": {
                              "type": "number",
                              "minimum": 0.1,
                              "maximum": 100
                            },
                            "factor": {
                              "type": "number",
                              "minimum": 0.001,
                              "maximum": 100
                            },
                            "modifier": {
                              "type": "string",
                              "enum": [
                                "none",
                                "log1p",
                                "ln1p",
                                "sqrt"
                              ]
                            },
                            "missing": {
                              "type": "number"
                            },
                            "min": {
                              "type": "number"
                            },
                            "max": {
                              "type": "number"
                            }
                          },
                          "required": [
                            "field"
                          ],
                          "additionalProperties": false
                        }
                      },
                      "pin": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "hide": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "redirect": {
                        "type": "string"
                      },
                      "banner": {
                        "type": "object",
                        "properties": {
                          "imageUrl": {
                            "type": "string"
                          },
                          "link": {
                            "type": "string"
                          },
                          "position": {
                            "type": "string",
                            "enum": [
                              "top",
                              "bottom",
                              "sidebar"
                            ],
                            "default": "top"
                          }
                        },
                        "required": [
                          "imageUrl",
                          "link"
                        ],
                        "additionalProperties": false
                      },
                      "filter": {
                        "type": "object",
                        "additionalProperties": {
                          "anyOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          ]
                        }
                      }
                    },
                    "additionalProperties": false
                  },
                  "priority": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000
                  },
                  "active": {
                    "type": "boolean"
                  },
                  "startDate": {
                    "anyOf": [
                      {
                        "type": "string",
                        "format": "date-time"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "endDate": {
                    "anyOf": [
                      {
                        "type": "string",
                        "format": "date-time"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Regla actualizada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Reglas"
        ],
        "summary": "Elimina una regla",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          },
          {
            "name": "ruleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la regla"
          }
        ],
        "responses": {
          "200": {
            "description": "Regla eliminada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/filters": {
      "get": {
        "tags": [
          "Filtros"
        ],
        "summary": "Lista filtros configurados",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "responses": {
          "200": {
            "description": "Filtros.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Filtros"
        ],
        "summary": "Crea un filtro",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "field": {
                    "type": "string",
                    "minLength": 1
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "CATEGORY",
                      "ATTRIBUTE",
                      "FEATURE",
                      "MANUFACTURER",
                      "SUPPLIER",
                      "PRICE",
                      "CUSTOM"
                    ]
                  },
                  "selector": {
                    "type": "string",
                    "enum": [
                      "CHECKBOX",
                      "RADIO",
                      "RANGE",
                      "COLOR",
                      "SIZE"
                    ],
                    "default": "CHECKBOX"
                  },
                  "position": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "config": {
                    "type": "object",
                    "properties": {
                      "valueGroups": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "key": {
                              "type": "string",
                              "pattern": "^g_[A-Za-z0-9]{3,40}$"
                            },
                            "label": {
                              "type": "string",
                              "maxLength": 120
                            },
                            "values": {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1,
                                "maxLength": 200
                              },
                              "minItems": 1,
                              "maxItems": 200
                            }
                          },
                          "required": [
                            "values"
                          ],
                          "additionalProperties": false
                        },
                        "maxItems": 100,
                        "description": "Valores unificados: cada grupo agrupa varios valores de la faceta en una sola opción del filtro. `values` son los ids de valor (atributos/características) o el texto (marca, categoría…); `label` es opcional (sin él hereda la etiqueta del primer miembro); `key` la asigna la API si falta. Un valor solo puede estar en un grupo. `[]` elimina todos los grupos."
                      }
                    },
                    "additionalProperties": true
                  },
                  "labels": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "language": {
                          "type": "string"
                        },
                        "label": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "language",
                        "label"
                      ],
                      "additionalProperties": false
                    }
                  }
                },
                "required": [
                  "name",
                  "field",
                  "type"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Filtro creado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/filters/{filterId}": {
      "put": {
        "tags": [
          "Filtros"
        ],
        "summary": "Modifica un filtro",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          },
          {
            "name": "filterId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID del filtro"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "field": {
                    "type": "string",
                    "minLength": 1
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "CATEGORY",
                      "ATTRIBUTE",
                      "FEATURE",
                      "MANUFACTURER",
                      "SUPPLIER",
                      "PRICE",
                      "CUSTOM"
                    ]
                  },
                  "selector": {
                    "type": "string",
                    "enum": [
                      "CHECKBOX",
                      "RADIO",
                      "RANGE",
                      "COLOR",
                      "SIZE"
                    ]
                  },
                  "position": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "active": {
                    "type": "boolean"
                  },
                  "config": {
                    "type": "object",
                    "properties": {
                      "valueGroups": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "key": {
                              "type": "string",
                              "pattern": "^g_[A-Za-z0-9]{3,40}$"
                            },
                            "label": {
                              "type": "string",
                              "maxLength": 120
                            },
                            "values": {
                              "type": "array",
                              "items": {
                                "type": "string",
                                "minLength": 1,
                                "maxLength": 200
                              },
                              "minItems": 1,
                              "maxItems": 200
                            }
                          },
                          "required": [
                            "values"
                          ],
                          "additionalProperties": false
                        },
                        "maxItems": 100,
                        "description": "Valores unificados: cada grupo agrupa varios valores de la faceta en una sola opción del filtro. `values` son los ids de valor (atributos/características) o el texto (marca, categoría…); `label` es opcional (sin él hereda la etiqueta del primer miembro); `key` la asigna la API si falta. Un valor solo puede estar en un grupo. `[]` elimina todos los grupos."
                      }
                    },
                    "additionalProperties": true
                  },
                  "labels": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "language": {
                          "type": "string"
                        },
                        "label": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "language",
                        "label"
                      ],
                      "additionalProperties": false
                    }
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Filtro actualizado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Filtros"
        ],
        "summary": "Elimina un filtro",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          },
          {
            "name": "filterId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID del filtro"
          }
        ],
        "responses": {
          "200": {
            "description": "Filtro eliminado.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/filters/values": {
      "get": {
        "tags": [
          "Filtros"
        ],
        "summary": "Valores presentes en el índice para el campo de un filtro",
        "description": "Lista los valores distintos (id, etiqueta en el idioma pedido y nº de productos) que el índice de producto tiene para un campo de filtro. El `id` devuelto es lo que hay que guardar en `config.valueGroups[].values` para unificar valores: el `valueId` en atributos y características acotados (`attributes.<id>`, `features.<id>`), el propio texto en marca, fabricante, categorías o disponibilidad. Hasta 1000 valores, ordenados por nº de productos; `truncated: true` avisa si hay más.",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          },
          {
            "name": "field",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Campo del filtro (p. ej. `attributes.3`, `features.12`, `manufacturer`, `categories.name`)."
          },
          {
            "name": "lang",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Idioma de las etiquetas (índice de ese idioma). Por defecto `es` o el primero disponible."
          }
        ],
        "responses": {
          "200": {
            "description": "`{ field, language, values: [{ id, label, count }], truncated }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/filters/sync": {
      "post": {
        "tags": [
          "Filtros"
        ],
        "summary": "Materializa los filtros automáticos a partir de las facetas del índice",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "responses": {
          "200": {
            "description": "Resultado de la sincronización.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/filters/auto-sync": {
      "put": {
        "tags": [
          "Filtros"
        ],
        "summary": "Activa o desactiva la sincronización automática de filtros",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "enabled"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Estado resultante.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/filters/reorder": {
      "put": {
        "tags": [
          "Filtros"
        ],
        "summary": "Reordena los filtros",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filterIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "movedId": {
                    "type": "string"
                  }
                },
                "required": [
                  "filterIds"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Filtros reordenados.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/filters/sorts": {
      "get": {
        "tags": [
          "Filtros"
        ],
        "summary": "Lista las opciones de ordenación",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "responses": {
          "200": {
            "description": "Ordenaciones.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Filtros"
        ],
        "summary": "Crea una opción de ordenación",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "field": {
                    "type": "string"
                  },
                  "direction": {
                    "type": "string",
                    "enum": [
                      "asc",
                      "desc"
                    ]
                  },
                  "labels": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "language": {
                          "type": "string"
                        },
                        "label": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "required": [
                  "field"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Ordenación creada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/stores/{storeId}/filters/sorts/{sortId}": {
      "delete": {
        "tags": [
          "Filtros"
        ],
        "summary": "Elimina una opción de ordenación",
        "parameters": [
          {
            "name": "storeId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la tienda (cuid). Con una key ligada a una tienda, debe ser esa tienda."
          },
          {
            "name": "sortId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "ID de la ordenación"
          }
        ],
        "responses": {
          "200": {
            "description": "Ordenación eliminada.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Datos inválidos (mensaje con el campo y el motivo).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "API key ausente, inválida, desactivada o caducada.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Scope insuficiente, tienda distinta a la de la key, o operación vetada por API key (solo panel).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Recurso no encontrado o de otra cuenta.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Límite de peticiones superado (cabecera Retry-After).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      },
      "BearerApiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "La misma API key como token Bearer."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "retryAfter": {
            "type": "integer",
            "description": "Solo en 429: segundos hasta poder reintentar."
          }
        },
        "required": [
          "success",
          "error"
        ]
      }
    }
  }
}