{
  "openapi": "3.1.0",
  "info": {
    "title": "Geoptie API",
    "version": "1.0.0",
    "summary": "Read and manage your AI-search visibility data.",
    "description": "Everything the Geoptie dashboard can do, over HTTP.\n\n**Authentication.** Every request needs an API key as `Authorization: Bearer <key>`, and the workspace must have an active or trialing subscription. Keys are created in the dashboard and scoped to one workspace.\n\n**Pagination** is cursor-based. Pass `next_cursor` back as `cursor` and stop when `has_more` is false. The cursor is opaque; do not parse it.\n\n**Dates** are ISO 8601. `from` is inclusive, `to` is exclusive. Omitting both gives the last 30 days.\n\n**Limits** exist to stop runaway clients, not to meter you: there is no monthly request ceiling. Every response carries `X-RateLimit-*`, and endpoints that spend money carry `X-Endpoint-*` as well. A 429 names which limit was hit.\n\n**Errors** always carry a stable `error.code`. Match on that, never on the message text. A verb that an endpoint does not declare returns 405 `method_not_allowed`.",
    "contact": {
      "name": "Geoptie support",
      "email": "support@geoptie.com"
    }
  },
  "servers": [
    {
      "url": "https://api.geoptie.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Brands",
      "description": "The brands you track. A brand owns its prompts, engines and country."
    },
    {
      "name": "Prompts",
      "description": "The questions you track across engines, the answers they returned, and the brands named in those answers. Plan limits apply to how many you can track."
    },
    {
      "name": "Visibility",
      "description": "How often and how prominently a brand appears in engine answers."
    },
    {
      "name": "Citations",
      "description": "Which pages engines cited when answering your prompts."
    },
    {
      "name": "Competitors",
      "description": "Your share of voice against the other brands appearing in your prompts."
    },
    {
      "name": "Topics",
      "description": "Optional grouping for prompts within a brand."
    },
    {
      "name": "Recommendations",
      "description": "Generated actions for improving visibility, and their lifecycle."
    },
    {
      "name": "Audit reports",
      "description": "On-demand GEO analysis of a single URL."
    },
    {
      "name": "Content generations",
      "description": "New articles, written from the pages engines already cite for the prompts you track. Research a brief first, then write the article from it."
    },
    {
      "name": "Content optimizations",
      "description": "Pages you already have, scored for AI search with the specific changes that would raise the score."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/v1/brands": {
      "get": {
        "tags": [
          "Brands"
        ],
        "summary": "List brands",
        "description": "Every brand this key can reach, with the engines and country each is measured on.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Rows per page. Max 999; the answers endpoint caps at 100.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 999
            },
            "example": 100
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page's `next_cursor`. Do not parse it.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Brand"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass back as `cursor`. Null when there are no more rows."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listBrands",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "post": {
        "tags": [
          "Brands"
        ],
        "summary": "Create a brand",
        "description": "Add a brand to start measuring. Returns the brand as created, including the engines it will be tracked on.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "domain"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "domain": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "category": {
                    "type": "string"
                  },
                  "aliases": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "country": {
                    "type": "string",
                    "description": "ISO 3166-1 alpha-2."
                  },
                  "engines": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Engine"
                    },
                    "description": "Up to four."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Brand"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          },
          "409": {
            "$ref": "#/components/responses/E409"
          }
        },
        "operationId": "createBrand",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 30,
          "per_hour": 300,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}": {
      "get": {
        "tags": [
          "Brands"
        ],
        "summary": "Get a brand",
        "description": "One brand's settings: name, domain, aliases, engines and country.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Brand"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "getBrand",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/visibility": {
      "get": {
        "tags": [
          "Visibility"
        ],
        "summary": "Brand visibility",
        "description": "How often and how prominently this brand appears in engine answers, day by day, with a summary for the window. Narrow it to one engine, a topic, or a single prompt.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive start date. Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-08-01"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive end date. Defaults to tomorrow.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          },
          {
            "name": "engine",
            "in": "query",
            "required": false,
            "explode": true,
            "description": "Repeatable. Omit for the all-engine rollup.",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/Engine"
              }
            },
            "example": [
              "chatgpt"
            ]
          },
          {
            "name": "topic_id",
            "in": "query",
            "required": false,
            "explode": true,
            "description": "Repeatable. Restrict to prompts in these topics.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              }
            }
          },
          {
            "name": "prompt_id",
            "in": "query",
            "required": false,
            "description": "Restrict to a single prompt. Not repeatable, and cannot be combined with `topic_id`. For metrics across many prompts use `GET /v1/brands/{id}/prompts?include=metrics`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3f9a2c14-8b7e-4d61-a052-c7e4b9138265"
          },
          {
            "name": "trends",
            "in": "query",
            "required": false,
            "description": "Set false for the summary only.",
            "schema": {
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "compare",
            "in": "query",
            "required": false,
            "description": "Adds the previous equal-length period and deltas.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BrandVisibility"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "getBrandVisibility",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/prompts": {
      "get": {
        "tags": [
          "Prompts"
        ],
        "summary": "List a brand's prompts",
        "description": "The prompts being measured for this brand. Add `include=metrics` for each prompt's visibility over a window.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Rows per page. Max 999; the answers endpoint caps at 100.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 999
            },
            "example": 100
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page's `next_cursor`. Do not parse it.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive start date. Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-08-01"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive end date. Defaults to tomorrow.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "active"
            }
          },
          {
            "name": "include",
            "in": "query",
            "required": false,
            "description": "Set to `metrics` to attach per-prompt metrics for the window. Costs one extra query regardless of prompt count.",
            "schema": {
              "type": "string",
              "enum": [
                "metrics"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Prompt"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass back as `cursor`. Null when there are no more rows."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listPrompts",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "post": {
        "tags": [
          "Prompts"
        ],
        "summary": "Start tracking prompts",
        "description": "Start measuring one or more prompts for this brand. Prompts already being tracked are left unchanged, and the response reports how many were added.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "prompts"
                ],
                "properties": {
                  "prompts": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1
                  },
                  "country": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "topic_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Submitted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PromptWriteResult"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          },
          "409": {
            "$ref": "#/components/responses/E409"
          }
        },
        "operationId": "trackPrompts",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 30,
          "per_hour": 300,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/prompts/{promptId}": {
      "patch": {
        "tags": [
          "Prompts"
        ],
        "summary": "Assign or clear a prompt's topic",
        "description": "File a prompt under a topic, or clear its topic by sending null.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "promptId",
            "in": "path",
            "required": true,
            "description": "Prompt id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3f9a2c14-8b7e-4d61-a052-c7e4b9138265"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "topic_id"
                ],
                "properties": {
                  "topic_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "updatePrompt",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 60,
          "per_hour": 600,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "delete": {
        "tags": [
          "Prompts"
        ],
        "summary": "Stop tracking a prompt",
        "description": "Stop measuring a prompt for this brand. It will not be run again.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "promptId",
            "in": "path",
            "required": true,
            "description": "Prompt id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3f9a2c14-8b7e-4d61-a052-c7e4b9138265"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "untrackPrompt",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 30,
          "per_hour": 300,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/topics": {
      "get": {
        "tags": [
          "Topics"
        ],
        "summary": "List topics",
        "description": "The topics defined for this brand. Topics group prompts so visibility can be read by theme.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many rows to return. This grain does not page: it is a complete set and `limit` truncates it, so check `capped` before treating the response as all of them.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 999
            },
            "example": 100
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Topic"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "Always false: this grain does not page."
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always null: this grain does not page."
                    },
                    "total_count": {
                      "type": "integer",
                      "description": "How many rows matched in total, before `limit`."
                    },
                    "capped": {
                      "type": "boolean",
                      "description": "True when `total_count` exceeds the rows returned, so this is a truncated set and not all of them."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listTopics",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "post": {
        "tags": [
          "Topics"
        ],
        "summary": "Create a topic",
        "description": "Create a topic to group this brand's prompts under.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Topic"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "createTopic",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 30,
          "per_hour": 300,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/topics/{topicId}": {
      "patch": {
        "tags": [
          "Topics"
        ],
        "summary": "Rename a topic",
        "description": "Change a topic's name. The prompts filed under it are unaffected.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "topicId",
            "in": "path",
            "required": true,
            "description": "Topic id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "5d2e8f31-9a4c-4b78-8e13-6f0a7c25d894"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Topic"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "renameTopic",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 30,
          "per_hour": 300,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "delete": {
        "tags": [
          "Topics"
        ],
        "summary": "Delete a topic",
        "description": "Delete a topic. Prompts filed under it stay tracked and become unfiled.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "topicId",
            "in": "path",
            "required": true,
            "description": "Topic id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "5d2e8f31-9a4c-4b78-8e13-6f0a7c25d894"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "deleteTopic",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 30,
          "per_hour": 300,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/prompts/{id}": {
      "get": {
        "tags": [
          "Prompts"
        ],
        "summary": "Get a prompt",
        "description": "One prompt: its text, the country it is measured in, when it last ran, and each of your brands' subscription to it with its topic and status. 404 if none of your brands tracks it.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Prompt id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PromptDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "getPrompt",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/prompts/{id}/responses": {
      "get": {
        "tags": [
          "Prompts"
        ],
        "summary": "Answers for a prompt",
        "description": "The full answer each engine gave for this prompt, day by day, as the text a user would have seen. Add `include` to expand each answer with the sources it cited and the brands it named, both of which belong to one answer rather than to the prompt as a whole.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Prompt id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "include",
            "in": "query",
            "required": false,
            "description": "Comma-separated expansions: `citations`, `mentions`. Off by default because this endpoint already returns full answer text; ask for them only when you want them alongside it.",
            "schema": {
              "type": "string"
            },
            "example": "citations,mentions"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Rows per page. Max 999; the answers endpoint caps at 100.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 999
            },
            "example": 100
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page's `next_cursor`. Do not parse it.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive start date. Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-08-01"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive end date. Defaults to tomorrow.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          },
          {
            "name": "engine",
            "in": "query",
            "required": false,
            "description": "Restrict to one engine. Omit for the all-engine rollup.",
            "schema": {
              "$ref": "#/components/schemas/Engine"
            },
            "example": "chatgpt"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Answer"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass back as `cursor`. Null when there are no more rows."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listAnswers",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": 600,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/prompts/{id}/mentions": {
      "get": {
        "tags": [
          "Prompts"
        ],
        "summary": "Brands mentioned in a prompt's answers",
        "description": "Every brand named in an answer to this prompt, including brands that are not yours, so you can see who the engines bring up. Each mention carries the `response_id` of the answer it was found in, which matches the `id` of a row from the answers endpoint.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Prompt id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "engine",
            "in": "query",
            "required": false,
            "description": "Restrict to one engine. Omit for the all-engine rollup.",
            "schema": {
              "$ref": "#/components/schemas/Engine"
            },
            "example": "chatgpt"
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive start date. Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-08-01"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive end date. Defaults to tomorrow.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Rows per page. Max 999; the answers endpoint caps at 100.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 999
            },
            "example": 100
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page's `next_cursor`. Do not parse it.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Mention"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass back as `cursor`. Null when there are no more rows."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listPromptMentions",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/citations": {
      "get": {
        "tags": [
          "Citations"
        ],
        "summary": "Citations for a brand",
        "description": "The complete raw citation feed for a brand: one row per prompt per day per page, newest day first, cursor-paginated with no ceiling. This is the endpoint for a full export into your own store.\n\nFor analysis you almost certainly want a different one. To rank what cites you, use `/citations/domains` or `/citations/urls`, which are already aggregated and ordered. To see the sources behind a single answer, use `include=citations` on the answers endpoint.\n\n`citation_count` here is the count for that prompt on that day and is not a running total. Omit `engine` and each row is the across-engine rollup, reported as `engine: all`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "domain",
            "in": "query",
            "required": false,
            "description": "Filter by root domain, e.g. `example.com`. Subdomains roll up.",
            "schema": {
              "type": "string"
            },
            "example": "example.com"
          },
          {
            "name": "engine",
            "in": "query",
            "required": false,
            "description": "Restrict to one engine. Omit for the all-engine rollup.",
            "schema": {
              "$ref": "#/components/schemas/Engine"
            },
            "example": "chatgpt"
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive start date. Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-08-01"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive end date. Defaults to tomorrow.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Rows per page. Max 999; the answers endpoint caps at 100.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 999
            },
            "example": 100
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page's `next_cursor`. Do not parse it.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Citation"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass back as `cursor`. Null when there are no more rows."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listCitations",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/citations/domains": {
      "get": {
        "tags": [
          "Citations"
        ],
        "summary": "Most-cited domains",
        "description": "The domains engines cite most when answering this brand's prompts, aggregated over the window and ranked by citation count. Top-N rather than paginated: `total_count` is how many domains matched and `capped` is true when there are more than this response carries.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "engine",
            "in": "query",
            "required": false,
            "description": "Restrict to one engine. Omit for the all-engine rollup.",
            "schema": {
              "$ref": "#/components/schemas/Engine"
            },
            "example": "chatgpt"
          },
          {
            "name": "topic",
            "in": "query",
            "required": false,
            "description": "Restrict to prompts in one topic.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive start date. Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-08-01"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive end date. Defaults to tomorrow.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many rows to return, ranked by citation count. Max 2000. There is no paging past this: check `capped` in the response.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 2000
            },
            "example": 100
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CitedDomain"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "Always false: this grain does not page."
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always null: this grain does not page."
                    },
                    "total_count": {
                      "type": "integer",
                      "description": "How many rows matched in total, before the limit."
                    },
                    "total_citations": {
                      "type": "integer",
                      "description": "Total citations across ALL matching rows. The denominator behind `citation_share`."
                    },
                    "capped": {
                      "type": "boolean",
                      "description": "True when `total_count` exceeds the rows returned, so this is the top N and not the complete set."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listCitedDomains",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/citations/urls": {
      "get": {
        "tags": [
          "Citations"
        ],
        "summary": "Most-cited pages",
        "description": "The individual pages engines cite most when answering this brand's prompts, aggregated over the window and ranked by citation count. Top-N rather than paginated: check `capped` before treating the response as the complete set.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "engine",
            "in": "query",
            "required": false,
            "description": "Restrict to one engine. Omit for the all-engine rollup.",
            "schema": {
              "$ref": "#/components/schemas/Engine"
            },
            "example": "chatgpt"
          },
          {
            "name": "topic",
            "in": "query",
            "required": false,
            "description": "Restrict to prompts in one topic.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive start date. Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-08-01"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive end date. Defaults to tomorrow.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many rows to return, ranked by citation count. Max 2000. There is no paging past this: check `capped` in the response.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 2000
            },
            "example": 100
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CitedUrl"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "Always false: this grain does not page."
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always null: this grain does not page."
                    },
                    "total_count": {
                      "type": "integer",
                      "description": "How many rows matched in total, before the limit."
                    },
                    "total_citations": {
                      "type": "integer",
                      "description": "Total citations across ALL matching rows. The denominator behind `citation_share`."
                    },
                    "capped": {
                      "type": "boolean",
                      "description": "True when `total_count` exceeds the rows returned, so this is the top N and not the complete set."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listCitedUrls",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/citations/trends": {
      "get": {
        "tags": [
          "Citations"
        ],
        "summary": "Citation trend for one domain or page",
        "description": "Daily citation counts for a single target, with the brand's total for the same days so you get share as well as volume. Pass `url` for one exact page, `domain` for a root domain (subdomains roll up), or neither to get the brand's own domain, i.e. how often engines cite you. Days the brand did not run are omitted; days it ran without citing the target are zero.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "domain",
            "in": "query",
            "required": false,
            "description": "Root domain to trend. Mutually exclusive with `url`.",
            "schema": {
              "type": "string"
            },
            "example": "example.com"
          },
          {
            "name": "url",
            "in": "query",
            "required": false,
            "description": "Exact page URL to trend. Mutually exclusive with `domain`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engine",
            "in": "query",
            "required": false,
            "description": "Restrict to one engine. Omit for the all-engine rollup.",
            "schema": {
              "$ref": "#/components/schemas/Engine"
            },
            "example": "chatgpt"
          },
          {
            "name": "topic",
            "in": "query",
            "required": false,
            "description": "Restrict to prompts in one topic.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive start date. Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-08-01"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive end date. Defaults to tomorrow.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CitationTrend"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "getCitationTrend",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/competitors": {
      "get": {
        "tags": [
          "Competitors"
        ],
        "summary": "Your brand versus its competitors",
        "description": "Which other brands appear alongside yours in the same answers, and your share of voice against them.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "engine",
            "in": "query",
            "required": false,
            "description": "Restrict to one engine. Omit for the all-engine rollup.",
            "schema": {
              "$ref": "#/components/schemas/Engine"
            },
            "example": "chatgpt"
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive start date. Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-08-01"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive end date. Defaults to tomorrow.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many rows to return. This grain does not page: it is a complete set and `limit` truncates it, so check `capped` before treating the response as all of them.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 999
            },
            "example": 100
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Competitor"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "Always false: this grain does not page."
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always null: this grain does not page."
                    },
                    "total_count": {
                      "type": "integer",
                      "description": "How many rows matched in total, before `limit`."
                    },
                    "capped": {
                      "type": "boolean",
                      "description": "True when `total_count` exceeds the rows returned, so this is a truncated set and not all of them."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listCompetitors",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/recommendations": {
      "get": {
        "tags": [
          "Recommendations"
        ],
        "summary": "List recommendations",
        "description": "Suggested actions for improving this brand's visibility, with the state of each.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many rows to return. This grain does not page: it is a complete set and `limit` truncates it, so check `capped` before treating the response as all of them.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 999
            },
            "example": 100
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "completed",
                "dismissed"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Recommendation"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "Always false: this grain does not page."
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always null: this grain does not page."
                    },
                    "total_count": {
                      "type": "integer",
                      "description": "How many rows matched in total, before `limit`."
                    },
                    "capped": {
                      "type": "boolean",
                      "description": "True when `total_count` exceeds the rows returned, so this is a truncated set and not all of them."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listRecommendations",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/recommendations/generate": {
      "post": {
        "tags": [
          "Recommendations"
        ],
        "summary": "Generate recommendations",
        "description": "Produce a fresh set of suggested actions for this brand, based on its current visibility and the content on its site.\n\nReturns straight away with `poll_after` and a `poll` path; the run itself takes 30 to 60 seconds on a small brand and longer on a large one. Poll the recommendations list and treat the run as finished once any recommendation's `last_seen_at` is later than `poll_after`. A run refreshes `last_seen_at` on every recommendation it finds, so this works even when the run turns up nothing new.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "classify_offset": {
                    "type": "integer",
                    "default": 0,
                    "description": "Skip past the actions you have already been given, the way the dashboard's Generate more button does. Leave it at 0 for a fresh set."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/RecommendationRunAccepted"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "generateRecommendations",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 2,
          "per_hour": 10,
          "max_concurrent": 2,
          "spends_model_budget": true
        }
      }
    },
    "/v1/brands/{id}/recommendations/{recId}": {
      "patch": {
        "tags": [
          "Recommendations"
        ],
        "summary": "Complete, dismiss or restore",
        "description": "Mark a recommendation as done or dismissed, or put it back to open.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "recId",
            "in": "path",
            "required": true,
            "description": "Recommendation id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "9c4b1a76-2e58-4f03-b7d9-3a5e8c162047"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "complete",
                      "dismiss",
                      "restore"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Recommendation"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "updateRecommendation",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 60,
          "per_hour": 600,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "delete": {
        "tags": [
          "Recommendations"
        ],
        "summary": "Delete a recommendation",
        "description": "Remove a recommendation from the list.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "recId",
            "in": "path",
            "required": true,
            "description": "Recommendation id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "9c4b1a76-2e58-4f03-b7d9-3a5e8c162047"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "deleteRecommendation",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 30,
          "per_hour": 300,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/audit-reports": {
      "get": {
        "tags": [
          "Audit reports"
        ],
        "summary": "List audit reports",
        "description": "Audit reports run for this workspace, newest first.",
        "parameters": [
          {
            "name": "brand_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive start date. Defaults to 30 days before `to`.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-08-01"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Exclusive end date. Defaults to tomorrow.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-01"
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many rows to return. This grain does not page: it is a complete set and `limit` truncates it, so check `capped` before treating the response as all of them.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 999
            },
            "example": 100
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AuditReport"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "Always false: this grain does not page."
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always null: this grain does not page."
                    },
                    "total_count": {
                      "type": "integer",
                      "description": "How many rows matched in total, before `limit`."
                    },
                    "capped": {
                      "type": "boolean",
                      "description": "True when `total_count` exceeds the rows returned, so this is a truncated set and not all of them."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listAuditReports",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "post": {
        "tags": [
          "Audit reports"
        ],
        "summary": "Run an audit",
        "description": "Audit a single URL for AI search. Returns straight away with an id and a `poll` path; the report is ready when its status is `complete`, usually within a minute or two.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "brand_id"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "brand_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The brand this report belongs to. Required: a report with no brand cannot be read back by a key limited to specific brands."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AuditReportAccepted"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "createAuditReport",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 5,
          "per_hour": 60,
          "max_concurrent": 2,
          "spends_model_budget": true
        }
      }
    },
    "/v1/audit-reports/{id}": {
      "get": {
        "tags": [
          "Audit reports"
        ],
        "summary": "Get an audit report",
        "description": "One audit report, with its score and findings once its status is `complete`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Report id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AuditReport"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "getAuditReport",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "delete": {
        "tags": [
          "Audit reports"
        ],
        "summary": "Delete an audit report",
        "description": "Remove an audit report and its findings.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Report id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "deleteAuditReport",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 30,
          "per_hour": 300,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/generations": {
      "get": {
        "tags": [
          "Content generations"
        ],
        "summary": "List generations",
        "description": "Articles this brand has generated, newest first. Rows are summary fields only: `brief_md`, `draft_md`, `slug` and the meta fields are omitted here and returned by a single fetch.\n\nThis lists content you generated. For pages you already had and want scored, see optimizations.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many rows to return. This grain does not page: it is a complete set and `limit` truncates it, so check `capped` before treating the response as all of them.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 999
            },
            "example": 100
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContentProject"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "Always false: this grain does not page."
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always null: this grain does not page."
                    },
                    "total_count": {
                      "type": "integer",
                      "description": "How many rows matched in total, before `limit`."
                    },
                    "capped": {
                      "type": "boolean",
                      "description": "True when `total_count` exceeds the rows returned, so this is a truncated set and not all of them."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listGenerations",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "post": {
        "tags": [
          "Content generations"
        ],
        "summary": "Generate a brief",
        "description": "Start a new article by researching it. This studies the pages engines already cite for the prompts you name and writes a brief from them; it does not write the article, which is the next step.\n\nReturns straight away with an id, a status of `generating_brief` and a `poll` path; the research takes a few minutes. Poll until `status` is `brief_ready`, then write the article from it.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "content_type",
                  "prompt_ids",
                  "cited_page_urls"
                ],
                "properties": {
                  "content_type": {
                    "$ref": "#/components/schemas/ContentType"
                  },
                  "prompt_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "The questions this article should win. At least one."
                  },
                  "cited_page_urls": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "The already-cited pages to study, from `/citations/urls`. At least one: nothing is chosen for you."
                  },
                  "title": {
                    "type": "string",
                    "description": "Leave it out and one is written for you."
                  },
                  "topic": {
                    "type": "string",
                    "description": "Optional. Taken from your prompts when you leave it out."
                  },
                  "instructions": {
                    "type": "string",
                    "description": "Anything else the article should account for, such as the audience or an angle to take."
                  },
                  "source_recommendation_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Records that this article came from a recommendation."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ContentAccepted"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "createGeneration",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 3,
          "per_hour": 20,
          "max_concurrent": 2,
          "spends_model_budget": true
        }
      }
    },
    "/v1/brands/{id}/generations/{generationId}": {
      "get": {
        "tags": [
          "Content generations"
        ],
        "summary": "Get a generation",
        "description": "One content project, including its brief and draft if they exist.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "generationId",
            "in": "path",
            "required": true,
            "description": "Generation id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "1a6f3d92-7c85-4e20-9b47-8d2c5f0a3e61"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ContentProject"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "getGeneration",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "patch": {
        "tags": [
          "Content generations"
        ],
        "summary": "Update a generation",
        "description": "Change a generation's title, topic, slug, meta fields or brief.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "generationId",
            "in": "path",
            "required": true,
            "description": "Generation id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "1a6f3d92-7c85-4e20-9b47-8d2c5f0a3e61"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "topic": {
                    "type": "string"
                  },
                  "slug": {
                    "type": "string"
                  },
                  "meta_title": {
                    "type": "string"
                  },
                  "meta_description": {
                    "type": "string"
                  },
                  "brief_md": {
                    "type": "string",
                    "description": "Rewrite the brief. Merged in, so the research behind it is kept. Change this before writing the article to change what gets written."
                  },
                  "draft_md": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ContentProject"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "updateGeneration",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 60,
          "per_hour": 600,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "delete": {
        "tags": [
          "Content generations"
        ],
        "summary": "Delete a generation",
        "description": "Remove a content project along with its brief and draft.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "generationId",
            "in": "path",
            "required": true,
            "description": "Generation id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "1a6f3d92-7c85-4e20-9b47-8d2c5f0a3e61"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "deleteGeneration",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 30,
          "per_hour": 300,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/generations/{generationId}/draft": {
      "post": {
        "tags": [
          "Content generations"
        ],
        "summary": "Write the article from its brief",
        "description": "Turn this generation's brief into the finished article. Call it once `status` is `brief_ready`.\n\nThe article is always written from the brief the generation holds. To change what gets written, PATCH `brief_md` first, then call this.\n\nReturns straight away with a status of `generating` and a `poll` path; poll until `status` is `ready`, then fetch the generation to read `draft_md`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "generationId",
            "in": "path",
            "required": true,
            "description": "Generation id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "1a6f3d92-7c85-4e20-9b47-8d2c5f0a3e61"
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ContentAccepted"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "generateDraft",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 3,
          "per_hour": 20,
          "max_concurrent": 2,
          "spends_model_budget": true
        }
      }
    },
    "/v1/brands/{id}/optimizations": {
      "get": {
        "tags": [
          "Content optimizations"
        ],
        "summary": "List optimizations",
        "description": "Pages analysed for this brand, with the score each received.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many rows to return. This grain does not page: it is a complete set and `limit` truncates it, so check `capped` before treating the response as all of them.",
            "schema": {
              "type": "integer",
              "default": 100,
              "maximum": 999
            },
            "example": 100
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Optimization"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "Always false: this grain does not page."
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always null: this grain does not page."
                    },
                    "total_count": {
                      "type": "integer",
                      "description": "How many rows matched in total, before `limit`."
                    },
                    "capped": {
                      "type": "boolean",
                      "description": "True when `total_count` exceeds the rows returned, so this is a truncated set and not all of them."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "listOptimizations",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "post": {
        "tags": [
          "Content optimizations"
        ],
        "summary": "Analyse a page or pasted content",
        "description": "Score a page for AI search and get specific fixes for it. Pass a `url` to fetch, or paste at least 100 characters of `content`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "content": {
                    "type": "string",
                    "minLength": 100
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Analysed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Optimization"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "createOptimization",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 3,
          "per_hour": 20,
          "max_concurrent": 2,
          "spends_model_budget": true
        }
      }
    },
    "/v1/brands/{id}/optimizations/{optId}": {
      "get": {
        "tags": [
          "Content optimizations"
        ],
        "summary": "Get an optimization",
        "description": "One analysis, with its score and the fixes suggested for that page.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "optId",
            "in": "path",
            "required": true,
            "description": "Optimization id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "8e5c2b40-6d19-4a73-95f8-2b7d1e463c05"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Optimization"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "getOptimization",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": null,
          "per_hour": null,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      },
      "delete": {
        "tags": [
          "Content optimizations"
        ],
        "summary": "Delete an optimization",
        "description": "Remove an analysis and its suggested fixes.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "optId",
            "in": "path",
            "required": true,
            "description": "Optimization id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "8e5c2b40-6d19-4a73-95f8-2b7d1e463c05"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "deleteOptimization",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 30,
          "per_hour": 300,
          "max_concurrent": null,
          "spends_model_budget": false
        }
      }
    },
    "/v1/brands/{id}/optimizations/{optId}/rescore": {
      "post": {
        "tags": [
          "Content optimizations"
        ],
        "summary": "Re-score an optimization",
        "description": "Run the analysis again on the same page and update its score.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Brand id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "b7c1e5a0-4f3d-42a8-9c6b-1e8d5a2f7043"
          },
          {
            "name": "optId",
            "in": "path",
            "required": true,
            "description": "Optimization id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "8e5c2b40-6d19-4a73-95f8-2b7d1e463c05"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Optimization"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/E400"
          },
          "401": {
            "$ref": "#/components/responses/E401"
          },
          "402": {
            "$ref": "#/components/responses/E402"
          },
          "403": {
            "$ref": "#/components/responses/E403"
          },
          "404": {
            "$ref": "#/components/responses/E404"
          },
          "429": {
            "$ref": "#/components/responses/E429"
          },
          "500": {
            "$ref": "#/components/responses/E500"
          }
        },
        "operationId": "rescoreOptimization",
        "x-geoptie-limits": {
          "rate_per_min": 120,
          "per_min": 3,
          "per_hour": 20,
          "max_concurrent": 2,
          "spends_model_budget": true
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key, e.g. `Authorization: Bearer gp_live_...`."
      }
    },
    "schemas": {
      "Engine": {
        "type": "string",
        "description": "Stable public engine identifier. Internal model names are never exposed.",
        "enum": [
          "chatgpt",
          "claude",
          "perplexity",
          "gemini",
          "google_ai_overviews",
          "google_ai_mode",
          "copilot"
        ]
      },
      "SourceType": {
        "type": "string",
        "description": "How the cited page is classified.",
        "enum": [
          "company",
          "blog",
          "news",
          "government",
          "social",
          "academic",
          "web_search",
          "other"
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine code. Match on this, not the message."
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "Brand": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "engines": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Engine"
            },
            "description": "Engines selected for this brand. Up to four."
          },
          "country": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 3166-1 alpha-2, or null for untargeted."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Prompt": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "text": {
            "type": [
              "string",
              "null"
            ]
          },
          "brand_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "archived"
            ]
          },
          "topic_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "tracked_since": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "metrics": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Metrics"
              }
            ],
            "description": "Only present when `include=metrics`."
          }
        }
      },
      "PromptDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "text": {
            "type": "string"
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "subscriptions": {
            "type": "array",
            "description": "One entry per brand of yours that tracks this prompt. Prompt rows are deduplicated by text, so several brands can track the same one, each under its own topic. Topic and status belong to the subscription, not the prompt.",
            "items": {
              "type": "object",
              "properties": {
                "brand_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "active",
                    "paused",
                    "archived"
                  ]
                },
                "topic_id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uuid"
                },
                "tracked_since": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_checked": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "EmbeddedCitation": {
        "type": "object",
        "description": "A source cited in one answer.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "position": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Where the source appeared in the answer. 1 is first."
          },
          "source_type": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SourceType"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "EmbeddedMention": {
        "type": "object",
        "description": "A brand named in one answer, yours or anyone's.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "brand_name": {
            "type": "string"
          },
          "position": {
            "type": [
              "integer",
              "null"
            ]
          },
          "sentiment": {
            "type": [
              "number",
              "null"
            ],
            "description": "-1 to 1."
          }
        }
      },
      "Answer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "prompt_id": {
            "type": "string",
            "format": "uuid"
          },
          "engine": {
            "$ref": "#/components/schemas/Engine"
          },
          "citations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmbeddedCitation"
            },
            "description": "Only present when `include` asks for it. An answer that cited nothing returns an empty array, never a missing field."
          },
          "mentions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmbeddedMention"
            },
            "description": "Only present when `include` asks for it. An answer that named no brand returns an empty array."
          },
          "answer": {
            "type": "string",
            "description": "The full text the engine returned."
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "checked_on": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "Metrics": {
        "type": "object",
        "properties": {
          "visibility_score": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100."
          },
          "share_of_voice": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100."
          },
          "detection_rate": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100. Share of analysed responses that mention the brand."
          },
          "top3_visibility": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100."
          },
          "avg_position": {
            "type": [
              "number",
              "null"
            ],
            "description": "1 is first. Lower is better. Null when never mentioned."
          },
          "avg_sentiment": {
            "type": [
              "number",
              "null"
            ],
            "description": "-1 to 1."
          },
          "mention_count": {
            "type": "integer"
          },
          "citation_count": {
            "type": "integer"
          },
          "responses_analyzed": {
            "type": "integer"
          },
          "responses_with_brand": {
            "type": "integer"
          }
        }
      },
      "TrendPoint": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date"
          },
          "no_data": {
            "type": "boolean",
            "description": "True when no snapshot exists for that day. Distinguishes 'measured and invisible' from 'not measured'."
          },
          "visibility_score": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100."
          },
          "share_of_voice": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100."
          },
          "detection_rate": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100. Share of analysed responses that mention the brand."
          },
          "top3_visibility": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100."
          },
          "avg_position": {
            "type": [
              "number",
              "null"
            ],
            "description": "1 is first. Lower is better. Null when never mentioned."
          },
          "avg_sentiment": {
            "type": [
              "number",
              "null"
            ],
            "description": "-1 to 1."
          },
          "mention_count": {
            "type": "integer"
          },
          "citation_count": {
            "type": "integer"
          },
          "responses_analyzed": {
            "type": "integer"
          },
          "responses_with_brand": {
            "type": "integer"
          }
        }
      },
      "BrandVisibility": {
        "type": "object",
        "properties": {
          "brand": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              },
              "domain": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "period": {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "format": "date"
              },
              "to": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "filters": {
            "type": "object",
            "properties": {
              "engines": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Engine"
                }
              },
              "topic_ids": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            }
          },
          "summary": {
            "$ref": "#/components/schemas/Metrics"
          },
          "trends": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TrendPoint"
            },
            "description": "Omitted when `trends=false`."
          },
          "previous": {
            "type": "object",
            "description": "Only when `compare=true`.",
            "properties": {
              "period": {
                "type": "object"
              },
              "summary": {
                "$ref": "#/components/schemas/Metrics"
              }
            }
          },
          "changes": {
            "type": "object",
            "description": "Only when `compare=true`. Absolute deltas.",
            "additionalProperties": {
              "type": [
                "number",
                "null"
              ]
            }
          }
        }
      },
      "Citation": {
        "type": "object",
        "properties": {
          "prompt_id": {
            "type": "string",
            "format": "uuid"
          },
          "date": {
            "type": "string",
            "format": "date"
          },
          "engine": {
            "type": "string",
            "description": "One of the `Engine` values, or the literal `\"all\"` for the across-engine rollup you get when the `engine` parameter is omitted. On an `\"all\"` row `citation_count` is the sum over engines and `avg_position` their mean, so never add an `\"all\"` row to per-engine rows."
          },
          "url": {
            "type": "string"
          },
          "domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "root_domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "source_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "citation_count": {
            "type": "integer"
          },
          "avg_position": {
            "type": [
              "number",
              "null"
            ]
          }
        }
      },
      "Mention": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "response_id": {
            "type": "string",
            "format": "uuid",
            "description": "The answer this mention was found in. Matches the `id` of a row from the answers endpoint."
          },
          "brand_name": {
            "type": "string",
            "description": "Any brand named in the answer, not only yours."
          },
          "position": {
            "type": [
              "integer",
              "null"
            ]
          },
          "sentiment": {
            "type": [
              "number",
              "null"
            ],
            "description": "-1 to 1."
          },
          "prompt_id": {
            "type": "string",
            "format": "uuid"
          },
          "engine": {
            "$ref": "#/components/schemas/Engine"
          },
          "checked_on": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "CitedDomain": {
        "type": "object",
        "properties": {
          "domain": {
            "type": "string"
          },
          "root_domain": {
            "type": "string",
            "description": "Group on this to fold subdomains together."
          },
          "citation_count": {
            "type": "integer",
            "description": "Citations across the whole window."
          },
          "avg_position": {
            "type": [
              "number",
              "null"
            ],
            "description": "1 is first. Lower is better."
          },
          "citation_share": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100. This domain's share of `total_citations`."
          }
        }
      },
      "CitedUrl": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          },
          "domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "root_domain": {
            "type": [
              "string",
              "null"
            ]
          },
          "citation_count": {
            "type": "integer",
            "description": "Citations across the whole window."
          },
          "avg_position": {
            "type": [
              "number",
              "null"
            ],
            "description": "1 is first. Lower is better."
          },
          "citation_share": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100. This page's share of `total_citations`."
          }
        }
      },
      "CitationTrendPoint": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date"
          },
          "citations": {
            "type": "integer",
            "description": "Citations of the target that day. Zero means the brand ran and the target was not cited; a day the brand did not run is omitted entirely."
          },
          "avg_position": {
            "type": [
              "number",
              "null"
            ]
          },
          "brand_total_citations": {
            "type": "integer",
            "description": "All citations for the brand that day, under the same filters."
          },
          "share": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100."
          }
        }
      },
      "CitationTrend": {
        "type": "object",
        "properties": {
          "target": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "domain",
                  "url"
                ]
              },
              "value": {
                "type": "string"
              },
              "is_own_domain": {
                "type": "boolean",
                "description": "True when neither `domain` nor `url` was passed and this defaulted to the brand's own domain."
              }
            }
          },
          "series": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CitationTrendPoint"
            }
          },
          "summary": {
            "type": "object",
            "properties": {
              "citations": {
                "type": "integer"
              },
              "avg_position": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Weighted by citations, so a heavily-cited day counts for more."
              },
              "share": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "0-100."
              },
              "brand_total_citations": {
                "type": "integer"
              },
              "change": {
                "type": "object",
                "description": "Against the immediately preceding window of the same length.",
                "properties": {
                  "absolute": {
                    "type": "number"
                  },
                  "percentage": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "Null when the previous window had no citations."
                  }
                }
              }
            }
          }
        }
      },
      "Competitor": {
        "type": "object",
        "properties": {
          "brand_name": {
            "type": "string"
          },
          "is_your_brand": {
            "type": "boolean"
          },
          "visibility_score": {
            "type": [
              "number",
              "null"
            ]
          },
          "share_of_voice": {
            "type": [
              "number",
              "null"
            ]
          },
          "detection_rate": {
            "type": [
              "number",
              "null"
            ]
          },
          "avg_position": {
            "type": [
              "number",
              "null"
            ]
          },
          "avg_sentiment": {
            "type": [
              "number",
              "null"
            ]
          },
          "top3_visibility": {
            "type": [
              "number",
              "null"
            ]
          },
          "mention_count": {
            "type": "integer"
          }
        }
      },
      "Topic": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "prompt_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "RecommendationWhy": {
        "type": "object",
        "description": "Why this action was suggested. Fields vary by recommendation `type`; only the ones relevant to that type are present.",
        "properties": {
          "prompts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The tracked prompts this action relates to."
          },
          "prompt": {
            "type": "string",
            "description": "A single prompt, on types that concern one."
          },
          "prompt_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "providers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Engine"
            },
            "description": "The engines where this was observed."
          },
          "exemplars": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "Pages that already rank for this, as examples to follow."
          },
          "recommended_format": {
            "type": "string",
            "description": "The content shape that tends to win here."
          },
          "url": {
            "type": "string",
            "description": "The page in question."
          },
          "matched_page": {
            "type": "string",
            "description": "The page on your own site this was matched to."
          },
          "action_type": {
            "type": "string"
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "response_id": {
            "type": "string",
            "format": "uuid",
            "description": "The answer that triggered this, on types derived from one."
          }
        }
      },
      "RecommendationRunAccepted": {
        "type": "object",
        "properties": {
          "brand_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "generating"
            ]
          },
          "poll_after": {
            "type": "string",
            "format": "date-time",
            "description": "The completion watermark, read from the database clock. Poll `poll` and treat the run as finished once any recommendation's `last_seen_at` is later than this."
          },
          "poll": {
            "type": "string",
            "description": "Path to poll for the result."
          }
        }
      },
      "Recommendation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "brand_id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "completed",
              "dismissed"
            ]
          },
          "title": {
            "type": "string"
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "priority_score": {
            "type": [
              "number",
              "null"
            ],
            "description": "Higher is more urgent. Use it to order the list."
          },
          "link": {
            "type": [
              "string",
              "null"
            ],
            "description": "The page this action is about, when it concerns one."
          },
          "why": {
            "description": "The evidence behind the recommendation.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/RecommendationWhy"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Null only on the brief/draft generation responses, which return the generated content without re-reading the row."
          },
          "last_seen_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a generation run last detected this recommendation. A run bumps it on every recommendation it finds, not only new ones, which is what makes it the completion signal for `POST /v1/brands/{id}/recommendations/generate`."
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "dismissed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "AuditAnalysis": {
        "type": "object",
        "description": "The audit findings. Sections vary with what could be measured for the page.",
        "properties": {
          "analysis_summary": {
            "type": "string",
            "description": "A short readable verdict on the page."
          },
          "dimension_scores": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "0-100 per dimension: ai_comprehension, content_freshness, citation_authority, competitive_context, answer_first_content, technical_optimization."
          },
          "detailed_analysis": {
            "type": "object",
            "additionalProperties": true,
            "description": "Per-dimension findings, each with the current content, an improved version and the reasoning."
          },
          "technical_data": {
            "type": "object",
            "additionalProperties": true,
            "description": "Measured page facts: meta tags, schema markup, heading structure, AI crawler access, content signals. `pageSpeed` is null when it could not be measured."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true,
            "description": "What the page declares about itself: title, language, schema types, publish and modified dates, detected content type."
          }
        }
      },
      "AuditReport": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "brand_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Null only on reports created before brand_id was required."
          },
          "url": {
            "type": "string"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "complete",
              "failed"
            ]
          },
          "score": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100. Null until status is complete."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why a `failed` report failed, in plain language, e.g. the page was unreachable. Null on every other status."
          },
          "analysis": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AuditAnalysis"
              }
            ],
            "description": "The findings. Present on a single fetch, omitted from lists."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "AuditReportAccepted": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "example": "pending"
          },
          "url": {
            "type": "string"
          },
          "brand_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "poll": {
            "type": "string",
            "description": "Path to poll for completion."
          }
        }
      },
      "ContentType": {
        "type": "string",
        "description": "The shape of article to write. `smart` picks the shape for you.",
        "enum": [
          "blog_post",
          "listicle",
          "comparison",
          "guide",
          "how_to",
          "smart"
        ]
      },
      "ContentStatus": {
        "type": "string",
        "description": "Where an article is in the pipeline. `generating_brief` and `generating` are transient; poll until one of the others.",
        "enum": [
          "generating_brief",
          "brief_ready",
          "generating",
          "ready",
          "failed"
        ]
      },
      "ContentAccepted": {
        "type": "object",
        "description": "Generation has started. The work continues after this response returns.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The article, which exists from this moment even though it is still being written."
          },
          "brand_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "$ref": "#/components/schemas/ContentStatus"
          },
          "poll": {
            "type": "string",
            "description": "Fetch this until `status` stops being a generating one."
          }
        }
      },
      "ContentProject": {
        "type": "object",
        "description": "A generated article. Fields the list query does not select are OMITTED rather than returned as null, so `brief_md`, `draft_md`, `slug`, `meta_title` and `meta_description` appear only on a single fetch.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "brand_id": {
            "type": "string",
            "format": "uuid"
          },
          "content_type": {
            "$ref": "#/components/schemas/ContentType"
          },
          "status": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ContentStatus"
              },
              {
                "type": "null"
              }
            ]
          },
          "topic": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "slug": {
            "type": [
              "string",
              "null"
            ],
            "description": "Single fetch only."
          },
          "meta_title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Single fetch only."
          },
          "meta_description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Single fetch only."
          },
          "brief_md": {
            "type": [
              "string",
              "null"
            ],
            "description": "The brief the article is written from, as markdown. Single fetch only."
          },
          "draft_md": {
            "type": [
              "string",
              "null"
            ],
            "description": "The article itself, as markdown. Single fetch only, and null until `status` is first `ready`. Re-running the draft keeps the previous article readable here until the new one replaces it, so a regeneration never blanks what you already had."
          },
          "source_recommendation_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Set when the article was written to act on a recommendation."
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "OptimizationDimension": {
        "type": "string",
        "description": "The aspect of the page being scored.",
        "enum": [
          "content_structure",
          "factual_density",
          "semantic_clarity",
          "answer_completeness",
          "authority_signals",
          "competitive_differentiation"
        ]
      },
      "OptimizationBreakdown": {
        "type": "object",
        "description": "How the page scored on one aspect.",
        "properties": {
          "dimension": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/OptimizationDimension"
              },
              {
                "type": "null"
              }
            ]
          },
          "score": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100 for this aspect."
          },
          "status": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the score is based on, in a sentence."
          }
        }
      },
      "OptimizationFix": {
        "type": "object",
        "description": "One change to make, and what it is worth.",
        "properties": {
          "title": {
            "type": [
              "string",
              "null"
            ],
            "description": "The change, in a few words."
          },
          "issue": {
            "type": [
              "string",
              "null"
            ],
            "description": "What is wrong today."
          },
          "impact": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "high",
              "medium",
              "low"
            ]
          },
          "dimension": {
            "description": "The aspect this change improves.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/OptimizationDimension"
              },
              {
                "type": "null"
              }
            ]
          },
          "excerpt": {
            "type": [
              "string",
              "null"
            ],
            "description": "The passage to change, when the change is to a specific one."
          },
          "rewrite": {
            "type": [
              "string",
              "null"
            ],
            "description": "Suggested replacement wording."
          },
          "projected_delta": {
            "type": [
              "number",
              "null"
            ],
            "description": "Estimated points this would add to the overall score. An estimate, not a measurement, useful mainly for deciding what to do first."
          }
        }
      },
      "OptimizationAnalysis": {
        "type": "object",
        "description": "The score and the changes that would raise it.",
        "properties": {
          "summary": {
            "type": [
              "string",
              "null"
            ],
            "description": "A short readable verdict on the page."
          },
          "geo_readiness_level": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "AI-Optimized",
              "AI-Ready",
              "Needs AI Optimization",
              "Not AI-Optimized"
            ],
            "description": "The overall score expressed as a band."
          },
          "content_truncated": {
            "type": "boolean",
            "description": "True when the page was longer than could be analysed in one pass, so the score covers only the part that was."
          },
          "breakdown": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OptimizationBreakdown"
            },
            "description": "The score broken down by aspect."
          },
          "recommendations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OptimizationFix"
            },
            "description": "The changes worth making, highest impact first."
          }
        }
      },
      "Optimization": {
        "type": "object",
        "description": "A page scored for AI search. Fields the list query does not select are OMITTED rather than returned as null, so `draft_md` and `analysis` appear only on a single fetch.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "brand_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "source_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "Whether the content came from a URL or was pasted in."
          },
          "source_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "The page that was analysed, when one was given."
          },
          "geo_score": {
            "type": [
              "number",
              "null"
            ],
            "description": "0-100 overall."
          },
          "draft_md": {
            "type": [
              "string",
              "null"
            ],
            "description": "The content that was analysed, as markdown. Single fetch only."
          },
          "analysis": {
            "description": "Single fetch only.",
            "oneOf": [
              {
                "$ref": "#/components/schemas/OptimizationAnalysis"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "PromptWriteResult": {
        "type": "object",
        "properties": {
          "brand_id": {
            "type": "string",
            "format": "uuid"
          },
          "submitted": {
            "type": "integer"
          },
          "created": {
            "type": "integer"
          },
          "already_tracked": {
            "type": "integer",
            "description": "Prompts in the request that were already being tracked."
          },
          "duplicates_in_request": {
            "type": "integer"
          },
          "invalid": {
            "type": "integer"
          }
        }
      }
    },
    "responses": {
      "E400": {
        "description": "Bad request. `missing_parameter`, `invalid_request`, `invalid_engine` or `invalid_date`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "E401": {
        "description": "`missing_api_key` or `invalid_api_key`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "E402": {
        "description": "`subscription_required`. The workspace has no active or trialing subscription.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "E403": {
        "description": "`plan_not_eligible` or `forbidden`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "E404": {
        "description": "`not_found`. Also returned for records belonging to another workspace.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "E409": {
        "description": "`plan_limit_reached`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "E429": {
        "description": "`rate_limited`, `quota_exceeded`, `endpoint_rate_limited` or `concurrency_limited`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "E500": {
        "description": "`internal_error`. Quote the `X-Request-Id` when reporting it.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
