{
  "openapi": "3.1.0",
  "info": {
    "title": "MaxiBestOf Developer API",
    "version": "1.0.0",
    "description": "MaxiBestOf public website API, sample sandbox, OAuth 2.0 authentication, and MCP transport. Website listing and sample data are public. MCP tools require a Bearer token and Pro access. Production MCP usage allows 1,000 tool calls per account per calendar month. Admin and private browser session routes are outside this contract. There are no public webhook subscriptions.",
    "contact": { "name": "MaxiBestOf", "url": "https://maxibestof.one/developers" }
  },
  "servers": [{ "url": "https://maxibestof.one" }],
  "externalDocs": { "description": "MaxiBestOf developer documentation", "url": "https://maxibestof.one/developers" },
  "security": [],
  "tags": [{ "name": "Websites" }, { "name": "Sandbox" }, { "name": "OAuth" }, { "name": "MCP" }],
  "paths": {
    "/api/websites": {
      "get": {
        "operationId": "listWebsites", "tags": ["Websites"], "summary": "List published website designs",
        "description": "Returns up to 32 websites per page. Stop when page reaches count.pages. No API key is required.",
        "parameters": [{ "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "default": 1 } }],
        "responses": {
          "200": { "description": "Published websites", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebsiteList" } } } },
          "500": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/developers/sandbox": {
      "get": {
        "operationId": "getSandboxSample", "tags": ["Sandbox"], "summary": "Get fixed sample website data",
        "description": "No credentials are required. This route does not access live data or perform mutations. It is not an OAuth or MCP test environment.",
        "responses": { "200": { "description": "Sample response", "content": { "application/json": {
          "schema": { "allOf": [{ "$ref": "#/components/schemas/WebsiteList" }, { "type": "object", "required": ["sandbox"], "properties": { "sandbox": { "const": true } } }] },
          "example": { "sandbox": true, "websites": [{ "id": 0, "title": "Sample design reference", "slug": "sample-design-reference", "fonts": [{ "name": "Sample Sans" }], "tags": [{ "name": "Portfolio" }] }], "count": { "pages": 1, "websites": 1 } }
        } } } }
      }
    },
    "/.well-known/oauth-authorization-server": {
      "get": {
        "operationId": "getOAuthAuthorizationServer", "tags": ["OAuth"], "summary": "Discover MaxiBestOf OAuth endpoints",
        "responses": { "200": { "description": "OAuth authorization server metadata", "content": { "application/json": { "schema": {
          "type": "object", "required": ["issuer", "authorization_endpoint", "token_endpoint", "registration_endpoint"],
          "properties": { "issuer": { "type": "string", "format": "uri" }, "authorization_endpoint": { "type": "string", "format": "uri" }, "token_endpoint": { "type": "string", "format": "uri" }, "registration_endpoint": { "type": "string", "format": "uri" }, "scopes_supported": { "type": "array", "items": { "type": "string" } }, "code_challenge_methods_supported": { "type": "array", "items": { "type": "string" } } }
        } } } } }
      }
    },
    "/.well-known/oauth-protected-resource": {
      "get": {
        "operationId": "getOAuthProtectedResource", "tags": ["OAuth"], "summary": "Discover the protected MCP resource",
        "responses": { "200": { "description": "OAuth protected resource metadata", "content": { "application/json": { "schema": {
          "type": "object", "required": ["resource", "authorization_servers", "scopes_supported"],
          "properties": { "resource": { "type": "string", "format": "uri" }, "authorization_servers": { "type": "array", "items": { "type": "string", "format": "uri" } }, "scopes_supported": { "type": "array", "items": { "type": "string" } }, "bearer_methods_supported": { "type": "array", "items": { "type": "string" } } }
        } } } } }
      }
    },
    "/api/mcp/register": {
      "post": {
        "operationId": "registerOAuthClient", "tags": ["OAuth"], "summary": "Register an OAuth client",
        "description": "Register a public client with token_endpoint_auth_method=none. Use the returned client_id for PKCE authorization. Client registrations expire after 90 days.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": {
          "type": "object", "required": ["redirect_uris"], "properties": {
            "client_name": { "type": "string" }, "redirect_uris": { "type": "array", "minItems": 1, "items": { "type": "string", "format": "uri" } },
            "token_endpoint_auth_method": { "type": "string", "const": "none", "default": "none" },
            "grant_types": { "type": "array", "items": { "type": "string", "enum": ["authorization_code", "refresh_token"] } },
            "response_types": { "type": "array", "items": { "type": "string", "const": "code" } }, "scope": { "type": "string", "const": "mcp:read" }
          }
        } } } },
        "responses": {
          "201": { "description": "Client registered", "content": { "application/json": { "schema": { "type": "object", "required": ["client_id", "redirect_uris", "token_endpoint_auth_method"], "properties": { "client_id": { "type": "string" }, "redirect_uris": { "type": "array", "items": { "type": "string", "format": "uri" } }, "token_endpoint_auth_method": { "type": "string" } } } } } },
          "400": { "$ref": "#/components/responses/OAuthError" }, "500": { "$ref": "#/components/responses/OAuthError" }
        }
      }
    },
    "/api/mcp/authorize": {
      "get": {
        "operationId": "authorizeOAuthClient", "tags": ["OAuth"], "summary": "Authorize an MCP client in a browser",
        "description": "Requires a registered callback URL and PKCE. Users without a session are redirected to sign in. Validate state on the callback before exchanging the code.",
        "parameters": [
          { "name": "response_type", "in": "query", "required": true, "schema": { "type": "string", "const": "code" } },
          { "name": "client_id", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "redirect_uri", "in": "query", "required": true, "schema": { "type": "string", "format": "uri" } },
          { "name": "state", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "code_challenge", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "code_challenge_method", "in": "query", "required": true, "schema": { "type": "string", "const": "S256" } },
          { "name": "scope", "in": "query", "schema": { "type": "string", "const": "mcp:read" } },
          { "name": "resource", "in": "query", "schema": { "type": "string", "format": "uri", "example": "https://maxibestof.one/mcp-server" } }
        ],
        "responses": {
          "302": { "description": "Redirect to login or registered callback with code, state, and iss", "headers": { "Location": { "schema": { "type": "string" } } } },
          "400": { "$ref": "#/components/responses/OAuthError" }, "500": { "$ref": "#/components/responses/OAuthError" }
        }
      }
    },
    "/api/mcp/token": {
      "post": {
        "operationId": "exchangeOAuthToken", "tags": ["OAuth"], "summary": "Exchange an authorization code or refresh token",
        "description": "Public clients use client_id and PKCE without a secret. Authorization codes expire after 10 minutes and are single use. Access tokens expire after 90 days. Refresh tokens expire after 180 days. Refresh responses do not include a new refresh token.",
        "requestBody": { "required": true, "content": {
          "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/TokenRequest" } },
          "application/json": { "schema": { "$ref": "#/components/schemas/TokenRequest" } }
        } },
        "responses": {
          "200": { "description": "Bearer access token", "content": { "application/json": { "schema": { "type": "object", "required": ["access_token", "token_type", "expires_in", "scope"], "properties": { "access_token": { "type": "string" }, "refresh_token": { "type": "string" }, "token_type": { "type": "string", "const": "Bearer" }, "expires_in": { "type": "integer" }, "scope": { "type": "string" } } } } } },
          "400": { "$ref": "#/components/responses/OAuthError" }, "401": { "$ref": "#/components/responses/OAuthError" }, "500": { "$ref": "#/components/responses/OAuthError" }
        }
      }
    },
    "/.well-known/mcp.json": {
      "get": {
        "operationId": "getMcpManifest", "tags": ["MCP"], "summary": "Discover the MaxiBestOf MCP server",
        "responses": { "200": { "description": "MCP endpoint, transport, and OAuth discovery links", "content": { "application/json": { "schema": { "type": "object", "required": ["name", "mcpServers", "authentication"], "properties": { "name": { "type": "string" }, "mcpServers": { "type": "object", "additionalProperties": { "type": "object", "required": ["url", "transport"], "properties": { "url": { "type": "string", "format": "uri" }, "transport": { "type": "string", "const": "streamable-http" } } } }, "authentication": { "type": "object" } } } } } } }
      }
    },
    "/mcp-server": {
      "post": {
        "operationId": "sendMcpMessage", "tags": ["MCP"], "summary": "Send an MCP JSON-RPC message over Streamable HTTP",
        "description": "Initialize, send notifications/initialized, then tools/list or tools/call. Available tools search websites, fonts, font pairings, sections, favorites, staff picks, and color palettes, and set a design brief. Use tools/list for current input schemas. Tool access and rate-limit failures return isError=true within a tool result. Clients must accept application/json and text/event-stream.",
        "security": [{ "McpOAuth": ["mcp:read"] }],
        "parameters": [
          { "name": "Accept", "in": "header", "required": true, "schema": { "type": "string", "example": "application/json, text/event-stream" } },
          { "name": "MCP-Protocol-Version", "in": "header", "description": "Send the negotiated version after initialization.", "schema": { "type": "string" } },
          { "name": "MCP-Session-Id", "in": "header", "description": "Send this header if initialization returned a session ID.", "schema": { "type": "string" } }
        ],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcMessage" }, "example": { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-03-26", "capabilities": {}, "clientInfo": { "name": "my-design-agent", "version": "1.0.0" } } } } } },
        "responses": {
          "200": { "description": "JSON-RPC response or event stream", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcMessage" } }, "text/event-stream": { "schema": { "type": "string" } } } },
          "202": { "description": "Notification accepted" },
          "400": { "description": "Invalid MCP request or protocol version" },
          "401": { "description": "Missing or invalid Bearer token", "headers": { "WWW-Authenticate": { "schema": { "type": "string" }, "description": "Bearer challenge with an OAuth protected resource metadata URL" } } }
        }
      },
      "get": { "operationId": "getMcpStream", "tags": ["MCP"], "summary": "Standalone SSE streams are not supported", "description": "Browser requests that accept text/html redirect to the developer portal.", "responses": { "405": { "description": "SSE GET is not supported", "headers": { "Allow": { "schema": { "type": "string", "const": "POST" } } } }, "302": { "description": "Browser redirect to developer documentation" }, "401": { "description": "Invalid Bearer token" } } }
    }
  },
  "components": {
    "securitySchemes": { "McpOAuth": { "type": "oauth2", "description": "OAuth authorization code with S256 PKCE and dynamic client registration. MCP tools require Pro access.", "flows": { "authorizationCode": { "authorizationUrl": "https://maxibestof.one/api/mcp/authorize", "tokenUrl": "https://maxibestof.one/api/mcp/token", "refreshUrl": "https://maxibestof.one/api/mcp/token", "scopes": { "mcp:read": "Read design inspiration and use MCP tools" } } } } },
    "schemas": {
      "WebsiteList": { "type": "object", "required": ["websites", "count"], "properties": {
        "websites": { "type": "array", "items": { "type": "object", "required": ["id", "title", "slug"], "properties": { "id": { "type": "integer" }, "title": { "type": ["string", "null"] }, "slug": { "type": ["string", "null"] }, "image": { "type": ["string", "null"] }, "thumbnail_desktop": { "type": ["string", "null"] }, "fonts": { "type": "array", "items": { "type": "object" } }, "tags": { "type": "array", "items": { "type": "object" } } } } },
        "count": { "type": "object", "required": ["pages", "websites"], "properties": { "pages": { "type": "integer", "minimum": 0 }, "websites": { "type": "integer", "minimum": 0 } } }
      } },
      "TokenRequest": { "oneOf": [
        { "type": "object", "required": ["grant_type", "code", "code_verifier", "client_id", "redirect_uri"], "properties": { "grant_type": { "const": "authorization_code" }, "code": { "type": "string" }, "code_verifier": { "type": "string" }, "client_id": { "type": "string" }, "redirect_uri": { "type": "string", "format": "uri" }, "resource": { "type": "string", "format": "uri" } } },
        { "type": "object", "required": ["grant_type", "refresh_token", "client_id"], "properties": { "grant_type": { "const": "refresh_token" }, "refresh_token": { "type": "string" }, "client_id": { "type": "string" } } }
      ] },
      "JsonRpcMessage": { "type": "object", "required": ["jsonrpc"], "properties": { "jsonrpc": { "type": "string", "const": "2.0" }, "id": { "type": ["string", "integer", "null"] }, "method": { "type": "string" }, "params": { "type": "object" }, "result": {}, "error": { "type": "object", "required": ["code", "message"], "properties": { "code": { "type": "integer" }, "message": { "type": "string" } } } } },
      "OAuthError": { "type": "object", "required": ["error"], "properties": { "error": { "type": "string" }, "error_description": { "type": "string" } } }
    },
    "responses": {
      "OAuthError": { "description": "OAuth request failed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OAuthError" } } } },
      "ServerError": { "description": "The catalog request failed" }
    }
  }
}
