{ "openapi": "3.1.0", "info": { "title": "Nordisk Renhet \u2014 Agent API Surface", "version": "1.0.0", "description": "Public API surfaces for AI agents interacting with Nordisk Renhet's\nShopify store. This document describes the machine-readable endpoints\navailable without authentication, plus the UCP MCP endpoint for\nagent-driven commerce.\n\nFor human-readable store information, see:\n- /llms.txt \u2014 LLM-optimized store index\n- /agents.md \u2014 Agent instructions\n- /pages/developers \u2014 Developer portal\n", "contact": { "name": "Nordisk Renhet", "url": "https://www.nordiskrenhet.com", "email": "hello@nordiskrenhet.com" } }, "servers": [ { "url": "https://www.nordiskrenhet.com", "description": "Production store" } ], "tags": [ { "name": "Catalog", "description": "Read-only product and collection browsing" }, { "name": "Commerce", "description": "UCP MCP shopping agent protocols" }, { "name": "Agent", "description": "Agent-facing documentation and metadata" }, { "name": "Storefront", "description": "Shopify Storefront API (GraphQL)" } ], "paths": { "/products/{handle}.json": { "get": { "operationId": "getProductByHandle", "summary": "Get product details as JSON", "description": "Returns a product's full details in JSON format, including\nvariants, pricing, images, and inventory. No authentication\nrequired.\n", "tags": [ "Catalog" ], "parameters": [ { "name": "handle", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Product handle (slug), e.g. \"nordisk-duschvattenfilter\"", "example": "nordisk-duschvattenfilter" } ], "responses": { "200": { "description": "Product JSON", "content": { "application/json": { "schema": { "type": "object", "properties": { "product": { "type": "object", "properties": { "id": { "type": "integer" }, "title": { "type": "string" }, "handle": { "type": "string" }, "variants": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer" }, "price": { "type": "string" }, "inventory_quantity": { "type": "integer" } } } }, "images": { "type": "array", "items": { "type": "object", "properties": { "src": { "type": "string", "format": "uri" } } } } } } } } } } }, "404": { "description": "Product not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } } } }, "/collections/all/products.json": { "get": { "operationId": "listAllProducts", "summary": "List all products as JSON", "description": "Returns a JSON array of all products in the store. Supports\npagination via ?page= and ?limit= parameters.\n", "tags": [ "Catalog" ], "parameters": [ { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 }, "description": "Page number for pagination" }, { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 250 }, "description": "Products per page" } ], "responses": { "200": { "description": "Products JSON array", "content": { "application/json": { "schema": { "type": "object", "properties": { "products": { "type": "array", "items": { "$ref": "#/components/schemas/ProductSummary" } } } } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } } } }, "/collections/{handle}/products.json": { "get": { "operationId": "listProductsByCollection", "summary": "List products in a collection", "description": "Returns products belonging to a specific collection.", "tags": [ "Catalog" ], "parameters": [ { "name": "handle", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Collection handle (slug)" }, { "name": "page", "in": "query", "schema": { "type": "integer", "default": 1 } }, { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50, "maximum": 250 } } ], "responses": { "200": { "description": "Collection products JSON", "content": { "application/json": { "schema": { "type": "object", "properties": { "products": { "type": "array", "items": { "$ref": "#/components/schemas/ProductSummary" } } } } } } }, "404": { "$ref": "#/components/responses/NotFound" }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } } } }, "/collections.json": { "get": { "operationId": "listCollections", "summary": "List all collections", "description": "Returns a JSON list of all collections.", "tags": [ "Catalog" ], "responses": { "200": { "description": "Collections JSON", "content": { "application/json": { "schema": { "type": "object", "properties": { "collections": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer" }, "handle": { "type": "string" }, "title": { "type": "string" } } } } } } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } }, "parameters": [ { "name": "Accept", "in": "header", "schema": { "type": "string", "enum": [ "text/html", "text/markdown", "application/json" ], "description": "Content negotiation header" }, "required": false, "description": "Content type negotiation" } ] } }, "/api/2024-07/graphql.json": { "post": { "operationId": "storefrontGraphQL", "summary": "Shopify Storefront GraphQL API", "description": "Public GraphQL endpoint for querying store data. Use the\nStorefront API to query products, collections, and content\nwithout authentication.\n\nRecommended queries:\n\n```graphql\n{ shop { name description } }\n```\n\n```graphql\n{\n products(first: 10) {\n edges {\n node {\n id\n title\n handle\n priceRange {\n minVariantPrice { amount currencyCode }\n }\n }\n }\n }\n}\n```\n\nSee https://shopify.dev/docs/storefront-api for full schema.\n", "tags": [ "Storefront" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "query" ], "properties": { "query": { "type": "string", "description": "GraphQL query string" }, "variables": { "type": "object", "description": "GraphQL variables (optional)" } } } } } }, "responses": { "200": { "description": "GraphQL response", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "object" }, "errors": { "type": "array", "items": { "type": "object" } } } } } } }, "400": { "description": "Bad request", "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } } } }, "/api/ucp/mcp": { "post": { "operationId": "ucpMCP", "summary": "UCP MCP commerce endpoint", "description": "JSON-RPC MCP server for agent-driven commerce. Use tools/list\nto discover available tools and their schemas.\n\nAvailable tools:\n- search_catalog \u2014 Search products by natural language\n- lookup_catalog \u2014 Look up products by ID\n- get_product \u2014 Get full product details\n- create_cart \u2014 Create a new shopping cart\n- get_cart \u2014 Get cart contents\n- update_cart \u2014 Update cart items\n- cancel_cart \u2014 Cancel a cart\n- create_checkout \u2014 Create a checkout\n- get_checkout \u2014 Get checkout details\n- update_checkout \u2014 Update checkout\n- complete_checkout \u2014 Complete purchase\n- cancel_checkout \u2014 Cancel a checkout\n- get_order \u2014 Get order details\n\nAll commerce operations require a UCP agent profile in the\nmeta parameter. See /agents.md for flow details.\n", "tags": [ "Commerce" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "jsonrpc", "id", "method" ], "properties": { "jsonrpc": { "type": "string", "enum": [ "2.0" ] }, "id": { "type": "integer" }, "method": { "type": "string", "description": "MCP method: tools/list, tools/call, resources/list" }, "params": { "type": "object" } } } } } }, "responses": { "200": { "description": "JSON-RPC response", "content": { "application/json": { "schema": { "type": "object" } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } } } }, "/agents.md": { "get": { "operationId": "getAgentInstructions", "summary": "Agent instructions document", "description": "Canonical agent-facing description of the store. Covers UCP\nendpoints, read-only data endpoints, and recommended integration\nflow.\n", "tags": [ "Agent" ], "responses": { "200": { "description": "Markdown document", "content": { "text/markdown": { "schema": { "type": "string" } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } }, "parameters": [ { "name": "Accept", "in": "header", "schema": { "type": "string", "enum": [ "text/html", "text/markdown", "application/json" ], "description": "Content negotiation header" }, "required": false, "description": "Content type negotiation" } ] } }, "/llms.txt": { "get": { "operationId": "getLLMsTxt", "summary": "LLM-optimized store index", "description": "LLMs.txt file for AI crawlers. Lists all content, products,\nand developer resources.\n", "tags": [ "Agent" ], "responses": { "200": { "description": "Markdown document", "content": { "text/markdown": { "schema": { "type": "string" } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } }, "parameters": [ { "name": "Accept", "in": "header", "schema": { "type": "string", "enum": [ "text/html", "text/markdown", "application/json" ], "description": "Content negotiation header" }, "required": false, "description": "Content type negotiation" } ] } }, "/robots.txt": { "get": { "operationId": "getRobotsTxt", "summary": "Robots.txt", "description": "Robots.txt with AI-crawler optimized rules.", "tags": [ "Agent" ], "responses": { "200": { "description": "Text file", "content": { "text/plain": { "schema": { "type": "string" } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } }, "parameters": [ { "name": "Accept", "in": "header", "schema": { "type": "string", "enum": [ "text/html", "text/markdown", "application/json" ], "description": "Content negotiation header" }, "required": false, "description": "Content type negotiation" } ] } }, "/sitemap.xml": { "get": { "operationId": "getSitemap", "summary": "XML sitemap index", "description": "Root sitemap index linking to all sub-sitemaps.", "tags": [ "Agent" ], "responses": { "200": { "description": "XML sitemap", "content": { "application/xml": { "schema": { "type": "string" } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } }, "parameters": [ { "name": "Accept", "in": "header", "schema": { "type": "string", "enum": [ "text/html", "text/markdown", "application/json" ], "description": "Content negotiation header" }, "required": false, "description": "Content type negotiation" } ] } }, "/sitemap_agentic_discovery.xml": { "get": { "operationId": "getAgenticSitemap", "summary": "Agent discovery sitemap", "description": "Lightweight sitemap for AI crawlers with agent-facing URLs.", "tags": [ "Agent" ], "responses": { "200": { "description": "XML sitemap", "content": { "application/xml": { "schema": { "type": "string" } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } }, "parameters": [ { "name": "Accept", "in": "header", "schema": { "type": "string", "enum": [ "text/html", "text/markdown", "application/json" ], "description": "Content negotiation header" }, "required": false, "description": "Content type negotiation" } ] } }, "/.well-known/ucp": { "get": { "operationId": "getUCPDiscovery", "summary": "UCP merchant profile", "description": "Universal Commerce Protocol (UCP) merchant profile. Returns\nsupported versions, service endpoints, capabilities, and\npayment handlers in JSON.\n", "tags": [ "Commerce" ], "responses": { "200": { "description": "UCP profile JSON", "content": { "application/json": { "schema": { "type": "object" } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } }, "parameters": [ { "name": "Accept", "in": "header", "schema": { "type": "string", "enum": [ "text/html", "text/markdown", "application/json" ], "description": "Content negotiation header" }, "required": false, "description": "Content type negotiation" } ] } }, "/openapi.json": { "get": { "operationId": "getOpenAPISpec", "summary": "This OpenAPI specification", "description": "OpenAPI 3.1 specification describing all public API surfaces\navailable on this store. Agents can use this to discover\nendpoints, understand request/response schemas, and integrate\nprogrammatically.\n", "tags": [ "Agent" ], "responses": { "200": { "description": "OpenAPI 3.1 JSON", "content": { "application/json": { "schema": { "type": "object" } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } }, "parameters": [ { "name": "Accept", "in": "header", "schema": { "type": "string", "enum": [ "text/html", "text/markdown", "application/json" ], "description": "Content negotiation header" }, "required": false, "description": "Content type negotiation" } ] } }, "/mcp.json": { "get": { "operationId": "getMCPManifest", "summary": "MCP server manifest", "description": "MCP (Model Context Protocol) server manifest file.", "tags": [ "Commerce" ], "responses": { "200": { "description": "MCP manifest JSON", "content": { "application/json": { "schema": { "type": "object" } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } }, "parameters": [ { "name": "Accept", "in": "header", "schema": { "type": "string", "enum": [ "text/html", "text/markdown", "application/json" ], "description": "Content negotiation header" }, "required": false, "description": "Content type negotiation" } ] } }, "/pages/pricing": { "get": { "operationId": "getPricingPage", "summary": "Pricing page", "description": "Product pricing information with schema.org/Offer structured data.", "tags": [ "Catalog" ], "responses": { "200": { "description": "HTML pricing page", "content": { "text/html": { "schema": { "type": "string" } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } }, "parameters": [ { "name": "Accept", "in": "header", "schema": { "type": "string", "enum": [ "text/html", "text/markdown", "application/json" ], "description": "Content negotiation header" }, "required": false, "description": "Content type negotiation" } ] } }, "/pages/developers": { "get": { "operationId": "getDeveloperPortal", "summary": "Developer portal", "description": "Developer resources, API keys, documentation, and quickstart guides.", "tags": [ "Agent" ], "responses": { "200": { "description": "HTML developer portal", "content": { "text/html": { "schema": { "type": "string" } } } }, "400": { "$ref": "#/components/responses/JsonError" }, "429": { "description": "Rate limit exceeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "resolution": { "type": "string" }, "retry_after": { "type": "integer", "description": "Seconds to wait before retrying" } }, "required": [ "error", "code" ] } } } } }, "parameters": [ { "name": "Accept", "in": "header", "schema": { "type": "string", "enum": [ "text/html", "text/markdown", "application/json" ], "description": "Content negotiation header" }, "required": false, "description": "Content type negotiation" } ] } } }, "components": { "schemas": { "ProductSummary": { "type": "object", "properties": { "id": { "type": "integer" }, "title": { "type": "string" }, "handle": { "type": "string" }, "variants": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "integer" }, "price": { "type": "string" }, "available": { "type": "boolean" } } } } } }, "ErrorResponse": { "type": "object", "required": [ "error" ], "properties": { "error": { "type": "string", "description": "Human-readable error message" }, "code": { "type": "string", "description": "Machine-readable error code" }, "resolution": { "type": "string", "description": "Hint for how to resolve the error" } } } }, "responses": { "NotFound": { "description": "Resource not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "error": "Resource not found", "code": "not_found", "resolution": "Check the URL and try again" } } } }, "JsonError": { "description": "Error response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } }, "securitySchemes": { "StorefrontAPIAuth": { "type": "oauth2", "description": "Shopify Storefront API uses public OAuth for public queries\n(no client secret needed) and private OAuth for mutations.\nSee https://shopify.dev/docs/storefront-api/authentication.\n", "flows": { "implicit": { "authorizationUrl": "https://www.nordiskrenhet.com/admin/oauth/authorize", "scopes": { "read_products": "Read product catalog", "read_content": "Read content pages and blogs", "unauthenticated_read_content": "Read public content", "unauthenticated_read_product_listings": "Read public product listings", "unauthenticated_read_product_tags": "Read public product tags", "unauthenticated_read_customers": "Read customer information", "unauthenticated_read_metaobjects": "Read public metaobjects", "unauthenticated_read_bundles": "Read public product bundles" } } } }, "UCPMCPAuth": { "type": "openIdConnect", "description": "UCP MCP endpoint requires a UCP agent profile in the meta\nparameter. See /agents.md for details.\n", "openIdConnectUrl": "https://ucp.dev/.well-known/openid-configuration" } } }, "x-security": [ { "StorefrontAPIAuth": [] }, { "UCPMCPAuth": [] } ], "x-agent-instructions": { "content-negotiation": "Accept: text/markdown is supported on the homepage and content\npages for LLM-friendly output. Always include Vary: Accept header.\n", "rate-limiting": "Storefront API: 120 requests per 60 seconds per IP.\nMCP endpoint: Rate-limited per IP. Honor Retry-After on 429.\n", "agent-discovery": "Start with /llms.txt for content overview, then /agents.md for\nintegration instructions, then /openapi.json for API details." } }