{
  "openapi": "3.1.0",
  "info": {
    "title": "Vinos.gt Storefront API",
    "version": "1.0.0",
    "summary": "Public, zero-auth read API and agentic commerce endpoints for vinos.gt",
    "description": "Vinos.gt is an online wine store in Guatemala (Shopify storefront). This specification covers two surfaces:\n\n1. **Read-only storefront JSON** — no authentication, no API key, no sandbox signup needed. Any agent can call these endpoints immediately. Prices are in GTQ (quetzales).\n2. **Agentic commerce (UCP/MCP)** — cart and checkout are performed through the Universal Commerce Protocol MCP endpoint (`POST /api/ucp/mcp`, JSON-RPC 2.0). Discover capabilities at `GET /.well-known/ucp`. **Payment always requires explicit buyer approval** — agents must never complete payment autonomously.\n\n**Permission model (least privilege):** catalog reads require no credentials or scopes; cart mutations are anonymous and session-scoped (cart token); checkout completion is gated by human buyer approval inside the UCP flow. There is no OAuth surface because no privileged scopes exist to request.\n\n**Errors:** all `.json`/`.js` endpoints return structured JSON errors (e.g. `422 {\"status\": 422, \"message\": \"Cart Error\", \"description\": \"...\"}`). The MCP endpoint returns JSON-RPC error objects with `code`, `message` and `data` hints.\n\n**Rate limits:** endpoints are rate-limited per IP. On `429`, back off exponentially.\n\nMore for agents: https://vinos.gt/llms.txt · https://vinos.gt/agents.md · https://vinos.gt/pages/developers",
    "contact": {
      "name": "Vinos.gt",
      "email": "pedidos@vinos.gt",
      "url": "https://vinos.gt/pages/contact"
    }
  },
  "servers": [
    { "url": "https://vinos.gt", "description": "Production storefront" }
  ],
  "security": [],
  "paths": {
    "/products.json": {
      "get": {
        "operationId": "listProducts",
        "summary": "List products",
        "description": "Paginated list of all published products with variants, prices (GTQ) and images. No authentication required.",
        "parameters": [
          { "name": "limit", "in": "query", "description": "Products per page (max 250).", "schema": { "type": "integer", "minimum": 1, "maximum": 250, "default": 30 } },
          { "name": "page", "in": "query", "description": "Page number, starting at 1.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }
        ],
        "responses": {
          "200": {
            "description": "Product list",
            "content": { "application/json": { "schema": { "type": "object", "required": ["products"], "properties": { "products": { "type": "array", "items": { "$ref": "#/components/schemas/Product" } } } } } }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/products/{handle}.json": {
      "get": {
        "operationId": "getProduct",
        "summary": "Get a product by handle",
        "description": "Full detail for one product, including all variants with price and availability. The handle is the last path segment of the product URL.",
        "parameters": [
          { "name": "handle", "in": "path", "required": true, "description": "Product handle, e.g. `gran-sasso-vino-tinto-montepulciano-dabruzzo-doc-750ml`.", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Product detail", "content": { "application/json": { "schema": { "type": "object", "required": ["product"], "properties": { "product": { "$ref": "#/components/schemas/Product" } } } } } },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/collections/{handle}/products.json": {
      "get": {
        "operationId": "listCollectionProducts",
        "summary": "List products in a collection",
        "description": "Products belonging to a collection. Useful collections: `all`, `tintos`, `blancos`, `rosados`, `espana`, `italia`, `argentina`, `estados-unidos`, `regalo-corporativo`.",
        "parameters": [
          { "name": "handle", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 250, "default": 30 } },
          { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "default": 1 } }
        ],
        "responses": {
          "200": { "description": "Products in the collection", "content": { "application/json": { "schema": { "type": "object", "required": ["products"], "properties": { "products": { "type": "array", "items": { "$ref": "#/components/schemas/Product" } } } } } } },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/search/suggest.json": {
      "get": {
        "operationId": "searchSuggest",
        "summary": "Search products",
        "description": "Predictive search over the catalog. Returns matching products with price and URL.",
        "parameters": [
          { "name": "q", "in": "query", "required": true, "description": "Search terms, e.g. `malbec`.", "schema": { "type": "string" } },
          { "name": "resources[type]", "in": "query", "required": true, "description": "Resource types to search.", "schema": { "type": "string", "enum": ["product", "page", "article", "collection", "query"], "default": "product" } },
          { "name": "resources[limit]", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 10, "default": 10 } }
        ],
        "responses": {
          "200": { "description": "Search results", "content": { "application/json": { "schema": { "type": "object", "properties": { "resources": { "type": "object", "properties": { "results": { "type": "object", "properties": { "products": { "type": "array", "items": { "$ref": "#/components/schemas/SearchProduct" } } } } } } } } } } },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/recommendations/products.json": {
      "get": {
        "operationId": "getProductRecommendations",
        "summary": "Get product recommendations",
        "description": "Products frequently bought with, or related to, a given product.",
        "parameters": [
          { "name": "product_id", "in": "query", "required": true, "description": "Numeric product id (from `listProducts`).", "schema": { "type": "integer", "format": "int64" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 10, "default": 4 } }
        ],
        "responses": {
          "200": { "description": "Recommended products", "content": { "application/json": { "schema": { "type": "object", "properties": { "products": { "type": "array", "items": { "$ref": "#/components/schemas/Product" } } } } } } },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/cart.js": {
      "get": {
        "operationId": "getCart",
        "summary": "Get the current cart",
        "description": "Returns the session cart (cookie-scoped). Amounts are integer cents of GTQ.",
        "responses": {
          "200": { "description": "Cart state", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Cart" } } } }
        }
      }
    },
    "/cart/add.js": {
      "post": {
        "operationId": "addToCart",
        "summary": "Add items to the cart",
        "description": "Adds variant(s) to the session cart. For agent-driven purchases prefer the UCP/MCP flow (`callUcpMcp`), which carries buyer approval semantics.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "type": "object", "required": ["id", "quantity"], "properties": { "id": { "type": "integer", "format": "int64", "description": "Variant id" }, "quantity": { "type": "integer", "minimum": 1 } } } } } } } }
        },
        "responses": {
          "200": { "description": "Items added", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "type": "object" } } } } } } },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/.well-known/ucp": {
      "get": {
        "operationId": "getUcpProfile",
        "summary": "UCP discovery document",
        "description": "Universal Commerce Protocol merchant profile: supported versions, MCP service endpoint, checkout/fulfillment/discount capabilities and payment handlers. Start here before transacting.",
        "responses": {
          "200": { "description": "UCP merchant profile", "content": { "application/json": { "schema": { "type": "object", "properties": { "ucp": { "type": "object", "description": "Versions, services (dev.ucp.shopping via MCP transport) and capabilities." } }, "additionalProperties": true } } } }
        }
      }
    },
    "/api/ucp/mcp": {
      "post": {
        "operationId": "callUcpMcp",
        "summary": "UCP MCP endpoint (JSON-RPC 2.0)",
        "description": "Model Context Protocol endpoint for agentic commerce. Call `tools/list` to discover tools (`search_catalog`, `create_cart`, `create_checkout`, `get_checkout`, `update_checkout`, `complete_checkout`) and their JSON Schemas, then `tools/call` to invoke them. Tool schema: https://ucp.dev/2026-08-25/services/shopping/mcp.openrpc.json. **`complete_checkout` requires explicit, contemporaneous buyer approval of payment.** Back off on HTTP 429.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "required": ["jsonrpc", "id", "method"], "properties": { "jsonrpc": { "type": "string", "const": "2.0" }, "id": { "type": ["integer", "string"] }, "method": { "type": "string", "enum": ["initialize", "tools/list", "tools/call"] }, "params": { "type": "object" } }, "examples": [{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }] } } }
        },
        "responses": {
          "200": { "description": "JSON-RPC result or error object", "content": { "application/json": { "schema": { "type": "object", "properties": { "jsonrpc": { "type": "string" }, "id": { "type": ["integer", "string", "null"] }, "result": { "type": "object" }, "error": { "$ref": "#/components/schemas/JsonRpcError" } } } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Product": {
        "type": "object",
        "description": "A wine product. Prices are decimal strings in GTQ.",
        "required": ["id", "title", "handle", "variants"],
        "properties": {
          "id": { "type": "integer", "format": "int64" },
          "title": { "type": "string" },
          "handle": { "type": "string", "description": "URL slug; product page is /products/{handle}" },
          "body_html": { "type": "string", "description": "Description (HTML, Spanish)" },
          "vendor": { "type": "string", "description": "Winery / bodega" },
          "product_type": { "type": "string" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "published_at": { "type": "string", "format": "date-time" },
          "variants": { "type": "array", "items": { "$ref": "#/components/schemas/Variant" } },
          "images": { "type": "array", "items": { "type": "object", "properties": { "src": { "type": "string", "format": "uri" }, "width": { "type": "integer" }, "height": { "type": "integer" } } } }
        }
      },
      "Variant": {
        "type": "object",
        "required": ["id", "price"],
        "properties": {
          "id": { "type": "integer", "format": "int64", "description": "Use as `id` in addToCart" },
          "title": { "type": "string" },
          "sku": { "type": "string" },
          "price": { "type": "string", "description": "Decimal GTQ, e.g. \"185.00\"" },
          "compare_at_price": { "type": ["string", "null"] },
          "available": { "type": "boolean" },
          "grams": { "type": "integer" }
        }
      },
      "SearchProduct": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "format": "int64" },
          "title": { "type": "string" },
          "handle": { "type": "string" },
          "url": { "type": "string" },
          "price": { "type": "string" },
          "available": { "type": "boolean" },
          "image": { "type": "string" }
        }
      },
      "Cart": {
        "type": "object",
        "properties": {
          "token": { "type": "string" },
          "item_count": { "type": "integer" },
          "total_price": { "type": "integer", "description": "Integer cents of GTQ" },
          "currency": { "type": "string", "const": "GTQ" },
          "items": { "type": "array", "items": { "type": "object" } }
        }
      },
      "StorefrontError": {
        "type": "object",
        "description": "Structured JSON error returned by .js/.json endpoints.",
        "properties": {
          "status": { "type": "integer", "description": "HTTP status code" },
          "message": { "type": "string", "description": "Error category, e.g. \"Cart Error\"" },
          "description": { "type": "string", "description": "Human/agent-readable resolution hint" }
        }
      },
      "JsonRpcError": {
        "type": "object",
        "properties": {
          "code": { "type": "integer" },
          "message": { "type": "string" },
          "data": { "type": "object", "description": "May include `code`, `content` and `continue_url` hints." }
        }
      }
    },
    "responses": {
      "NotFound": {
        "description": "Resource not found. Recover via /sitemap.xml, /llms.txt or /pages/developers.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StorefrontError" } } }
      },
      "UnprocessableEntity": {
        "description": "Validation error with resolution hint.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StorefrontError" }, "example": { "status": 422, "message": "Cart Error", "description": "Cannot find variant" } } }
      },
      "RateLimited": {
        "description": "Rate limited per IP. Back off exponentially and retry.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StorefrontError" } } }
      }
    }
  }
}
