{
  "openapi": "3.0.3",
  "info": {
    "title": "Funnelkeeper API",
    "version": "0.2.0",
    "description": "The money layer for AI-built products. Joins ad spend, funnel events, and revenue to answer whether spend paid back. The Keeper proposes; a human approves — nothing changes spend without a tap."
  },
  "servers": [
    {
      "url": "https://funnelkeeper.fly.dev"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An account API key (fk_live_…), a dashboard session token (fk_sess_…), or the operator token."
      }
    },
    "schemas": {
      "SignupResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "verification_sent"
            ]
          }
        },
        "required": [
          "ok",
          "status"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "SignupRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "password": {
            "type": "string",
            "minLength": 10,
            "maxLength": 200,
            "description": "At least 10 characters."
          }
        },
        "required": [
          "email",
          "password"
        ]
      },
      "Account": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The ACCOUNT (tenant) id — products and API keys hang off this."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "The authenticated person's email."
          },
          "role": {
            "type": "string",
            "enum": [
              "customer",
              "operator"
            ],
            "description": "The tenant's role."
          },
          "member_role": {
            "type": "string",
            "enum": [
              "owner",
              "member"
            ],
            "description": "The person's role within the account."
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "verified": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "email",
          "role",
          "verified",
          "created_at"
        ]
      },
      "LoginResponse": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "Session bearer token (fk_sess_…). Expires after 7 idle days."
          },
          "expires_at": {
            "type": "string"
          },
          "account": {
            "$ref": "#/components/schemas/Account"
          }
        },
        "required": [
          "token",
          "expires_at",
          "account"
        ]
      },
      "LoginRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "password": {
            "type": "string"
          }
        },
        "required": [
          "email",
          "password"
        ]
      },
      "Ok": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          }
        },
        "required": [
          "ok"
        ]
      },
      "TeamMember": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "member_role": {
            "type": "string",
            "enum": [
              "owner",
              "member"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "invited",
              "disabled"
            ]
          },
          "created_at": {
            "type": "string"
          },
          "last_seen_at": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "id",
          "email",
          "name",
          "member_role",
          "status",
          "created_at",
          "last_seen_at"
        ]
      },
      "InviteRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "member_role": {
            "type": "string",
            "enum": [
              "owner",
              "member"
            ],
            "default": "member"
          }
        },
        "required": [
          "email"
        ]
      },
      "MemberRoleRequest": {
        "type": "object",
        "properties": {
          "member_role": {
            "type": "string",
            "enum": [
              "owner",
              "member"
            ]
          }
        },
        "required": [
          "member_role"
        ]
      },
      "AcceptInviteRequest": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string"
          },
          "password": {
            "type": "string",
            "minLength": 10,
            "maxLength": 200
          },
          "name": {
            "type": "string",
            "maxLength": 80
          }
        },
        "required": [
          "token",
          "password"
        ]
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          },
          "last_used_at": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "id",
          "name",
          "prefix",
          "created_at",
          "last_used_at"
        ]
      },
      "ApiKeyCreated": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "key": {
            "type": "string",
            "description": "The full API key (fk_live_…). Shown exactly once — store it now."
          }
        },
        "required": [
          "id",
          "name",
          "key"
        ]
      },
      "ProductRow": {
        "type": "object",
        "properties": {
          "product_id": {
            "type": "string",
            "format": "uuid"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "stage": {
            "type": "string",
            "enum": [
              "validation",
              "live",
              "killed"
            ]
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3
          },
          "gate_result": {
            "type": "string",
            "nullable": true,
            "enum": [
              "pass",
              "fail",
              "pivot"
            ]
          },
          "gate_due_date": {
            "type": "string",
            "nullable": true
          },
          "spend_7d_cents": {
            "type": "integer"
          },
          "spend_30d_cents": {
            "type": "integer"
          },
          "spend_prev_30d_cents": {
            "type": "integer"
          },
          "visits_30d": {
            "type": "integer"
          },
          "leads_30d": {
            "type": "integer"
          },
          "revenue_30d_cents": {
            "type": "integer"
          },
          "revenue_prev_30d_cents": {
            "type": "integer"
          },
          "customers_30d": {
            "type": "integer"
          },
          "cac_cents": {
            "type": "integer",
            "nullable": true,
            "description": "Null when no customers were acquired in the window — never a fake zero."
          },
          "ltv_cents": {
            "type": "integer",
            "nullable": true
          },
          "ltv_cac": {
            "type": "number",
            "nullable": true
          },
          "pending_cards": {
            "type": "integer"
          }
        },
        "required": [
          "product_id",
          "slug",
          "name",
          "stage",
          "currency",
          "gate_result",
          "gate_due_date",
          "spend_7d_cents",
          "spend_30d_cents",
          "spend_prev_30d_cents",
          "visits_30d",
          "leads_30d",
          "revenue_30d_cents",
          "revenue_prev_30d_cents",
          "customers_30d",
          "cac_cents",
          "ltv_cents",
          "ltv_cac",
          "pending_cards"
        ],
        "description": "All money is integer cents in the product's own currency."
      },
      "CreateProductRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "slug": {
            "type": "string",
            "minLength": 2,
            "maxLength": 60,
            "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$"
          },
          "domain": {
            "type": "string",
            "maxLength": 255
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "default": "USD"
          }
        },
        "required": [
          "name",
          "slug"
        ]
      },
      "QueueCard": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "product_slug": {
            "type": "string",
            "nullable": true
          },
          "card_type": {
            "type": "string",
            "enum": [
              "proposal",
              "insight",
              "alert",
              "gate_result",
              "policy_block"
            ]
          },
          "headline": {
            "type": "string",
            "maxLength": 90
          },
          "metric_line": {
            "type": "string"
          },
          "detail": {
            "type": "string",
            "nullable": true
          },
          "proposed_by": {
            "type": "string",
            "enum": [
              "keeper",
              "system"
            ]
          },
          "action_spec": {
            "type": "object",
            "nullable": true,
            "additionalProperties": {
              "nullable": true
            }
          },
          "status": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "id",
          "product_slug",
          "card_type",
          "headline",
          "metric_line",
          "detail",
          "proposed_by",
          "action_spec",
          "status",
          "created_at",
          "expires_at"
        ]
      },
      "ResolveRequest": {
        "type": "object",
        "properties": {
          "actor": {
            "type": "string",
            "description": "Operator-only override; everyone else IS the actor (their authenticated email)."
          },
          "snoozeHours": {
            "type": "integer",
            "minimum": 0,
            "exclusiveMinimum": true
          }
        }
      },
      "ConnectStartRequest": {
        "type": "object",
        "properties": {
          "product_slug": {
            "type": "string"
          },
          "kinds": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "ga4",
                "gtm",
                "google_ads"
              ]
            },
            "minItems": 1
          },
          "client": {
            "type": "string",
            "enum": [
              "dashboard",
              "cli",
              "mcp",
              "api"
            ],
            "default": "api"
          }
        },
        "required": [
          "product_slug",
          "kinds"
        ]
      },
      "ProposalRequest": {
        "type": "object",
        "properties": {
          "network": {
            "type": "string",
            "enum": [
              "meta",
              "google"
            ]
          },
          "campaign_id": {
            "type": "string"
          },
          "from_cents": {
            "type": "integer",
            "minimum": 0
          },
          "to_cents": {
            "type": "integer",
            "minimum": 0,
            "exclusiveMinimum": true,
            "description": "Proposed daily cap, integer cents."
          },
          "rationale": {
            "type": "string",
            "minLength": 10,
            "maxLength": 2000,
            "description": "Why. Shown to the human on the card."
          },
          "rollback_if": {
            "type": "object",
            "additionalProperties": {
              "nullable": true
            },
            "description": "Condition under which the change should be reverted, e.g. {\"cac_usd_above\": 80, \"window_days\": 5}."
          },
          "headline": {
            "type": "string",
            "maxLength": 90
          }
        },
        "required": [
          "network",
          "to_cents",
          "rationale"
        ]
      },
      "DistributionRequest": {
        "type": "object",
        "properties": {
          "productSlug": {
            "type": "string"
          },
          "channel": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "utmCampaign": {
            "type": "string"
          },
          "note": {
            "type": "string"
          }
        },
        "required": [
          "productSlug",
          "channel",
          "kind"
        ]
      },
      "WidgetLayout": {
        "type": "object",
        "properties": {
          "x": {
            "type": "integer",
            "minimum": 0,
            "maximum": 11
          },
          "y": {
            "type": "integer",
            "minimum": 0,
            "maximum": 500
          },
          "w": {
            "type": "integer",
            "minimum": 1,
            "maximum": 12
          },
          "h": {
            "type": "integer",
            "minimum": 1,
            "maximum": 60
          }
        },
        "required": [
          "x",
          "y",
          "w",
          "h"
        ],
        "description": "Grid placement: 12 columns, 32px rows, in grid units. Widgets without a layout auto-pack below the placed ones (size gives their width hint)."
      },
      "DashboardWidget": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "minLength": 1,
                "maxLength": 40,
                "description": "Stable id within the dashboard, e.g. 'w1'."
              },
              "title": {
                "type": "string",
                "maxLength": 80,
                "description": "Optional custom title; each type has a sensible default."
              },
              "size": {
                "type": "string",
                "enum": [
                  "xs",
                  "sm",
                  "md",
                  "lg"
                ],
                "description": "Width shorthand when no layout is given: xs=2/12, sm=3/12, md=6/12, lg=12/12."
              },
              "layout": {
                "$ref": "#/components/schemas/WidgetLayout"
              },
              "type": {
                "type": "string",
                "enum": [
                  "metric"
                ]
              },
              "metric": {
                "type": "string",
                "enum": [
                  "spend_30d",
                  "revenue_30d",
                  "customers_30d",
                  "cac",
                  "ltv",
                  "ltv_cac",
                  "visits_30d",
                  "leads_30d",
                  "pending_cards"
                ]
              }
            },
            "required": [
              "id",
              "type",
              "metric"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "minLength": 1,
                "maxLength": 40,
                "description": "Stable id within the dashboard, e.g. 'w1'."
              },
              "title": {
                "type": "string",
                "maxLength": 80,
                "description": "Optional custom title; each type has a sensible default."
              },
              "size": {
                "type": "string",
                "enum": [
                  "xs",
                  "sm",
                  "md",
                  "lg"
                ],
                "description": "Width shorthand when no layout is given: xs=2/12, sm=3/12, md=6/12, lg=12/12."
              },
              "layout": {
                "$ref": "#/components/schemas/WidgetLayout"
              },
              "type": {
                "type": "string",
                "enum": [
                  "timeseries"
                ]
              },
              "series": {
                "type": "string",
                "enum": [
                  "spend",
                  "revenue",
                  "visits",
                  "leads",
                  "customers"
                ]
              },
              "days": {
                "type": "integer",
                "minimum": 0,
                "exclusiveMinimum": true,
                "maximum": 365,
                "default": 30
              }
            },
            "required": [
              "id",
              "type",
              "series"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "minLength": 1,
                "maxLength": 40,
                "description": "Stable id within the dashboard, e.g. 'w1'."
              },
              "title": {
                "type": "string",
                "maxLength": 80,
                "description": "Optional custom title; each type has a sensible default."
              },
              "size": {
                "type": "string",
                "enum": [
                  "xs",
                  "sm",
                  "md",
                  "lg"
                ],
                "description": "Width shorthand when no layout is given: xs=2/12, sm=3/12, md=6/12, lg=12/12."
              },
              "layout": {
                "$ref": "#/components/schemas/WidgetLayout"
              },
              "type": {
                "type": "string",
                "enum": [
                  "funnel"
                ]
              },
              "days": {
                "type": "integer",
                "minimum": 0,
                "exclusiveMinimum": true,
                "maximum": 365,
                "default": 30
              }
            },
            "required": [
              "id",
              "type"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "minLength": 1,
                "maxLength": 40,
                "description": "Stable id within the dashboard, e.g. 'w1'."
              },
              "title": {
                "type": "string",
                "maxLength": 80,
                "description": "Optional custom title; each type has a sensible default."
              },
              "size": {
                "type": "string",
                "enum": [
                  "xs",
                  "sm",
                  "md",
                  "lg"
                ],
                "description": "Width shorthand when no layout is given: xs=2/12, sm=3/12, md=6/12, lg=12/12."
              },
              "layout": {
                "$ref": "#/components/schemas/WidgetLayout"
              },
              "type": {
                "type": "string",
                "enum": [
                  "payback"
                ]
              }
            },
            "required": [
              "id",
              "type"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "minLength": 1,
                "maxLength": 40,
                "description": "Stable id within the dashboard, e.g. 'w1'."
              },
              "title": {
                "type": "string",
                "maxLength": 80,
                "description": "Optional custom title; each type has a sensible default."
              },
              "size": {
                "type": "string",
                "enum": [
                  "xs",
                  "sm",
                  "md",
                  "lg"
                ],
                "description": "Width shorthand when no layout is given: xs=2/12, sm=3/12, md=6/12, lg=12/12."
              },
              "layout": {
                "$ref": "#/components/schemas/WidgetLayout"
              },
              "type": {
                "type": "string",
                "enum": [
                  "queue"
                ]
              }
            },
            "required": [
              "id",
              "type"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "minLength": 1,
                "maxLength": 40,
                "description": "Stable id within the dashboard, e.g. 'w1'."
              },
              "title": {
                "type": "string",
                "maxLength": 80,
                "description": "Optional custom title; each type has a sensible default."
              },
              "size": {
                "type": "string",
                "enum": [
                  "xs",
                  "sm",
                  "md",
                  "lg"
                ],
                "description": "Width shorthand when no layout is given: xs=2/12, sm=3/12, md=6/12, lg=12/12."
              },
              "layout": {
                "$ref": "#/components/schemas/WidgetLayout"
              },
              "type": {
                "type": "string",
                "enum": [
                  "breakdown"
                ]
              },
              "measure": {
                "type": "string",
                "enum": [
                  "spend",
                  "revenue_first",
                  "revenue_last"
                ]
              },
              "days": {
                "type": "integer",
                "minimum": 0,
                "exclusiveMinimum": true,
                "maximum": 365,
                "default": 30
              }
            },
            "required": [
              "id",
              "type",
              "measure"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "minLength": 1,
                "maxLength": 40,
                "description": "Stable id within the dashboard, e.g. 'w1'."
              },
              "title": {
                "type": "string",
                "maxLength": 80,
                "description": "Optional custom title; each type has a sensible default."
              },
              "size": {
                "type": "string",
                "enum": [
                  "xs",
                  "sm",
                  "md",
                  "lg"
                ],
                "description": "Width shorthand when no layout is given: xs=2/12, sm=3/12, md=6/12, lg=12/12."
              },
              "layout": {
                "$ref": "#/components/schemas/WidgetLayout"
              },
              "type": {
                "type": "string",
                "enum": [
                  "note"
                ]
              },
              "text": {
                "type": "string",
                "maxLength": 2000
              }
            },
            "required": [
              "id",
              "type",
              "text"
            ]
          }
        ]
      },
      "DashboardSpec": {
        "type": "object",
        "properties": {
          "widgets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DashboardWidget"
            },
            "maxItems": 24
          }
        },
        "required": [
          "widgets"
        ]
      },
      "Dashboard": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "product_slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "spec": {
            "$ref": "#/components/schemas/DashboardSpec"
          },
          "created_by": {
            "type": "string"
          },
          "updated_by": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "product_slug",
          "name",
          "description",
          "spec",
          "created_by",
          "updated_by",
          "created_at",
          "updated_at"
        ]
      },
      "CreateDashboardRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "description": {
            "type": "string",
            "maxLength": 300
          },
          "spec": {
            "$ref": "#/components/schemas/DashboardSpec"
          },
          "prompt": {
            "type": "string",
            "minLength": 3,
            "maxLength": 500,
            "description": "Describe the dashboard in plain words; the Keeper composes a spec from it. Ignored when spec is given."
          }
        },
        "required": [
          "name"
        ],
        "description": "Provide a full spec (agents usually do), a prompt to compose one, or neither to start blank."
      },
      "GeneratedDashboard": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "spec": {
            "$ref": "#/components/schemas/DashboardSpec"
          },
          "matched": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What the composer recognised in the prompt — shown so the draft is honest about what it understood."
          }
        },
        "required": [
          "name",
          "spec",
          "matched"
        ]
      },
      "GenerateDashboardRequest": {
        "type": "object",
        "properties": {
          "prompt": {
            "type": "string",
            "minLength": 3,
            "maxLength": 500
          }
        },
        "required": [
          "prompt"
        ]
      },
      "UpdateDashboardRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "description": {
            "type": "string",
            "nullable": true,
            "maxLength": 300
          },
          "spec": {
            "$ref": "#/components/schemas/DashboardSpec"
          }
        }
      },
      "AttributionChannel": {
        "type": "object",
        "properties": {
          "channel": {
            "type": "string"
          },
          "spend_cents": {
            "type": "integer"
          },
          "revenue_first_cents": {
            "type": "integer",
            "description": "Revenue whose identity's FIRST touch was this channel."
          },
          "revenue_last_cents": {
            "type": "integer",
            "description": "Revenue whose identity's LAST touch was this channel."
          },
          "transactions_first": {
            "type": "integer"
          },
          "customers_first": {
            "type": "integer",
            "description": "Customers first acquired in the window, by first touch."
          },
          "cac_cents": {
            "type": "integer",
            "nullable": true
          },
          "roas": {
            "type": "number",
            "nullable": true,
            "description": "First-touch revenue / spend. Null when there is no spend."
          }
        },
        "required": [
          "channel",
          "spend_cents",
          "revenue_first_cents",
          "revenue_last_cents",
          "transactions_first",
          "customers_first",
          "cac_cents",
          "roas"
        ]
      },
      "AttributionReport": {
        "type": "object",
        "properties": {
          "window_days": {
            "type": "integer"
          },
          "channels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttributionChannel"
            },
            "description": "Includes 'unattributed' as its own row — never smeared across channels."
          },
          "totals": {
            "type": "object",
            "properties": {
              "revenue_cents": {
                "type": "integer"
              },
              "spend_cents": {
                "type": "integer"
              },
              "attributed_first_cents": {
                "type": "integer"
              },
              "unattributed_cents": {
                "type": "integer"
              }
            },
            "required": [
              "revenue_cents",
              "spend_cents",
              "attributed_first_cents",
              "unattributed_cents"
            ]
          },
          "coverage": {
            "type": "object",
            "properties": {
              "identities_total": {
                "type": "integer"
              },
              "identities_with_channel": {
                "type": "integer"
              },
              "events_total": {
                "type": "integer"
              },
              "events_with_channel": {
                "type": "integer"
              },
              "transactions_total": {
                "type": "integer"
              },
              "transactions_with_identity": {
                "type": "integer"
              }
            },
            "required": [
              "identities_total",
              "identities_with_channel",
              "events_total",
              "events_with_channel",
              "transactions_total",
              "transactions_with_identity"
            ],
            "description": "How complete the attribution inputs are — the honesty panel."
          }
        },
        "required": [
          "window_days",
          "channels",
          "totals",
          "coverage"
        ]
      },
      "TimeseriesRow": {
        "type": "object",
        "properties": {
          "day": {
            "type": "string",
            "description": "YYYY-MM-DD"
          },
          "spend_cents": {
            "type": "integer"
          },
          "revenue_cents": {
            "type": "integer"
          },
          "visits": {
            "type": "integer"
          },
          "leads": {
            "type": "integer"
          },
          "customers": {
            "type": "integer"
          }
        },
        "required": [
          "day",
          "spend_cents",
          "revenue_cents",
          "visits",
          "leads",
          "customers"
        ]
      },
      "FunnelStep": {
        "type": "object",
        "properties": {
          "stage": {
            "type": "string",
            "enum": [
              "impression",
              "visit",
              "engaged",
              "lead",
              "qualified",
              "signup",
              "activated",
              "converted",
              "payment",
              "churned"
            ]
          },
          "label": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          }
        },
        "required": [
          "stage",
          "label"
        ]
      },
      "FunnelDefinition": {
        "type": "object",
        "properties": {
          "steps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FunnelStep"
            }
          },
          "is_default": {
            "type": "boolean",
            "description": "True while the product is on the built-in ladder (nothing saved yet)."
          },
          "available": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "stage": {
                  "type": "string",
                  "enum": [
                    "impression",
                    "visit",
                    "engaged",
                    "lead",
                    "qualified",
                    "signup",
                    "activated",
                    "converted",
                    "payment",
                    "churned"
                  ]
                },
                "volume_30d": {
                  "type": "integer"
                },
                "events_30d": {
                  "type": "integer"
                },
                "sources": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Which adapters have written this stage in the last 30 days."
                }
              },
              "required": [
                "stage",
                "volume_30d",
                "events_30d",
                "sources"
              ]
            },
            "description": "Every canonical stage with its recent tracking status — what's wired vs what would render empty."
          }
        },
        "required": [
          "steps",
          "is_default",
          "available"
        ]
      },
      "FunnelDefinitionRequest": {
        "type": "object",
        "properties": {
          "steps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FunnelStep"
            },
            "minItems": 2,
            "maxItems": 10
          }
        },
        "required": [
          "steps"
        ]
      },
      "FunnelSeriesRow": {
        "type": "object",
        "properties": {
          "day": {
            "type": "string"
          },
          "stage": {
            "type": "string",
            "enum": [
              "impression",
              "visit",
              "engaged",
              "lead",
              "qualified",
              "signup",
              "activated",
              "converted",
              "payment",
              "churned"
            ]
          },
          "volume": {
            "type": "integer"
          }
        },
        "required": [
          "day",
          "stage",
          "volume"
        ]
      },
      "HealthCheck": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pass",
              "warn",
              "fail",
              "skip"
            ],
            "description": "skip = not applicable / no data yet; excluded from the score but shown with its setup action."
          },
          "evidence": {
            "type": "string",
            "description": "The one metric line that justifies the status."
          },
          "action": {
            "type": "string",
            "nullable": true,
            "description": "What to do about it. Null when passing."
          }
        },
        "required": [
          "id",
          "label",
          "status",
          "evidence",
          "action"
        ]
      },
      "HealthReport": {
        "type": "object",
        "properties": {
          "product_slug": {
            "type": "string"
          },
          "generated_at": {
            "type": "string"
          },
          "window_days": {
            "type": "integer"
          },
          "score": {
            "type": "integer",
            "nullable": true,
            "description": "0–100 across all scoreable checks; null until any data exists."
          },
          "grade": {
            "type": "string",
            "nullable": true
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "enum": [
                    "tracking",
                    "funnel",
                    "advertising",
                    "seo_social"
                  ]
                },
                "label": {
                  "type": "string"
                },
                "score": {
                  "type": "integer",
                  "nullable": true
                },
                "checks": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/HealthCheck"
                  }
                }
              },
              "required": [
                "key",
                "label",
                "score",
                "checks"
              ]
            }
          }
        },
        "required": [
          "product_slug",
          "generated_at",
          "window_days",
          "score",
          "grade",
          "categories"
        ]
      },
      "HistoryEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "occurred_at": {
            "type": "string"
          },
          "actor": {
            "type": "string",
            "description": "A human's email, or 'keeper' / 'system' / 'policy'."
          },
          "via": {
            "type": "string",
            "description": "dashboard | mcp | api | job"
          },
          "action": {
            "type": "string"
          },
          "product_slug": {
            "type": "string",
            "nullable": true
          },
          "subject_ref": {
            "type": "string",
            "nullable": true
          },
          "card_headline": {
            "type": "string",
            "nullable": true,
            "description": "When the subject is a Keeper card, its headline."
          },
          "card_type": {
            "type": "string",
            "nullable": true
          },
          "payload": {
            "type": "object",
            "additionalProperties": {
              "nullable": true
            }
          }
        },
        "required": [
          "id",
          "occurred_at",
          "actor",
          "via",
          "action",
          "product_slug",
          "subject_ref",
          "card_headline",
          "card_type",
          "payload"
        ]
      }
    },
    "parameters": {}
  },
  "paths": {
    "/auth/signup": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Create an account",
        "description": "Self-serve signup. Sends a verification email; the account cannot log in until verified. Responds identically whether or not the email is already registered.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SignupRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification email sent (or already registered).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignupResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/verify": {
      "get": {
        "tags": [
          "auth"
        ],
        "summary": "Verify an email address",
        "description": "Consumes the emailed token. On success redirects (302) to the dashboard login; from an API client treat any 2xx/3xx as verified.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "token",
            "in": "query"
          }
        ],
        "responses": {
          "302": {
            "description": "Verified; redirecting to the dashboard."
          },
          "400": {
            "description": "Invalid or expired token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/login": {
      "post": {
        "tags": [
          "auth"
        ],
        "summary": "Log in",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "401": {
            "description": "Wrong credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Email not yet verified.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/logout": {
      "post": {
        "tags": [
          "auth"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Log out (revoke this session)",
        "responses": {
          "200": {
            "description": "Session revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          }
        }
      }
    },
    "/auth/me": {
      "get": {
        "tags": [
          "auth"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Who am I",
        "responses": {
          "200": {
            "description": "The authenticated account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          }
        }
      }
    },
    "/team": {
      "get": {
        "tags": [
          "team"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "List the account's team",
        "responses": {
          "200": {
            "description": "Members, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TeamMember"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/team/invites": {
      "post": {
        "tags": [
          "team"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Invite a person to the account",
        "description": "Owners only. Returns the single-use invite URL (also emailed when an email service is configured) — valid 7 days; the invitee sets their password at /auth/accept-invite.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InviteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Invited.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "user_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "invite_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_in_hours": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "ok",
                    "user_id",
                    "invite_url",
                    "expires_in_hours"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Not an owner.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Email already has a login.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/team/{id}/disable": {
      "post": {
        "tags": [
          "team"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Disable a team member",
        "description": "Owners only. Revokes their sessions immediately. You can't disable yourself, and the last active owner can't be disabled.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "Guard rail refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Not an owner.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/team/{id}/enable": {
      "post": {
        "tags": [
          "team"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Re-enable a team member",
        "description": "Owners only.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "Guard rail refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Not an owner.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/team/{id}/role": {
      "post": {
        "tags": [
          "team"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Change a member's role",
        "description": "Owners only. The last active owner can't be demoted.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MemberRoleRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "description": "Guard rail refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/accept-invite": {
      "post": {
        "tags": [
          "team"
        ],
        "summary": "Accept an invite",
        "description": "Public — the single-use invite token is the credential. Sets the password and returns a logged-in session.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AcceptInviteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Joined; session created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or expired invite.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/auth/keys": {
      "get": {
        "tags": [
          "auth"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "List API keys",
        "responses": {
          "200": {
            "description": "Keys (prefixes only).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ApiKey"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "auth"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create an API key",
        "description": "The response contains the full key exactly once.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80,
                    "default": "default"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreated"
                }
              }
            }
          }
        }
      }
    },
    "/auth/keys/{id}/revoke": {
      "post": {
        "tags": [
          "auth"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Revoke an API key",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          }
        }
      }
    },
    "/portfolio": {
      "get": {
        "tags": [
          "portfolio"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "All products in your account",
        "description": "One row per product: spend, traffic, revenue, CAC, LTV:CAC, gate status, pending Keeper cards. Operators see every account.",
        "responses": {
          "200": {
            "description": "Products.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ProductRow"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/products": {
      "post": {
        "tags": [
          "portfolio"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create a product",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateProductRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "slug": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "stage": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "slug",
                    "name",
                    "currency",
                    "stage"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Slug taken.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}": {
      "get": {
        "tags": [
          "portfolio"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "One product's portfolio row",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The row.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductRow"
                }
              }
            }
          },
          "404": {
            "description": "Not yours or doesn't exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/funnel": {
      "get": {
        "tags": [
          "portfolio"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Funnel stage × channel volumes",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "exclusiveMinimum": true,
              "maximum": 365,
              "default": 30
            },
            "required": false,
            "name": "days",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Rows.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "stage": {
                        "type": "string"
                      },
                      "channel": {
                        "type": "string",
                        "nullable": true
                      },
                      "events": {
                        "type": "integer"
                      },
                      "volume": {
                        "type": "integer"
                      }
                    },
                    "required": [
                      "stage",
                      "channel",
                      "events",
                      "volume"
                    ]
                  }
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/payback": {
      "get": {
        "tags": [
          "portfolio"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Cohort payback curves + per-channel CAC",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "cohorts: cumulative revenue per cohort-day; cac: 90-day spend / first-touch customers per channel.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "cohorts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "channel": {
                            "type": "string"
                          },
                          "cohort_month": {
                            "type": "string"
                          },
                          "day_n": {
                            "type": "integer"
                          },
                          "identities": {
                            "type": "integer"
                          },
                          "cum_revenue_cents": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "channel",
                          "cohort_month",
                          "day_n",
                          "identities",
                          "cum_revenue_cents"
                        ]
                      }
                    },
                    "cac": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "channel": {
                            "type": "string"
                          },
                          "spend_cents": {
                            "type": "integer"
                          },
                          "customers": {
                            "type": "integer"
                          },
                          "cac_cents": {
                            "type": "integer",
                            "nullable": true
                          }
                        },
                        "required": [
                          "channel",
                          "spend_cents",
                          "customers",
                          "cac_cents"
                        ]
                      }
                    }
                  },
                  "required": [
                    "cohorts",
                    "cac"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/queue": {
      "get": {
        "tags": [
          "queue"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Pending Keeper cards",
        "description": "The HITL spine. The Keeper proposes; only a human resolution moves a card. Nothing changes spend without a tap.",
        "responses": {
          "200": {
            "description": "Cards.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/QueueCard"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/queue/{id}/approve": {
      "post": {
        "tags": [
          "queue"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Approve a card",
        "description": "Approving a spend proposal is policy-checked (daily caps, network walls, portfolio ceiling) and can change ad spend. resolved_by records the authenticated human.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolveRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "403": {
            "description": "Policy wall refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such pending card in your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/queue/{id}/reject": {
      "post": {
        "tags": [
          "queue"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Dismiss a card",
        "description": "Removes the card from the queue; recorded with the authenticated actor.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolveRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "403": {
            "description": "Policy wall refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such pending card in your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/queue/{id}/snooze": {
      "post": {
        "tags": [
          "queue"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Snooze a card",
        "description": "Reappears after snoozeHours (default 24).",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolveRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "403": {
            "description": "Policy wall refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such pending card in your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/queue/{id}/restore": {
      "post": {
        "tags": [
          "queue"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Restore a snoozed/expired card",
        "description": "Only ever returns a card to pending; can never resolve one.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResolveRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "403": {
            "description": "Policy wall refused it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such pending card in your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/connections": {
      "get": {
        "tags": [
          "connections"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Integration inventory",
        "description": "Status, redacted config, last sync/error per source. Secrets never appear here.",
        "responses": {
          "200": {
            "description": "Connections.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "kind": {
                        "type": "string"
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "pending",
                          "active",
                          "error",
                          "disabled"
                        ]
                      },
                      "config": {
                        "type": "object",
                        "nullable": true,
                        "additionalProperties": {
                          "nullable": true
                        }
                      },
                      "last_sync_at": {
                        "type": "string",
                        "nullable": true
                      },
                      "last_error": {
                        "type": "string",
                        "nullable": true
                      },
                      "product_slug": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "kind",
                      "status",
                      "config",
                      "last_sync_at",
                      "last_error",
                      "product_slug"
                    ]
                  }
                }
              }
            }
          }
        }
      }
    },
    "/connect/google/start": {
      "post": {
        "tags": [
          "connections"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Begin connecting Google sources",
        "description": "Returns an authorization URL for a HUMAN to open (agents: hand it to your user). One Google grant serves ga4, gtm, and google_ads together. Then poll /connect/google/status.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConnectStartRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The URL and the state to poll with.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "state": {
                      "type": "string"
                    },
                    "auth_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_at": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "state",
                    "auth_url",
                    "expires_at"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/connect/google/status": {
      "get": {
        "tags": [
          "connections"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Poll an in-progress Google connection",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "state",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "pending | complete (with entity options for the select step) | error.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "complete",
                        "error"
                      ]
                    },
                    "error": {
                      "type": "string"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string"
                          },
                          "connection_id": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "kind",
                          "connection_id",
                          "status"
                        ]
                      }
                    },
                    "options": {
                      "type": "object",
                      "additionalProperties": {
                        "nullable": true
                      }
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/connect/google/select": {
      "post": {
        "tags": [
          "connections"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Finish a Google connection",
        "description": "Choose which GA4 property / GTM container / Ads customer to track, from the options in the status payload.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "state": {
                    "type": "string"
                  },
                  "ga4_property_id": {
                    "type": "string"
                  },
                  "gtm_container_path": {
                    "type": "string"
                  },
                  "ads_customer_id": {
                    "type": "string"
                  }
                },
                "required": [
                  "state"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Activated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "activated": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "ok",
                    "activated"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/connections/semrush": {
      "post": {
        "tags": [
          "connections"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Connect SEMrush",
        "description": "Stores your SEMrush API key encrypted. Never test-called — calls burn your units; snapshots run weekly.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "api_key": {
                    "type": "string"
                  },
                  "domain": {
                    "type": "string"
                  },
                  "database": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "default": "us"
                  }
                },
                "required": [
                  "api_key",
                  "domain"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stored.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "connection_id": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "connection_id"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/connections/{kind}/test": {
      "post": {
        "tags": [
          "connections"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Test a connection",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "ga4",
                "gtm",
                "google_ads",
                "semrush",
                "mysql"
              ]
            },
            "required": true,
            "name": "kind",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "ok + human-readable detail.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "detail": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "ok",
                    "detail"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/spend": {
      "get": {
        "tags": [
          "agent"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Daily spend by channel and campaign",
        "description": "Includes the product's daily cap, whether it is currently binding, and the portfolio ceiling.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "exclusiveMinimum": true,
              "maximum": 365,
              "default": 30
            },
            "required": false,
            "name": "days",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Spend rows + caps.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rows": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "day": {
                            "type": "string"
                          },
                          "channel": {
                            "type": "string"
                          },
                          "campaign_id": {
                            "type": "string",
                            "nullable": true
                          },
                          "campaign_name": {
                            "type": "string",
                            "nullable": true
                          },
                          "spend_cents": {
                            "type": "integer"
                          },
                          "impressions": {
                            "type": "integer",
                            "nullable": true
                          },
                          "clicks": {
                            "type": "integer",
                            "nullable": true
                          },
                          "currency": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "day",
                          "channel",
                          "campaign_id",
                          "campaign_name",
                          "spend_cents",
                          "impressions",
                          "clicks",
                          "currency"
                        ]
                      }
                    },
                    "caps": {
                      "type": "object",
                      "properties": {
                        "daily_budget_cap_cents": {
                          "type": "integer"
                        },
                        "stage": {
                          "type": "string"
                        },
                        "binding": {
                          "type": "boolean"
                        },
                        "portfolio_monthly_ceiling_cents": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "daily_budget_cap_cents",
                        "stage",
                        "binding",
                        "portfolio_monthly_ceiling_cents"
                      ]
                    }
                  },
                  "required": [
                    "rows",
                    "caps"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/proposals": {
      "post": {
        "tags": [
          "agent"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Propose a spend change (budget.propose)",
        "description": "The ONLY write that can lead to an ad-network change, and it never executes directly: it queues a card for a HUMAN to approve. Policy caps are enforced here, at the boundary — an over-cap proposal is rejected with policy_code rather than parked. Proposals expire after 72 hours.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProposalRequest"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued for a human.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "proposal_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending_human"
                      ]
                    },
                    "expires_in_hours": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "proposal_id",
                    "status",
                    "expires_in_hours"
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Refused by the policy engine.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "policy_code": {
                      "type": "string",
                      "enum": [
                        "policy_refused"
                      ]
                    }
                  },
                  "required": [
                    "error",
                    "policy_code"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/distribution": {
      "post": {
        "tags": [
          "agent"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Log a distribution event (events.log)",
        "description": "Record outreach an agent or human performed — a post shipped, a listing submitted, an email sent — so its traffic can be joined back via utm_campaign.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DistributionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Logged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "required": [
                    "ok",
                    "id"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/dashboards": {
      "get": {
        "tags": [
          "dashboards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "List a product's dashboards",
        "description": "User- and agent-built dashboards. The built-in Overview is not stored — clients render it from a constant spec.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Dashboards, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Dashboard"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "dashboards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Create a dashboard",
        "description": "Pass a full widget spec to build it exactly (the agent path), a prompt to have the Keeper compose one, or neither to start blank. Audited.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDashboardRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dashboard"
                }
              }
            }
          },
          "400": {
            "description": "Spec failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/dashboards/generate": {
      "post": {
        "tags": [
          "dashboards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Draft a dashboard from a description",
        "description": "Deterministic composer: maps the words in your description onto known widgets. Returns a draft spec — nothing is saved until you POST it to /dashboards.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateDashboardRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The draft.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GeneratedDashboard"
                }
              }
            }
          }
        }
      }
    },
    "/dashboards/{id}": {
      "post": {
        "tags": [
          "dashboards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Update a dashboard",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateDashboardRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Dashboard"
                }
              }
            }
          },
          "404": {
            "description": "Not yours or doesn't exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/dashboards/{id}/delete": {
      "post": {
        "tags": [
          "dashboards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Delete a dashboard",
        "description": "Dashboards are configuration, not facts — deletion is real and audited.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/attribution": {
      "get": {
        "tags": [
          "attribution"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Revenue by channel under first- and last-touch models",
        "description": "Spend, first-touch and last-touch revenue, customers, CAC and ROAS per channel, plus attribution coverage. Unattributed revenue is its own row, shown honestly.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "exclusiveMinimum": true,
              "maximum": 365,
              "default": 30
            },
            "required": false,
            "name": "days",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The report.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttributionReport"
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/timeseries": {
      "get": {
        "tags": [
          "attribution"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Daily spend, revenue, visits, leads, customers",
        "description": "One row per day, zero-filled — the series behind dashboard trend widgets.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "exclusiveMinimum": true,
              "maximum": 365,
              "default": 90
            },
            "required": false,
            "name": "days",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Rows, oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TimeseriesRow"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/funnel/definition": {
      "get": {
        "tags": [
          "funnel"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "The product's funnel definition + tracking status per stage",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Steps and stage availability.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FunnelDefinition"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "funnel"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Save the funnel definition",
        "description": "Ordered, labelled steps drawn from the canonical stage taxonomy. Audited; the funnel page and health checks use it immediately.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FunnelDefinitionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "steps": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FunnelStep"
                      }
                    }
                  },
                  "required": [
                    "ok",
                    "steps"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid steps.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/funnel/series": {
      "get": {
        "tags": [
          "funnel"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Daily volume per funnel stage",
        "description": "Powers 'the funnel today vs over time': clients sum windows for side-by-side comparison and draw per-step trends from the same rows.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "exclusiveMinimum": true,
              "maximum": 365,
              "default": 60
            },
            "required": false,
            "name": "days",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Rows, oldest first. Days with no events are absent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/FunnelSeriesRow"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/products/{slug}/health-report": {
      "get": {
        "tags": [
          "health"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Audit the product: site & tracking, funnel, advertising, SEO & social",
        "description": "Deterministic checks over the warehouse — a scored, actionable audit in the spirit of an SEO site health report. Every non-pass check carries the action that fixes it.",
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "required": true,
            "name": "slug",
            "in": "path"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "exclusiveMinimum": true,
              "maximum": 365,
              "default": 30
            },
            "required": false,
            "name": "days",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The report.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthReport"
                }
              }
            }
          }
        }
      }
    },
    "/history": {
      "get": {
        "tags": [
          "history"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Every change and decision, human or AI",
        "description": "The audit log as a timeline: Keeper cards raised, human approvals/dismissals (resolved_by), policy blocks, proposals, config edits. Product-scoped events only; account-plumbing events (logins) are not included for tenants.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Limit to one product slug."
            },
            "required": false,
            "name": "product",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "exclusiveMinimum": true,
              "maximum": 365,
              "default": 90
            },
            "required": false,
            "name": "days",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 0,
              "exclusiveMinimum": true,
              "maximum": 500,
              "default": 200
            },
            "required": false,
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Events, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/HistoryEvent"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/health": {
      "get": {
        "tags": [
          "health"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "summary": "Pipeline health",
        "description": "Job runs (with consecutive failures), per-source data freshness, queue backlog, and the share of revenue that cannot be attributed — shown honestly, never smeared across channels.",
        "responses": {
          "200": {
            "description": "Health report.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobs": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": {
                          "nullable": true
                        }
                      }
                    },
                    "sources": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": {
                          "nullable": true
                        }
                      }
                    },
                    "queue": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": {
                        "nullable": true
                      }
                    },
                    "attribution": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": {
                          "nullable": true
                        }
                      }
                    },
                    "products": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": {
                          "nullable": true
                        }
                      }
                    },
                    "now": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "jobs",
                    "sources",
                    "queue",
                    "attribution",
                    "products",
                    "now"
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}