{
  "openapi": "3.0.3",
  "info": {
    "title": "Burt Analytics API",
    "version": "1.0.0",
    "description": "Organization-scoped analytics over agent runs and the attribution records that connect people to those runs. Authenticate with an organization API key (`Authorization: Bearer burt_sk_...`) that includes the `analytics:read` scope."
  },
  "servers": [{ "url": "https://api.burthq.com" }],
  "security": [{ "bearerAuth": [] }],
  "paths": {
    "/api/v1/analytics/catalog": {
      "get": {
        "operationId": "getAnalyticsCatalog",
        "summary": "Get the analytics catalog",
        "description": "Returns the versioned catalog of models, metrics, and dimensions accepted by the query endpoint. Use it to discover valid member names before building a query.",
        "parameters": [{ "$ref": "#/components/parameters/RequestId" }],
        "responses": {
          "200": {
            "description": "The published catalog.",
            "headers": {
              "x-request-id": { "$ref": "#/components/headers/RequestId" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CatalogResponse" },
                "example": {
                  "catalogVersion": "2026-08-24.2",
                  "models": [
                    {
                      "name": "agent_runs",
                      "description": "One row for each agent run. Sub-agent work and the supporting browser, phone, inbound-phone, and platform runs it starts are not counted separately. The date range filters on createdAt.",
                      "grain": "one agent run; sub-agent and supporting runs are not counted separately",
                      "eventClock": "createdAt",
                      "metrics": [
                        {
                          "name": "run_count",
                          "type": "number",
                          "description": "Agent runs, whatever their status.",
                          "additive": true,
                          "deprecated": null
                        },
                        {
                          "name": "completed_run_count",
                          "type": "number",
                          "description": "Agent runs that completed successfully (COMPLETED).",
                          "additive": true,
                          "deprecated": null
                        },
                        {
                          "name": "failed_run_count",
                          "type": "number",
                          "description": "Agent runs that failed (FAILED).",
                          "additive": true,
                          "deprecated": null
                        },
                        {
                          "name": "cancelled_run_count",
                          "type": "number",
                          "description": "Agent runs that were cancelled (CANCELLED).",
                          "additive": true,
                          "deprecated": null
                        },
                        {
                          "name": "active_run_count",
                          "type": "number",
                          "description": "Agent runs still working or waiting: PENDING, RUNNING, WAITING_FOR_SLOT, CANCELLING, AWAITING_REPLY, SLEEPING, PAUSING, or PAUSED.",
                          "additive": true,
                          "deprecated": null
                        },
                        {
                          "name": "run_success_rate",
                          "type": "number",
                          "description": "Completed agent runs divided by the sum of completed and failed runs. Cancelled and active runs are excluded from both sides. The result is null when the denominator is zero.",
                          "additive": false,
                          "deprecated": null
                        },
                        {
                          "name": "unique_initiator_count",
                          "type": "number",
                          "description": "Distinct identified starters. A starter may be a person or an automated starter. Starters without identifiers are excluded.",
                          "additive": false,
                          "deprecated": null
                        }
                      ],
                      "dimensions": [
                        {
                          "name": "agent_id",
                          "type": "string",
                          "description": "Task-agent identifier for the agent run. Filters accept either the agent UUID or its optional slug. Rows for soft-deleted agents are still returned. The agent_name companion comes from the current joined task-agent record, so renames affect historical results. Slug resolution is point-in-time: a slug filter matches whichever agent currently holds that slug, not the agent it identified when the filter was written. Because agent_id always returns the agent UUID, pin the UUID instead of the slug for filters that must stay stable across renames.",
                          "companions": [
                            {
                              "name": "agent_name",
                              "type": "string",
                              "description": "Current display name from the joined task-agent record. Soft-deleted agents remain joinable, but renames affect historical results."
                            }
                          ],
                          "deprecated": null
                        },
                        {
                          "name": "user_identifier",
                          "type": "string",
                          "description": "Identifier for the person or automated starter that started the run, such as a normalized email address or phone number. Rows without an identifier group together and return null.",
                          "companions": [
                            {
                              "name": "user_name",
                              "type": "string",
                              "description": "Lexicographically greatest non-null display name in the user-identifier group."
                            }
                          ],
                          "deprecated": null
                        },
                        {
                          "name": "user_type",
                          "type": "string",
                          "description": "Class of the starter: internal, external, automated, unknown, or null when absent. The automated value marks an automated starter, which is a schedule or integration rather than a person.",
                          "companions": [],
                          "deprecated": null
                        },
                        {
                          "name": "day",
                          "type": "date",
                          "description": "Creation day in the requested IANA timezone, formatted YYYY-MM-DD. Filters take the same format. Only days with data are returned; the series is not gap-filled.",
                          "companions": [],
                          "deprecated": null
                        }
                      ]
                    },
                    {
                      "name": "agent_user_activity",
                      "description": "One row each time a person is recorded as starting, being associated with, or interacting with an agent run. The date range filters on occurredAt.",
                      "grain": "one attribution record for an identified person and an agent run",
                      "eventClock": "occurredAt",
                      "metrics": [
                        {
                          "name": "attributed_run_count",
                          "type": "number",
                          "description": "Unique person-and-run combinations with at least one attribution record.",
                          "additive": false,
                          "deprecated": null
                        },
                        {
                          "name": "initiated_run_count",
                          "type": "number",
                          "description": "Unique person-and-run combinations in which the person started the run.",
                          "additive": false,
                          "deprecated": null
                        },
                        {
                          "name": "associated_run_count",
                          "type": "number",
                          "description": "associated_run_count = attributed_run_count - initiated_run_count. Both values come from the attribution records selected by the query's date range and filters.",
                          "additive": false,
                          "deprecated": null
                        },
                        {
                          "name": "interacted_run_count",
                          "type": "number",
                          "description": "Unique person-and-run combinations with at least one recorded interaction.",
                          "additive": false,
                          "deprecated": null
                        },
                        {
                          "name": "interaction_count",
                          "type": "number",
                          "description": "Recorded interactions. Unlike the run metrics above, every interaction counts.",
                          "additive": true,
                          "deprecated": null
                        },
                        {
                          "name": "completed_attributed_run_count",
                          "type": "number",
                          "description": "Unique person-and-run combinations whose run is currently COMPLETED.",
                          "additive": false,
                          "deprecated": null
                        },
                        {
                          "name": "failed_attributed_run_count",
                          "type": "number",
                          "description": "Unique person-and-run combinations whose run is currently FAILED.",
                          "additive": false,
                          "deprecated": null
                        },
                        {
                          "name": "cancelled_attributed_run_count",
                          "type": "number",
                          "description": "Unique person-and-run combinations whose run is currently CANCELLED.",
                          "additive": false,
                          "deprecated": null
                        },
                        {
                          "name": "active_attributed_run_count",
                          "type": "number",
                          "description": "Unique person-and-run combinations whose run currently has a non-terminal status.",
                          "additive": false,
                          "deprecated": null
                        }
                      ],
                      "dimensions": [
                        {
                          "name": "agent_id",
                          "type": "string",
                          "description": "Task-agent identifier for the attributed agent run. Filters accept either the agent UUID or its optional slug. The agent_name companion comes from the current joined task-agent record. Slug resolution is point-in-time: a slug filter matches whichever agent currently holds that slug, not the agent it identified when the filter was written. Because agent_id always returns the agent UUID, pin the UUID instead of the slug for filters that must stay stable across renames.",
                          "companions": [
                            {
                              "name": "agent_name",
                              "type": "string",
                              "description": "Current display name from the joined task-agent record."
                            }
                          ],
                          "deprecated": null
                        },
                        {
                          "name": "user_identifier",
                          "type": "string",
                          "description": "Normalized email address or E.164 phone number for the attributed person.",
                          "companions": [
                            {
                              "name": "user_name",
                              "type": "string",
                              "description": "Lexicographically greatest non-null captured display name in the user-identifier group."
                            }
                          ],
                          "deprecated": null
                        },
                        {
                          "name": "user_domain",
                          "type": "string",
                          "description": "Lowercased email domain when the person is identified by an email address. Null when the person is identified by a phone number.",
                          "companions": [],
                          "deprecated": null
                        },
                        {
                          "name": "activity_type",
                          "type": "string",
                          "description": "Attribution activity: initiated, associated, or interacted.",
                          "companions": [],
                          "deprecated": null
                        },
                        {
                          "name": "run_initiation_type",
                          "type": "string",
                          "description": "How the agent run started: human, automated when an automated starter began it, or unknown. The value is frozen when the attribution record is written.",
                          "companions": [],
                          "deprecated": null
                        },
                        {
                          "name": "source",
                          "type": "string",
                          "description": "Channel that produced the attribution record.",
                          "companions": [],
                          "deprecated": null
                        },
                        {
                          "name": "day",
                          "type": "date",
                          "description": "Day of the attribution record in the requested IANA timezone, formatted YYYY-MM-DD. Filters use the same format.",
                          "companions": [],
                          "deprecated": null
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/InsufficientScope" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    },
    "/api/v1/analytics/query": {
      "post": {
        "operationId": "runAnalyticsQuery",
        "summary": "Run an analytics query",
        "description": "Runs a query against a versioned analytics catalog model and returns a columnar table. Totals queries (no dimensions) return exactly one row. Grouped, dimension-ordered queries may paginate with an opaque `cursor`.",
        "parameters": [{ "$ref": "#/components/parameters/RequestId" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyticsQueryRequest"
              },
              "example": {
                "model": "agent_runs",
                "metrics": [
                  "run_count",
                  "completed_run_count",
                  "failed_run_count"
                ],
                "dimensions": [],
                "filters": [],
                "range": {
                  "start": "2026-08-01",
                  "end": "2026-08-14",
                  "timezone": "UTC"
                },
                "order": [],
                "limit": 100
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Columnar query results.",
            "headers": {
              "x-request-id": { "$ref": "#/components/headers/RequestId" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyticsQueryResponse"
                },
                "example": {
                  "queryId": "550e8400-e29b-41d4-a716-446655440000",
                  "catalogVersion": "2026-08-24.2",
                  "asOf": "2026-08-21T00:00:00.000Z",
                  "range": {
                    "start": "2026-08-01",
                    "end": "2026-08-14",
                    "timezone": "UTC",
                    "startUtc": "2026-08-01T00:00:00.000Z",
                    "endUtc": "2026-08-15T00:00:00.000Z"
                  },
                  "columns": [
                    {
                      "name": "run_count",
                      "type": "number",
                      "member": "run_count"
                    },
                    {
                      "name": "completed_run_count",
                      "type": "number",
                      "member": "completed_run_count"
                    },
                    {
                      "name": "failed_run_count",
                      "type": "number",
                      "member": "failed_run_count"
                    }
                  ],
                  "rows": [[25, 17, 4]],
                  "cursor": null,
                  "warnings": []
                }
              }
            }
          },
          "400": {
            "description": "Invalid request. `error.code` is one of `invalid_query`, `unknown_member`, `range_too_large`, or `invalid_cursor`.",
            "headers": {
              "x-request-id": { "$ref": "#/components/headers/RequestId" }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/QueryErrorEnvelope" },
                "examples": {
                  "invalidQuery": {
                    "summary": "Invalid query grammar",
                    "value": {
                      "error": {
                        "code": "invalid_query",
                        "message": "Invalid analytics query.",
                        "request_id": "req_01J5Y7E9K2M4P6Q8R0S1T3V5W7"
                      }
                    }
                  },
                  "unknownMember": {
                    "summary": "Unknown catalog member",
                    "value": {
                      "error": {
                        "code": "unknown_member",
                        "message": "Unknown analytics member.",
                        "param": "not_a_member",
                        "request_id": "req_01J5Y7E9K2M4P6Q8R0S1T3V5W7"
                      }
                    }
                  },
                  "rangeTooLarge": {
                    "summary": "Date range exceeds 366 days",
                    "value": {
                      "error": {
                        "code": "range_too_large",
                        "message": "Analytics date range is too large.",
                        "request_id": "req_01J5Y7E9K2M4P6Q8R0S1T3V5W7"
                      }
                    }
                  },
                  "invalidCursor": {
                    "summary": "Invalid or mismatched cursor",
                    "value": {
                      "error": {
                        "code": "invalid_cursor",
                        "message": "Invalid analytics cursor.",
                        "request_id": "req_01J5Y7E9K2M4P6Q8R0S1T3V5W7"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/InsufficientScope" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Organization API key with the `analytics:read` scope."
      }
    },
    "parameters": {
      "RequestId": {
        "name": "x-request-id",
        "in": "header",
        "required": false,
        "description": "Optional caller-supplied request ID. Values must contain 1–64 ASCII letters, digits, or `._:/=-`; invalid values are replaced with a generated UUID.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 64,
          "pattern": "^[A-Za-z0-9._:/=-]+$"
        }
      }
    },
    "headers": {
      "RequestId": {
        "description": "Request identifier. On errors this matches `error.request_id`.",
        "schema": { "type": "string" }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": { "type": "integer", "minimum": 1, "maximum": 60 }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "A missing or malformed key returns this response without spending an admission limit. A well-formed unknown or revoked key normally returns the same response, but can return 429 while the global credential-verification budget is exhausted.",
        "headers": {
          "x-request-id": { "$ref": "#/components/headers/RequestId" }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/UnauthorizedErrorEnvelope"
            },
            "examples": {
              "unauthorized": {
                "summary": "Invalid API key",
                "value": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Invalid API key",
                    "request_id": "550e8400-e29b-41d4-a716-446655440000"
                  }
                }
              }
            }
          }
        }
      },
      "InsufficientScope": {
        "description": "The key is valid but does not include `analytics:read`.",
        "headers": {
          "x-request-id": { "$ref": "#/components/headers/RequestId" }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/InsufficientScopeErrorEnvelope"
            },
            "examples": {
              "insufficientScope": {
                "summary": "Missing analytics scope",
                "value": {
                  "error": {
                    "code": "insufficient_scope",
                    "message": "API key does not have the required scope",
                    "request_id": "550e8400-e29b-41d4-a716-446655440000"
                  }
                }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "A per-key rate limit, the global unknown-credential verification budget, or an analytics concurrency limit was exceeded. Read `Retry-After`.",
        "headers": {
          "x-request-id": { "$ref": "#/components/headers/RequestId" },
          "Retry-After": { "$ref": "#/components/headers/RetryAfter" }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitedErrorEnvelope"
            },
            "examples": {
              "rateLimited": {
                "summary": "Rate limit exceeded",
                "value": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests",
                    "request_id": "550e8400-e29b-41d4-a716-446655440000"
                  }
                }
              },
              "concurrencyLimited": {
                "summary": "Analytics concurrency limit exceeded",
                "value": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many concurrent requests",
                    "request_id": "550e8400-e29b-41d4-a716-446655440000"
                  }
                }
              }
            }
          }
        }
      },
      "InternalError": {
        "description": "An unexpected application or infrastructure failure occurred.",
        "headers": {
          "x-request-id": { "$ref": "#/components/headers/RequestId" }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/InternalErrorEnvelope" },
            "examples": {
              "internalError": {
                "summary": "Unexpected server failure",
                "value": {
                  "error": {
                    "code": "internal_error",
                    "message": "An internal error occurred",
                    "request_id": "550e8400-e29b-41d4-a716-446655440000"
                  }
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "UnauthorizedErrorEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": ["code", "message", "request_id"],
            "properties": {
              "code": { "type": "string", "enum": ["unauthorized"] },
              "message": { "type": "string", "enum": ["Invalid API key"] },
              "request_id": { "type": "string" }
            }
          }
        }
      },
      "InsufficientScopeErrorEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": ["code", "message", "request_id"],
            "properties": {
              "code": { "type": "string", "enum": ["insufficient_scope"] },
              "message": {
                "type": "string",
                "enum": ["API key does not have the required scope"]
              },
              "request_id": { "type": "string" }
            }
          }
        }
      },
      "RateLimitedErrorEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": ["code", "message", "request_id"],
            "properties": {
              "code": { "type": "string", "enum": ["rate_limited"] },
              "message": { "type": "string" },
              "request_id": { "type": "string" }
            }
          }
        }
      },
      "InternalErrorEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": ["code", "message", "request_id"],
            "properties": {
              "code": { "type": "string", "enum": ["internal_error"] },
              "message": {
                "type": "string",
                "enum": ["An internal error occurred"]
              },
              "request_id": { "type": "string" }
            }
          }
        }
      },
      "QueryErrorEnvelope": {
        "type": "object",
        "additionalProperties": false,
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": ["code", "message", "request_id"],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_query",
                  "unknown_member",
                  "range_too_large",
                  "invalid_cursor"
                ]
              },
              "message": { "type": "string" },
              "param": {
                "type": "string",
                "description": "Request field or member that caused the error, when applicable."
              },
              "request_id": {
                "type": "string",
                "description": "Matches the `x-request-id` response header."
              }
            }
          }
        }
      },
      "AnalyticsQueryRequest": {
        "type": "object",
        "required": ["model", "metrics", "range"],
        "additionalProperties": false,
        "properties": {
          "model": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "enum": ["agent_runs", "agent_user_activity"],
            "description": "Catalog model name. The current catalog supports `agent_runs` and `agent_user_activity`."
          },
          "metrics": {
            "type": "array",
            "minItems": 1,
            "maxItems": 16,
            "uniqueItems": true,
            "items": { "type": "string", "minLength": 1, "maxLength": 64 },
            "description": "Unique catalog metric names to select."
          },
          "dimensions": {
            "type": "array",
            "maxItems": 8,
            "default": [],
            "uniqueItems": true,
            "items": { "type": "string", "minLength": 1, "maxLength": 64 },
            "description": "Unique catalog dimensions to group by."
          },
          "filters": {
            "type": "array",
            "maxItems": 8,
            "default": [],
            "items": { "$ref": "#/components/schemas/QueryFilter" },
            "description": "Dimension filters: `eq` with one `value`, or `in` with 1–50 `values`."
          },
          "range": { "$ref": "#/components/schemas/QueryRange" },
          "order": {
            "type": "array",
            "maxItems": 8,
            "default": [],
            "uniqueItems": true,
            "x-unique-by": "member",
            "items": { "$ref": "#/components/schemas/QueryOrderTerm" },
            "description": "Up to eight unique order terms. Members must be selected metrics or dimensions. Totals queries cannot specify order terms."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500,
            "default": 100,
            "description": "Maximum rows returned per page."
          },
          "cursor": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "description": "Opaque continuation cursor from a previous grouped response. Not accepted on totals or metric-ordered queries."
          }
        }
      },
      "QueryFilter": {
        "oneOf": [
          {
            "type": "object",
            "required": ["member", "op", "value"],
            "additionalProperties": false,
            "properties": {
              "member": { "type": "string", "minLength": 1, "maxLength": 64 },
              "op": { "type": "string", "enum": ["eq"] },
              "value": { "type": "string", "minLength": 1, "maxLength": 200 }
            }
          },
          {
            "type": "object",
            "required": ["member", "op", "values"],
            "additionalProperties": false,
            "properties": {
              "member": { "type": "string", "minLength": 1, "maxLength": 64 },
              "op": { "type": "string", "enum": ["in"] },
              "values": {
                "type": "array",
                "minItems": 1,
                "maxItems": 50,
                "items": { "type": "string", "minLength": 1, "maxLength": 200 }
              }
            }
          }
        ]
      },
      "QueryRange": {
        "type": "object",
        "required": ["start", "end", "timezone"],
        "additionalProperties": false,
        "properties": {
          "start": {
            "type": "string",
            "format": "date",
            "description": "Inclusive, valid calendar date (`YYYY-MM-DD`) in the requested timezone. Must be on or before `end`."
          },
          "end": {
            "type": "string",
            "format": "date",
            "description": "Inclusive, valid calendar date (`YYYY-MM-DD`). The range may cover at most 366 days."
          },
          "timezone": {
            "type": "string",
            "description": "Canonical IANA timezone identifier, for example `America/Chicago` or `UTC`. Numeric offsets and legacy aliases are rejected."
          }
        }
      },
      "QueryOrderTerm": {
        "type": "object",
        "required": ["member", "direction"],
        "additionalProperties": false,
        "properties": {
          "member": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "A selected metric or dimension name."
          },
          "direction": { "type": "string", "enum": ["asc", "desc"] }
        }
      },
      "AnalyticsQueryResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "queryId",
          "catalogVersion",
          "asOf",
          "range",
          "columns",
          "rows",
          "cursor",
          "warnings"
        ],
        "properties": {
          "queryId": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier for this execution."
          },
          "catalogVersion": {
            "type": "string",
            "description": "Catalog version used to produce this response."
          },
          "asOf": {
            "type": "string",
            "format": "date-time",
            "description": "Inclusion boundary used for this response and any cursor continuation."
          },
          "range": {
            "type": "object",
            "additionalProperties": false,
            "required": ["start", "end", "timezone", "startUtc", "endUtc"],
            "properties": {
              "start": { "type": "string", "format": "date" },
              "end": { "type": "string", "format": "date" },
              "timezone": { "type": "string" },
              "startUtc": {
                "type": "string",
                "format": "date-time",
                "description": "Inclusive UTC instant the range resolves to."
              },
              "endUtc": {
                "type": "string",
                "format": "date-time",
                "description": "Exclusive UTC instant the range resolves to."
              }
            }
          },
          "columns": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Column" },
            "description": "Ordered column descriptors; a selected dimension is followed by its companions, then metrics."
          },
          "rows": {
            "type": "array",
            "items": {
              "type": "array",
              "items": {
                "oneOf": [
                  { "type": "string" },
                  { "type": "number" },
                  { "type": "string", "nullable": true, "enum": [null] }
                ]
              }
            },
            "description": "Values in the same order as `columns`."
          },
          "cursor": {
            "type": "string",
            "nullable": true,
            "description": "Continuation cursor for the next page, or `null` when there is no next page."
          },
          "warnings": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Warning" },
            "description": "Non-fatal semantic notices."
          }
        }
      },
      "Column": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name", "type", "member"],
        "properties": {
          "name": { "type": "string", "description": "Output column name." },
          "type": {
            "type": "string",
            "enum": ["string", "number", "date"],
            "description": "Value type of the column."
          },
          "member": {
            "type": "string",
            "description": "Catalog member this column belongs to; companion columns share their dimension's member name."
          }
        }
      },
      "Warning": {
        "type": "object",
        "additionalProperties": false,
        "required": ["code", "member", "message"],
        "properties": {
          "code": { "type": "string", "enum": ["non_additive_metric"] },
          "member": { "type": "string" },
          "message": { "type": "string" }
        }
      },
      "CatalogResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": ["catalogVersion", "models"],
        "properties": {
          "catalogVersion": {
            "type": "string",
            "description": "Version of the published member set."
          },
          "models": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CatalogModel" }
          }
        }
      },
      "CatalogModel": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "description",
          "grain",
          "eventClock",
          "metrics",
          "dimensions"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Model name sent as `model` in query requests."
          },
          "description": {
            "type": "string",
            "description": "Authoritative description of what the model counts."
          },
          "grain": {
            "type": "string",
            "description": "What one row of the model represents."
          },
          "eventClock": {
            "type": "string",
            "description": "Timestamp used to bucket rows into date ranges."
          },
          "metrics": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CatalogMetric" }
          },
          "dimensions": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CatalogDimension" }
          }
        }
      },
      "CatalogMetric": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name", "type", "description", "additive", "deprecated"],
        "properties": {
          "name": {
            "type": "string",
            "description": "Metric name for the `metrics` array in query requests."
          },
          "type": { "type": "string", "enum": ["number"] },
          "description": { "type": "string" },
          "additive": {
            "type": "boolean",
            "description": "Whether values can be summed across groups."
          },
          "deprecated": { "$ref": "#/components/schemas/DeprecatedInfo" }
        }
      },
      "CatalogDimension": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name", "type", "description", "companions", "deprecated"],
        "properties": {
          "name": {
            "type": "string",
            "description": "Dimension name for the `dimensions` and `filters` arrays."
          },
          "type": { "type": "string", "enum": ["string", "date"] },
          "description": { "type": "string" },
          "companions": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CatalogCompanion" },
            "description": "Fields returned automatically when this dimension is selected."
          },
          "deprecated": { "$ref": "#/components/schemas/DeprecatedInfo" }
        }
      },
      "CatalogCompanion": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name", "type", "description"],
        "properties": {
          "name": { "type": "string" },
          "type": { "type": "string", "enum": ["string"] },
          "description": { "type": "string" }
        }
      },
      "DeprecatedInfo": {
        "type": "object",
        "additionalProperties": false,
        "nullable": true,
        "description": "Deprecation metadata for a metric or dimension when set, otherwise `null`. Deprecated members remain queryable and do not automatically add a warning.",
        "properties": {
          "since": {
            "type": "string",
            "description": "Catalog version since which the member was deprecated."
          },
          "note": {
            "type": "string",
            "description": "Guidance on what to use instead."
          }
        },
        "required": ["since", "note"]
      }
    }
  }
}
