{
  "openapi": "3.1.0",
  "info": {
    "title": "COR-X Storefront Read API",
    "version": "2026-08-28",
    "summary": "Public read-only catalog endpoints for the COR-X storefront, plus the UCP/MCP transaction endpoint.",
    "description": "Machine-readable surface of the COR-X store (www.corx.sk), a Slovak sports nutrition brand.\n\nThe catalog endpoints are public, unauthenticated, read-only Shopify storefront endpoints. They are the correct way for an agent to read current prices, variants and stock instead of scraping HTML.\n\nWrites (cart, checkout, order) are NOT in this REST surface. They live on the UCP/MCP endpoint documented under /api/ucp/mcp, which speaks JSON-RPC 2.0 over Streamable HTTP. Payment always requires explicit, contemporaneous buyer approval; agents must never finalize a payment autonomously.\n\nPrices are decimal strings in EUR. COR-X is currently not VAT-registered, so listed prices are final.",
    "contact": {
      "name": "COR-X",
      "email": "support@corx.sk",
      "url": "https://www.corx.sk/pages/contact"
    },
    "license": {
      "name": "Store terms of service",
      "url": "https://www.corx.sk/policies/terms-of-service"
    }
  },
  "servers": [
    {
      "url": "https://www.corx.sk",
      "description": "Production storefront"
    }
  ],
  "externalDocs": {
    "description": "Agent instructions, purchase rules and discovery endpoints",
    "url": "https://www.corx.sk/agents.md"
  },
  "tags": [
    {
      "name": "catalog",
      "description": "Read products, variants and prices."
    },
    {
      "name": "search",
      "description": "Free-text product search."
    },
    {
      "name": "commerce",
      "description": "Agent-driven cart and checkout via UCP/MCP."
    },
    {
      "name": "discovery",
      "description": "Machine-readable descriptions of this store."
    }
  ],
  "security": [],
  "paths": {
    "/collections/{collection_handle}/products.json": {
      "get": {
        "operationId": "listCollectionProducts",
        "tags": [
          "catalog"
        ],
        "summary": "List products in a collection",
        "description": "Returns every product in a collection with all variants, prices and per-variant availability. Use collection handle \"produkty\" for the curated COR-X range (7 products) or \"all\" for everything the store publishes. This is the recommended single call for \"what does COR-X sell and what does it cost\".",
        "security": [],
        "parameters": [
          {
            "name": "collection_handle",
            "in": "path",
            "required": true,
            "description": "Collection handle. Use \"produkty\" for the main range or \"all\" for every published product.",
            "schema": {
              "type": "string",
              "examples": [
                "produkty",
                "all"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Products per page, 1 to 250. Defaults to 30.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 250,
              "default": 30
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "One-based page number for paging through large collections.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of products.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "products"
                  ],
                  "properties": {
                    "products": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Product"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such collection handle.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/products/{product_handle}.json": {
      "get": {
        "operationId": "getProduct",
        "tags": [
          "catalog"
        ],
        "summary": "Get one product by handle",
        "description": "Returns a single product with its full variant list, option names, images and HTML description. Known handles: x-electrolytes, creatine-mx, x-pace, vykonnostny-set-x-electrolytes, bezecka-flasa, sportovy-shaker-flip-the-switch. See the caveat on Variant.available before reporting stock to a buyer.",
        "security": [],
        "parameters": [
          {
            "name": "product_handle",
            "in": "path",
            "required": true,
            "description": "URL handle of the product.",
            "schema": {
              "type": "string",
              "examples": [
                "x-electrolytes",
                "creatine-mx",
                "x-pace"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The product.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "product"
                  ],
                  "properties": {
                    "product": {
                      "$ref": "#/components/schemas/Product"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such product handle.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/search/suggest.json": {
      "get": {
        "operationId": "searchProducts",
        "tags": [
          "search"
        ],
        "summary": "Search products by free text",
        "description": "Free-text product search across the storefront. Slovak query terms work best, for example \"elektrolyty\", \"kreatin\", \"hydratacia\". Note that each result carries a verbose body field containing rendered HTML; prefer getProduct or listCollectionProducts when you need clean fields.",
        "security": [],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Search term.",
            "schema": {
              "type": "string",
              "examples": [
                "elektrolyty",
                "kreatin"
              ]
            }
          },
          {
            "name": "resources[type]",
            "in": "query",
            "required": false,
            "description": "Comma-separated resource types to search. Use \"product\" for catalog search.",
            "schema": {
              "type": "string",
              "default": "product",
              "examples": [
                "product",
                "product,article"
              ]
            }
          },
          {
            "name": "resources[limit]",
            "in": "query",
            "required": false,
            "description": "Maximum results per resource type, 1 to 10.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Search results grouped by resource type.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "resources": {
                      "type": "object",
                      "properties": {
                        "results": {
                          "type": "object",
                          "properties": {
                            "products": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "title": {
                                    "type": "string"
                                  },
                                  "handle": {
                                    "type": "string"
                                  },
                                  "url": {
                                    "type": "string"
                                  },
                                  "price": {
                                    "type": "string",
                                    "description": "Price in EUR as a decimal string."
                                  },
                                  "available": {
                                    "type": "boolean"
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "The q parameter was missing or empty.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ParamError"
                }
              }
            }
          }
        }
      }
    },
    "/api/ucp/mcp": {
      "post": {
        "operationId": "callCommerceTool",
        "tags": [
          "commerce"
        ],
        "summary": "Call a UCP commerce tool over MCP (JSON-RPC 2.0)",
        "description": "Universal Commerce Protocol endpoint, Streamable HTTP transport, JSON-RPC 2.0 envelope. Send method \"tools/list\" to discover the current tool set and its JSON Schemas; as of 28.08.2026 it exposes 13 tools: search_catalog, lookup_catalog, get_product, create_cart, get_cart, update_cart, cancel_cart, create_checkout, get_checkout, update_checkout, complete_checkout, cancel_checkout, get_order. Money values in tool responses are integer minor units paired with a currency code, so 3495 EUR means 34,95 €. complete_checkout requires explicit, contemporaneous buyer approval; never call it autonomously. Buyer-scoped reads such as get_order need a customer access token obtained through the customerAccountOAuth scheme below.",
        "security": [
          {},
          {
            "customerAccountOAuth": [
              "customer-account-mcp-api:full"
            ]
          }
        ],
        "parameters": [
          {
            "name": "Accept",
            "in": "header",
            "required": true,
            "description": "Must accept both JSON and SSE: \"application/json, text/event-stream\".",
            "schema": {
              "type": "string",
              "default": "application/json, text/event-stream"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              },
              "examples": {
                "listTools": {
                  "summary": "Discover the available tools",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "tools/list",
                    "params": {}
                  }
                },
                "searchCatalog": {
                  "summary": "Search the catalog",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/call",
                    "params": {
                      "name": "search_catalog",
                      "arguments": {
                        "query": "elektrolyty"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response. Tool-level failures arrive as a JSON-RPC error object, not an HTTP error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "422": {
            "description": "The JSON-RPC envelope was accepted but the call could not be served, for example an unknown method. The body is still a JSON-RPC response carrying an error object, so parse it rather than treating the HTTP status as opaque.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited per IP. Back off and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/ucp": {
      "get": {
        "operationId": "getUcpDiscovery",
        "tags": [
          "discovery"
        ],
        "summary": "Get the UCP merchant discovery profile",
        "description": "Returns supported UCP versions, service endpoints, capabilities and payment handlers. Start here before transacting so you bind to a version the store actually supports.",
        "security": [],
        "responses": {
          "200": {
            "description": "UCP merchant profile.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ucp": {
                      "type": "object",
                      "properties": {
                        "version": {
                          "type": "string",
                          "description": "Latest supported protocol date, e.g. 2026-04-08."
                        },
                        "supported_versions": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string"
                          }
                        },
                        "services": {
                          "type": "object"
                        },
                        "capabilities": {
                          "type": "object"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Returned if agent commerce is disabled for this merchant. Treat as \"UCP not available\".",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/ucp/{version}": {
      "get": {
        "operationId": "getUcpVersionProfile",
        "tags": [
          "discovery"
        ],
        "summary": "Get the UCP profile for one protocol version",
        "description": "Returns the merchant profile pinned to a specific protocol version, so an agent can bind to a version it implements instead of following whatever \"latest\" becomes. Supported values are listed in getUcpDiscovery under supported_versions; as of 28.08.2026 those are 2026-04-08 and 2026-01-23.",
        "security": [],
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "description": "Protocol version date.",
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$|^draft$",
              "examples": [
                "2026-04-08",
                "2026-01-23"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Version-pinned UCP profile.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Unsupported version. The response lists the versions that are supported.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UcpVersionError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "customerAccountOAuth": {
        "type": "oauth2",
        "description": "Shopify customer-account OAuth 2.0 with PKCE (S256). Needed ONLY for buyer-scoped operations, such as reading a buyer's own orders through the MCP endpoint. Every catalog endpoint in this spec is public and must be called with no credentials. Request the narrowest scope that covers the job. Protected-resource metadata (RFC 9728) is published at https://www.corx.sk/.well-known/oauth-protected-resource.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://shopify.com/authentication/89402573137/oauth/authorize",
            "tokenUrl": "https://shopify.com/authentication/89402573137/oauth/token",
            "refreshUrl": "https://shopify.com/authentication/89402573137/oauth/token",
            "scopes": {
              "openid": "Authenticate the buyer and receive an ID token.",
              "email": "Read the buyer's email address.",
              "customer-account-api:full": "Full access to the signed-in buyer's own account data via the Customer Account API.",
              "customer-account-mcp-api:full": "Full access to the signed-in buyer's own account data via the Customer Account MCP API."
            }
          }
        }
      }
    },
    "schemas": {
      "Product": {
        "type": "object",
        "description": "A storefront product with its purchasable variants.",
        "required": [
          "id",
          "title",
          "handle",
          "variants"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "description": "Shopify product id."
          },
          "title": {
            "type": "string"
          },
          "handle": {
            "type": "string",
            "description": "URL slug, stable identifier for getProduct."
          },
          "body_html": {
            "type": "string",
            "description": "Product description as HTML."
          },
          "vendor": {
            "type": "string"
          },
          "product_type": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "published_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "variants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Variant"
            }
          },
          "options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Option"
            }
          },
          "images": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Image"
            }
          }
        }
      },
      "Variant": {
        "type": "object",
        "description": "One purchasable variant, for example a single flavour.",
        "required": [
          "id",
          "title",
          "price",
          "available"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "description": "Variant id. This is what a cart line item references."
          },
          "title": {
            "type": "string",
            "description": "Variant name, typically the flavour or pack size."
          },
          "sku": {
            "type": [
              "string",
              "null"
            ]
          },
          "price": {
            "type": "string",
            "description": "Price in EUR as a decimal string, e.g. \"34.95\"."
          },
          "compare_at_price": {
            "type": [
              "string",
              "null"
            ],
            "description": "Reference price when discounted, else null."
          },
          "available": {
            "type": "boolean",
            "description": "Whether this variant can be added to a cart under its inventory policy. This is NOT proof that physical stock exists: a variant set to keep selling when out of stock stays true at zero and at negative inventory. These public endpoints do not expose inventory_quantity, so read true as \"orderable\", never as \"in stock\", and do not promise a buyer that an item is on the shelf."
          },
          "option1": {
            "type": [
              "string",
              "null"
            ]
          },
          "option2": {
            "type": [
              "string",
              "null"
            ]
          },
          "option3": {
            "type": [
              "string",
              "null"
            ]
          },
          "grams": {
            "type": "integer",
            "description": "Shipping weight in grams."
          },
          "requires_shipping": {
            "type": "boolean"
          },
          "taxable": {
            "type": "boolean"
          },
          "position": {
            "type": "integer"
          }
        }
      },
      "Option": {
        "type": "object",
        "description": "A variant axis, for example Flavour.",
        "properties": {
          "name": {
            "type": "string"
          },
          "position": {
            "type": "integer"
          },
          "values": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Image": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "src": {
            "type": "string",
            "format": "uri"
          },
          "width": {
            "type": "integer"
          },
          "height": {
            "type": "integer"
          },
          "position": {
            "type": "integer"
          },
          "variant_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "integer",
              "string"
            ],
            "description": "Correlation id echoed back in the response."
          },
          "method": {
            "type": "string",
            "description": "MCP method, e.g. \"tools/list\" or \"tools/call\".",
            "examples": [
              "tools/list",
              "tools/call"
            ]
          },
          "params": {
            "type": "object",
            "description": "Method parameters. For tools/call: name plus arguments."
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": [
          "jsonrpc"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "integer",
              "string",
              "null"
            ]
          },
          "result": {
            "type": "object",
            "description": "Present on success."
          },
          "error": {
            "type": "object",
            "description": "Present on failure.",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              },
              "data": {
                "type": "object"
              }
            }
          }
        }
      },
      "ParamError": {
        "type": "object",
        "description": "Structured parameter-validation error, returned with HTTP 422.",
        "properties": {
          "status": {
            "type": "integer",
            "examples": [
              422
            ]
          },
          "message": {
            "type": "string",
            "examples": [
              "Invalid parameter error"
            ]
          },
          "description": {
            "type": "string",
            "description": "Which parameter failed and why.",
            "examples": [
              "param is missing or the value is empty: q"
            ]
          }
        }
      },
      "UcpVersionError": {
        "type": "object",
        "description": "Unsupported protocol version, returned with HTTP 400.",
        "properties": {
          "error": {
            "type": "string",
            "examples": [
              "Unsupported version, supported versions are: draft, 2026-04-08, 2026-01-23"
            ]
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Generic error payload. Note that the catalog 404s (unknown product or collection handle) return an EMPTY body with an application/json content-type, so on those the HTTP status is the only signal. Do not parse the body before checking the status.",
        "properties": {
          "errors": {
            "type": [
              "string",
              "object"
            ],
            "description": "Human-readable error description when present."
          }
        }
      }
    }
  }
}