{
  "id": "elmo-api",
  "bundled": {
    "openapi": "3.2.0",
    "info": {
      "title": "Elmo API",
      "description": "Read and manage the brands, prompts, competitors, and AI-visibility analytics of this deployment.\n\n## Authentication\n\nEvery request carries a Bearer token in the `Authorization` header. Two kinds of token are accepted.\n\n**Instance admin keys** come from the operator's `ADMIN_API_KEYS` environment variable. They reach every organization and every endpoint, including the admin-only ones, and are not subject to plan limits or rate limits.\n\n**Organization keys** (`elmo_…`) are issued from the dashboard. A key acts inside exactly one organization, is issued either read or read-write access, and may be narrowed further to a subset of that organization's brands. Plan limits, read-only mode, and per-key rate limits all apply.\n\n## Scopes\n\nA key holds one of two scopes. `read` reaches every read operation; `write` adds every create, update, and delete one, and is issued alongside `read` rather than instead of it. Each operation lists what it requires in `x-elmo-scopes` — an operation listing none is reachable by any valid key. Admin keys hold both implicitly.\n\n| Scope | Grants |\n| --- | --- |\n| `read` | Read brands, prompts, competitors, runs, citations, analytics, and the organization's plan and usage |\n| `write` | Everything `read` grants, plus creating, updating, and deleting brands, prompts, and competitors |\n\nNothing finer is offered, because the operations actually worth withholding are not ones a scope can withhold. Deleting a *prompt* has no scope at all: it takes every run and citation with it, the dashboard offers no such action, and `PATCH /prompts/{promptId}` with `enabled: false` stops the tracking without losing the history. It is reachable with an instance admin key only.\n\nThere is deliberately no billing write scope: the API cannot change a subscription, an add-on quantity, or a payment method under any key, including an admin key. Operations marked `x-elmo-admin-only` are reachable with an instance admin key only; no scope grants them. Report generation and brand analysis spend provider budget with no organization to bill it to; deleting a prompt destroys tracked history the dashboard itself will not. For the same reason a single *field* can be admin-only: `enabled` on `PATCH /brands/{brandId}` stops a brand being sampled at all, so setting it with an organization key is a `403`.\n\n## Access and errors\n\nA brand outside the calling key's reach answers `404` with the same body a brand that does not exist would, so a key can never probe another tenant's data. A key that is valid but lacks the scope an operation requires answers `403` naming the missing scope.\n\nErrors share one envelope — `{ error, message, code }` — where `code` is a stable machine-readable string. Treat an unrecognized `code` as its HTTP status implies; new values are added without a version bump.\n\n## Conventions\n\nTimestamps are ISO-8601 UTC; dates are `YYYY-MM-DD`. Collections take `page` (1-based) and `limit` (max 100) and answer with a `pagination` object. Analytics endpoints take `start` + `end` as ISO 8601 timestamps, half-open, and answer with the whole window in one response; daily series are labelled in UTC. `/prompts/{promptId}/snapshot` keeps the `startDate`/`endDate` calendar days it shipped with.\n\n## Stability\n\nAn operation whose response shape may still change carries `x-stability: experimental`, and says so in the first line of its description. Everything else is stable: its shape will only ever gain fields. The absence of the marker is the promise.",
      "version": "1.1.0",
      "contact": {
        "name": "Elmo",
        "url": "https://github.com/elmohq/elmo"
      },
      "license": {
        "name": "MIT",
        "url": "https://github.com/elmohq/elmo/blob/main/LICENSE.md"
      }
    },
    "servers": [
      {
        "url": "https://app.elmohq.com/api/v1",
        "description": "Elmo API"
      }
    ],
    "security": [
      {
        "BearerAuth": []
      }
    ],
    "components": {
      "securitySchemes": {
        "BearerAuth": {
          "type": "http",
          "scheme": "bearer",
          "description": "An instance admin key from `ADMIN_API_KEYS`, or an organization key (`elmo_…`) issued from the dashboard."
        }
      },
      "schemas": {
        "Prompt": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "format": "uuid",
              "description": "Unique identifier for the prompt"
            },
            "brandId": {
              "type": "string",
              "description": "Brand identifier this prompt belongs to"
            },
            "value": {
              "type": "string",
              "description": "The actual prompt text"
            },
            "enabled": {
              "type": "boolean",
              "description": "Whether the prompt is currently enabled"
            },
            "tags": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "User-defined tags for categorizing this prompt"
            },
            "systemTags": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Auto-computed system tags (e.g., 'branded', 'unbranded'). Read-only."
            },
            "createdAt": {
              "type": "string",
              "format": "date-time",
              "description": "Timestamp when the prompt was created"
            },
            "updatedAt": {
              "type": "string",
              "format": "date-time",
              "description": "Timestamp when the prompt was last updated"
            },
            "premiumModels": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Models this prompt is tracked on grounded. Each entry spends one premium pairing."
            }
          },
          "required": [
            "id",
            "brandId",
            "value",
            "enabled",
            "tags",
            "systemTags",
            "createdAt",
            "updatedAt",
            "premiumModels"
          ]
        },
        "CreatePromptRequest": {
          "type": "object",
          "properties": {
            "brandId": {
              "type": "string",
              "description": "Brand identifier this prompt belongs to"
            },
            "value": {
              "type": "string",
              "description": "The prompt text",
              "minLength": 1
            },
            "tags": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "User-defined tags for categorizing this prompt"
            }
          },
          "required": [
            "brandId",
            "value"
          ]
        },
        "UpdatePromptRequest": {
          "type": "object",
          "minProperties": 1,
          "properties": {
            "value": {
              "type": "string",
              "description": "The prompt text",
              "minLength": 1
            },
            "enabled": {
              "type": "boolean",
              "description": "Whether the prompt is enabled"
            },
            "tags": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "User-defined tags for categorizing this prompt"
            },
            "premiumModels": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Replaces the prompt's grounded models. Checked against the organization's premium pool."
            }
          }
        },
        "Error": {
          "type": "object",
          "properties": {
            "error": {
              "type": "string",
              "description": "Error type"
            },
            "message": {
              "type": "string",
              "description": "Detailed error message"
            },
            "code": {
              "type": "string",
              "description": "Stable machine-readable code. Deliberately not an enum: new values are added without a version bump, so treat an unrecognized one as its HTTP status implies. Currently: `unauthorized`, `insufficient_scope`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `method_not_allowed`, `read_only`, `no_active_plan`, `brand_limit`, `prompt_limit`, `model_not_in_plan`, `model_picks_exceeded`, `premium_not_in_plan`, `premium_pool_exhausted`, `cadence_faster_than_plan`, `internal_error`."
            }
          },
          "required": [
            "error"
          ]
        },
        "CreateReportRequest": {
          "type": "object",
          "properties": {
            "brandName": {
              "type": "string",
              "description": "The brand name to analyze",
              "minLength": 1
            },
            "brandAliases": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "maxItems": 10,
              "description": "Other names the brand goes by (spellings, abbreviations, product names). A mention of any of them counts as a brand mention."
            },
            "brandWebsite": {
              "type": "string",
              "description": "The brand's website — a domain (nike.com) or a full URL. A URL with a path (e.g. https://www.nike.com/golf) scopes the analysis to that page; mentions are tracked against its domain either way.",
              "minLength": 1
            },
            "manualPrompts": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Optional list of custom prompts to include in the report"
            }
          },
          "required": [
            "brandName",
            "brandWebsite"
          ]
        },
        "ReportSummary": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "format": "uuid",
              "description": "Unique identifier for the report"
            },
            "brandName": {
              "type": "string",
              "description": "Brand name analyzed"
            },
            "brandAliases": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Other names counted as brand mentions"
            },
            "brandWebsite": {
              "type": "string",
              "description": "Brand website URL"
            },
            "status": {
              "type": "string",
              "enum": [
                "pending",
                "processing",
                "completed",
                "failed"
              ],
              "description": "Current report status"
            },
            "createdAt": {
              "type": "string",
              "format": "date-time",
              "description": "Timestamp when the report was created"
            },
            "completedAt": {
              "type": [
                "string",
                "null"
              ],
              "format": "date-time",
              "description": "Timestamp when the report completed"
            }
          },
          "required": [
            "id",
            "brandName",
            "brandAliases",
            "brandWebsite",
            "status",
            "createdAt",
            "completedAt"
          ]
        },
        "ReportPromptSnapshot": {
          "type": "object",
          "description": "Per-prompt snapshot of raw mention data from a report. Consumers compute SoV and other derived metrics.",
          "properties": {
            "promptValue": {
              "type": "string",
              "description": "The prompt text that was evaluated"
            },
            "totalRuns": {
              "type": "integer",
              "description": "Number of evaluation runs for this prompt"
            },
            "mentions": {
              "$ref": "#/components/schemas/MentionsSummary"
            }
          },
          "required": [
            "promptValue",
            "totalRuns",
            "mentions"
          ]
        },
        "MentionsSummary": {
          "type": "object",
          "description": "Aggregated mention counts for a prompt across its evaluation runs.",
          "properties": {
            "mentionsTotal": {
              "type": "integer",
              "description": "Total brand + competitor mentions across all runs"
            },
            "brandMentionsTotal": {
              "type": "integer",
              "description": "Number of runs where the brand was mentioned"
            },
            "competitorMentionsTotal": {
              "type": "integer",
              "description": "Total count of individual competitor mentions across all runs (a single run mentioning 3 competitors counts as 3)"
            },
            "mentionsTopK": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/MentionEntry"
              },
              "description": "Top-K competitor entities ranked by mention count"
            }
          },
          "required": [
            "mentionsTotal",
            "brandMentionsTotal",
            "competitorMentionsTotal",
            "mentionsTopK"
          ]
        },
        "MentionEntry": {
          "type": "object",
          "properties": {
            "entity": {
              "type": "string",
              "description": "Competitor entity name"
            },
            "count": {
              "type": "integer",
              "description": "Number of runs where this entity was mentioned"
            }
          },
          "required": [
            "entity",
            "count"
          ]
        },
        "CitedUrlEntry": {
          "type": "object",
          "properties": {
            "url": {
              "type": "string",
              "description": "The cited URL"
            },
            "title": {
              "type": [
                "string",
                "null"
              ],
              "description": "Page title if available"
            },
            "count": {
              "type": "integer",
              "description": "Number of times this URL was cited"
            }
          },
          "required": [
            "url",
            "title",
            "count"
          ]
        },
        "AnalyzeBrandRequest": {
          "type": "object",
          "properties": {
            "website": {
              "type": "string",
              "description": "Brand's website — a hostname or a full URL. A URL with a path (e.g. https://www.nike.com/golf) is analyzed as given; the returned website is always its domain.",
              "minLength": 1
            },
            "brandName": {
              "type": "string",
              "description": "Optional brand name hint. If omitted, inferred from the domain."
            },
            "maxCompetitors": {
              "type": "integer",
              "description": "Maximum number of competitor suggestions. 0 disables competitor generation entirely.",
              "minimum": 0,
              "maximum": 100,
              "default": 20
            },
            "maxPrompts": {
              "type": "integer",
              "description": "Maximum number of suggested prompts. 0 disables prompt generation entirely.",
              "minimum": 0,
              "maximum": 100,
              "default": 30
            }
          },
          "required": [
            "website"
          ]
        },
        "OnboardingSuggestion": {
          "type": "object",
          "properties": {
            "brandName": {
              "type": "string"
            },
            "website": {
              "type": "string",
              "description": "Cleaned hostname"
            },
            "additionalDomains": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "aliases": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "competitors": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "domains": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "aliases": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "name",
                  "domains",
                  "aliases"
                ]
              }
            },
            "suggestedPrompts": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "prompt": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "prompt",
                  "tags"
                ]
              }
            }
          },
          "required": [
            "brandName",
            "website",
            "additionalDomains",
            "aliases",
            "competitors",
            "suggestedPrompts"
          ]
        },
        "CreateBrandRequest": {
          "type": "object",
          "description": "Create a brand. Skips onboarding. The first entry in `domains` is treated as the brand's primary website; the rest are stored as additional domains.",
          "properties": {
            "id": {
              "type": "string",
              "minLength": 1
            },
            "name": {
              "type": "string",
              "minLength": 1
            },
            "domains": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "minItems": 1,
              "description": "Brand domains. The first entry is the primary website; remaining entries are additional domains."
            },
            "aliases": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "competitors": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "domains": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "aliases": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "name"
                ]
              }
            },
            "prompts": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "value": {
                    "type": "string",
                    "minLength": 1
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "enabled": {
                    "type": "boolean",
                    "default": true
                  }
                },
                "required": [
                  "value"
                ]
              }
            },
            "organizationId": {
              "type": "string",
              "description": "Organization to create the brand in.\n\n| | Omitted | Present |\n| --- | --- | --- |\n| **Organization key** | creates in the key's own organization | must name the key's own organization; any other value is a `400` |\n| **Admin key** | provisions a new organization named after the brand id | creates in the named organization, which must already exist — `404` if it does not |\n\nAn admin key omitting this field is currently the only way to create an organization over the API."
            }
          },
          "required": [
            "id",
            "name",
            "domains"
          ]
        },
        "UpdateBrandRequest": {
          "type": "object",
          "description": "Update brand-level fields. At least one field must be provided. Provided arrays replace the stored values verbatim. When `domains` is provided, the first entry becomes the primary website and the rest become additional domains. Prompts and competitors are managed via /prompts and /competitors.",
          "minProperties": 1,
          "properties": {
            "brandName": {
              "type": "string",
              "minLength": 1
            },
            "domains": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "minItems": 1,
              "description": "Brand domains. The first entry is the primary website; remaining entries are additional domains."
            },
            "aliases": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "enabled": {
              "type": "boolean",
              "description": "Whether the brand is sampled at all. **Modifiable only with an instance admin key**: setting it with an organization key is a `403`, because disabling ends tracking silently while the plan keeps being billed and no dashboard control does it at any role."
            }
          }
        },
        "Brand": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "User-supplied brand identifier (e.g. \"acme\")"
            },
            "name": {
              "type": "string"
            },
            "domains": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Brand domains. The first entry is the primary website; remaining entries are additional domains."
            },
            "aliases": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "enabled": {
              "type": "boolean"
            },
            "onboarded": {
              "type": "boolean"
            },
            "createdAt": {
              "type": "string",
              "format": "date-time"
            },
            "updatedAt": {
              "type": "string",
              "format": "date-time"
            },
            "organizationId": {
              "type": "string",
              "description": "The organization that owns this brand and is billed for it."
            },
            "enabledModels": {
              "description": "Models this brand is tracked on. Null means the deployment default.",
              "type": [
                "array",
                "null"
              ],
              "items": {
                "type": "string"
              }
            },
            "delayOverrideHours": {
              "type": [
                "integer",
                "null"
              ],
              "description": "Sampling cadence override in hours. Null means the plan cadence."
            }
          },
          "required": [
            "id",
            "name",
            "domains",
            "aliases",
            "enabled",
            "onboarded",
            "createdAt",
            "updatedAt",
            "organizationId",
            "enabledModels",
            "delayOverrideHours"
          ]
        },
        "BrandsList": {
          "type": "object",
          "properties": {
            "data": {
              "description": "The items on this page. Read this rather than the named key below.",
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Brand"
              }
            },
            "brands": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Brand"
              },
              "deprecated": true,
              "description": "Deprecated: read `data` instead. Kept while the one known consumer migrates, and removed in a future release."
            },
            "pagination": {
              "$ref": "#/components/schemas/Pagination"
            }
          },
          "required": [
            "data",
            "brands",
            "pagination"
          ]
        },
        "Competitor": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "format": "uuid"
            },
            "brandId": {
              "type": "string"
            },
            "name": {
              "type": "string"
            },
            "domains": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "aliases": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "createdAt": {
              "type": "string",
              "format": "date-time"
            },
            "updatedAt": {
              "type": "string",
              "format": "date-time"
            }
          },
          "required": [
            "id",
            "brandId",
            "name",
            "domains",
            "aliases",
            "createdAt",
            "updatedAt"
          ]
        },
        "CompetitorsList": {
          "type": "object",
          "properties": {
            "data": {
              "description": "The items on this page. Read this rather than the named key below.",
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Competitor"
              }
            },
            "competitors": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Competitor"
              },
              "deprecated": true,
              "description": "Deprecated: read `data` instead. Kept while the one known consumer migrates, and removed in a future release."
            },
            "pagination": {
              "$ref": "#/components/schemas/Pagination"
            }
          },
          "required": [
            "data",
            "competitors",
            "pagination"
          ]
        },
        "CreateCompetitorRequest": {
          "type": "object",
          "description": "Add a competitor to a brand.",
          "properties": {
            "brandId": {
              "type": "string",
              "minLength": 1
            },
            "name": {
              "type": "string",
              "minLength": 1
            },
            "domains": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "aliases": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "brandId",
            "name"
          ]
        },
        "UpdateCompetitorRequest": {
          "type": "object",
          "description": "Update a competitor. At least one field must be provided. Provided arrays replace the stored values verbatim.",
          "minProperties": 1,
          "properties": {
            "name": {
              "type": "string",
              "minLength": 1
            },
            "domains": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "aliases": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        },
        "PromptSnapshot": {
          "type": "object",
          "properties": {
            "brandId": {
              "type": "string",
              "description": "Brand identifier this prompt belongs to"
            },
            "promptId": {
              "type": "string",
              "format": "uuid",
              "description": "Unique identifier for the prompt"
            },
            "promptValue": {
              "type": "string",
              "description": "The prompt text"
            },
            "startDate": {
              "type": "string",
              "format": "date",
              "description": "Start of the queried date range (YYYY-MM-DD)"
            },
            "endDate": {
              "type": "string",
              "format": "date",
              "description": "End of the queried date range (YYYY-MM-DD)"
            },
            "mentions": {
              "$ref": "#/components/schemas/MentionsSummary"
            },
            "citations": {
              "type": "object",
              "properties": {
                "citationsTotal": {
                  "type": "integer",
                  "description": "Total number of citations across all runs"
                },
                "brandCitationsTotal": {
                  "type": "integer",
                  "description": "Number of citations to brand-owned domains"
                },
                "competitorCitationsTotal": {
                  "type": "integer",
                  "description": "Number of citations to competitor-owned domains"
                },
                "citedUrlsTopK": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CitedUrlEntry"
                  },
                  "description": "Top-K cited URLs ranked by frequency"
                }
              },
              "required": [
                "citationsTotal",
                "brandCitationsTotal",
                "competitorCitationsTotal",
                "citedUrlsTopK"
              ]
            }
          },
          "required": [
            "brandId",
            "promptId",
            "promptValue",
            "startDate",
            "endDate",
            "mentions",
            "citations"
          ]
        },
        "Pagination": {
          "type": "object",
          "properties": {
            "page": {
              "type": "integer"
            },
            "limit": {
              "type": "integer"
            },
            "total": {
              "type": "integer",
              "description": "Total items matching the request."
            },
            "totalPages": {
              "type": "integer"
            }
          },
          "required": [
            "page",
            "limit",
            "total",
            "totalPages"
          ]
        },
        "DateRange": {
          "description": "The window the response was computed over, echoed back as the half-open instants it resolved to.",
          "type": "object",
          "properties": {
            "start": {
              "description": "Inclusive lower bound, in UTC.",
              "type": "string",
              "format": "date-time"
            },
            "end": {
              "description": "Exclusive upper bound, in UTC.",
              "type": "string",
              "format": "date-time"
            }
          },
          "required": [
            "start",
            "end"
          ]
        },
        "ApiKeyIdentity": {
          "type": "object",
          "properties": {
            "keyType": {
              "type": "string",
              "description": "`admin` for an instance key, `organization` for a dashboard-issued key.",
              "enum": [
                "admin",
                "organization"
              ]
            },
            "organizationId": {
              "type": [
                "string",
                "null"
              ],
              "description": "The organization this key acts inside. Null for admin keys."
            },
            "organizationName": {
              "type": [
                "string",
                "null"
              ]
            },
            "scopes": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "read",
                  "write"
                ]
              },
              "description": "Scopes this key holds. A read-write key lists both; an admin key always does."
            },
            "brandIds": {
              "description": "Brands the key is narrowed to, or null when it reaches every brand in its organization. Never an empty array — a restriction to no brands is rejected at creation rather than treated as no restriction.",
              "type": [
                "array",
                "null"
              ],
              "items": {
                "type": "string"
              }
            },
            "createdAt": {
              "type": [
                "string",
                "null"
              ],
              "format": "date-time"
            },
            "expiresAt": {
              "type": [
                "string",
                "null"
              ],
              "description": "When the key stops working, if it has an expiry.",
              "format": "date-time"
            },
            "rateLimit": {
              "type": [
                "object",
                "null"
              ],
              "properties": {
                "limit": {
                  "type": "integer"
                },
                "window": {
                  "type": "string",
                  "enum": [
                    "minute",
                    "hour"
                  ]
                }
              },
              "description": "The key's configured limit — generous by design: it exists to stop a runaway loop, not to meter normal use. Enforcement is a fixed window and approximate under concurrency.",
              "required": [
                "limit",
                "window"
              ]
            },
            "createdBy": {
              "description": "The key's label, as given when it was issued. Null for admin keys.",
              "type": [
                "string",
                "null"
              ]
            },
            "lastUsedAt": {
              "type": [
                "string",
                "null"
              ],
              "format": "date-time"
            }
          },
          "required": [
            "keyType",
            "organizationId",
            "organizationName",
            "scopes",
            "brandIds",
            "createdAt",
            "expiresAt",
            "rateLimit",
            "createdBy",
            "lastUsedAt"
          ]
        },
        "Organization": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string"
            },
            "name": {
              "type": "string"
            },
            "slug": {
              "type": "string"
            },
            "brandCount": {
              "type": "integer"
            },
            "createdAt": {
              "type": "string",
              "format": "date-time"
            }
          },
          "required": [
            "id",
            "name",
            "slug",
            "brandCount",
            "createdAt"
          ]
        },
        "PlanLimits": {
          "description": "The organization's plan limits. Every field is null on deployments without billing.",
          "type": "object",
          "properties": {
            "maxBrands": {
              "type": [
                "integer",
                "null"
              ],
              "description": "Null means no limit."
            },
            "maxPrompts": {
              "type": [
                "integer",
                "null"
              ],
              "description": "Enabled prompts across the whole organization. Null means no limit."
            },
            "modelPicks": {
              "type": [
                "integer",
                "null"
              ],
              "description": "Models trackable per brand."
            },
            "modelMenu": {
              "description": "Models the plan may pick from. Null means any.",
              "type": [
                "array",
                "null"
              ],
              "items": {
                "type": "string"
              }
            },
            "standardRunsPerDay": {
              "type": [
                "integer",
                "null"
              ]
            },
            "premiumPool": {
              "type": "integer",
              "description": "Prompt/model pairings trackable grounded."
            },
            "premiumRunsPerDay": {
              "type": "integer"
            }
          },
          "required": [
            "maxBrands",
            "maxPrompts",
            "modelPicks",
            "modelMenu",
            "standardRunsPerDay",
            "premiumPool",
            "premiumRunsPerDay"
          ]
        },
        "OrganizationBilling": {
          "description": "Read-only view of an organization's subscription, limits, and usage. The API has no way to change any of it: there is no billing write endpoint and no billing write scope. Stripe identifiers, payment methods, and invoices are deliberately not exposed — those live in the Stripe customer portal.",
          "type": "object",
          "properties": {
            "organizationId": {
              "type": "string"
            },
            "billingEnabled": {
              "type": "boolean",
              "description": "False on self-hosted deployments, where nothing is metered."
            },
            "plan": {
              "description": "Null when the organization has no subscription, or when billing is disabled.",
              "anyOf": [
                {
                  "$ref": "#/components/schemas/BillingPlan"
                },
                {
                  "type": "null"
                }
              ]
            },
            "limits": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/PlanLimits"
                },
                {
                  "type": "null"
                }
              ]
            },
            "usage": {
              "type": "object",
              "properties": {
                "brands": {
                  "type": "integer"
                },
                "enabledPrompts": {
                  "type": "integer"
                },
                "premiumPairingsAssigned": {
                  "type": "integer"
                }
              },
              "required": [
                "brands",
                "enabledPrompts",
                "premiumPairingsAssigned"
              ]
            }
          },
          "required": [
            "organizationId",
            "billingEnabled",
            "plan",
            "limits",
            "usage"
          ]
        },
        "BillingPlan": {
          "type": "object",
          "properties": {
            "key": {
              "type": [
                "string",
                "null"
              ],
              "description": "Plan identifier, or `custom` for a negotiated plan."
            },
            "name": {
              "type": "string",
              "description": "Display name."
            },
            "standing": {
              "type": "string",
              "description": "What the subscription means for service, and the only field a caller needs to act on: `active` and `grace` keep tracking running, `paused` stops it, `none` is unsubscribed. The payment provider's own status string is deliberately not passed through.",
              "enum": [
                "active",
                "grace",
                "paused",
                "none"
              ]
            },
            "interval": {
              "type": [
                "string",
                "null"
              ],
              "enum": [
                "monthly",
                "annual",
                null
              ],
              "description": "Null on a negotiated plan, which carries no billing interval."
            },
            "periodEnd": {
              "type": [
                "string",
                "null"
              ],
              "format": "date-time"
            },
            "cancelAtPeriodEnd": {
              "type": "boolean"
            },
            "trackingActive": {
              "type": "boolean",
              "description": "Whether prompts are currently being sampled."
            }
          },
          "required": [
            "key",
            "name",
            "standing",
            "cancelAtPeriodEnd",
            "trackingActive"
          ]
        },
        "VisibilityPoint": {
          "type": "object",
          "properties": {
            "date": {
              "type": "string",
              "format": "date"
            },
            "visibility": {
              "type": [
                "number",
                "null"
              ],
              "minimum": 0,
              "maximum": 1,
              "description": "Null on days with no runs to plot; the series is not gap-filled with zeros. Ratio 0–1; multiply by 100 for a percentage."
            }
          },
          "required": [
            "date",
            "visibility"
          ]
        },
        "ShareOfVoiceEntry": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string",
              "description": "Brand or competitor name."
            },
            "isBrand": {
              "type": "boolean",
              "description": "True for the tracked brand's own row."
            },
            "mentions": {
              "type": "integer"
            },
            "prompts": {
              "type": "integer",
              "description": "Distinct prompts this entity appeared in."
            },
            "share": {
              "type": "number",
              "minimum": 0,
              "maximum": 1,
              "description": "This entity's share of all brand and competitor mentions. Ratio 0–1; multiply by 100 for a percentage."
            }
          },
          "required": [
            "name",
            "isBrand",
            "mentions",
            "prompts",
            "share"
          ]
        },
        "ShareOfVoicePoint": {
          "type": "object",
          "properties": {
            "date": {
              "type": "string",
              "format": "date"
            },
            "share": {
              "type": [
                "number",
                "null"
              ],
              "minimum": 0,
              "maximum": 1,
              "description": "The brand's share on this day. Null on days with no runs. Ratio 0–1; multiply by 100 for a percentage."
            }
          },
          "required": [
            "date",
            "share"
          ]
        },
        "CitationDomain": {
          "type": "object",
          "properties": {
            "domain": {
              "type": "string"
            },
            "category": {
              "type": "string",
              "description": "Which side of the citation landscape the domain sits on.",
              "enum": [
                "brand",
                "competitor",
                "editorial",
                "reviews",
                "ecommerce",
                "social",
                "developer",
                "pr",
                "reference",
                "institutional",
                "other"
              ]
            },
            "count": {
              "type": "integer",
              "description": "Citations to this domain in the window."
            },
            "share": {
              "type": "number",
              "minimum": 0,
              "maximum": 1,
              "description": "Share of all citations in the window. Ratio 0–1; multiply by 100 for a percentage."
            },
            "promptCount": {
              "type": "integer",
              "description": "Distinct prompts whose answers cited this domain."
            },
            "previousCount": {
              "type": "integer",
              "description": "Citations in the equal-length window immediately before this one."
            },
            "changeFactor": {
              "type": [
                "number",
                "null"
              ],
              "minimum": 0,
              "description": "How this window compares with the equal-length window before it, as a multiplier of the previous count: 2 means twice as many citations, 0.5 means half, 1 means unchanged. Not a percentage change — 1.5 is \"1.5x\", which is a 50% increase. Null when the domain had no citations in the previous window, so there is nothing to divide by."
            }
          },
          "required": [
            "domain",
            "category",
            "count",
            "share",
            "promptCount",
            "previousCount",
            "changeFactor"
          ]
        },
        "CitationDomainList": {
          "type": "object",
          "properties": {
            "brandId": {
              "type": "string"
            },
            "range": {
              "$ref": "#/components/schemas/DateRange"
            },
            "totals": {
              "type": "object",
              "properties": {
                "citations": {
                  "type": "integer"
                },
                "uniqueDomains": {
                  "type": "integer"
                },
                "uniqueUrls": {
                  "type": "integer"
                }
              },
              "required": [
                "citations",
                "uniqueDomains",
                "uniqueUrls"
              ]
            },
            "data": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/CitationDomain"
              },
              "description": "Every domain cited over the window, ordered by citation count."
            }
          },
          "required": [
            "brandId",
            "range",
            "totals",
            "data"
          ]
        },
        "CitationUrl": {
          "type": "object",
          "properties": {
            "url": {
              "type": "string",
              "format": "uri"
            },
            "domain": {
              "type": "string"
            },
            "title": {
              "type": [
                "string",
                "null"
              ]
            },
            "category": {
              "type": "string",
              "description": "Which side of the citation landscape the domain sits on.",
              "enum": [
                "brand",
                "competitor",
                "editorial",
                "reviews",
                "ecommerce",
                "social",
                "developer",
                "pr",
                "reference",
                "institutional",
                "other"
              ]
            },
            "pageType": {
              "type": "string",
              "description": "What kind of page was cited.",
              "enum": [
                "homepage",
                "article",
                "listicle",
                "howto",
                "comparison",
                "review",
                "forum",
                "video",
                "doc",
                "product",
                "info",
                "search",
                "shopping",
                "other"
              ]
            },
            "count": {
              "type": "integer"
            },
            "promptCount": {
              "type": "integer"
            },
            "isNew": {
              "type": "boolean",
              "description": "Not cited in the equal-length window immediately before this one."
            }
          },
          "required": [
            "url",
            "domain",
            "title",
            "category",
            "pageType",
            "count",
            "promptCount",
            "isNew"
          ]
        },
        "CitationUrlList": {
          "type": "object",
          "properties": {
            "brandId": {
              "type": "string"
            },
            "range": {
              "$ref": "#/components/schemas/DateRange"
            },
            "totals": {
              "type": "object",
              "properties": {
                "citations": {
                  "type": "integer"
                },
                "uniqueDomains": {
                  "type": "integer"
                },
                "uniqueUrls": {
                  "type": "integer"
                }
              },
              "required": [
                "citations",
                "uniqueDomains",
                "uniqueUrls"
              ]
            },
            "data": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/CitationUrl"
              },
              "description": "Every URL cited over the window, ordered by citation count."
            }
          },
          "required": [
            "brandId",
            "range",
            "totals",
            "data"
          ]
        },
        "FanoutQuery": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "description": "A search the engine ran while answering."
            },
            "runs": {
              "type": "integer"
            },
            "promptCount": {
              "type": "integer"
            }
          },
          "required": [
            "query",
            "runs",
            "promptCount"
          ]
        },
        "BrandQueryFanout": {
          "type": "object",
          "properties": {
            "brandId": {
              "type": "string"
            },
            "range": {
              "$ref": "#/components/schemas/DateRange"
            },
            "totalQueries": {
              "type": "integer"
            },
            "uniqueQueries": {
              "type": "integer"
            },
            "fanoutRuns": {
              "type": "integer",
              "description": "Runs that searched at all. Engines that don't expose their searches contribute runs but no queries."
            },
            "totalRuns": {
              "type": "integer"
            },
            "avgQueriesPerRun": {
              "type": "number"
            },
            "coverageRate": {
              "type": "number",
              "minimum": 0,
              "maximum": 1,
              "description": "Share of fan-out query instances whose answer mentioned the brand. Ratio 0–1; multiply by 100 for a percentage."
            },
            "data": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/FanoutQuery"
              },
              "description": "Every distinct sub-query over the window, ordered by the runs that ran it."
            }
          },
          "required": [
            "brandId",
            "range",
            "totalQueries",
            "uniqueQueries",
            "fanoutRuns",
            "totalRuns",
            "avgQueriesPerRun",
            "coverageRate",
            "data"
          ]
        },
        "PromptPerformance": {
          "type": "object",
          "properties": {
            "promptId": {
              "type": "string",
              "format": "uuid"
            },
            "value": {
              "type": "string"
            },
            "tags": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "totalRuns": {
              "type": "integer"
            },
            "brandMentionRate": {
              "type": "number",
              "minimum": 0,
              "maximum": 1,
              "description": "Share of this prompt's runs mentioning the brand. Ratio 0–1; multiply by 100 for a percentage."
            },
            "competitorMentionRate": {
              "type": "number",
              "minimum": 0,
              "maximum": 1,
              "description": "Share of this prompt's runs mentioning any tracked competitor. Ratio 0–1; multiply by 100 for a percentage."
            },
            "lastRunAt": {
              "type": [
                "string",
                "null"
              ],
              "format": "date-time"
            },
            "firstEvaluatedAt": {
              "type": [
                "string",
                "null"
              ],
              "format": "date-time"
            }
          },
          "required": [
            "promptId",
            "value",
            "tags",
            "totalRuns",
            "brandMentionRate",
            "competitorMentionRate",
            "lastRunAt",
            "firstEvaluatedAt"
          ]
        },
        "PromptPerformanceList": {
          "type": "object",
          "properties": {
            "brandId": {
              "type": "string"
            },
            "range": {
              "$ref": "#/components/schemas/DateRange"
            },
            "data": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/PromptPerformance"
              },
              "description": "Every prompt sampled over the window, with its results."
            }
          },
          "required": [
            "brandId",
            "range",
            "data"
          ]
        },
        "RunSummary": {
          "description": "A single model answer, without its text. Fetch `GET /prompts/{promptId}/runs/{runId}` for the answer itself.",
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "format": "uuid"
            },
            "promptId": {
              "type": "string",
              "format": "uuid"
            },
            "brandId": {
              "type": "string"
            },
            "model": {
              "type": "string"
            },
            "provider": {
              "type": [
                "string",
                "null"
              ],
              "description": "How the answer was obtained. Informational; not a stable enum."
            },
            "webSearchEnabled": {
              "type": "boolean",
              "description": "Whether the engine answered with its own web search on."
            },
            "brandMentioned": {
              "type": "boolean"
            },
            "competitorsMentioned": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "webQueries": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Searches the engine ran, where it exposes them."
            },
            "citationCount": {
              "type": "integer"
            },
            "createdAt": {
              "type": "string",
              "format": "date-time"
            }
          },
          "required": [
            "id",
            "promptId",
            "brandId",
            "model",
            "provider",
            "webSearchEnabled",
            "brandMentioned",
            "competitorsMentioned",
            "webQueries",
            "citationCount",
            "createdAt"
          ]
        },
        "RunCitation": {
          "type": "object",
          "properties": {
            "url": {
              "type": "string",
              "format": "uri"
            },
            "domain": {
              "type": "string"
            },
            "title": {
              "type": [
                "string",
                "null"
              ]
            },
            "citationIndex": {
              "type": "integer",
              "description": "Position within the answer's citation list."
            }
          },
          "required": [
            "url",
            "domain",
            "title",
            "citationIndex"
          ]
        },
        "Run": {
          "description": "One model answer with its normalized text and citations.",
          "allOf": [
            {
              "$ref": "#/components/schemas/RunSummary"
            },
            {
              "type": "object",
              "properties": {
                "answer": {
                  "type": "object",
                  "properties": {
                    "text": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The answer, normalized out of the provider's response. Null when nothing could be extracted."
                    }
                  },
                  "required": [
                    "text"
                  ]
                },
                "citations": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RunCitation"
                  }
                }
              },
              "required": [
                "answer",
                "citations"
              ]
            }
          ]
        },
        "RunList": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/RunSummary"
              }
            },
            "pagination": {
              "$ref": "#/components/schemas/Pagination"
            }
          },
          "required": [
            "data",
            "pagination"
          ]
        },
        "OrganizationList": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Organization"
              }
            },
            "pagination": {
              "$ref": "#/components/schemas/Pagination"
            }
          },
          "required": [
            "data",
            "pagination"
          ]
        },
        "Tag": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string",
              "description": "The tag, normalized to lower case."
            },
            "promptCount": {
              "type": "integer",
              "description": "Prompts in this brand carrying the tag."
            },
            "system": {
              "type": "boolean",
              "description": "True for `branded` and `unbranded`, which Elmo computes from the prompt text. A system tag always appears here; applying it to a prompt as a user tag overrides the computed classification rather than creating a new tag."
            }
          },
          "required": [
            "name",
            "promptCount",
            "system"
          ]
        },
        "TagList": {
          "type": "object",
          "properties": {
            "brandId": {
              "type": "string"
            },
            "data": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Tag"
              },
              "description": "System tags first, then user tags alphabetically — the order the dashboard's filter shows."
            }
          },
          "required": [
            "brandId",
            "data"
          ]
        },
        "OpportunityPrompt": {
          "type": "object",
          "properties": {
            "text": {
              "type": "string",
              "description": "The prompt as the report names it."
            },
            "promptId": {
              "type": [
                "string",
                "null"
              ],
              "description": "The tracked prompt it resolved to, or null when it didn't match one.",
              "format": "uuid"
            }
          },
          "required": [
            "text",
            "promptId"
          ]
        },
        "CitedPage": {
          "type": "object",
          "properties": {
            "url": {
              "type": "string",
              "format": "uri"
            },
            "domain": {
              "type": "string"
            },
            "title": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "required": [
            "url",
            "domain",
            "title"
          ]
        },
        "Opportunity": {
          "type": "object",
          "properties": {
            "category": {
              "type": "string",
              "description": "Which workstream this belongs to: `creation` is net-new content, `existing-content` is a page that could win the mention with a refresh, `outreach` is earning a placement on a third-party site assistants cite, `social` is the community conversations they pull from.",
              "enum": [
                "creation",
                "existing-content",
                "outreach",
                "social"
              ]
            },
            "title": {
              "type": "string",
              "description": "Short and action-oriented — the concrete surface or angle."
            },
            "why": {
              "type": "string",
              "description": "Why it is worth doing, in plain language."
            },
            "relatedPrompts": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/OpportunityPrompt"
              },
              "description": "Tracked prompts this would help. May be empty."
            },
            "yourCitations": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/CitedPage"
              },
              "description": "Pages on the brand's own domains already cited for those prompts."
            },
            "competitorCitations": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/CitedPage"
              },
              "description": "Pages on competitor domains cited for those prompts."
            }
          },
          "required": [
            "category",
            "title",
            "why",
            "relatedPrompts",
            "yourCitations",
            "competitorCitations"
          ]
        },
        "BrandOpportunities": {
          "description": "The brand's latest Opportunities report — the same LLM-generated analysis the dashboard shows, read from the append-only history rather than regenerated. Elmo decides when to produce a new one; there is no way to trigger generation over the API, because it spends provider budget with no per-call metering behind it.",
          "type": "object",
          "properties": {
            "brandId": {
              "type": "string"
            },
            "status": {
              "type": "string",
              "description": "`ready` when a report is present. `insufficient-data` when the brand hasn't accumulated enough tracked answers to say anything useful yet, in which case the lists are empty.",
              "enum": [
                "ready",
                "insufficient-data"
              ]
            },
            "generatedAt": {
              "type": [
                "string",
                "null"
              ],
              "format": "date-time"
            },
            "model": {
              "type": [
                "string",
                "null"
              ],
              "description": "The model that produced the report, when recorded."
            },
            "summary": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "A few bullets on where the brand stands and the through-line of the plan."
            },
            "opportunities": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Opportunity"
              },
              "description": "Prioritized, highest impact first."
            },
            "risks": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Caveats: hard-to-win areas, or tactics to avoid."
            }
          },
          "required": [
            "brandId",
            "status",
            "generatedAt",
            "model",
            "summary",
            "opportunities",
            "risks"
          ]
        },
        "ModelVisibility": {
          "type": "object",
          "properties": {
            "model": {
              "type": "string"
            },
            "label": {
              "type": "string"
            },
            "runs": {
              "type": "integer"
            },
            "brandMentions": {
              "type": "integer"
            },
            "visibility": {
              "type": [
                "number",
                "null"
              ],
              "minimum": 0,
              "maximum": 1,
              "description": "Share of this model's runs that mentioned the brand. Ratio 0–1; multiply by 100 for a percentage."
            },
            "citations": {
              "type": "integer"
            }
          },
          "required": [
            "model",
            "label",
            "runs",
            "brandMentions",
            "visibility",
            "citations"
          ]
        },
        "BrandAnalytics": {
          "description": "Every non-paginated figure for a brand over one window. There is no parameter for selecting a subset: the four computations behind this share one scope resolution and run concurrently, so a subset would save a caller a fraction of one request and cost everyone a parameter to reason about.",
          "type": "object",
          "properties": {
            "brandId": {
              "type": "string"
            },
            "brandName": {
              "type": "string"
            },
            "range": {
              "$ref": "#/components/schemas/DateRange"
            },
            "visibility": {
              "type": "object",
              "description": "How often the brand is mentioned at all.",
              "properties": {
                "current": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "minimum": 0,
                  "maximum": 1,
                  "description": "The last plotted point — the number the dashboard hero shows. Ratio 0–1; multiply by 100 for a percentage."
                },
                "series": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/VisibilityPoint"
                  }
                }
              },
              "required": [
                "current",
                "series"
              ]
            },
            "shareOfVoice": {
              "type": "object",
              "description": "The brand against its tracked competitors.",
              "properties": {
                "brand": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "minimum": 0,
                  "maximum": 1,
                  "description": "The brand's own share at the end of the window. Ratio 0–1; multiply by 100 for a percentage."
                },
                "entries": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShareOfVoiceEntry"
                  },
                  "description": "Leaderboard, highest share first."
                },
                "series": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ShareOfVoicePoint"
                  },
                  "description": "The brand's share over time."
                }
              },
              "required": [
                "brand",
                "entries",
                "series"
              ]
            },
            "models": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/ModelVisibility"
              },
              "description": "Per-model visibility and run counts. Only models that produced a run in the window appear."
            },
            "totals": {
              "type": "object",
              "properties": {
                "runs": {
                  "type": "integer"
                },
                "prompts": {
                  "type": "integer",
                  "description": "Enabled prompts counted in the window."
                },
                "citations": {
                  "type": "integer"
                },
                "uniqueDomains": {
                  "type": "integer"
                },
                "uniqueUrls": {
                  "type": "integer"
                }
              },
              "required": [
                "runs",
                "prompts",
                "citations",
                "uniqueDomains",
                "uniqueUrls"
              ]
            }
          },
          "required": [
            "brandId",
            "brandName",
            "range",
            "visibility",
            "shareOfVoice",
            "models",
            "totals"
          ]
        },
        "ModelList": {
          "type": "object",
          "properties": {
            "data": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Model"
              }
            }
          },
          "required": [
            "data"
          ]
        },
        "Model": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "Identifier used by the `model` filter, e.g. `chatgpt`."
            },
            "label": {
              "type": "string",
              "description": "Human-readable name."
            },
            "premiumCapable": {
              "type": "boolean",
              "description": "Whether this model can be tracked grounded, spending a premium pairing."
            },
            "configured": {
              "type": "boolean",
              "description": "Whether this deployment is actually wired to reach it."
            }
          },
          "required": [
            "id",
            "label",
            "premiumCapable",
            "configured"
          ]
        }
      },
      "responses": {
        "UnauthorizedError": {
          "description": "Authentication required",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Error"
              }
            }
          }
        },
        "NotFoundError": {
          "description": "Resource not found",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Error"
              }
            }
          }
        },
        "ValidationError": {
          "description": "Invalid request data",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Error"
              }
            }
          }
        },
        "InternalServerError": {
          "description": "Internal server error",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Error"
              }
            }
          }
        },
        "ConflictError": {
          "description": "Resource already exists",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Error"
              }
            }
          }
        },
        "ForbiddenError": {
          "description": "The key is valid but not permitted: a missing scope, an admin-only endpoint, or a write in read-only (demo) mode.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Error"
              }
            }
          }
        },
        "RateLimitError": {
          "description": "Per-key rate limit exceeded.",
          "headers": {
            "Retry-After": {
              "description": "Seconds until the limit resets.",
              "schema": {
                "type": "integer"
              }
            },
            "X-RateLimit-Limit": {
              "description": "Requests allowed per window.",
              "schema": {
                "type": "integer"
              }
            },
            "X-RateLimit-Remaining": {
              "description": "Requests left in the current window. Counted with a read-modify-write per request, so treat it as a guide and back off on 429 rather than riding it to zero. Sent only when the remainder is known.",
              "schema": {
                "type": "integer"
              }
            },
            "X-RateLimit-Reset": {
              "description": "Unix seconds at which the window resets.",
              "schema": {
                "type": "integer"
              }
            }
          },
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Error"
              }
            }
          }
        },
        "PaymentRequiredError": {
          "description": "The organization has no active subscription. Cloud deployments only.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Error"
              }
            }
          }
        }
      },
      "parameters": {
        "BrandIdPath": {
          "name": "brandId",
          "in": "path",
          "description": "Brand identifier.",
          "required": true,
          "schema": {
            "type": "string"
          }
        },
        "Page": {
          "name": "page",
          "in": "query",
          "description": "Page number, 1-based.",
          "required": false,
          "schema": {
            "type": "integer",
            "minimum": 1,
            "default": 1
          }
        },
        "Limit": {
          "name": "limit",
          "in": "query",
          "description": "Items per page. Values above the maximum are clamped, not rejected.",
          "required": false,
          "schema": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 20
          }
        },
        "WindowStart": {
          "name": "start",
          "in": "query",
          "description": "Inclusive lower bound of the window, an ISO 8601 timestamp such as `2026-01-01T00:00:00Z`. A bare `YYYY-MM-DD` is rejected: that is `/prompts/{promptId}/snapshot`'s spelling and means a local calendar day there.",
          "required": true,
          "schema": {
            "type": "string",
            "format": "date-time"
          }
        },
        "WindowEnd": {
          "name": "end",
          "in": "query",
          "description": "Exclusive upper bound of the window, an ISO 8601 timestamp. The window is half-open: a run at exactly `end` is outside it.",
          "required": true,
          "schema": {
            "type": "string",
            "format": "date-time"
          }
        },
        "ModelFilter": {
          "name": "model",
          "in": "query",
          "description": "Restrict to one model, e.g. `chatgpt`. See `GET /models`.",
          "required": false,
          "schema": {
            "type": "string"
          }
        },
        "TagsFilter": {
          "name": "tags",
          "in": "query",
          "description": "Comma-separated prompt tags. Only prompts carrying at least one of them are counted.",
          "required": false,
          "schema": {
            "type": "string"
          }
        }
      },
      "requestBodies": {
        "CreateBrandRequest": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateBrandRequest"
              }
            }
          }
        },
        "UpdateBrandRequest": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateBrandRequest"
              }
            }
          }
        },
        "CreateCompetitorRequest": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCompetitorRequest"
              }
            }
          }
        },
        "UpdateCompetitorRequest": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCompetitorRequest"
              }
            }
          }
        },
        "CreatePromptRequest": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePromptRequest"
              }
            }
          }
        },
        "UpdatePromptRequest": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePromptRequest"
              }
            }
          }
        },
        "CreateReportRequest": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateReportRequest"
              }
            }
          }
        },
        "AnalyzeBrandRequest": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnalyzeBrandRequest"
              }
            }
          }
        }
      }
    },
    "paths": {
      "/brands": {
        "get": {
          "summary": "List brands",
          "operationId": "listBrands",
          "tags": [
            "Brands"
          ],
          "parameters": [
            {
              "$ref": "#/components/parameters/Page"
            },
            {
              "$ref": "#/components/parameters/Limit"
            }
          ],
          "responses": {
            "200": {
              "description": "Paginated list of brands",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/BrandsList"
                  }
                }
              }
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "read"
          ],
          "description": ""
        },
        "post": {
          "summary": "Create a brand",
          "operationId": "createBrand",
          "tags": [
            "Brands"
          ],
          "requestBody": {
            "$ref": "#/components/requestBodies/CreateBrandRequest"
          },
          "responses": {
            "201": {
              "description": "Brand created",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Brand"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "402": {
              "$ref": "#/components/responses/PaymentRequiredError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "409": {
              "$ref": "#/components/responses/ConflictError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "write"
          ],
          "description": ""
        }
      },
      "/brands/{brandId}": {
        "get": {
          "summary": "Get a brand",
          "operationId": "getBrand",
          "tags": [
            "Brands"
          ],
          "parameters": [
            {
              "name": "brandId",
              "in": "path",
              "required": true,
              "description": "Brand identifier",
              "schema": {
                "type": "string",
                "minLength": 1
              }
            }
          ],
          "responses": {
            "200": {
              "description": "Brand",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Brand"
                  }
                }
              }
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "read"
          ],
          "description": ""
        },
        "patch": {
          "summary": "Update a brand",
          "operationId": "updateBrand",
          "tags": [
            "Brands"
          ],
          "parameters": [
            {
              "name": "brandId",
              "in": "path",
              "required": true,
              "description": "Brand identifier",
              "schema": {
                "type": "string",
                "minLength": 1
              }
            }
          ],
          "requestBody": {
            "$ref": "#/components/requestBodies/UpdateBrandRequest"
          },
          "responses": {
            "200": {
              "description": "Brand updated",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Brand"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "402": {
              "$ref": "#/components/responses/PaymentRequiredError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "409": {
              "$ref": "#/components/responses/ConflictError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "write"
          ],
          "description": ""
        }
      },
      "/brands/{brandId}/analytics": {
        "get": {
          "summary": "Get a brand's analytics",
          "description": "Visibility, share of voice, the per-model breakdown and the citation totals for one window, in one request.\n\nThe long lists — cited domains and URLs, sub-queries, per-prompt results — are endpoints of their own. Everything else is always included.",
          "operationId": "getBrandAnalytics",
          "tags": [
            "Analytics"
          ],
          "x-elmo-scopes": [
            "read"
          ],
          "parameters": [
            {
              "$ref": "#/components/parameters/BrandIdPath"
            },
            {
              "$ref": "#/components/parameters/WindowStart"
            },
            {
              "$ref": "#/components/parameters/WindowEnd"
            },
            {
              "$ref": "#/components/parameters/ModelFilter"
            },
            {
              "$ref": "#/components/parameters/TagsFilter"
            }
          ],
          "responses": {
            "200": {
              "description": "Get a brand's analytics",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/BrandAnalytics"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/brands/{brandId}/citations/domains": {
        "get": {
          "summary": "List cited domains",
          "description": "Domains the engines cited when answering this brand's prompts, categorized and compared against the equal-length window immediately before this one.",
          "operationId": "listBrandCitationDomains",
          "tags": [
            "Analytics"
          ],
          "x-elmo-scopes": [
            "read"
          ],
          "parameters": [
            {
              "$ref": "#/components/parameters/BrandIdPath"
            },
            {
              "$ref": "#/components/parameters/WindowStart"
            },
            {
              "$ref": "#/components/parameters/WindowEnd"
            },
            {
              "$ref": "#/components/parameters/ModelFilter"
            },
            {
              "$ref": "#/components/parameters/TagsFilter"
            }
          ],
          "responses": {
            "200": {
              "description": "List cited domains",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/CitationDomainList"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/brands/{brandId}/citations/urls": {
        "get": {
          "summary": "List cited URLs",
          "description": "Individual pages the engines cited, with their category and page type.",
          "operationId": "listBrandCitationUrls",
          "tags": [
            "Analytics"
          ],
          "x-elmo-scopes": [
            "read"
          ],
          "parameters": [
            {
              "$ref": "#/components/parameters/BrandIdPath"
            },
            {
              "$ref": "#/components/parameters/WindowStart"
            },
            {
              "$ref": "#/components/parameters/WindowEnd"
            },
            {
              "$ref": "#/components/parameters/ModelFilter"
            },
            {
              "$ref": "#/components/parameters/TagsFilter"
            }
          ],
          "responses": {
            "200": {
              "description": "List cited URLs",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/CitationUrlList"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/brands/{brandId}/opportunities": {
        "get": {
          "summary": "Get the latest opportunities report",
          "description": "**Experimental — the response shape may still change.**\n\nThe brand's Opportunities report: a prioritized set of ways to get cited more often, with the tracked prompts and cited pages behind each one. The same analysis the dashboard shows, from the same code.\n\nGeneration is inline and synchronous. A stored report is served while it is fresh and regenerated when it is not, so there is nothing to poll for and no way to be handed a stale one — but a request that triggers a generation waits for it. The freshness window bounds the cost: however many callers ask, one generation per brand per window. There is deliberately no `POST`, which would spend provider budget with nothing metering it per call.",
          "operationId": "getBrandOpportunities",
          "tags": [
            "Opportunities"
          ],
          "x-elmo-scopes": [
            "read"
          ],
          "parameters": [
            {
              "$ref": "#/components/parameters/BrandIdPath"
            }
          ],
          "responses": {
            "200": {
              "description": "Get the latest opportunities report",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/BrandOpportunities"
                  }
                }
              }
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-stability": "experimental"
        }
      },
      "/brands/{brandId}/prompt-performance": {
        "get": {
          "summary": "List prompt performance",
          "description": "Per-prompt mention rates over the window. The analytics counterpart to `GET /prompts?brandId=`, which returns prompt configuration rather than results.\n\nA prompt the brand stopped tracking is not sampled, so it has no results over the window and does not appear here.",
          "operationId": "listBrandPromptPerformance",
          "tags": [
            "Analytics"
          ],
          "x-elmo-scopes": [
            "read"
          ],
          "parameters": [
            {
              "$ref": "#/components/parameters/BrandIdPath"
            },
            {
              "$ref": "#/components/parameters/WindowStart"
            },
            {
              "$ref": "#/components/parameters/WindowEnd"
            },
            {
              "$ref": "#/components/parameters/ModelFilter"
            },
            {
              "$ref": "#/components/parameters/TagsFilter"
            }
          ],
          "responses": {
            "200": {
              "description": "List prompt performance",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/PromptPerformanceList"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/brands/{brandId}/query-fanout": {
        "get": {
          "summary": "Get query fan-out",
          "description": "The searches engines ran while answering this brand's prompts. Engines that don't expose their searches still contribute runs, so `coverageRate` is the honest denominator.",
          "operationId": "getBrandQueryFanout",
          "tags": [
            "Analytics"
          ],
          "x-elmo-scopes": [
            "read"
          ],
          "parameters": [
            {
              "$ref": "#/components/parameters/BrandIdPath"
            },
            {
              "$ref": "#/components/parameters/WindowStart"
            },
            {
              "$ref": "#/components/parameters/WindowEnd"
            },
            {
              "$ref": "#/components/parameters/ModelFilter"
            },
            {
              "$ref": "#/components/parameters/TagsFilter"
            }
          ],
          "responses": {
            "200": {
              "description": "Get query fan-out",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/BrandQueryFanout"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/brands/{brandId}/tags": {
        "get": {
          "summary": "List a brand's tags",
          "description": "Every tag in use on the brand's prompts, with how many carry each — enough to build the same filter the dashboard shows without paging the whole prompt list to derive it.\n\nTags are not a resource of their own: a tag exists exactly as long as some prompt carries it. `branded` and `unbranded` are computed by Elmo and always listed.",
          "operationId": "listBrandTags",
          "tags": [
            "Tags"
          ],
          "x-elmo-scopes": [
            "read"
          ],
          "parameters": [
            {
              "$ref": "#/components/parameters/BrandIdPath"
            }
          ],
          "responses": {
            "200": {
              "description": "List a brand's tags",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/TagList"
                  }
                }
              }
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/competitors": {
        "get": {
          "summary": "List competitors",
          "operationId": "listCompetitors",
          "tags": [
            "Competitors"
          ],
          "parameters": [
            {
              "name": "brandId",
              "in": "query",
              "required": false,
              "schema": {
                "type": "string"
              }
            },
            {
              "$ref": "#/components/parameters/Page"
            },
            {
              "$ref": "#/components/parameters/Limit"
            }
          ],
          "responses": {
            "200": {
              "description": "Paginated list of competitors",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/CompetitorsList"
                  }
                }
              }
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "read"
          ],
          "description": ""
        },
        "post": {
          "summary": "Add a competitor",
          "operationId": "createCompetitor",
          "tags": [
            "Competitors"
          ],
          "requestBody": {
            "$ref": "#/components/requestBodies/CreateCompetitorRequest"
          },
          "responses": {
            "201": {
              "description": "Competitor created",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Competitor"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "402": {
              "$ref": "#/components/responses/PaymentRequiredError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "409": {
              "$ref": "#/components/responses/ConflictError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "write"
          ],
          "description": ""
        }
      },
      "/competitors/{competitorId}": {
        "get": {
          "summary": "Get a competitor",
          "operationId": "getCompetitor",
          "tags": [
            "Competitors"
          ],
          "parameters": [
            {
              "name": "competitorId",
              "in": "path",
              "required": true,
              "description": "Competitor identifier (UUID)",
              "schema": {
                "type": "string",
                "format": "uuid"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "Competitor",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Competitor"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "read"
          ],
          "description": ""
        },
        "patch": {
          "summary": "Update a competitor",
          "operationId": "updateCompetitor",
          "tags": [
            "Competitors"
          ],
          "parameters": [
            {
              "name": "competitorId",
              "in": "path",
              "required": true,
              "description": "Competitor identifier (UUID)",
              "schema": {
                "type": "string",
                "format": "uuid"
              }
            }
          ],
          "requestBody": {
            "$ref": "#/components/requestBodies/UpdateCompetitorRequest"
          },
          "responses": {
            "200": {
              "description": "Competitor updated",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Competitor"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "402": {
              "$ref": "#/components/responses/PaymentRequiredError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "409": {
              "$ref": "#/components/responses/ConflictError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "write"
          ],
          "description": ""
        },
        "delete": {
          "summary": "Delete a competitor",
          "operationId": "deleteCompetitor",
          "tags": [
            "Competitors"
          ],
          "parameters": [
            {
              "name": "competitorId",
              "in": "path",
              "required": true,
              "description": "Competitor identifier (UUID)",
              "schema": {
                "type": "string",
                "format": "uuid"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "Competitor deleted (returns the deleted competitor)",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Competitor"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "402": {
              "$ref": "#/components/responses/PaymentRequiredError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "409": {
              "$ref": "#/components/responses/ConflictError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "write"
          ],
          "description": ""
        }
      },
      "/me": {
        "get": {
          "summary": "Describe the calling key",
          "description": "What this key is, which organization and brands it reaches, and which scopes it holds. Requires no scope, so it is always safe to call first when wiring up an integration.",
          "operationId": "getMe",
          "tags": [
            "Identity"
          ],
          "x-elmo-scopes": [],
          "responses": {
            "200": {
              "description": "Describe the calling key",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/ApiKeyIdentity"
                  }
                }
              }
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/models": {
        "get": {
          "summary": "List trackable models",
          "description": "The answer engines this deployment can track, so a client can build a model filter without hardcoding ids that differ between deployments.\n\nRequires no scope.",
          "operationId": "listModels",
          "tags": [
            "Models"
          ],
          "x-elmo-scopes": [],
          "responses": {
            "200": {
              "description": "List trackable models",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/ModelList"
                  }
                }
              }
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/organizations": {
        "get": {
          "summary": "List organizations",
          "description": "One entry for an organization key — the one it acts inside. Every organization for an instance admin key. No scope required: a key can only ever see the organization it is already bound to.",
          "operationId": "listOrganizations",
          "tags": [
            "Organizations"
          ],
          "x-elmo-scopes": [],
          "parameters": [
            {
              "$ref": "#/components/parameters/Page"
            },
            {
              "$ref": "#/components/parameters/Limit"
            }
          ],
          "responses": {
            "200": {
              "description": "List organizations",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/OrganizationList"
                  }
                }
              }
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/organizations/{organizationId}": {
        "get": {
          "summary": "Get an organization",
          "description": "No scope required. An organization outside the key's reach answers `404`, identically to one that does not exist.",
          "operationId": "getOrganization",
          "tags": [
            "Organizations"
          ],
          "x-elmo-scopes": [],
          "parameters": [
            {
              "name": "organizationId",
              "in": "path",
              "required": true,
              "schema": {
                "type": "string"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "Get an organization",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Organization"
                  }
                }
              }
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/organizations/{organizationId}/billing": {
        "get": {
          "summary": "Get billing state",
          "description": "The organization's plan, its limits, and how much of each limit is already spent — enough for an integration to know a write will be rejected before attempting it.\n\nThis endpoint is read-only by construction. No API key of any kind can change a subscription, an add-on quantity, or a payment method; there is no billing write endpoint and no billing write scope. Stripe identifiers, invoices, and payment methods are never returned.\n\nDeployments without billing answer `200` with `billingEnabled: false`, a null plan, and null limits, so callers need no special case.",
          "operationId": "getOrganizationBilling",
          "tags": [
            "Organizations"
          ],
          "x-elmo-scopes": [
            "read"
          ],
          "parameters": [
            {
              "name": "organizationId",
              "in": "path",
              "required": true,
              "schema": {
                "type": "string"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "Get billing state",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/OrganizationBilling"
                  }
                }
              }
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/prompts": {
        "get": {
          "summary": "List all prompts",
          "description": "Retrieve a paginated list of all prompts across all brands",
          "operationId": "listPrompts",
          "tags": [
            "Prompts"
          ],
          "parameters": [
            {
              "name": "brandId",
              "in": "query",
              "description": "Filter prompts by brand ID",
              "required": false,
              "schema": {
                "type": "string"
              }
            },
            {
              "$ref": "#/components/parameters/Page"
            },
            {
              "name": "limit",
              "in": "query",
              "description": "Items per page. Values above the maximum are clamped, not rejected. This list had no ceiling before, so the maximum is set to bound a runaway query rather than to change what an existing caller gets back.",
              "required": false,
              "schema": {
                "type": "integer",
                "minimum": 1,
                "maximum": 1000,
                "default": 20
              }
            },
            {
              "name": "enabled",
              "in": "query",
              "description": "Only prompts with this tracking state.",
              "required": false,
              "schema": {
                "type": "boolean"
              }
            },
            {
              "name": "tags",
              "in": "query",
              "description": "Comma-separated tags. A prompt matches if it carries any of them.",
              "required": false,
              "schema": {
                "type": "string"
              }
            },
            {
              "name": "q",
              "in": "query",
              "description": "Case-insensitive substring match on prompt text.",
              "required": false,
              "schema": {
                "type": "string"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "List of prompts",
              "content": {
                "application/json": {
                  "schema": {
                    "type": "object",
                    "properties": {
                      "data": {
                        "description": "The items on this page. Read this rather than the named key below.",
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/Prompt"
                        }
                      },
                      "prompts": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/Prompt"
                        },
                        "deprecated": true,
                        "description": "Deprecated: read `data` instead. Kept while the one known consumer migrates, and removed in a future release."
                      },
                      "pagination": {
                        "type": "object",
                        "properties": {
                          "page": {
                            "type": "integer"
                          },
                          "limit": {
                            "type": "integer"
                          },
                          "total": {
                            "type": "integer"
                          },
                          "totalPages": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "page",
                          "limit",
                          "total",
                          "totalPages"
                        ]
                      }
                    },
                    "required": [
                      "data",
                      "prompts",
                      "pagination"
                    ]
                  }
                }
              }
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "read"
          ]
        },
        "post": {
          "summary": "Create a new prompt",
          "description": "Create a new prompt for a brand. This will automatically schedule the prompt for execution.",
          "operationId": "createPrompt",
          "tags": [
            "Prompts"
          ],
          "requestBody": {
            "$ref": "#/components/requestBodies/CreatePromptRequest"
          },
          "responses": {
            "201": {
              "description": "Prompt created successfully",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Prompt"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "402": {
              "$ref": "#/components/responses/PaymentRequiredError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "409": {
              "$ref": "#/components/responses/ConflictError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "write"
          ]
        }
      },
      "/prompts/{promptId}": {
        "get": {
          "summary": "Get a prompt",
          "description": "Retrieve a specific prompt by ID",
          "operationId": "getPrompt",
          "tags": [
            "Prompts"
          ],
          "parameters": [
            {
              "name": "promptId",
              "in": "path",
              "required": true,
              "description": "The ID of the prompt",
              "schema": {
                "type": "string",
                "format": "uuid"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "Prompt details",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Prompt"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "read"
          ]
        },
        "patch": {
          "summary": "Update a prompt",
          "description": "Update a prompt's properties. Only provided fields will be updated. Toggling `enabled` schedules or unschedules the recurring run job.",
          "operationId": "updatePrompt",
          "tags": [
            "Prompts"
          ],
          "parameters": [
            {
              "name": "promptId",
              "in": "path",
              "required": true,
              "description": "The ID of the prompt to update",
              "schema": {
                "type": "string",
                "format": "uuid"
              }
            }
          ],
          "requestBody": {
            "$ref": "#/components/requestBodies/UpdatePromptRequest"
          },
          "responses": {
            "200": {
              "description": "Prompt updated successfully",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Prompt"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "402": {
              "$ref": "#/components/responses/PaymentRequiredError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "409": {
              "$ref": "#/components/responses/ConflictError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "write"
          ]
        },
        "delete": {
          "summary": "Delete a prompt",
          "description": "Permanently delete a prompt and cancel all related scheduled jobs. This will also cascade delete all associated prompt runs.\n\nRequires an instance admin key; organization keys receive `403`. The dashboard has no delete either — stop tracking a prompt with `PATCH /prompts/{promptId}` and `enabled: false`, which keeps its history and frees the plan slot.",
          "operationId": "deletePrompt",
          "tags": [
            "Prompts"
          ],
          "parameters": [
            {
              "name": "promptId",
              "in": "path",
              "required": true,
              "description": "The ID of the prompt to delete",
              "schema": {
                "type": "string",
                "format": "uuid"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "Prompt deleted (returns the deleted prompt)",
              "content": {
                "application/json": {
                  "schema": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/Prompt"
                      },
                      {
                        "type": "object",
                        "properties": {
                          "deletedRunsCount": {
                            "type": "integer",
                            "description": "Number of related prompt_runs rows deleted by the cascade."
                          }
                        },
                        "required": [
                          "deletedRunsCount"
                        ]
                      }
                    ]
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "402": {
              "$ref": "#/components/responses/PaymentRequiredError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "409": {
              "$ref": "#/components/responses/ConflictError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [],
          "x-elmo-admin-only": true
        }
      },
      "/prompts/{promptId}/runs": {
        "get": {
          "summary": "List runs for a prompt",
          "description": "Individual model answers behind the aggregates, newest first, without their text — the list stays small enough to page through. Fetch `GET /prompts/{promptId}/runs/{runId}` for the answer itself.",
          "operationId": "listPromptRuns",
          "tags": [
            "Runs"
          ],
          "x-elmo-scopes": [
            "read"
          ],
          "parameters": [
            {
              "name": "promptId",
              "in": "path",
              "required": true,
              "schema": {
                "type": "string",
                "format": "uuid"
              }
            },
            {
              "$ref": "#/components/parameters/WindowStart"
            },
            {
              "$ref": "#/components/parameters/WindowEnd"
            },
            {
              "$ref": "#/components/parameters/ModelFilter"
            },
            {
              "$ref": "#/components/parameters/Page"
            },
            {
              "$ref": "#/components/parameters/Limit"
            }
          ],
          "responses": {
            "200": {
              "description": "List runs for a prompt",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/RunList"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/prompts/{promptId}/runs/{runId}": {
        "get": {
          "summary": "Get a run",
          "description": "One model answer, with its text normalized out of the provider’s response and its citations in the order the engine listed them. A run belonging to some other prompt answers `404`. The provider’s raw payload is deliberately not exposed — its shape belongs to the provider, not to this API.",
          "operationId": "getRun",
          "tags": [
            "Runs"
          ],
          "x-elmo-scopes": [
            "read"
          ],
          "parameters": [
            {
              "name": "promptId",
              "in": "path",
              "required": true,
              "schema": {
                "type": "string",
                "format": "uuid"
              }
            },
            {
              "name": "runId",
              "in": "path",
              "required": true,
              "schema": {
                "type": "string",
                "format": "uuid"
              }
            }
          ],
          "responses": {
            "200": {
              "description": "Get a run",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/Run"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          }
        }
      },
      "/prompts/{promptId}/snapshot": {
        "get": {
          "summary": "Get prompt snapshot",
          "description": "Get an aggregated snapshot of mention and citation analytics for a specific prompt over a date range. Use this to identify competitive gaps, track brand visibility trends, and discover top cited URLs.",
          "operationId": "getPromptSnapshot",
          "tags": [
            "Snapshots"
          ],
          "parameters": [
            {
              "name": "promptId",
              "in": "path",
              "required": true,
              "description": "The ID of the prompt",
              "schema": {
                "type": "string",
                "format": "uuid"
              }
            },
            {
              "name": "startDate",
              "in": "query",
              "required": true,
              "description": "Start of date range (YYYY-MM-DD)",
              "schema": {
                "type": "string",
                "format": "date"
              },
              "example": "2026-01-08"
            },
            {
              "name": "endDate",
              "in": "query",
              "required": true,
              "description": "End of date range (YYYY-MM-DD)",
              "schema": {
                "type": "string",
                "format": "date"
              },
              "example": "2026-01-15"
            },
            {
              "name": "kMentions",
              "in": "query",
              "required": false,
              "description": "Number of top competitor entities to return in mentionsTopK",
              "schema": {
                "type": "integer",
                "minimum": 1,
                "maximum": 50,
                "default": 5
              }
            },
            {
              "name": "kCitations",
              "in": "query",
              "required": false,
              "description": "Number of top cited URLs to return in citedUrlsTopK",
              "schema": {
                "type": "integer",
                "minimum": 1,
                "maximum": 50,
                "default": 10
              }
            }
          ],
          "responses": {
            "200": {
              "description": "Prompt snapshot with mention and citation analytics",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/PromptSnapshot"
                  },
                  "example": {
                    "brandId": "brand_nike",
                    "promptId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                    "promptValue": "best running shoes for marathon training",
                    "startDate": "2026-01-08",
                    "endDate": "2026-01-15",
                    "mentions": {
                      "mentionsTotal": 210,
                      "brandMentionsTotal": 42,
                      "competitorMentionsTotal": 130,
                      "mentionsTopK": [
                        {
                          "entity": "Adidas",
                          "count": 58
                        },
                        {
                          "entity": "New Balance",
                          "count": 45
                        },
                        {
                          "entity": "ASICS",
                          "count": 27
                        }
                      ]
                    },
                    "citations": {
                      "citationsTotal": 1240,
                      "brandCitationsTotal": 85,
                      "competitorCitationsTotal": 620,
                      "citedUrlsTopK": [
                        {
                          "url": "https://www.runnersworld.com/gear/best-running-shoes/",
                          "title": "Best Running Shoes 2026",
                          "count": 72
                        },
                        {
                          "url": "https://www.nike.com/running/marathon",
                          "title": "Nike Marathon Collection",
                          "count": 34
                        }
                      ]
                    }
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [
            "read"
          ]
        }
      },
      "/reports": {
        "post": {
          "summary": "Create a report",
          "description": "Create a new AI Share of Voice report and queue it for generation. The report will evaluate the brand across multiple AI engines (ChatGPT, Claude, Google AI) using generated and optional custom prompts.\n\nRequires an instance admin key; organization keys receive `403`.",
          "operationId": "createReport",
          "tags": [
            "Reports"
          ],
          "requestBody": {
            "$ref": "#/components/requestBodies/CreateReportRequest"
          },
          "responses": {
            "201": {
              "description": "Report created and queued for generation",
              "content": {
                "application/json": {
                  "schema": {
                    "type": "object",
                    "properties": {
                      "reportId": {
                        "type": "string",
                        "format": "uuid",
                        "description": "Unique identifier for the created report"
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "pending"
                        ],
                        "description": "Initial report status"
                      },
                      "brandName": {
                        "type": "string"
                      },
                      "brandAliases": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "brandWebsite": {
                        "type": "string"
                      },
                      "createdAt": {
                        "type": "string",
                        "format": "date-time"
                      }
                    },
                    "required": [
                      "reportId",
                      "status",
                      "brandName",
                      "brandAliases",
                      "brandWebsite",
                      "createdAt"
                    ]
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "402": {
              "$ref": "#/components/responses/PaymentRequiredError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "409": {
              "$ref": "#/components/responses/ConflictError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [],
          "x-elmo-admin-only": true
        },
        "get": {
          "summary": "List reports",
          "description": "Retrieve a paginated list of all reports, ordered by creation date (newest first).\n\nRequires an instance admin key; organization keys receive `403`.",
          "operationId": "listReports",
          "tags": [
            "Reports"
          ],
          "parameters": [
            {
              "$ref": "#/components/parameters/Page"
            },
            {
              "$ref": "#/components/parameters/Limit"
            }
          ],
          "responses": {
            "200": {
              "description": "Paginated list of reports",
              "content": {
                "application/json": {
                  "schema": {
                    "type": "object",
                    "properties": {
                      "data": {
                        "description": "The items on this page. Read this rather than the named key below.",
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/ReportSummary"
                        }
                      },
                      "reports": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/ReportSummary"
                        },
                        "deprecated": true,
                        "description": "Deprecated: read `data` instead. Kept while the one known consumer migrates, and removed in a future release."
                      },
                      "pagination": {
                        "type": "object",
                        "properties": {
                          "page": {
                            "type": "integer"
                          },
                          "limit": {
                            "type": "integer"
                          },
                          "total": {
                            "type": "integer"
                          },
                          "totalPages": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "page",
                          "limit",
                          "total",
                          "totalPages"
                        ]
                      }
                    },
                    "required": [
                      "data",
                      "reports",
                      "pagination"
                    ]
                  }
                }
              }
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [],
          "x-elmo-admin-only": true
        }
      },
      "/reports/{reportId}": {
        "get": {
          "summary": "Get report status and data",
          "description": "Poll a report's status. When completed, returns per-prompt snapshot data with raw mention counts. Consumers are responsible for computing SoV and other derived metrics from the raw data.\n\nRequires an instance admin key; organization keys receive `403`.",
          "operationId": "getReport",
          "tags": [
            "Reports"
          ],
          "parameters": [
            {
              "name": "reportId",
              "in": "path",
              "required": true,
              "description": "The ID of the report",
              "schema": {
                "type": "string",
                "format": "uuid"
              }
            },
            {
              "name": "kMentions",
              "in": "query",
              "required": false,
              "description": "Number of top competitor entities to return in each prompt's mentionsTopK",
              "schema": {
                "type": "integer",
                "minimum": 1,
                "maximum": 50,
                "default": 5
              }
            }
          ],
          "responses": {
            "200": {
              "description": "Report status and data. When status is 'completed', includes per-prompt snapshot data with raw mention counts.",
              "content": {
                "application/json": {
                  "schema": {
                    "type": "object",
                    "properties": {
                      "reportId": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "pending",
                          "processing",
                          "completed",
                          "failed"
                        ]
                      },
                      "progress": {
                        "type": "integer",
                        "description": "Generation progress percentage (0-100). Available while processing."
                      },
                      "brandName": {
                        "type": "string"
                      },
                      "brandAliases": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "brandWebsite": {
                        "type": "string"
                      },
                      "createdAt": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "completedAt": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "date-time"
                      },
                      "prompts": {
                        "type": "array",
                        "items": {
                          "$ref": "#/components/schemas/ReportPromptSnapshot"
                        },
                        "description": "Per-prompt snapshot data. Only present when status is 'completed'."
                      },
                      "unstable": {
                        "type": "object",
                        "description": "Derived stats (SoV, visibility, etc.). Format may change between versions. Only present when status is 'completed'.",
                        "properties": {
                          "sov": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "minimum": 0,
                            "maximum": 1,
                            "description": "Overall share of voice (0-1): brand_mentions / (brand_mentions + competitor_mentions). Null when no mentions at all."
                          },
                          "visibility": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 1,
                            "description": "Brand visibility (0-1): brand_mentions / total_prompt_runs (how often the brand appears at all across all prompt runs)"
                          },
                          "totalPrompts": {
                            "type": "integer",
                            "description": "Total number of prompts evaluated"
                          },
                          "totalPromptRuns": {
                            "type": "integer",
                            "description": "Total number of prompt runs across all prompts (each prompt is executed multiple times across different AI engines)"
                          },
                          "promptsWithBrandMentions": {
                            "type": "integer",
                            "description": "Number of prompts where the brand was mentioned in at least one prompt run"
                          },
                          "promptRunsWithBrandMentions": {
                            "type": "integer",
                            "description": "Number of individual prompt runs where the brand was mentioned"
                          },
                          "competitors": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "name": {
                                  "type": "string",
                                  "description": "Competitor name"
                                },
                                "sov": {
                                  "type": "number",
                                  "minimum": 0,
                                  "maximum": 1,
                                  "description": "Share of voice (0-1): this competitor's mentions / total mentions (brand + all competitors)"
                                },
                                "visibility": {
                                  "type": "number",
                                  "minimum": 0,
                                  "maximum": 1,
                                  "description": "Visibility (0-1): prompt runs mentioning this competitor / total prompt runs"
                                },
                                "promptsWithMentions": {
                                  "type": "integer",
                                  "description": "Number of prompts where this competitor was mentioned in at least one run"
                                },
                                "promptRunsWithMentions": {
                                  "type": "integer",
                                  "description": "Number of individual prompt runs where this competitor was mentioned"
                                }
                              },
                              "required": [
                                "name",
                                "sov",
                                "visibility",
                                "promptsWithMentions",
                                "promptRunsWithMentions"
                              ]
                            },
                            "description": "Per-competitor SoV breakdown, sorted by SoV descending"
                          }
                        }
                      }
                    },
                    "required": [
                      "reportId",
                      "status",
                      "brandName",
                      "brandAliases",
                      "brandWebsite",
                      "createdAt",
                      "completedAt"
                    ]
                  },
                  "example": {
                    "reportId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                    "status": "completed",
                    "brandName": "Acme Corp",
                    "brandAliases": [
                      "Acme"
                    ],
                    "brandWebsite": "acme.com",
                    "createdAt": "2026-04-08T10:00:00Z",
                    "completedAt": "2026-04-08T10:05:00Z",
                    "prompts": [
                      {
                        "promptValue": "best project management tools",
                        "totalRuns": 4,
                        "mentions": {
                          "mentionsTotal": 7,
                          "brandMentionsTotal": 3,
                          "competitorMentionsTotal": 4,
                          "mentionsTopK": [
                            {
                              "entity": "Competitor A",
                              "count": 2
                            },
                            {
                              "entity": "Competitor B",
                              "count": 2
                            }
                          ]
                        }
                      }
                    ],
                    "unstable": {
                      "sov": 0.42,
                      "visibility": 0.6,
                      "totalPrompts": 70,
                      "totalPromptRuns": 280,
                      "promptsWithBrandMentions": 28,
                      "promptRunsWithBrandMentions": 168,
                      "competitors": [
                        {
                          "name": "Competitor A",
                          "sov": 0.35,
                          "visibility": 0.35,
                          "promptsWithMentions": 45,
                          "promptRunsWithMentions": 98
                        },
                        {
                          "name": "Competitor B",
                          "sov": 0.15,
                          "visibility": 0.15,
                          "promptsWithMentions": 20,
                          "promptRunsWithMentions": 42
                        }
                      ]
                    }
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "404": {
              "$ref": "#/components/responses/NotFoundError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [],
          "x-elmo-admin-only": true
        }
      },
      "/tools/analyze": {
        "post": {
          "summary": "Analyze a website",
          "description": "Run brand analysis without persisting anything. Returns suggested additional domains, aliases, competitors, and prompts.\n\nRequires an instance admin key; organization keys receive `403`.",
          "operationId": "analyzeBrand",
          "tags": [
            "Tools"
          ],
          "requestBody": {
            "$ref": "#/components/requestBodies/AnalyzeBrandRequest"
          },
          "responses": {
            "200": {
              "description": "Brand analysis suggestion",
              "content": {
                "application/json": {
                  "schema": {
                    "$ref": "#/components/schemas/OnboardingSuggestion"
                  }
                }
              }
            },
            "400": {
              "$ref": "#/components/responses/ValidationError"
            },
            "401": {
              "$ref": "#/components/responses/UnauthorizedError"
            },
            "402": {
              "$ref": "#/components/responses/PaymentRequiredError"
            },
            "403": {
              "$ref": "#/components/responses/ForbiddenError"
            },
            "409": {
              "$ref": "#/components/responses/ConflictError"
            },
            "429": {
              "$ref": "#/components/responses/RateLimitError"
            },
            "500": {
              "$ref": "#/components/responses/InternalServerError"
            }
          },
          "x-elmo-scopes": [],
          "x-elmo-admin-only": true
        }
      }
    },
    "tags": [
      {
        "name": "Brands",
        "description": "Manage brand records"
      },
      {
        "name": "Tools",
        "description": "One-shot helpers (e.g. brand analysis) that don't persist anything"
      },
      {
        "name": "Prompts",
        "description": "Manage brand prompts"
      },
      {
        "name": "Competitors",
        "description": "Manage brand competitors"
      },
      {
        "name": "Snapshots",
        "description": "Aggregated analytics snapshots for prompts"
      },
      {
        "name": "Reports",
        "description": "Generate and retrieve AI Share of Voice reports"
      },
      {
        "name": "Identity",
        "description": "What the calling key is and what it may reach"
      },
      {
        "name": "Models",
        "description": "The answer engines this deployment can track"
      },
      {
        "name": "Organizations",
        "description": "Organizations, their plan limits, and their usage"
      },
      {
        "name": "Analytics",
        "description": "Aggregated visibility, share of voice, citations, and query fan-out for a brand"
      },
      {
        "name": "Runs",
        "description": "Individual model answers behind the aggregates"
      },
      {
        "name": "Tags",
        "description": "The tags in use on a brand's prompts"
      },
      {
        "name": "Opportunities",
        "description": "Where a brand could win more citations, and why"
      }
    ]
  }
}