{"openapi":"3.1.0","info":{"title":"Pinturas Popular API","version":"1.0.0","summary":"Read-only catalogue API: products, colors, stores, news, tips, trends and FAQ.","description":"Stable, read-only, versioned API of Pinturas Popular (paint manufacturer, Dominican Republic).\n\nEvery request needs an API key, sent as `x-api-key: <key>` or `Authorization: Bearer <key>`. Request a key through /contacto.\n\nRate limit: 60 requests per 60 seconds per client IP. Every response carries RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset and RateLimit-Policy; a 429 adds Retry-After.\n\nErrors share one JSON shape: `{ \"error\": { \"code\", \"message\", \"hint\", \"docs\" }, \"statusCode\", \"statusMessage\" }`.\n\n## Versioning and deprecation\n\nThe major version is part of the path (`/api/v1`). Within v1 changes are backwards compatible only: new endpoints, new optional parameters and new response fields may appear, so clients must ignore unknown fields. Removing or renaming fields, parameters or endpoints, or changing their meaning, requires a new major version (`/api/v2`).\n\nBefore an operation or version is removed it is marked as deprecated in this document (`deprecated: true`) and its responses carry a `Deprecation` header (RFC 9745) and a `Sunset` header (RFC 8594) with the removal date, at least 180 days in advance, plus a `Link` with `rel=\"deprecation\"` to the migration notes. Nothing is deprecated today.\n\nHuman documentation: /docs/api.","license":{"name":"Proprietary","url":"https://pinturas-popular-staging.zunamicorp.com/legal/aviso-legal"},"contact":{"name":"Pinturas Popular","url":"https://pinturas-popular-staging.zunamicorp.com/contacto"}},"x-api-lifecycle":{"status":"stable","versioning":"path-major","deprecationHeaders":["Deprecation","Sunset"],"deprecationNoticeDays":180},"externalDocs":{"description":"API documentation","url":"https://pinturas-popular-staging.zunamicorp.com/docs/api"},"servers":[{"url":"https://pinturas-popular-staging.zunamicorp.com/api/v1","description":"API v1"}],"security":[{"ApiKeyHeader":[]},{"BearerAuth":[]}],"tags":[{"name":"Products","description":"Paint and coating catalogue."},{"name":"Colors","description":"Color chart, families and lines."},{"name":"Stores","description":"Points of sale and distributors."},{"name":"Content","description":"News, tips, trends and frequently asked questions."}],"paths":{"/products":{"get":{"operationId":"listProducts","tags":["Products"],"summary":"List products","description":"Paginated list of published products. Use it to search the catalogue by name or SKU, or to filter by business unit, category, finish and usage (interior, exterior). Sorted like the website catalogue.","parameters":[{"name":"unidad","in":"query","required":false,"description":"Business unit slug, e.g. arquitectonico, industrial, madera.","schema":{"type":"string"}},{"name":"categoria","in":"query","required":false,"description":"Product category slug.","schema":{"type":"string"}},{"name":"finish","in":"query","required":false,"description":"Finish filter (case-insensitive contains). Repeat the parameter to match any of several finishes.","schema":{"type":"array","items":{"type":"string"}},"style":"form","explode":true},{"name":"is_ready_mix","in":"query","required":false,"description":"Only ready-mix products. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"is_color_matcher","in":"query","required":false,"description":"Only products available in the color matcher. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"has_color_base","in":"query","required":false,"description":"Only products with a tintable color base. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"is_third_party","in":"query","required":false,"description":"Only third-party brand products. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"interior","in":"query","required":false,"description":"Only products suitable for interiors. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"exterior","in":"query","required":false,"description":"Only products suitable for exteriors. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"slug","in":"query","required":false,"description":"Exact product slug.","schema":{"type":"string"}},{"name":"search","in":"query","required":false,"description":"Case-insensitive \"contains\" search over product name and SKU.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of rows to return. Defaults to 12 when missing, empty or not a number.","schema":{"type":"integer","minimum":1,"default":12}},{"name":"offset","in":"query","required":false,"description":"Number of rows to skip for pagination. Defaults to 0.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Paginated list of products.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ProductListItem"}},"meta":{"$ref":"#/components/schemas/CollectionMeta"}}}}}},"400":{"description":"Invalid query parameters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_parameters","message":"Invalid query parameters","hint":"Check the query parameters against the OpenAPI document at /openapi.json.","docs":"/docs/api"},"statusCode":400,"statusMessage":"Invalid query parameters","message":"Invalid query parameters","data":{"issues":[{"path":"lat","message":"Invalid input: expected number, received NaN"}]}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}},"/products/{slug}":{"get":{"operationId":"getProductBySlug","tags":["Products"],"summary":"Get a product","description":"Full detail of one published product: technical data, pack sizes, compatible colors, attributes, technical and safety sheets. Returns 404 when the slug does not exist or is unpublished.","parameters":[{"name":"slug","in":"path","required":true,"description":"URL slug of the product (lowercase letters, digits and hyphens).","schema":{"type":"string","pattern":"^[a-z0-9-]+$"}}],"responses":{"200":{"description":"The product.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ProductDetail"}}}}}},"400":{"description":"Invalid query parameters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_parameters","message":"Invalid query parameters","hint":"Check the query parameters against the OpenAPI document at /openapi.json.","docs":"/docs/api"},"statusCode":400,"statusMessage":"Invalid query parameters","message":"Invalid query parameters","data":{"issues":[{"path":"lat","message":"Invalid input: expected number, received NaN"}]}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"404":{"description":"Resource not found or unpublished.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"not_found","message":"Product not found","hint":"Check the path and slug. The list of endpoints is in /openapi.json.","docs":"/docs/api"},"statusCode":404,"statusMessage":"Product not found","message":"Product not found"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}},"/colors":{"get":{"operationId":"listColors","tags":["Colors"],"summary":"List colors","description":"Paginated color chart. Filter by family, undertone, category, base or line, or search by name or code.","parameters":[{"name":"color_family_id","in":"query","required":false,"description":"Color family id (see listColorFamilies).","schema":{"type":"string"}},{"name":"undertone","in":"query","required":false,"description":"Undertone filter. Repeat the parameter to match any of several values.","schema":{"type":"array","items":{"type":"string","enum":["cool","neutral","warm"]}},"style":"form","explode":true},{"name":"is_best_seller","in":"query","required":false,"description":"Only best-selling colors. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"category_id","in":"query","required":false,"description":"Color category id.","schema":{"type":"string"}},{"name":"base_id","in":"query","required":false,"description":"Color base id.","schema":{"type":"string"}},{"name":"color_line_id","in":"query","required":false,"description":"Color line id (see listColorLines).","schema":{"type":"string"}},{"name":"search","in":"query","required":false,"description":"Case-insensitive \"contains\" search over color name and code.","schema":{"type":"string"}},{"name":"sort_by","in":"query","required":false,"description":"Sort order: `code`, `family`, or name when omitted or any other value.","schema":{"type":"string","enum":["code","family","name"]}},{"name":"limit","in":"query","required":false,"description":"Maximum number of rows to return. Defaults to 30 when missing, empty or not a number.","schema":{"type":"integer","minimum":1,"default":30}},{"name":"offset","in":"query","required":false,"description":"Number of rows to skip for pagination. Defaults to 0.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Paginated list of colors.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Color"}},"meta":{"$ref":"#/components/schemas/CollectionMeta"}}}}}},"400":{"description":"Invalid query parameters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_parameters","message":"Invalid query parameters","hint":"Check the query parameters against the OpenAPI document at /openapi.json.","docs":"/docs/api"},"statusCode":400,"statusMessage":"Invalid query parameters","message":"Invalid query parameters","data":{"issues":[{"path":"lat","message":"Invalid input: expected number, received NaN"}]}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}},"/color-families":{"get":{"operationId":"listColorFamilies","tags":["Colors"],"summary":"List color families","description":"All color families with their swatch. Not paginated. Use the ids to filter listColors.","parameters":[],"responses":{"200":{"description":"All color families.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ColorFamily"}},"meta":{"$ref":"#/components/schemas/CollectionMeta"}}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}},"/color-lines":{"get":{"operationId":"listColorLines","tags":["Colors"],"summary":"List color lines","description":"All color lines, optionally restricted to one color category. Not paginated.","parameters":[{"name":"category_id","in":"query","required":false,"description":"Color category id (matches the line base category).","schema":{"type":"string"}}],"responses":{"200":{"description":"All color lines.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ColorLine"}},"meta":{"$ref":"#/components/schemas/CollectionMeta"}}}}}},"400":{"description":"Invalid query parameters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_parameters","message":"Invalid query parameters","hint":"Check the query parameters against the OpenAPI document at /openapi.json.","docs":"/docs/api"},"statusCode":400,"statusMessage":"Invalid query parameters","message":"Invalid query parameters","data":{"issues":[{"path":"lat","message":"Invalid input: expected number, received NaN"}]}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}},"/stores":{"get":{"operationId":"listStores","tags":["Stores"],"summary":"List or find nearby stores","description":"Points of sale. Standard mode filters by region, services and category. Nearby mode starts when both `lat` and `lng` are sent: rows are ordered by distance and include `distance_km`. Not paginated.","parameters":[{"name":"region","in":"query","required":false,"description":"Region name (contains).","schema":{"type":"string"}},{"name":"has_redimix","in":"query","required":false,"description":"Only stores with Redimix service. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"has_color_matcher","in":"query","required":false,"description":"Only stores with a color matcher. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"has_rostolin","in":"query","required":false,"description":"Only stores selling Rostolin. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"category","in":"query","required":false,"description":"Store category id.","schema":{"type":"string"}},{"name":"search","in":"query","required":false,"description":"Case-insensitive \"contains\" search over store name and address.","schema":{"type":"string"}},{"name":"lat","in":"query","required":false,"description":"Latitude in decimal degrees. Send together with lng to enable nearby mode.","schema":{"type":"number","minimum":-90,"maximum":90}},{"name":"lng","in":"query","required":false,"description":"Longitude in decimal degrees. Send together with lat to enable nearby mode.","schema":{"type":"number","minimum":-180,"maximum":180}},{"name":"radiusKm","in":"query","required":false,"description":"Nearby mode only: maximum distance in km.","schema":{"type":"number","exclusiveMinimum":0}},{"name":"limit","in":"query","required":false,"description":"Nearby mode only: maximum number of stores.","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"Stores matching the filters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Store"}},"meta":{"$ref":"#/components/schemas/CollectionMeta"}}}}}},"400":{"description":"Invalid query parameters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_parameters","message":"Invalid query parameters","hint":"Check the query parameters against the OpenAPI document at /openapi.json.","docs":"/docs/api"},"statusCode":400,"statusMessage":"Invalid query parameters","message":"Invalid query parameters","data":{"issues":[{"path":"lat","message":"Invalid input: expected number, received NaN"}]}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}},"/noticias":{"get":{"operationId":"listNoticias","tags":["Content"],"summary":"List news","description":"Paginated company news, newest first.","parameters":[{"name":"categoria","in":"query","required":false,"description":"Slug of the news category to filter by.","schema":{"type":"string"}},{"name":"featured","in":"query","required":false,"description":"Only featured news. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"announcement","in":"query","required":false,"description":"Only announcements. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"search","in":"query","required":false,"description":"Case-insensitive \"contains\" search over title and excerpt.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of rows to return. Defaults to 12 when missing, empty or not a number.","schema":{"type":"integer","minimum":1,"default":12}},{"name":"offset","in":"query","required":false,"description":"Number of rows to skip for pagination. Defaults to 0.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Paginated list of news.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/NoticiaListItem"}},"meta":{"$ref":"#/components/schemas/CollectionMeta"}}}}}},"400":{"description":"Invalid query parameters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_parameters","message":"Invalid query parameters","hint":"Check the query parameters against the OpenAPI document at /openapi.json.","docs":"/docs/api"},"statusCode":400,"statusMessage":"Invalid query parameters","message":"Invalid query parameters","data":{"issues":[{"path":"lat","message":"Invalid input: expected number, received NaN"}]}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}},"/noticias/{slug}":{"get":{"operationId":"getNoticiaBySlug","tags":["Content"],"summary":"Get a news article","description":"Full news article with HTML content. Returns 404 when not found or unpublished.","parameters":[{"name":"slug","in":"path","required":true,"description":"URL slug of the news article (lowercase letters, digits and hyphens).","schema":{"type":"string","pattern":"^[a-z0-9-]+$"}}],"responses":{"200":{"description":"The news article.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/NoticiaDetail"}}}}}},"400":{"description":"Invalid query parameters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_parameters","message":"Invalid query parameters","hint":"Check the query parameters against the OpenAPI document at /openapi.json.","docs":"/docs/api"},"statusCode":400,"statusMessage":"Invalid query parameters","message":"Invalid query parameters","data":{"issues":[{"path":"lat","message":"Invalid input: expected number, received NaN"}]}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"404":{"description":"Resource not found or unpublished.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"not_found","message":"Product not found","hint":"Check the path and slug. The list of endpoints is in /openapi.json.","docs":"/docs/api"},"statusCode":404,"statusMessage":"Product not found","message":"Product not found"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}},"/tips":{"get":{"operationId":"listTips","tags":["Content"],"summary":"List painting tips","description":"Paginated how-to guides about preparing surfaces and painting, newest first.","parameters":[{"name":"categoria","in":"query","required":false,"description":"Slug of the tip category to filter by.","schema":{"type":"string"}},{"name":"featured","in":"query","required":false,"description":"Only featured tips. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"search","in":"query","required":false,"description":"Case-insensitive \"contains\" search over title and excerpt.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of rows to return. Defaults to 12 when missing, empty or not a number.","schema":{"type":"integer","minimum":1,"default":12}},{"name":"offset","in":"query","required":false,"description":"Number of rows to skip for pagination. Defaults to 0.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Paginated list of tips.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TipListItem"}},"meta":{"$ref":"#/components/schemas/CollectionMeta"}}}}}},"400":{"description":"Invalid query parameters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_parameters","message":"Invalid query parameters","hint":"Check the query parameters against the OpenAPI document at /openapi.json.","docs":"/docs/api"},"statusCode":400,"statusMessage":"Invalid query parameters","message":"Invalid query parameters","data":{"issues":[{"path":"lat","message":"Invalid input: expected number, received NaN"}]}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}},"/tips/{slug}":{"get":{"operationId":"getTipBySlug","tags":["Content"],"summary":"Get a tip","description":"Full tip with HTML content. Returns 404 when not found or unpublished.","parameters":[{"name":"slug","in":"path","required":true,"description":"URL slug of the tip (lowercase letters, digits and hyphens).","schema":{"type":"string","pattern":"^[a-z0-9-]+$"}}],"responses":{"200":{"description":"The tip.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/TipDetail"}}}}}},"400":{"description":"Invalid query parameters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_parameters","message":"Invalid query parameters","hint":"Check the query parameters against the OpenAPI document at /openapi.json.","docs":"/docs/api"},"statusCode":400,"statusMessage":"Invalid query parameters","message":"Invalid query parameters","data":{"issues":[{"path":"lat","message":"Invalid input: expected number, received NaN"}]}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"404":{"description":"Resource not found or unpublished.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"not_found","message":"Product not found","hint":"Check the path and slug. The list of endpoints is in /openapi.json.","docs":"/docs/api"},"statusCode":404,"statusMessage":"Product not found","message":"Product not found"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}},"/tendencias":{"get":{"operationId":"listTendencias","tags":["Content"],"summary":"List color trends","description":"Paginated color and decoration trends, newest first. Each row includes its gallery and color palette.","parameters":[{"name":"categoria","in":"query","required":false,"description":"Slug of the trend category to filter by.","schema":{"type":"string"}},{"name":"featured","in":"query","required":false,"description":"Only featured trends. The filter is applied only when the value is exactly `true`; any other value is ignored.","schema":{"type":"string","enum":["true"]}},{"name":"search","in":"query","required":false,"description":"Case-insensitive \"contains\" search over title and excerpt.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of rows to return. Defaults to 6 when missing, empty or not a number.","schema":{"type":"integer","minimum":1,"default":6}},{"name":"offset","in":"query","required":false,"description":"Number of rows to skip for pagination. Defaults to 0.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Paginated list of trends.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TendenciaListItem"}},"meta":{"$ref":"#/components/schemas/CollectionMeta"}}}}}},"400":{"description":"Invalid query parameters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_parameters","message":"Invalid query parameters","hint":"Check the query parameters against the OpenAPI document at /openapi.json.","docs":"/docs/api"},"statusCode":400,"statusMessage":"Invalid query parameters","message":"Invalid query parameters","data":{"issues":[{"path":"lat","message":"Invalid input: expected number, received NaN"}]}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}},"/tendencias/{slug}":{"get":{"operationId":"getTendenciaBySlug","tags":["Content"],"summary":"Get a color trend","description":"Full trend with content, gallery and palette. Returns 404 when not found or unpublished.","parameters":[{"name":"slug","in":"path","required":true,"description":"URL slug of the trend (lowercase letters, digits and hyphens).","schema":{"type":"string","pattern":"^[a-z0-9-]+$"}}],"responses":{"200":{"description":"The trend.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/TendenciaDetail"}}}}}},"400":{"description":"Invalid query parameters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_parameters","message":"Invalid query parameters","hint":"Check the query parameters against the OpenAPI document at /openapi.json.","docs":"/docs/api"},"statusCode":400,"statusMessage":"Invalid query parameters","message":"Invalid query parameters","data":{"issues":[{"path":"lat","message":"Invalid input: expected number, received NaN"}]}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"404":{"description":"Resource not found or unpublished.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"not_found","message":"Product not found","hint":"Check the path and slug. The list of endpoints is in /openapi.json.","docs":"/docs/api"},"statusCode":404,"statusMessage":"Product not found","message":"Product not found"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}},"/faq":{"get":{"operationId":"listFaq","tags":["Content"],"summary":"List frequently asked questions","description":"Paginated FAQ entries ordered as on the website. Use it to answer customer questions about products and purchases.","parameters":[{"name":"categoria","in":"query","required":false,"description":"Slug of the FAQ category to filter by.","schema":{"type":"string"}},{"name":"search","in":"query","required":false,"description":"Case-insensitive \"contains\" search over question and answer.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Maximum number of rows to return. Defaults to 12 when missing, empty or not a number.","schema":{"type":"integer","minimum":1,"default":12}},{"name":"offset","in":"query","required":false,"description":"Number of rows to skip for pagination. Defaults to 0.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Paginated list of FAQ entries.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FaqItem"}},"meta":{"$ref":"#/components/schemas/CollectionMeta"}}}}}},"400":{"description":"Invalid query parameters.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_parameters","message":"Invalid query parameters","hint":"Check the query parameters against the OpenAPI document at /openapi.json.","docs":"/docs/api"},"statusCode":400,"statusMessage":"Invalid query parameters","message":"Invalid query parameters","data":{"issues":[{"path":"lat","message":"Invalid input: expected number, received NaN"}]}}}}},"401":{"description":"Missing or invalid API key.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"invalid_api_key","message":"Invalid or missing API key","hint":"Send your key in the \"x-api-key\" header or as \"Authorization: Bearer <key>\". Request a key through /contacto.","docs":"/docs/api"},"statusCode":401,"statusMessage":"Invalid or missing API key","message":"Invalid or missing API key"}}}},"429":{"description":"Rate limit exceeded.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"Too Many Requests","hint":"Too many requests. Wait the number of seconds in the Retry-After header before retrying.","docs":"/docs/api"},"statusCode":429,"statusMessage":"Too Many Requests","message":"Too Many Requests"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"internal_error","message":"Internal server error","hint":"Unexpected server error. Retry later; if it persists, contact us through /contacto.","docs":"/docs/api"},"statusCode":500,"statusMessage":"Internal server error","message":"Internal server error"}}}}}}}},"components":{"securitySchemes":{"ApiKeyHeader":{"type":"apiKey","in":"header","name":"x-api-key","description":"Static API key. Request one through /contacto."},"BearerAuth":{"type":"http","scheme":"bearer","description":"The same API key sent as a bearer token."}},"headers":{"RateLimitLimit":{"description":"Requests allowed in the current window.","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimitReset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"RateLimitPolicy":{"description":"Quota policy, e.g. `60;w=60`.","schema":{"type":"string"}},"RetryAfter":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"schemas":{"CollectionMeta":{"type":"object","required":["total","limit","offset"],"properties":{"total":{"type":"integer","description":"Rows matching the filters before pagination."},"limit":{"type":"integer","description":"Page size applied. Non-paginated collections report the number of rows."},"offset":{"type":"integer","description":"Rows skipped. Always 0 for non-paginated collections."}}},"Error":{"type":"object","required":["error","statusCode","statusMessage"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable machine-readable error code.","examples":["invalid_api_key","invalid_parameters","invalid_request","unauthorized","forbidden","not_found","method_not_allowed","gone","payload_too_large","unsupported_media_type","unprocessable_entity","rate_limited","internal_error","bad_gateway","service_unavailable"]},"message":{"type":"string","description":"Human-readable summary."},"hint":{"type":"string","description":"How to fix or work around the error."},"docs":{"type":"string","description":"Link to the human documentation."}}},"statusCode":{"type":"integer","description":"HTTP status code (kept for backwards compatibility)."},"statusMessage":{"type":"string","description":"Same as error.message (kept for backwards compatibility)."},"message":{"type":"string","description":"Same as error.message (kept for backwards compatibility)."},"data":{"type":"object","description":"Extra details. For invalid_parameters it contains `issues` with the failing parameter paths.","additionalProperties":true}}},"CategoryRef":{"type":"object","required":["id","name","slug"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"description":"Category reference."},"BusinessUnitRef":{"type":"object","required":["id","name","slug"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"description":"Business unit reference."},"FileRef":{"type":"object","description":"Stored file. Download it from /api/assets/{id}.","required":["id"],"properties":{"id":{"type":"string"},"filename_disk":{"type":["string","null"]}}},"PackSizeRef":{"type":"object","properties":{"pack_size_id":{"oneOf":[{"type":"object","required":["id","label"],"properties":{"id":{"type":"string"},"label":{"type":"string"},"unit_type":{"type":["string","null"]}},"additionalProperties":true},{"type":"null"}]}}},"ProductListItem":{"type":"object","required":["id","name","slug"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"sku":{"type":["string","null"]},"finish":{"type":["string","null"],"description":"Finish label, e.g. mate, satinado, brillante."},"coverage_m2_per_gallon":{"type":["number","null"],"description":"Coverage in square metres per gallon."},"is_ready_mix":{"type":"boolean"},"is_color_matcher":{"type":"boolean"},"has_color_base":{"type":"boolean"},"is_third_party":{"type":"boolean"},"brand":{"type":["string","null"]},"interior":{"type":"boolean"},"exterior":{"type":"boolean"},"image":{"oneOf":[{"$ref":"#/components/schemas/FileRef"},{"type":"null"}]},"category_id":{"oneOf":[{"$ref":"#/components/schemas/CategoryRef"},{"type":"null"}]},"business_unit_id":{"oneOf":[{"$ref":"#/components/schemas/BusinessUnitRef"},{"type":"null"}]},"pack_sizes":{"type":"array","items":{"$ref":"#/components/schemas/PackSizeRef"}}}},"ProductDetail":{"type":"object","description":"Full product with technical data and every relation (colors, pack sizes, attributes, files).","required":["id","name","slug"],"additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"sku":{"type":["string","null"]},"description":{"type":["string","null"],"description":"HTML description."},"short_description":{"type":["string","null"]},"coverage":{"type":["string","null"]},"drying_time":{"type":["string","null"]},"finish":{"type":["string","null"]},"application":{"type":["string","null"]},"features":{},"usage":{"type":["string","null"]},"surface_preparation":{"type":["string","null"]},"product_preparation":{"type":["string","null"]},"interior":{"type":["boolean","null"]},"exterior":{"type":["boolean","null"]},"image":{"oneOf":[{"$ref":"#/components/schemas/FileRef"},{"type":"null"}]},"technical_sheet":{"oneOf":[{"$ref":"#/components/schemas/FileRef"},{"type":"null"}]},"safety_sheet":{"oneOf":[{"$ref":"#/components/schemas/FileRef"},{"type":"null"}]},"category_id":{"oneOf":[{"$ref":"#/components/schemas/CategoryRef"},{"type":"null"}]},"business_unit_id":{"oneOf":[{"$ref":"#/components/schemas/BusinessUnitRef"},{"type":"null"}]},"pack_sizes":{"type":"array","items":{"type":"object","additionalProperties":true}},"colors":{"type":"array","items":{"type":"object","additionalProperties":true}},"attributes":{"type":"array","items":{"type":"object","additionalProperties":true}},"files":{"type":"array","items":{"type":"object","additionalProperties":true}}}},"Color":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"code":{"type":["string","null"],"description":"Commercial color code. Not unique."},"hex":{"type":["string","null"],"description":"Hex value, e.g. #F7C03B."},"undertone":{"type":["string","null"],"description":"cool, neutral or warm."},"is_best_seller":{"type":["boolean","null"]},"color_family_id":{"type":["string","null"]},"category_id":{"type":["string","null"]},"base_id":{"type":["string","null"]}}},"ColorFamily":{"type":"object","required":["id","name","slug","swatch_hex"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"swatch_hex":{"type":"string"},"has_outline":{"type":["boolean","null"]}}},"ColorLine":{"type":"object","required":["id","name","slug"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"sort":{"type":["integer","null"]},"base_id":{"oneOf":[{"type":"object","properties":{"name":{"type":["string","null"]},"category_id":{"type":["string","null"]}}},{"type":"null"}]}}},"Store":{"type":"object","required":["id","name","address_full","phone","latitude","longitude"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"address_short":{"type":["string","null"]},"address_full":{"type":"string"},"phone":{"type":"string"},"latitude":{"type":"string","description":"Decimal degrees as a string (numeric(10,6))."},"longitude":{"type":"string","description":"Decimal degrees as a string (numeric(10,6))."},"has_redimix":{"type":["boolean","null"]},"has_color_matcher":{"type":["boolean","null"]},"has_rostolin":{"type":"boolean"},"category_id":{"type":["string","null"]},"region":{"type":["string","null"]},"district":{"type":["string","null"]},"hours_weekday_open":{"type":["string","null"]},"hours_weekday_close":{"type":["string","null"]},"hours_saturday_open":{"type":["string","null"]},"hours_saturday_close":{"type":["string","null"]},"hours_sunday_open":{"type":["string","null"]},"hours_sunday_close":{"type":["string","null"]},"distance_km":{"type":"number","description":"Only in nearby mode (lat and lng sent): geodesic distance in km, 2 decimals."}}},"NoticiaListItem":{"type":"object","required":["id","title","slug"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"slug":{"type":"string"},"excerpt":{"type":["string","null"]},"is_featured":{"type":["boolean","null"]},"is_announcement":{"type":["boolean","null"]},"video_url":{"type":["string","null"]},"date_created":{"type":["string","null"],"format":"date-time"},"category_id":{"oneOf":[{"$ref":"#/components/schemas/CategoryRef"},{"type":"null"}]},"featured_image":{"oneOf":[{"$ref":"#/components/schemas/FileRef"},{"type":"null"}]}}},"NoticiaDetail":{"allOf":[{"$ref":"#/components/schemas/NoticiaListItem"},{"type":"object","additionalProperties":true,"properties":{"content":{"type":["string","null"],"description":"HTML body."},"date_updated":{"type":["string","null"],"format":"date-time"},"meta_title":{"type":["string","null"]},"meta_description":{"type":["string","null"]},"og_image":{"type":["string","null"]}}}]},"TipListItem":{"type":"object","required":["id","title","slug"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"slug":{"type":"string"},"excerpt":{"type":["string","null"]},"author":{"type":["string","null"]},"reading_time":{"type":["integer","null"],"description":"Estimated reading time in minutes."},"is_featured":{"type":["boolean","null"]},"date_created":{"type":["string","null"],"format":"date-time"},"category_id":{"oneOf":[{"$ref":"#/components/schemas/CategoryRef"},{"type":"null"}]},"featured_image":{"oneOf":[{"$ref":"#/components/schemas/FileRef"},{"type":"null"}]}}},"TipDetail":{"allOf":[{"$ref":"#/components/schemas/TipListItem"},{"type":"object","additionalProperties":true,"properties":{"content":{"type":["string","null"],"description":"HTML body."},"date_updated":{"type":["string","null"],"format":"date-time"},"meta_title":{"type":["string","null"]},"meta_description":{"type":["string","null"]},"og_image":{"type":["string","null"]}}}]},"TendenciaColor":{"type":"object","properties":{"colors_id":{"oneOf":[{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"code":{"type":["string","null"]},"hex":{"type":["string","null"]}}},{"type":"null"}]}}},"TendenciaListItem":{"type":"object","required":["id","title","slug","gallery","colors"],"properties":{"id":{"type":"string"},"title":{"type":"string"},"slug":{"type":"string"},"excerpt":{"type":["string","null"]},"is_featured":{"type":["boolean","null"]},"date_created":{"type":["string","null"],"format":"date-time"},"category_id":{"oneOf":[{"$ref":"#/components/schemas/CategoryRef"},{"type":"null"}]},"featured_image":{"oneOf":[{"type":"object","required":["id"],"properties":{"id":{"type":"string"}}},{"type":"null"}]},"gallery":{"type":"array","items":{"type":"object","properties":{"directus_files_id":{"oneOf":[{"type":"object","required":["id"],"properties":{"id":{"type":"string"}},"additionalProperties":true},{"type":"null"}]}}}},"colors":{"type":"array","items":{"$ref":"#/components/schemas/TendenciaColor"}}}},"TendenciaDetail":{"allOf":[{"$ref":"#/components/schemas/TendenciaListItem"},{"type":"object","additionalProperties":true,"properties":{"content":{"type":["string","null"],"description":"HTML body."},"meta_title":{"type":["string","null"]},"meta_description":{"type":["string","null"]}}}]},"FaqItem":{"type":"object","required":["id","question","answer"],"properties":{"id":{"type":"string"},"question":{"type":"string"},"answer":{"type":"string","description":"HTML answer."},"sort":{"type":["integer","null"]},"category_id":{"oneOf":[{"$ref":"#/components/schemas/CategoryRef"},{"type":"null"}]}}}}}}