{
  "openapi": "3.1.0",
  "info": {
    "title": "K Detalle Storefront API",
    "version": "1.0.0",
    "summary": "Public commerce API for kdetalle.com.mx — candles, party favors and religious keepsakes from Guadalajara, Mexico.",
    "description": "Machine-readable surface of the K Detalle online store (built on Shopify). All catalog and cart endpoints below are public and require no authentication or API key. Prices are in MXN. Agent-driven checkout is available through the Universal Commerce Protocol (UCP) MCP endpoint; discovery starts at GET /.well-known/ucp. Errors on JSON endpoints are returned as JSON (see components). Rate limits apply per IP; back off on HTTP 429.",
    "contact": {
      "name": "K Detalle",
      "url": "https://www.kdetalle.com.mx/pages/contact"
    },
    "termsOfService": "https://www.kdetalle.com.mx/policies/terms-of-service"
  },
  "servers": [
    { "url": "https://www.kdetalle.com.mx", "description": "Production storefront" }
  ],
  "externalDocs": {
    "description": "Agent instructions (agents.md) — canonical agent-facing description of this store",
    "url": "https://www.kdetalle.com.mx/agents.md"
  },
  "tags": [
    { "name": "catalog", "description": "Read-only product and collection data. No auth required." },
    { "name": "search", "description": "Storefront search. No auth required." },
    { "name": "cart", "description": "Session cart (cookie-based AJAX API). No auth required." },
    { "name": "mcp", "description": "Model Context Protocol endpoints (JSON-RPC 2.0 over Streamable HTTP)." },
    { "name": "discovery", "description": "Agent discovery documents." }
  ],
  "paths": {
    "/products.json": {
      "get": {
        "operationId": "listProducts",
        "tags": ["catalog"],
        "summary": "List published products",
        "description": "Returns published products with variants, prices (MXN), images and body HTML. Paginate with limit and page.",
        "parameters": [
          { "name": "limit", "in": "query", "description": "Max products per page (1-250, default 30).", "schema": { "type": "integer", "minimum": 1, "maximum": 250, "default": 30 } },
          { "name": "page", "in": "query", "description": "1-based page number.", "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": "getProductByHandle",
        "tags": ["catalog"],
        "summary": "Get one product by handle",
        "description": "Returns a single published product. The handle is the URL slug, e.g. velas-virgen-de-guadalupe.",
        "parameters": [
          { "name": "handle", "in": "path", "required": true, "description": "Product URL handle.", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "The product.", "content": { "application/json": { "schema": { "type": "object", "required": ["product"], "properties": { "product": { "$ref": "#/components/schemas/Product" } } } } } },
          "404": { "$ref": "#/components/responses/NotFoundJson" }
        }
      }
    },
    "/collections/{handle}/products.json": {
      "get": {
        "operationId": "listCollectionProducts",
        "tags": ["catalog"],
        "summary": "List products in a collection",
        "description": "Returns published products belonging to the collection. Use handle 'all' for the full catalog. Collection handles are listed in /sitemap.xml.",
        "parameters": [
          { "name": "handle", "in": "path", "required": true, "description": "Collection URL handle (e.g. all, velas-artesanales, lazos).", "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/NotFoundJson" }
        }
      }
    },
    "/search/suggest.json": {
      "get": {
        "operationId": "searchSuggest",
        "tags": ["search"],
        "summary": "Predictive search",
        "description": "Full-text predictive search over products, collections, pages and articles.",
        "parameters": [
          { "name": "q", "in": "query", "required": true, "description": "Search query, e.g. 'vela guadalupe'.", "schema": { "type": "string" } },
          { "name": "resources[type]", "in": "query", "description": "Comma-separated resource types to search.", "schema": { "type": "string", "enum": ["product", "collection", "page", "article", "query", "product,collection,page,article"] } },
          { "name": "resources[limit]", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 10, "default": 4 } }
        ],
        "responses": {
          "200": { "description": "Search suggestions grouped by resource type.", "content": { "application/json": { "schema": { "type": "object", "properties": { "resources": { "type": "object", "properties": { "results": { "type": "object", "additionalProperties": true } } } } } } } },
          "422": { "$ref": "#/components/responses/CartOrQueryError" }
        }
      }
    },
    "/cart.js": {
      "get": {
        "operationId": "getCart",
        "tags": ["cart"],
        "summary": "Get the current session cart",
        "description": "Returns the cart bound to the request's session cookie. A fresh session returns an empty cart.",
        "responses": {
          "200": { "description": "The cart.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Cart" } } } }
        }
      }
    },
    "/cart/add.js": {
      "post": {
        "operationId": "addToCart",
        "tags": ["cart"],
        "summary": "Add items to the cart",
        "description": "Adds one or more variant line items to the session cart. Variant ids come from Product.variants[].id.",
        "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", "description": "Variant id." },
                        "quantity": { "type": "integer", "minimum": 1 },
                        "properties": { "type": "object", "description": "Optional line-item customization properties.", "additionalProperties": { "type": "string" } }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Items added; returns the created line items.", "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/CartItem" } } } } } } },
          "422": { "$ref": "#/components/responses/CartOrQueryError" }
        }
      }
    },
    "/cart/update.js": {
      "post": {
        "operationId": "updateCart",
        "tags": ["cart"],
        "summary": "Update cart quantities or note",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "properties": { "updates": { "type": "object", "description": "Map of variant id to new quantity (0 removes).", "additionalProperties": { "type": "integer", "minimum": 0 } }, "note": { "type": "string" } } } } }
        },
        "responses": {
          "200": { "description": "The updated cart.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Cart" } } } },
          "422": { "$ref": "#/components/responses/CartOrQueryError" }
        }
      }
    },
    "/cart/change.js": {
      "post": {
        "operationId": "changeCartLine",
        "tags": ["cart"],
        "summary": "Change one cart line",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "required": ["quantity"], "properties": { "line": { "type": "integer", "description": "1-based line index." }, "id": { "type": "string", "description": "Line item key (alternative to line)." }, "quantity": { "type": "integer", "minimum": 0 } } } } }
        },
        "responses": {
          "200": { "description": "The updated cart.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Cart" } } } },
          "422": { "$ref": "#/components/responses/CartOrQueryError" }
        }
      }
    },
    "/cart/clear.js": {
      "post": {
        "operationId": "clearCart",
        "tags": ["cart"],
        "summary": "Empty the cart",
        "responses": {
          "200": { "description": "The emptied cart.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Cart" } } } }
        }
      }
    },
    "/api/mcp": {
      "post": {
        "operationId": "storefrontMcp",
        "tags": ["mcp"],
        "summary": "Storefront MCP endpoint (JSON-RPC 2.0)",
        "description": "Model Context Protocol server over Streamable HTTP. No authentication required. Call method 'initialize', then 'tools/list' to discover tools: search_shop_policies_and_faqs, get_cart, update_cart. UCP catalog and checkout tools live at /api/ucp/mcp. Errors follow JSON-RPC 2.0 (error.code, error.message).",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcRequest" } } }
        },
        "responses": {
          "200": { "description": "JSON-RPC response.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcResponse" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/ucp/mcp": {
      "post": {
        "operationId": "ucpShoppingMcp",
        "tags": ["mcp"],
        "summary": "UCP shopping MCP endpoint (catalog, cart, checkout)",
        "description": "Universal Commerce Protocol MCP binding. Tools (discover with 'tools/list'): search_catalog, lookup_catalog, get_product, create_cart, create_checkout, update_checkout, complete_checkout. UCP catalog tools require a 'meta.ucp-agent.profile' in arguments. Checkout completion requires explicit buyer approval of payment - agents must not complete payment without contemporaneous human consent. Schema reference: https://ucp.dev/2026-08-25/services/shopping/mcp.openrpc.json",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcRequest" } } }
        },
        "responses": {
          "200": { "description": "JSON-RPC response.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcResponse" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/.well-known/ucp": {
      "get": {
        "operationId": "getUcpProfile",
        "tags": ["discovery"],
        "summary": "UCP merchant discovery profile",
        "description": "Returns the store's Universal Commerce Protocol profile: supported versions, service endpoints, capabilities and payment handlers.",
        "responses": {
          "200": { "description": "UCP profile document.", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } }
        }
      }
    },
    "/agents.md": {
      "get": {
        "operationId": "getAgentInstructions",
        "tags": ["discovery"],
        "summary": "Agent instructions (canonical)",
        "description": "Canonical agent-facing description of the store: when to use it, endpoints, protocols and policies. Also served at /llms.txt and /llms-full.txt.",
        "responses": {
          "200": { "description": "Markdown document.", "content": { "text/markdown": { "schema": { "type": "string" } } } }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "operationId": "getSitemap",
        "tags": ["discovery"],
        "summary": "XML sitemap index",
        "responses": {
          "200": { "description": "Sitemap index linking product, collection, page and blog sitemaps.", "content": { "application/xml": { "schema": { "type": "string" } } } }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "customerAccountOAuth": {
        "type": "oauth2",
        "description": "OAuth 2.0 (authorization code + PKCE) for the Customer Account API and Customer Accounts MCP - only needed for order history and account data, never for catalog or cart. Authorization server metadata (RFC 8414, includes scopes_supported): https://shopify.com/authentication/65977155695/.well-known/oauth-authorization-server. Protected-resource metadata (RFC 9728): https://www.kdetalle.com.mx/.well-known/oauth-protected-resource",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://shopify.com/authentication/65977155695/oauth/authorize",
            "tokenUrl": "https://shopify.com/authentication/65977155695/oauth/token",
            "scopes": {
              "openid": "OpenID Connect identity.",
              "email": "Customer email address.",
              "customer-account-api:full": "Read and manage the authenticated customer's own account, orders and addresses.",
              "customer-account-mcp-api:full": "Access the Customer Accounts MCP server on behalf of the authenticated customer."
            }
          }
        }
      }
    },
    "responses": {
      "NotFoundJson": {
        "description": "Resource not found. JSON endpoints return HTTP 404 with an empty JSON body; HTML routes return a 404 page with recovery links.",
        "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } }
      },
      "CartOrQueryError": {
        "description": "Validation error.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StorefrontError" } } }
      },
      "RateLimited": {
        "description": "Too many requests from this IP. Retry with exponential backoff; honor the Retry-After header when present."
      }
    },
    "schemas": {
      "StorefrontError": {
        "type": "object",
        "description": "Structured error returned by cart and search endpoints.",
        "required": ["status", "message"],
        "properties": {
          "status": { "type": "integer", "description": "HTTP status code, e.g. 422." },
          "message": { "type": "string", "description": "Error class, e.g. 'Cart Error'." },
          "description": { "type": "string", "description": "Human-readable resolution hint, e.g. 'Cannot find variant'." }
        },
        "examples": [ { "status": 422, "message": "Cart Error", "description": "Cannot find variant" } ]
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": ["jsonrpc", "method", "id"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": { "oneOf": [ { "type": "integer" }, { "type": "string" } ] },
          "method": { "type": "string", "description": "MCP method: initialize, tools/list, tools/call, resources/list, prompts/list." },
          "params": { "type": "object", "additionalProperties": true }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": ["jsonrpc", "id"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": { "oneOf": [ { "type": "integer" }, { "type": "string" } ] },
          "result": { "type": "object", "additionalProperties": true },
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "integer" },
              "message": { "type": "string" },
              "data": { "additionalProperties": true }
            }
          }
        }
      },
      "Product": {
        "type": "object",
        "required": ["id", "title", "handle", "variants"],
        "properties": {
          "id": { "type": "integer" },
          "title": { "type": "string" },
          "handle": { "type": "string", "description": "URL slug; product page is /products/{handle}." },
          "body_html": { "type": "string", "description": "Product description as HTML." },
          "vendor": { "type": "string" },
          "product_type": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" },
          "published_at": { "type": ["string", "null"], "format": "date-time" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "variants": { "type": "array", "items": { "$ref": "#/components/schemas/Variant" } },
          "images": { "type": "array", "items": { "$ref": "#/components/schemas/ProductImage" } },
          "options": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "position": { "type": "integer" }, "values": { "type": "array", "items": { "type": "string" } } } } }
        }
      },
      "Variant": {
        "type": "object",
        "required": ["id", "title", "price"],
        "properties": {
          "id": { "type": "integer", "description": "Use this id with /cart/add.js." },
          "title": { "type": "string" },
          "sku": { "type": ["string", "null"] },
          "price": { "type": "string", "description": "Decimal string in MXN, e.g. '350.00'." },
          "compare_at_price": { "type": ["string", "null"] },
          "available": { "type": "boolean" },
          "requires_shipping": { "type": "boolean" },
          "grams": { "type": "integer" },
          "position": { "type": "integer" },
          "option1": { "type": ["string", "null"] },
          "option2": { "type": ["string", "null"] },
          "option3": { "type": ["string", "null"] }
        }
      },
      "ProductImage": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "src": { "type": "string", "format": "uri" },
          "width": { "type": "integer" },
          "height": { "type": "integer" },
          "position": { "type": "integer" }
        }
      },
      "CartItem": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "description": "Variant id." },
          "key": { "type": "string", "description": "Line item key for /cart/change.js." },
          "quantity": { "type": "integer" },
          "title": { "type": "string" },
          "price": { "type": "integer", "description": "Unit price in MXN cents." },
          "line_price": { "type": "integer", "description": "Line total in MXN cents." },
          "url": { "type": "string" },
          "handle": { "type": "string" },
          "properties": { "type": ["object", "null"], "additionalProperties": true }
        }
      },
      "Cart": {
        "type": "object",
        "required": ["token", "items", "item_count", "total_price", "currency"],
        "properties": {
          "token": { "type": "string" },
          "note": { "type": ["string", "null"] },
          "item_count": { "type": "integer" },
          "total_price": { "type": "integer", "description": "Total in MXN cents." },
          "currency": { "type": "string", "const": "MXN" },
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/CartItem" } }
        }
      }
    }
  }
}
