{
  "openapi": "3.1.0",
  "info": {
    "title": "Wicks of Wit Storefront API",
    "version": "2026-08-25",
    "summary": "Public, read-only storefront data endpoints and agentic commerce entry points for the Wicks of Wit online store.",
    "description": "Wicks of Wit is a Shopify-hosted store selling hand-poured, witty candles.\n\nThis document describes the **public, read-only JSON endpoints** the storefront exposes, plus the **agentic commerce entry points** (UCP / MCP) an autonomous agent should prefer for search, cart, and checkout.\n\n## Which surface should an agent use?\n\n| Goal | Use |\n| --- | --- |\n| Browse or read catalog data | The read-only JSON endpoints in this document. No authentication. |\n| Search, build a cart, or check out | The MCP endpoints under `/api/ucp/mcp` and `/api/mcp`. See `/llms.txt`. |\n| Purchase on a buyer's behalf | The Shop skill at https://shop.app/SKILL.md |\n\n## Rules for agents\n\n- **Checkout requires contemporaneous human approval.** Never finalize a payment without explicit buyer consent.\n- The read-only endpoints below require **no authentication and no scopes** (`security: []`). They are the least-privilege way to read catalog data.\n- Authenticated customer-account operations use the OAuth scopes declared in `components.securitySchemes.customerAccountOAuth`.\n- Respect `429` responses with backoff.\n\n## Further machine-readable discovery\n\n- `GET /llms.txt` — agent instructions (markdown)\n- `GET /agents.md` — agent instructions (markdown)\n- `GET /.well-known/ucp` — Universal Commerce Protocol merchant profile\n- `GET /.well-known/oauth-protected-resource` — RFC 9728 protected-resource metadata\n- `GET /sitemap.xml` — sitemap index",
    "contact": { "name": "Wicks of Wit", "email": "hello@wicksofwit.com", "url": "https://wicksofwit.com/pages/contact" },
    "license": { "name": "Proprietary", "identifier": "LicenseRef-Proprietary" }
  },
  "servers": [{ "url": "https://wicksofwit.com", "description": "Production storefront" }],
  "externalDocs": { "description": "Agent instructions (llms.txt)", "url": "https://wicksofwit.com/llms.txt" },
  "security": [],
  "tags": [
    { "name": "catalog", "description": "Read-only product and collection data. No authentication required." },
    { "name": "search", "description": "Read-only storefront search. No authentication required." },
    { "name": "cart", "description": "Read-only cart state for the current session." },
    { "name": "discovery", "description": "Machine-readable discovery documents for agents." }
  ],
  "paths": {
    "/products/{handle}.json": {
      "get": {
        "operationId": "getProduct",
        "summary": "Get a single product by handle",
        "description": "Returns the full public record for one product. Requires no authentication.",
        "tags": ["catalog"],
        "security": [],
        "parameters": [{
          "name": "handle", "in": "path", "required": true,
          "description": "The product's URL handle, e.g. `250-years-of-this-light-a-candle`.",
          "schema": { "type": "string", "pattern": "^[a-z0-9-]+$" },
          "example": "250-years-of-this-light-a-candle"
        }],
        "responses": {
          "200": {
            "description": "The product.",
            "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": "Returns published products in a collection. Use `handle: all` for the full catalog. Paginate with `page`; an empty `products` array means there are no further pages.",
        "tags": ["catalog"],
        "security": [],
        "parameters": [
          { "name": "handle", "in": "path", "required": true, "description": "Collection handle, or `all` for every published product.", "schema": { "type": "string", "pattern": "^[a-z0-9-]+$" }, "example": "all" },
          { "name": "limit", "in": "query", "required": false, "description": "Products per page (1-250).", "schema": { "type": "integer", "minimum": 1, "maximum": 250, "default": 30 } },
          { "name": "page", "in": "query", "required": false, "description": "1-based page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }
        ],
        "responses": {
          "200": {
            "description": "Products in the collection. Note: an unknown collection handle also returns 200 with an empty array.",
            "content": { "application/json": { "schema": { "type": "object", "required": ["products"], "properties": { "products": { "type": "array", "items": { "$ref": "#/components/schemas/Product" } } } } } }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/search/suggest.json": {
      "get": {
        "operationId": "searchSuggest",
        "summary": "Search products, collections, pages and articles",
        "tags": ["search"],
        "security": [],
        "parameters": [
          { "name": "q", "in": "query", "required": true, "description": "Search terms.", "schema": { "type": "string", "minLength": 1 }, "example": "candle" },
          { "name": "resources[type]", "in": "query", "required": false, "description": "Comma-separated resource types to search.", "schema": { "type": "string", "default": "product", "examples": ["product,collection,page,article"] } },
          { "name": "resources[limit]", "in": "query", "required": false, "description": "Results per resource type (1-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", "additionalProperties": true } } } } } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/cart.js": {
      "get": {
        "operationId": "getCart",
        "summary": "Get the current session cart",
        "description": "Returns the cart bound to the caller's session cookie. An agent with no session receives an empty cart. Read-only; to modify a cart or check out, use the MCP endpoints described in `/llms.txt`.",
        "tags": ["cart"],
        "security": [],
        "responses": {
          "200": { "description": "The current cart.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Cart" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/.well-known/ucp": {
      "get": {
        "operationId": "getUcpProfile",
        "summary": "Universal Commerce Protocol merchant profile",
        "description": "Returns supported UCP versions, service endpoints, capabilities and payment handlers. This is the entry point for agent-driven commerce.",
        "tags": ["discovery"],
        "security": [],
        "responses": { "200": { "description": "UCP merchant profile.", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } } }
      }
    },
    "/.well-known/oauth-protected-resource": {
      "get": {
        "operationId": "getProtectedResourceMetadata",
        "summary": "RFC 9728 protected-resource metadata",
        "description": "Identifies the authorization server that issues tokens for authenticated operations against this resource. The scopes it accepts are declared in `components.securitySchemes.customerAccountOAuth`.",
        "tags": ["discovery"],
        "security": [],
        "responses": { "200": { "description": "Protected-resource metadata.", "content": { "application/json": { "schema": { "type": "object", "required": ["resource", "authorization_servers"], "properties": { "resource": { "type": "string", "format": "uri" }, "authorization_servers": { "type": "array", "items": { "type": "string", "format": "uri" } }, "bearer_methods_supported": { "type": "array", "items": { "type": "string" } } } } } } } }
      }
    },
    "/llms.txt": {
      "get": {
        "operationId": "getLlmsTxt",
        "summary": "Agent instructions",
        "description": "Markdown instructions describing how agents should interact with this store.",
        "tags": ["discovery"],
        "security": [],
        "responses": { "200": { "description": "Agent instructions.", "content": { "text/markdown": { "schema": { "type": "string" } } } } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "customerAccountOAuth": {
        "type": "oauth2",
        "description": "Customer Account OAuth 2.0. Required only for authenticated customer-account operations (order history, saved addresses) and for the authenticated customer-account MCP surface. None of the endpoints in this document require it — request the narrowest scope your task needs.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://shopify.com/authentication/73122250992/oauth/authorize",
            "tokenUrl": "https://shopify.com/authentication/73122250992/oauth/token",
            "refreshUrl": "https://shopify.com/authentication/73122250992/oauth/token",
            "scopes": {
              "openid": "Authenticate the customer and issue an ID token. Does not grant access to store data.",
              "email": "Read the authenticated customer's email address.",
              "customer-account-api:full": "Full read and write access to the authenticated customer's own account: orders, addresses, and profile.",
              "customer-account-mcp-api:full": "Full access to the authenticated customer-account MCP surface on behalf of that customer."
            }
          }
        }
      }
    },
    "responses": {
      "NotFound": {
        "description": "The requested resource does not exist.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "not_found", "message": "No product exists with that handle.", "resolution": "Confirm the handle via GET /collections/all/products.json or GET /sitemap.xml, then retry.", "documentation_url": "https://wicksofwit.com/llms.txt" } } } }
      },
      "RateLimited": {
        "description": "Too many requests. Back off and retry after the interval in `Retry-After`.",
        "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer" } } },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "rate_limited", "message": "Request rate exceeded for this client.", "resolution": "Wait for the number of seconds in the Retry-After header, then retry with exponential backoff.", "documentation_url": "https://wicksofwit.com/llms.txt" } } } }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Structured error payload. Agents should branch on `code`, surface `message`, and act on `resolution`.",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "string", "description": "Stable machine-readable identifier.", "enum": ["not_found", "rate_limited", "bad_request", "unauthorized", "server_error"] },
              "message": { "type": "string", "description": "Human-readable explanation." },
              "resolution": { "type": "string", "description": "Concrete next step an agent can take to recover." },
              "documentation_url": { "type": "string", "format": "uri", "description": "Where to read more." }
            }
          }
        }
      },
      "Product": {
        "type": "object",
        "required": ["id", "title", "handle", "variants"],
        "properties": {
          "id": { "type": "integer", "format": "int64" },
          "title": { "type": "string" },
          "handle": { "type": "string", "description": "URL slug; the product page is https://wicksofwit.com/products/{handle}" },
          "body_html": { "type": "string", "description": "Product description as HTML." },
          "published_at": { "type": ["string", "null"], "format": "date-time" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" },
          "vendor": { "type": "string" },
          "product_type": { "type": "string" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "variants": { "type": "array", "items": { "$ref": "#/components/schemas/Variant" } },
          "images": { "type": "array", "items": { "$ref": "#/components/schemas/Image" } },
          "image": { "oneOf": [{ "$ref": "#/components/schemas/Image" }, { "type": "null" }] },
          "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", "format": "int64" },
          "title": { "type": "string" },
          "price": { "type": "string", "description": "Decimal string in the store's currency (USD)." },
          "sku": { "type": ["string", "null"] },
          "available": { "type": "boolean", "description": "Whether the variant can currently be purchased." },
          "requires_shipping": { "type": "boolean" },
          "taxable": { "type": "boolean" },
          "position": { "type": "integer" }
        }
      },
      "Image": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "format": "int64" },
          "src": { "type": "string", "format": "uri" },
          "width": { "type": "integer" },
          "height": { "type": "integer" },
          "alt": { "type": ["string", "null"] }
        }
      },
      "Cart": {
        "type": "object",
        "properties": {
          "token": { "type": "string" },
          "item_count": { "type": "integer" },
          "total_price": { "type": "integer", "description": "Total in the currency's minor units (cents)." },
          "currency": { "type": "string", "examples": ["USD"] },
          "requires_shipping": { "type": "boolean" },
          "items": { "type": "array", "items": { "type": "object", "additionalProperties": true } }
        }
      }
    }
  }
}
