{
  "openapi": "3.1.0",
  "info": {
    "title": "MetricFold API",
    "version": "1.2.0",
    "description": "Workspace-scoped site provisioning, analytics evidence and live streams."
  },
  "servers": [{"url": "https://metricfold.com/api/v1"}],
  "security": [{"bearerAuth": []}],
  "paths": {
    "/sites": {
      "get": {
        "operationId": "listSites",
        "summary": "List sites",
        "x-required-ability": "sites:read",
        "parameters": [{"name": "per_page", "in": "query", "schema": {"type": "integer", "minimum": 1, "maximum": 100, "default": 25}}],
        "responses": {"200": {"description": "Paginated sites", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SitePage"}}}}, "401": {"$ref": "#/components/responses/Unauthenticated"}, "403": {"$ref": "#/components/responses/Forbidden"}}
      },
      "post": {
        "operationId": "createSite",
        "summary": "Register a website",
        "x-required-ability": "sites:write",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": false, "required": ["name", "origin"], "properties": {"name": {"type": "string", "maxLength": 100}, "origin": {"type": "string", "format": "uri", "maxLength": 300}}}}}},
        "responses": {"201": {"description": "Created site and one-time verification token", "headers": {"Location": {"schema": {"type": "string", "format": "uri"}}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Installation"}}}}, "401": {"$ref": "#/components/responses/Unauthenticated"}, "403": {"$ref": "#/components/responses/Forbidden"}, "422": {"description": "Invalid or duplicate origin"}}
      }
    },
    "/portfolio": {
      "get": {
        "operationId": "getPortfolioReport",
        "summary": "Read a consolidated workspace portfolio",
        "description": "Returns a bounded multi-site report and the latest selected social or custom API metrics without connector credentials.",
        "x-required-ability": "analytics:read",
        "parameters": [{"$ref": "#/components/parameters/Days"}],
        "responses": {"200": {"description": "Workspace-scoped portfolio", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PortfolioResponse"}}}}, "401": {"$ref": "#/components/responses/Unauthenticated"}, "403": {"$ref": "#/components/responses/Forbidden"}}
      }
    },
    "/portfolio/stream": {
      "get": {
        "operationId": "streamPortfolio",
        "summary": "Stream consolidated workspace snapshots",
        "description": "Each SSE message is a complete replaceable snapshot. Reconnect with Last-Event-ID to receive a monotonic cursor greater than the supplied cursor.",
        "x-required-ability": "stream:read",
        "parameters": [{"$ref": "#/components/parameters/Days"}, {"$ref": "#/components/parameters/LastEventId"}],
        "responses": {"200": {"description": "Server-Sent Events named portfolio", "headers": {"Cache-Control": {"schema": {"type": "string"}}}, "content": {"text/event-stream": {"schema": {"type": "string"}}}}, "401": {"$ref": "#/components/responses/Unauthenticated"}, "403": {"$ref": "#/components/responses/Forbidden"}}
      }
    },
    "/sites/{site}/installation": {
      "get": {
        "operationId": "getSiteInstallation",
        "summary": "Read installation manifest",
        "x-required-ability": "sites:read",
        "parameters": [{"$ref": "#/components/parameters/Site"}],
        "responses": {"200": {"description": "Installation manifest without verification token", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Installation"}}}}, "404": {"$ref": "#/components/responses/NotFound"}}
      }
    },
    "/sites/{site}/edge-secret/rotate": {
      "post": {
        "operationId": "rotateSiteEdgeSecret",
        "summary": "Rotate the server-only first-party edge secret",
        "description": "Immediately revokes the previous secret and returns the replacement once inside a complete installation manifest.",
        "x-required-ability": "sites:write",
        "parameters": [{"$ref": "#/components/parameters/Site"}],
        "responses": {"200": {"description": "Installation manifest containing the one-time replacement edge secret", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Installation"}}}}, "404": {"$ref": "#/components/responses/NotFound"}}
      }
    },
    "/sites/{site}/tracking-plan": {
      "get": {
        "operationId": "getTrackingPlan",
        "summary": "List the site's accepted client and server events",
        "x-required-ability": "sites:read",
        "parameters": [{"$ref": "#/components/parameters/Site"}],
        "responses": {"200": {"description": "Versioned tracking-plan events", "content": {"application/json": {"schema": {"type": "object", "required": ["data"], "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/TrackingPlanEvent"}}}}}}}, "404": {"$ref": "#/components/responses/NotFound"}}
      },
      "post": {
        "operationId": "createTrackingPlanEvent",
        "summary": "Add an event to the site's tracking contract",
        "x-required-ability": "sites:write",
        "parameters": [{"$ref": "#/components/parameters/Site"}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TrackingPlanEventInput"}}}},
        "responses": {"201": {"description": "Tracking event created", "content": {"application/json": {"schema": {"type": "object", "properties": {"data": {"$ref": "#/components/schemas/TrackingPlanEvent"}}}}}}, "404": {"$ref": "#/components/responses/NotFound"}, "422": {"description": "Invalid or duplicate event definition"}}
      }
    },
    "/sites/{site}/tracking-plan/{trackingEvent}": {
      "patch": {
        "operationId": "updateTrackingPlanEvent",
        "summary": "Change the display, allowlist, conversion role or active state",
        "x-required-ability": "sites:write",
        "parameters": [{"$ref": "#/components/parameters/Site"}, {"$ref": "#/components/parameters/TrackingEvent"}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": false, "properties": {"display_name": {"type": "string", "maxLength": 100}, "allowed_properties": {"type": "array", "maxItems": 24, "uniqueItems": true, "items": {"type": "string"}}, "is_conversion": {"type": "boolean"}, "is_active": {"type": "boolean"}}}}}},
        "responses": {"200": {"description": "Tracking event updated"}, "404": {"$ref": "#/components/responses/NotFound"}, "422": {"description": "Invalid update"}}
      },
      "delete": {
        "operationId": "deleteTrackingPlanEvent",
        "summary": "Remove an event from the site's tracking contract",
        "x-required-ability": "sites:write",
        "parameters": [{"$ref": "#/components/parameters/Site"}, {"$ref": "#/components/parameters/TrackingEvent"}],
        "responses": {"204": {"description": "Tracking event removed"}, "404": {"$ref": "#/components/responses/NotFound"}}
      }
    },
    "/sites/{site}/attribution-tokens": {
      "post": {
        "operationId": "createAttributionToken",
        "summary": "Create a short-lived checkout attribution token",
        "description": "Server-to-server only. Returns the opaque token once; attach it to checkout metadata as metricfold_attribution.",
        "x-required-ability": "attribution:write",
        "parameters": [{"$ref": "#/components/parameters/Site"}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AttributionTokenInput"}}}},
        "responses": {"201": {"description": "One-time attribution token", "content": {"application/json": {"schema": {"type": "object", "required": ["data", "warning"], "properties": {"data": {"type": "object", "required": ["token", "expires_at", "checkout_metadata"], "properties": {"token": {"type": "string", "pattern": "^mfa_[A-Za-z0-9]{48}$"}, "expires_at": {"type": "string", "format": "date-time"}, "checkout_metadata": {"type": "object"}}}, "warning": {"type": "string"}}}}}}, "404": {"$ref": "#/components/responses/NotFound"}, "422": {"description": "Invalid or identifying attribution input"}}
      }
    },
    "/sites/{site}/payment-connections": {
      "get": {
        "operationId": "listPaymentConnections",
        "summary": "List verified payment webhook connections",
        "x-required-ability": "connections:read",
        "parameters": [{"$ref": "#/components/parameters/Site"}],
        "responses": {"200": {"description": "Payment connections without secrets", "content": {"application/json": {"schema": {"type": "object", "required": ["data"], "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/PaymentConnection"}}}}}}}, "404": {"$ref": "#/components/responses/NotFound"}}
      },
      "post": {
        "operationId": "createPaymentConnection",
        "summary": "Connect a signed payment-provider webhook",
        "x-required-ability": "connections:write",
        "parameters": [{"$ref": "#/components/parameters/Site"}],
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PaymentConnectionInput"}}}},
        "responses": {"201": {"description": "Payment connection and provider webhook URL", "content": {"application/json": {"schema": {"type": "object", "properties": {"data": {"$ref": "#/components/schemas/PaymentConnection"}}}}}}, "404": {"$ref": "#/components/responses/NotFound"}, "422": {"description": "Invalid provider, secret or product boundary"}}
      }
    },
    "/sites/{site}/payment-connections/{paymentConnection}": {
      "delete": {
        "operationId": "deletePaymentConnection",
        "summary": "Stop and remove a payment connection",
        "x-required-ability": "connections:write",
        "parameters": [{"$ref": "#/components/parameters/Site"}, {"$ref": "#/components/parameters/PaymentConnection"}],
        "responses": {"204": {"description": "Connection removed"}, "404": {"$ref": "#/components/responses/NotFound"}}
      }
    },
    "/sites/{site}/report": {
      "get": {
        "operationId": "getAnalyticsReport",
        "summary": "Read a bounded analytics report",
        "x-required-ability": "analytics:read",
        "parameters": [{"$ref": "#/components/parameters/Site"}, {"$ref": "#/components/parameters/Days"}],
        "responses": {"200": {"description": "Web and product analytics report", "content": {"application/json": {"schema": {"type": "object"}}}}, "404": {"$ref": "#/components/responses/NotFound"}}
      }
    },
    "/sites/{site}/insights": {
      "get": {
        "operationId": "getAnalyticsInsights",
        "summary": "Read bounded AI-ready evidence",
        "x-required-ability": "insights:read",
        "parameters": [{"$ref": "#/components/parameters/Site"}, {"$ref": "#/components/parameters/Days"}],
        "responses": {"200": {"description": "Evidence, limits and hypotheses", "content": {"application/json": {"schema": {"type": "object", "required": ["schema", "interpretation_contract", "evidence"], "properties": {"schema": {"type": "integer", "const": 1}, "interpretation_contract": {"type": "object"}, "hypotheses": {"type": "array", "items": {"type": "object"}}, "evidence": {"type": "object"}}}}}}, "404": {"$ref": "#/components/responses/NotFound"}}
      }
    },
    "/sites/{site}/stream": {
      "get": {
        "operationId": "streamRealtimeAnalytics",
        "summary": "Stream live analytics snapshots",
        "x-required-ability": "stream:read",
        "parameters": [{"$ref": "#/components/parameters/Site"}, {"$ref": "#/components/parameters/LastEventId"}],
        "responses": {"200": {"description": "Server-Sent Events", "content": {"text/event-stream": {"schema": {"type": "string"}}}}, "404": {"$ref": "#/components/responses/NotFound"}}
      }
    },
    "/mcp": {
      "post": {
        "operationId": "callMcp",
        "summary": "MCP JSON-RPC endpoint",
        "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "required": ["jsonrpc", "method"], "properties": {"jsonrpc": {"type": "string", "const": "2.0"}, "id": {"type": ["string", "integer", "null"]}, "method": {"type": "string"}, "params": {"type": "object"}}}}}},
        "responses": {"200": {"description": "MCP JSON-RPC result or error", "content": {"application/json": {"schema": {"type": "object"}}}}, "202": {"description": "Initialization notification accepted"}, "401": {"$ref": "#/components/responses/Unauthenticated"}}
      }
    }
  },
  "components": {
    "securitySchemes": {"bearerAuth": {"type": "http", "scheme": "bearer", "bearerFormat": "mf_live_*"}},
    "parameters": {
      "Site": {"name": "site", "in": "path", "required": true, "schema": {"type": "string", "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$"}},
      "TrackingEvent": {"name": "trackingEvent", "in": "path", "required": true, "schema": {"type": "string", "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$"}},
      "PaymentConnection": {"name": "paymentConnection", "in": "path", "required": true, "schema": {"type": "string", "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$"}},
      "Days": {"name": "days", "in": "query", "schema": {"type": "integer", "minimum": 1, "maximum": 90, "default": 30}},
      "LastEventId": {"name": "Last-Event-ID", "in": "header", "description": "Optional numeric cursor from the last received SSE message.", "schema": {"type": "string", "pattern": "^[0-9]{1,18}$"}}
    },
    "responses": {
      "Unauthenticated": {"description": "Missing, malformed, expired or revoked credential"},
      "Forbidden": {"description": "Credential lacks the required ability"},
      "NotFound": {"description": "Unknown site or site outside the credential workspace"}
    },
    "schemas": {
      "Site": {"type": "object", "required": ["id", "name", "host", "status", "public_key", "allowed_origins"], "properties": {"id": {"type": "string"}, "name": {"type": "string"}, "host": {"type": "string"}, "status": {"type": "string"}, "public_key": {"type": "string"}, "allowed_origins": {"type": "array", "items": {"type": "string", "format": "uri"}}, "verified_at": {"type": ["string", "null"], "format": "date-time"}}},
      "SitePage": {"type": "object", "required": ["data", "current_page", "per_page", "total"], "properties": {"data": {"type": "array", "items": {"$ref": "#/components/schemas/Site"}}, "current_page": {"type": "integer"}, "per_page": {"type": "integer"}, "total": {"type": "integer"}}},
      "TrackingPlanEventInput": {"type": "object", "additionalProperties": false, "required": ["event_name", "display_name", "owner"], "properties": {"event_name": {"type": "string", "pattern": "^[a-z][a-z0-9_.-]{1,63}$"}, "display_name": {"type": "string", "maxLength": 100}, "owner": {"type": "string", "enum": ["client", "server"]}, "allowed_properties": {"type": "array", "maxItems": 24, "uniqueItems": true, "items": {"type": "string", "pattern": "^[a-z][a-z0-9_]{0,63}$"}}, "is_conversion": {"type": "boolean", "default": false}}},
      "TrackingPlanEvent": {"allOf": [{"$ref": "#/components/schemas/TrackingPlanEventInput"}, {"type": "object", "required": ["id", "is_active", "version"], "properties": {"id": {"type": "string"}, "is_active": {"type": "boolean"}, "version": {"type": "integer", "minimum": 1}}}]},
      "AttributionTouch": {"type": "object", "additionalProperties": false, "maxProperties": 9, "properties": {"source": {"type": "string", "maxLength": 160}, "channel": {"type": "string", "maxLength": 160}, "referrer_host": {"type": "string", "maxLength": 160}, "utm_source": {"type": "string", "maxLength": 160}, "utm_medium": {"type": "string", "maxLength": 160}, "utm_campaign": {"type": "string", "maxLength": 160}, "utm_term": {"type": "string", "maxLength": 160}, "utm_content": {"type": "string", "maxLength": 160}, "landing_path": {"type": "string", "maxLength": 160, "pattern": "^/[^?#]*$"}}},
      "AttributionTokenInput": {"type": "object", "additionalProperties": false, "properties": {"first_touch": {"$ref": "#/components/schemas/AttributionTouch"}, "last_touch": {"$ref": "#/components/schemas/AttributionTouch"}, "visit_hash": {"type": ["string", "null"], "pattern": "^[a-f0-9]{64}$"}, "ttl_days": {"type": "integer", "minimum": 1, "maximum": 90, "default": 30}}},
      "PaymentConnectionInput": {"type": "object", "additionalProperties": false, "required": ["provider", "label", "account_reference", "signing_secret"], "properties": {"provider": {"type": "string", "enum": ["dodo", "stripe", "lemon_squeezy", "paddle", "polar", "generic"]}, "label": {"type": "string", "maxLength": 100}, "account_reference": {"type": "string", "maxLength": 190}, "signing_secret": {"type": "string", "minLength": 16, "maxLength": 512, "writeOnly": true}, "allowed_product_ids": {"type": "array", "maxItems": 50, "uniqueItems": true, "items": {"type": "string", "maxLength": 190}}}},
      "PaymentConnection": {"type": "object", "required": ["id", "provider", "status", "allowed_product_ids", "webhook_url"], "properties": {"id": {"type": "string"}, "provider": {"type": "string"}, "label": {"type": ["string", "null"]}, "status": {"type": "string"}, "allowed_product_ids": {"type": "array", "items": {"type": "string"}}, "webhook_url": {"type": "string", "format": "uri"}, "last_verified_at": {"type": ["string", "null"], "format": "date-time"}, "last_error_code": {"type": ["string", "null"]}}},
      "ConnectedMetric": {"type": "object", "required": ["id", "connection_id", "provider", "label", "status", "metric_key", "unit", "value"], "properties": {"id": {"type": "string"}, "connection_id": {"type": "string"}, "provider": {"type": "string"}, "label": {"type": "string"}, "status": {"type": "string"}, "metric_key": {"type": "string"}, "unit": {"type": "string", "enum": ["count", "percent", "currency", "seconds", "bytes"]}, "value": {"type": ["number", "null"]}, "observed_at": {"type": ["string", "null"], "format": "date-time"}, "next_sync_at": {"type": ["string", "null"], "format": "date-time"}, "last_error_code": {"type": ["string", "null"]}}},
      "PortfolioResponse": {"type": "object", "required": ["schema", "portfolio", "connected_metrics"], "properties": {"schema": {"type": "integer", "const": 1}, "portfolio": {"type": "object", "required": ["window", "currency", "overview", "sites", "trend"], "properties": {"window": {"type": "object"}, "currency": {"type": "string"}, "overview": {"type": "object", "additionalProperties": {"type": "number"}}, "sites": {"type": "array", "items": {"type": "object"}}, "trend": {"type": "array", "items": {"type": "object"}}}}, "connected_metrics": {"type": "array", "items": {"$ref": "#/components/schemas/ConnectedMetric"}}}},
      "Installation": {"type": "object", "required": ["schema", "site", "collector", "traffic_classification", "tracker", "reverse_proxy"], "properties": {"schema": {"type": "integer", "const": 4}, "site": {"$ref": "#/components/schemas/Site"}, "collector": {"type": "object"}, "traffic_classification": {"type": "object", "required": ["mode", "endpoint", "upstream_url", "human_evidence_url", "model_owner", "decision_boundary"]}, "tracker": {"type": "object"}, "reverse_proxy": {"type": "object"}, "verification": {"type": "object", "description": "Present only in the create response."}, "edge": {"type": "object", "writeOnly": true, "description": "Present only when a site is created or its edge secret is rotated."}}}
    }
  }
}
