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
    are write-only beacons and form submissions used for analytics and lead capture;
    the fourth serves the agent-readable Markdown mirror of the documentation site.

    None of these endpoints require authentication. All write endpoints reply with a
    204 No Content or a tiny JSON `{ "ok": true }` acknowledgement and are safe to
    retry. The Markdown endpoint is idempotent and cacheable.
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
        deployed SDK origin. Events are classified by the edge Worker and written
        to the Analytics Engine dataset. No cookies, no PII, no auth.
      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
        submission to D1 and forwards a notification email via MailChannels.
      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
        so we can count agent-driven activations. Payload is a `mcp_tool_invocation`
        event; the Worker normalizes it into the same beacon pipeline.
      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
        version at `/docs/{slug}.html` is rendered from the same file. Served
        with `Content-Type: text/markdown; charset=utf-8` and standard cache
        headers (`ETag`, `Last-Modified`, `Cache-Control: public, max-age=300`).
      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
        web framework. Served with `Content-Type: text/markdown;
        charset=utf-8` and standard cache headers.
      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
        (heart-rate, battery, CGM, lock, beacon, peripheral-chat).
        Served with `Content-Type: text/markdown; charset=utf-8`
        and standard cache headers.
      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
        (e.g. cordova-ble-vs-webble, react-native-ble-plx-vs-webble,
        flutter-blue-plus-vs-webble). Served with `Content-Type:
        text/markdown; charset=utf-8` and standard cache headers.
      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
        page (e.g. extension-not-detected, device-disconnects,
        gatt-operation-failed, notifications-not-firing). Served
        with `Content-Type: text/markdown; charset=utf-8` and
        standard cache headers.
      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
            (AGENT_DISCOVERABILITY_PLAYBOOK.md §8.1).
          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
