{
  "openapi": "3.1.0",
  "info": {
    "title": "Dreamey Storefront API",
    "version": "2026-10-07",
    "summary": "Read-only catalog, cart, and agent-commerce endpoints for the Dreamey storefront.",
    "description": "Machine-readable description of the public HTTP surface of https://dreamey.co.\n\nDreamey sells eucalyptus-fiber bedding (pillowcases, sheets, duvet covers, bundles) built on CloudThera fabric technology. The store runs on Shopify, so this specification documents the Shopify storefront endpoints that are reachable on the dreamey.co origin.\n\nThree families of endpoint are described:\n\n1. Public JSON catalog endpoints. No authentication. Use these to read products and collections.\n2. Cart (AJAX) endpoints. Session-scoped via cookie. Use these to build a cart before checkout.\n3. Agent commerce endpoints. The Universal Commerce Protocol (UCP) discovery document and MCP endpoint, plus the Storefront GraphQL API.\n\nAgent guidance, including the rules that apply to automated buyers, is published as markdown at /agents.md and mirrored at /llms.txt. Read one of those before transacting.\n\nCheckout requires contemporaneous human approval. Agents must not finalize payment without explicit buyer consent.",
    "contact": {
      "name": "Dreamey",
      "url": "https://dreamey.co/pages/contact"
    },
    "x-agent-instructions": "https://dreamey.co/agents.md",
    "x-llms-txt": "https://dreamey.co/llms.txt"
  },
  "servers": [
    { "url": "https://dreamey.co", "description": "Production storefront" }
  ],
  "externalDocs": {
    "description": "Agent instructions for this store (markdown)",
    "url": "https://dreamey.co/agents.md"
  },
  "security": [ {} ],
  "tags": [
    { "name": "catalog", "description": "Public product and collection data. No authentication." },
    { "name": "cart", "description": "Session-scoped cart manipulation. Cookie based." },
    { "name": "search", "description": "Storefront search." },
    { "name": "agent-commerce", "description": "UCP discovery, MCP, and GraphQL surfaces for automated clients." },
    { "name": "discovery", "description": "Well-known metadata documents." }
  ],
  "paths": {
    "/products.json": {
      "get": {
        "tags": ["catalog"],
        "operationId": "listProducts",
        "summary": "List published products",
        "description": "Returns published products with variants, options, images, and prices. Paginate with page and limit.",
        "security": [ {} ],
        "parameters": [
          { "name": "limit", "in": "query", "description": "Products per page (1-250).", "schema": { "type": "integer", "minimum": 1, "maximum": 250, "default": 30 } },
          { "name": "page", "in": "query", "description": "1-indexed page number.", "schema": { "type": "integer", "minimum": 1, "default": 1 } }
        ],
        "responses": {
          "200": {
            "description": "A page of products.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProductList" } } }
          }
        }
      }
    },
    "/collections.json": {
      "get": {
        "tags": ["catalog"],
        "operationId": "listCollections",
        "summary": "List published collections",
        "description": "Returns collections published to the Online Store sales channel. Dreamey merchandises from /pages/shop rather than collection pages, so this list may be sparse.",
        "security": [ {} ],
        "parameters": [
          { "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": "A page of collections.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CollectionList" } } }
          }
        }
      }
    },
    "/collections/{collection_handle}/products.json": {
      "get": {
        "tags": ["catalog"],
        "operationId": "listCollectionProducts",
        "summary": "List products in a collection",
        "description": "Use the handle `all` to read the full published catalog.",
        "security": [ {} ],
        "parameters": [
          { "name": "collection_handle", "in": "path", "required": true, "description": "Collection handle, for example `all`.", "schema": { "type": "string" }, "example": "all" },
          { "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": "A page of products in the collection.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProductList" } } }
          },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/products/{product_handle}.js": {
      "get": {
        "tags": ["catalog"],
        "operationId": "getProduct",
        "summary": "Get a single product",
        "description": "Returns one product as JSON. Served with a `text/javascript` content type by the platform; the body is valid JSON.",
        "security": [ {} ],
        "parameters": [
          { "name": "product_handle", "in": "path", "required": true, "schema": { "type": "string" }, "example": "pillowcases" }
        ],
        "responses": {
          "200": {
            "description": "The product.",
            "content": { "text/javascript": { "schema": { "$ref": "#/components/schemas/Product" } } }
          },
          "404": {
            "description": "No published product with that handle. The body is empty.",
            "content": { "text/javascript": { "schema": { "type": "string" } } }
          }
        }
      }
    },
    "/search/suggest.json": {
      "get": {
        "tags": ["search"],
        "operationId": "searchSuggest",
        "summary": "Predictive search",
        "security": [ {} ],
        "parameters": [
          { "name": "q", "in": "query", "required": true, "description": "Search terms.", "schema": { "type": "string" }, "example": "sheets" },
          { "name": "resources[type]", "in": "query", "description": "Comma separated resource types, for example `product,page,article`.", "schema": { "type": "string" }, "example": "product" },
          { "name": "resources[limit]", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 10, "default": 10 } }
        ],
        "responses": {
          "200": {
            "description": "Suggested resources.",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "resources": { "type": "object" } } } } }
          },
          "422": {
            "description": "A required parameter is missing or empty.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" },
                "example": { "status": 422, "message": "Invalid parameter error", "description": "param is missing or the value is empty: q" }
              }
            }
          }
        }
      }
    },
    "/cart.js": {
      "get": {
        "tags": ["cart"],
        "operationId": "getCart",
        "summary": "Get the current cart",
        "description": "Returns the cart bound to the caller's `cart` cookie. Served with a `text/javascript` content type; the body is valid JSON.",
        "security": [ {} ],
        "responses": {
          "200": {
            "description": "The current cart.",
            "content": { "text/javascript": { "schema": { "$ref": "#/components/schemas/Cart" } } }
          }
        }
      }
    },
    "/cart/add.js": {
      "post": {
        "tags": ["cart"],
        "operationId": "addCartItems",
        "summary": "Add line items to the cart",
        "security": [ {} ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["items"],
                "properties": {
                  "items": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "object",
                      "required": ["id", "quantity"],
                      "properties": {
                        "id": { "type": "integer", "format": "int64", "description": "Variant id, not product id." },
                        "quantity": { "type": "integer", "minimum": 1 },
                        "properties": { "type": "object", "description": "Custom line item properties." }
                      }
                    }
                  }
                }
              },
              "example": { "items": [ { "id": 53317124456757, "quantity": 1 } ] }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Items added.",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/CartLineItem" } } } } } }
          },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" }
        }
      }
    },
    "/cart/change.js": {
      "post": {
        "tags": ["cart"],
        "operationId": "changeCartItem",
        "summary": "Change the quantity of one line item",
        "security": [ {} ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["quantity"],
                "properties": {
                  "id": { "type": "string", "description": "Line item key." },
                  "line": { "type": "integer", "minimum": 1, "description": "1-indexed line position. Use instead of `id`." },
                  "quantity": { "type": "integer", "minimum": 0, "description": "Set to 0 to remove the line." }
                }
              },
              "example": { "line": 1, "quantity": 2 }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated cart.",
            "content": { "text/javascript": { "schema": { "$ref": "#/components/schemas/Cart" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" }
        }
      }
    },
    "/cart/update.js": {
      "post": {
        "tags": ["cart"],
        "operationId": "updateCart",
        "summary": "Update cart attributes, note, or line quantities",
        "security": [ {} ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "updates": { "type": "object", "description": "Map of variant id to quantity." },
                  "note": { "type": "string" },
                  "attributes": { "type": "object" }
                }
              },
              "example": { "note": "Leave at the front desk" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated cart.",
            "content": { "text/javascript": { "schema": { "$ref": "#/components/schemas/Cart" } } }
          },
          "422": { "$ref": "#/components/responses/UnprocessableEntity" }
        }
      }
    },
    "/api/{api_version}/graphql.json": {
      "post": {
        "tags": ["agent-commerce"],
        "operationId": "storefrontGraphql",
        "summary": "Shopify Storefront GraphQL API",
        "description": "Full catalog, cart, and checkout surface. Requires a public Storefront access token in the `X-Shopify-Storefront-Access-Token` header. The token this storefront uses is published in the page payload of every storefront page and is intended for client-side read traffic, subject to the scopes listed under the `storefrontAccessToken` security scheme.",
        "security": [ { "storefrontAccessToken": [] } ],
        "parameters": [
          { "name": "api_version", "in": "path", "required": true, "description": "Shopify API version, YYYY-MM.", "schema": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}$" }, "example": "2025-07" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["query"],
                "properties": {
                  "query": { "type": "string" },
                  "variables": { "type": "object" },
                  "operationName": { "type": "string" }
                }
              },
              "example": { "query": "{ shop { name primaryDomain { url } } }" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A GraphQL response. GraphQL-level failures are reported in `errors` with HTTP 200.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GraphQLResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "430": { "description": "Shopify security rejection. Back off and retry." }
        }
      }
    },
    "/api/ucp/mcp": {
      "post": {
        "tags": ["agent-commerce"],
        "operationId": "ucpMcp",
        "summary": "Universal Commerce Protocol MCP endpoint",
        "description": "JSON-RPC 2.0 over HTTP, implementing the Model Context Protocol for agent-driven commerce. Call `tools/list` to discover available tools and their schemas. Typical flow: search_catalog, create_cart, create_checkout, update_checkout, complete_checkout.\n\nCompleting a checkout requires explicit, contemporaneous buyer approval. Rate limited per IP; back off on 429.",
        "security": [ {}, { "customerAccountOAuth": ["dev.ucp.shopping.catalog.search:read", "dev.ucp.shopping.checkout:manage"] } ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/JsonRpcRequest" },
              "example": { "jsonrpc": "2.0", "id": 1, "method": "tools/list" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A JSON-RPC response. Protocol-level failures are reported in `error` with HTTP 200.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/.well-known/ucp": {
      "get": {
        "tags": ["discovery"],
        "operationId": "ucpDiscovery",
        "summary": "UCP merchant profile",
        "description": "Supported UCP versions, service endpoints, capabilities, and payment handlers.",
        "security": [ {} ],
        "responses": {
          "200": {
            "description": "The UCP merchant profile.",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "ucp": { "type": "object" } } } } }
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource": {
      "get": {
        "tags": ["discovery"],
        "operationId": "protectedResourceMetadata",
        "summary": "RFC 9728 protected resource metadata",
        "description": "Identifies the authorization servers that issue tokens for this origin. The scopes those servers support are enumerated under the `customerAccountOAuth` security scheme in this document, and at the authorization server's own /.well-known/openid-configuration.",
        "security": [ {} ],
        "responses": {
          "200": {
            "description": "Protected resource metadata.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProtectedResourceMetadata" } } }
          }
        }
      }
    },
    "/agents.md": {
      "get": {
        "tags": ["discovery"],
        "operationId": "agentInstructions",
        "summary": "Agent instructions (markdown)",
        "description": "The canonical agent-facing description of this store. Mirrored at /llms.txt.",
        "security": [ {} ],
        "responses": {
          "200": { "description": "Markdown instructions.", "content": { "text/markdown": { "schema": { "type": "string" } } } }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "tags": ["discovery"],
        "operationId": "llmsTxt",
        "summary": "Agent instructions mirror (markdown)",
        "security": [ {} ],
        "responses": {
          "200": { "description": "Markdown instructions.", "content": { "text/markdown": { "schema": { "type": "string" } } } }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "tags": ["discovery"],
        "operationId": "sitemap",
        "summary": "XML sitemap index",
        "security": [ {} ],
        "responses": {
          "200": { "description": "Sitemap index.", "content": { "application/xml": { "schema": { "type": "string" } } } }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "storefrontAccessToken": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Shopify-Storefront-Access-Token",
        "description": "Public Storefront API access token. Grants the unauthenticated, least-privilege scopes listed below. It cannot read orders, customers other than the bearer's own session, or any admin resource.\n\nGranted scopes:\n- `unauthenticated_read_product_listings` read published products and collections\n- `unauthenticated_read_product_inventory` read inventory levels\n- `unauthenticated_read_product_tags` read product tags\n- `unauthenticated_read_content` read pages, blogs, and articles\n- `unauthenticated_read_selling_plans` read subscription selling plans\n- `unauthenticated_write_checkouts` create and update carts and checkouts\n- `unauthenticated_read_checkouts` read carts and checkouts created with this token",
        "x-scopes": [
          "unauthenticated_read_product_listings",
          "unauthenticated_read_product_inventory",
          "unauthenticated_read_product_tags",
          "unauthenticated_read_content",
          "unauthenticated_read_selling_plans",
          "unauthenticated_write_checkouts",
          "unauthenticated_read_checkouts"
        ]
      },
      "customerAccountOAuth": {
        "type": "oauth2",
        "description": "OAuth 2.0 against the authorization server advertised by /.well-known/oauth-protected-resource. Request the narrowest scope that satisfies the task. `dev.ucp.shopping.catalog.search:read` is sufficient for browsing; `dev.ucp.shopping.checkout:manage` is required to build and submit a checkout, and payment still requires explicit buyer approval.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://shopify.com/authentication/93163356469/oauth/authorize",
            "tokenUrl": "https://shopify.com/authentication/93163356469/oauth/token",
            "refreshUrl": "https://shopify.com/authentication/93163356469/oauth/token",
            "scopes": {
              "openid": "Authenticate the buyer and receive a subject identifier.",
              "email": "Read the buyer's email address.",
              "customer-account-api:full": "Full access to the buyer's own customer account, including their orders and addresses.",
              "customer-account-mcp-api:full": "Access the customer account MCP surface on the buyer's behalf.",
              "dev.ucp.shopping.catalog.search:read": "Search the product catalog. Read only.",
              "dev.ucp.shopping.checkout:manage": "Create and update checkouts. Completing payment still requires contemporaneous buyer approval."
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Malformed request.",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } },
          "text/javascript": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid credentials. Consult /.well-known/oauth-protected-resource for the authorization server.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "No such resource.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "UnprocessableEntity": {
        "description": "The request was well formed but could not be applied, for example an unknown variant id or insufficient stock.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" },
            "example": { "status": 422, "message": "Cart Error", "description": "Cannot find variant" }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limited. Back off and retry.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "title": "Error",
        "description": "Structured error body returned by the cart and agent-commerce endpoints.",
        "required": ["status", "message"],
        "properties": {
          "status": { "type": "integer", "description": "HTTP status code, repeated in the body.", "example": 422 },
          "message": { "type": "string", "description": "Short, stable error category suitable for branching on.", "example": "Cart Error" },
          "description": { "type": "string", "description": "Human readable explanation and resolution hint.", "example": "Cannot find variant" }
        },
        "examples": [
          { "status": 422, "message": "Cart Error", "description": "Cannot find variant" }
        ]
      },
      "ProductList": {
        "type": "object",
        "required": ["products"],
        "properties": { "products": { "type": "array", "items": { "$ref": "#/components/schemas/Product" } } }
      },
      "Product": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "format": "int64" },
          "title": { "type": "string" },
          "handle": { "type": "string" },
          "body_html": { "type": "string" },
          "published_at": { "type": "string", "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": { "type": "object" } },
          "options": { "type": "array", "items": { "type": "object" } }
        }
      },
      "Variant": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "format": "int64", "description": "Pass this as items[].id to /cart/add.js." },
          "title": { "type": "string" },
          "sku": { "type": ["string", "null"] },
          "price": { "type": "string" },
          "available": { "type": "boolean" },
          "options": { "type": "array", "items": { "type": "string" } }
        }
      },
      "CollectionList": {
        "type": "object",
        "required": ["collections"],
        "properties": { "collections": { "type": "array", "items": { "type": "object" } } }
      },
      "Cart": {
        "type": "object",
        "properties": {
          "token": { "type": "string" },
          "item_count": { "type": "integer" },
          "total_price": { "type": "integer", "description": "In the smallest currency unit." },
          "currency": { "type": "string", "example": "USD" },
          "items": { "type": "array", "items": { "$ref": "#/components/schemas/CartLineItem" } }
        }
      },
      "CartLineItem": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "format": "int64" },
          "key": { "type": "string" },
          "product_id": { "type": "integer", "format": "int64" },
          "variant_id": { "type": "integer", "format": "int64" },
          "title": { "type": "string" },
          "quantity": { "type": "integer" },
          "price": { "type": "integer" },
          "line_price": { "type": "integer" }
        }
      },
      "GraphQLResponse": {
        "type": "object",
        "properties": {
          "data": { "type": ["object", "null"] },
          "errors": { "type": "array", "items": { "type": "object", "properties": { "message": { "type": "string" } } } },
          "extensions": { "type": "object" }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": ["jsonrpc", "method"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": { "type": ["string", "integer"] },
          "method": { "type": "string", "example": "tools/list" },
          "params": { "type": "object" }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": ["jsonrpc"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": { "type": ["string", "integer", "null"] },
          "result": { "type": "object" },
          "error": {
            "type": "object",
            "properties": {
              "code": { "type": "integer" },
              "message": { "type": "string" },
              "data": { "type": "object" }
            }
          }
        }
      },
      "ProtectedResourceMetadata": {
        "type": "object",
        "properties": {
          "resource": { "type": "string", "format": "uri" },
          "authorization_servers": { "type": "array", "items": { "type": "string", "format": "uri" } },
          "bearer_methods_supported": { "type": "array", "items": { "type": "string" } }
        }
      }
    }
  }
}
