{
  "openapi": "3.1.0",
  "info": {
    "title": "WebBLE public HTTP surface",
    "version": "2.0.0-beta.2",
    "description": "Public HTTP endpoints served from https://ioswebble.com. Three of these endpoints\nare write-only beacons and form submissions used for analytics and lead capture;\nthe fourth serves the agent-readable Markdown mirror of the documentation site.\n\nNone of these endpoints require authentication. All write endpoints reply with a\n204 No Content or a tiny JSON `{ \"ok\": true }` acknowledgement and are safe to\nretry. The Markdown endpoint is idempotent and cacheable.\n"
  },
  "servers": [
    {
      "url": "https://ioswebble.com",
      "description": "Production"
    }
  ],
  "paths": {
    "/beacon": {
      "post": {
        "summary": "Record an analytics event",
        "description": "Accepts a single analytics event from the website, the MCP server, or a\ndeployed SDK origin. Events are classified by the edge Worker and written\nto the Analytics Engine dataset. No cookies, no PII, no auth.\n",
        "operationId": "postBeacon",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BeaconEvent"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Event accepted"
          }
        }
      }
    },
    "/forms/hardware-inquiry": {
      "post": {
        "summary": "Submit a hardware-company inquiry",
        "description": "Lead-capture form from the /for-hardware-companies page. Persists the\nsubmission to D1 and forwards a notification email via MailChannels.\n",
        "operationId": "postHardwareInquiry",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HardwareInquiry"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Inquiry stored",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          }
        }
      }
    },
    "/mcp-telemetry": {
      "post": {
        "summary": "Record an MCP tool invocation",
        "description": "Called by the published @ios-web-bluetooth/mcp server on every tool call\nso we can count agent-driven activations. Payload is a `mcp_tool_invocation`\nevent; the Worker normalizes it into the same beacon pipeline.\n",
        "operationId": "postMcpTelemetry",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/McpTelemetryEvent"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Event accepted"
          }
        }
      }
    },
    "/docs-md/{slug}.md": {
      "get": {
        "summary": "Fetch an agent-readable Markdown doc",
        "description": "Returns the canonical Markdown source of a documentation page. The HTML\nversion at `/docs/{slug}.html` is rendered from the same file. Served\nwith `Content-Type: text/markdown; charset=utf-8` and standard cache\nheaders (`ETag`, `Last-Modified`, `Cache-Control: public, max-age=300`).\n",
        "operationId": "getDocMarkdown",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "required": true,
            "description": "Page slug without extension (for example `quickstart`, `quickstart-react`, `api-reference`).",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-/]*$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Markdown document",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Unknown slug"
          }
        }
      }
    },
    "/docs-md/quickstart-{framework}.md": {
      "get": {
        "summary": "Fetch a framework-specific quickstart guide",
        "description": "Returns the canonical Markdown quickstart guide for a given\nweb framework. Served with `Content-Type: text/markdown;\ncharset=utf-8` and standard cache headers.\n",
        "operationId": "getQuickstartMarkdown",
        "parameters": [
          {
            "in": "path",
            "name": "framework",
            "required": true,
            "description": "Target web framework.",
            "schema": {
              "type": "string",
              "enum": [
                "html",
                "react",
                "vue",
                "svelte",
                "angular",
                "next"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Markdown document",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Unknown framework"
          }
        }
      }
    },
    "/examples-md/{slug}.md": {
      "get": {
        "summary": "Fetch a recipe / example Markdown file",
        "description": "Returns the canonical Markdown source for a device recipe\n(heart-rate, battery, CGM, lock, beacon, peripheral-chat).\nServed with `Content-Type: text/markdown; charset=utf-8`\nand standard cache headers.\n",
        "operationId": "getExampleMarkdown",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "required": true,
            "description": "Example slug without extension (for example `heart-rate`, `battery`, `cgm`).",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]*$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Markdown document",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Unknown slug"
          }
        }
      }
    },
    "/docs-md/compare/{slug}.md": {
      "get": {
        "summary": "Fetch a comparison / vs-page Markdown file",
        "description": "Returns the canonical Markdown source for a comparison page\n(e.g. cordova-ble-vs-webble, react-native-ble-plx-vs-webble,\nflutter-blue-plus-vs-webble). Served with `Content-Type:\ntext/markdown; charset=utf-8` and standard cache headers.\n",
        "operationId": "getCompareMarkdown",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "required": true,
            "description": "Comparison slug without extension.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]*$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Markdown document",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Unknown slug"
          }
        }
      }
    },
    "/docs-md/troubleshooting/{slug}.md": {
      "get": {
        "summary": "Fetch a troubleshooting guide Markdown file",
        "description": "Returns the canonical Markdown source for a troubleshooting\npage (e.g. extension-not-detected, device-disconnects,\ngatt-operation-failed, notifications-not-firing). Served\nwith `Content-Type: text/markdown; charset=utf-8` and\nstandard cache headers.\n",
        "operationId": "getTroubleshootingMarkdown",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "required": true,
            "description": "Troubleshooting slug without extension.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]*$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Markdown document",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "Unknown slug"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Ok": {
        "type": "object",
        "required": [
          "ok"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "BeaconEvent": {
        "type": "object",
        "required": [
          "event"
        ],
        "properties": {
          "event": {
            "type": "string",
            "description": "Event name. Enumerated from the CRO event taxonomy\n(AGENT_DISCOVERABILITY_PLAYBOOK.md §8.1).\n",
            "enum": [
              "page_view",
              "engaged_session",
              "demo_scan_initiated",
              "demo_scan_succeeded",
              "intent_signal",
              "hero_install_copy",
              "cta_install_click",
              "cta_demo_click",
              "lead_qualified",
              "sdk_loaded_origin",
              "sdk_request_device",
              "sdk_retained_d14",
              "premium_api_called",
              "agent_ingest_fetch",
              "mcp_tool_invocation",
              "agent_recipe_fetch",
              "origin_retained_d14",
              "premium_intent"
            ]
          },
          "props": {
            "type": "object",
            "description": "Event-specific properties. Shape depends on `event`.",
            "additionalProperties": true
          },
          "ts": {
            "type": "integer",
            "format": "int64",
            "description": "Client-side timestamp in milliseconds since the Unix epoch."
          }
        }
      },
      "HardwareInquiry": {
        "type": "object",
        "required": [
          "name",
          "email",
          "company",
          "message"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 320
          },
          "company": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "message": {
            "type": "string",
            "minLength": 1,
            "maxLength": 5000
          }
        }
      },
      "McpTelemetryEvent": {
        "type": "object",
        "required": [
          "tool"
        ],
        "properties": {
          "tool": {
            "type": "string",
            "description": "Name of the invoked MCP tool."
          },
          "client_name": {
            "type": "string",
            "description": "Reported MCP client name (for example `cursor`, `claude-code`, `windsurf`, `codex`)."
          },
          "client_version": {
            "type": "string"
          },
          "success": {
            "type": "boolean"
          },
          "duration_ms": {
            "type": "integer",
            "minimum": 0
          }
        }
      }
    }
  }
}
