{
  "openapi": "3.1.0",
  "info": {
    "title": "Clerion API",
    "version": "1.0.0",
    "summary": "Website intelligence for AI founders.",
    "description": "The HTTP surface of Clerion that a customer's own systems call. Analytics are read through the MCP server at /mcp, which serves the same aggregates the dashboard shows and can change or delete nothing. Deploys are recorded with one POST from CI. Visitor rows are never exposed: every figure is an aggregate.",
    "termsOfService": "https://getclerion.com/terms",
    "contact": { "name": "Clerion", "url": "https://getclerion.com/contact", "email": "hello@getclerion.com" }
  },
  "externalDocs": { "description": "Clerion docs", "url": "https://getclerion.com/docs" },
  "servers": [{ "url": "https://api.getclerion.com" }],
  "tags": [
    { "name": "Deploys", "description": "Mark a release so the Errors page and the briefing can compare the 24 hours before with the 24 hours after." },
    { "name": "MCP", "description": "Model Context Protocol over Streamable HTTP: 26 read-only analytics tools plus clerion_connect_site." },
    { "name": "Discovery", "description": "OAuth 2.1 metadata for MCP clients (RFC 9728, RFC 8414)." }
  ],
  "paths": {
    "/api/v1/deploys": {
      "post": {
        "operationId": "recordDeploy",
        "tags": ["Deploys"],
        "summary": "Record a deploy",
        "description": "Called from the end of a deploy job. Clerion marks the moment on the Errors page with the error rate and conversion rate before and after, and the briefing can name the deploy behind a change. Guide: https://getclerion.com/docs/deploy-markers",
        "security": [{ "clerionKey": [] }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeployInput" } } }
        },
        "responses": {
          "200": { "description": "The deploy as stored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeployResponse" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "mcp",
        "tags": ["MCP"],
        "summary": "MCP endpoint (Streamable HTTP)",
        "description": "JSON-RPC 2.0 messages of the Model Context Protocol. Connect it as an HTTP MCP server in Claude, Cursor or any client; the client discovers the tools with tools/list. Every tool is read-only and returns aggregates. Unauthenticated requests get 401 with a WWW-Authenticate header pointing at the protected-resource metadata, so OAuth-capable clients sign in on their own. Guide: https://getclerion.com/mcp",
        "security": [{ "oauth": ["analytics:read"] }, { "clerionKey": [] }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcRequest" } } }
        },
        "responses": {
          "200": {
            "description": "The JSON-RPC response, as JSON or as a server-sent event stream depending on the Accept header.",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/JsonRpcResponse" } },
              "text/event-stream": { "schema": { "type": "string" } }
            }
          },
          "401": { "description": "No or invalid credentials. WWW-Authenticate names the resource metadata." }
        }
      }
    },
    "/.well-known/oauth-protected-resource": {
      "get": {
        "operationId": "getProtectedResourceMetadata",
        "tags": ["Discovery"],
        "summary": "Protected resource metadata (RFC 9728)",
        "description": "Which authorization server issues tokens for /mcp and which scopes exist.",
        "responses": { "200": { "description": "RFC 9728 document.", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } } }
      }
    },
    "/.well-known/oauth-authorization-server": {
      "get": {
        "operationId": "getAuthorizationServerMetadata",
        "tags": ["Discovery"],
        "summary": "Authorization server metadata (RFC 8414)",
        "description": "Endpoints for a public client: authorization code with PKCE (S256), dynamic client registration, no client secret.",
        "responses": { "200": { "description": "RFC 8414 document.", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "clerionKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "The account key from the dashboard, under Your model. Replacing the key there revokes it everywhere."
      },
      "oauth": {
        "type": "oauth2",
        "description": "OAuth 2.1 for MCP clients: authorization code with PKCE, dynamic client registration at /oauth/register. Discover it at /.well-known/oauth-authorization-server.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://api.getclerion.com/oauth/authorize",
            "tokenUrl": "https://api.getclerion.com/oauth/token",
            "scopes": { "analytics:read": "Read aggregate analytics for the sites on the account. Nothing can be changed or deleted." }
          }
        }
      }
    },
    "schemas": {
      "DeployInput": {
        "type": "object",
        "required": ["websiteId"],
        "properties": {
          "websiteId": { "type": "string", "description": "The site id from the dashboard, for example site_abc123." },
          "label": { "type": "string", "maxLength": 80, "description": "What shipped: a version, a commit, or a sentence." },
          "deployedAt": { "type": "string", "format": "date-time", "description": "When visitors started getting the new version. Defaults to now; must be within the last week." }
        }
      },
      "DeployResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "const": true },
          "data": { "type": "object", "properties": { "deployed_at": { "type": "string", "format": "date-time" }, "label": { "type": ["string", "null"] } } }
        }
      },
      "Error": {
        "type": "object",
        "properties": { "success": { "type": "boolean", "const": false }, "error": { "type": "string" }, "code": { "type": "string" } }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": ["jsonrpc", "method"],
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": { "type": ["string", "integer", "null"] },
          "method": { "type": "string", "description": "initialize, tools/list, tools/call, and the other MCP methods." },
          "params": { "type": "object", "additionalProperties": true }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "properties": {
          "jsonrpc": { "type": "string", "const": "2.0" },
          "id": { "type": ["string", "integer", "null"] },
          "result": { "type": "object", "additionalProperties": true },
          "error": { "type": "object", "properties": { "code": { "type": "integer" }, "message": { "type": "string" } } }
        }
      }
    },
    "responses": {
      "Error": { "description": "The request failed; error says why in plain words.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    }
  }
}
