{
  "openapi": "3.0.3",
  "info": {
    "title": "Glim Public API",
    "version": "1.0.0",
    "description": "HTTP API for scripts, CI, and backends used by the Glim MCP server. Hosted remote MCP and local stdio clients both call this surface. Authenticate with `Authorization: Bearer` using a Supabase session JWT, a user API key (`sgpt_...`), or an OAuth-managed connector key issued by hosted MCP (same header; those keys are hidden from the Settings UI). API key management (`/v1/keys`) requires a JWT. Base path is `/v1` under the deployed host. Versioning and deprecation policy: https://tryglim.ai/api-versioning-policy.md",
    "contact": {
      "email": "miguel@tryglim.ai"
    }
  },
  "externalDocs": {
    "description": "API versioning and deprecation policy",
    "url": "https://tryglim.ai/api-versioning-policy.md"
  },
  "x-versioning-policy": {
    "strategy": "URL major version",
    "current": "v1",
    "deprecation": "RFC 9745 Deprecation and deprecation Link headers",
    "sunset": "RFC 8594 Sunset header when retirement is scheduled",
    "notice": "12 months targeted",
    "url": "https://tryglim.ai/api-versioning-policy.md"
  },
  "servers": [{ "url": "https://tryglim.ai/api", "description": "Production (Netlify rewrite to Supabase Edge Function)" }],
  "tags": [
    { "name": "Projects", "description": "User projects" },
    { "name": "Analyze", "description": "Visibility runs" },
    { "name": "Content", "description": "Generated content" },
    { "name": "Readiness", "description": "Website LLM readiness" },
    { "name": "Prompts", "description": "Prompt suggestions" },
    { "name": "Reports", "description": "Reports and latest run" },
    { "name": "Agent Tools", "description": "Agent-safe visibility and agency helper routes" },
    { "name": "Keys", "description": "User API keys (JWT only)" }
  ],
  "paths": {
    "/v1/projects": {
      "get": {
        "operationId": "listProjects",
        "summary": "List projects",
        "tags": ["Projects"],
        "security": [{ "bearerAuth": [] }],
        "responses": {
          "200": {
            "description": "Projects for the authenticated user",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ProjectListResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/client-workspaces": {
      "get": {
        "operationId": "listClientWorkspaces",
        "summary": "List agency client workspaces",
        "tags": ["Agent Tools"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "includePitchWorkspaces", "in": "query", "schema": { "type": "boolean" } }
        ],
        "responses": {
          "200": { "description": "Client workspaces", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericEnvelope" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/account-status": {
      "get": {
        "operationId": "getAccountStatus",
        "summary": "Get account and quota status",
        "tags": ["Agent Tools"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "query", "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": { "description": "Account status", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericEnvelope" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/visibility-trends": {
      "get": {
        "operationId": "getVisibilityTrends",
        "summary": "Get visibility trends",
        "tags": ["Agent Tools"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "timeframe", "in": "query", "schema": { "type": "string", "enum": ["7d", "30d", "90d", "all"] } }
        ],
        "responses": {
          "200": { "description": "Visibility trends", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericEnvelope" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/competitors/{competitor}": {
      "get": {
        "operationId": "getCompetitorDetail",
        "summary": "Get compact competitor detail",
        "tags": ["Agent Tools"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "competitor", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "provider", "in": "query", "schema": { "type": "string", "enum": ["all", "openai", "claude", "perplexity", "gemini"] } },
          { "name": "timeframe", "in": "query", "schema": { "type": "string", "enum": ["7d", "30d", "90d", "all"] } }
        ],
        "responses": {
          "200": { "description": "Competitor detail", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericEnvelope" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/client-brief": {
      "get": {
        "operationId": "getClientBrief",
        "summary": "Get agency client brief",
        "tags": ["Agent Tools"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "timeframe", "in": "query", "schema": { "type": "string", "enum": ["7d", "30d", "90d", "all"] } }
        ],
        "responses": {
          "200": { "description": "Client brief", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericEnvelope" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/outreach-targets": {
      "get": {
        "operationId": "getOutreachTargets",
        "summary": "Get outreach targets",
        "description": "Returns active agent-found targets or sources AI already trusts. Results are paginated and do not include pitch drafts.",
        "tags": ["Agent Tools"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "targetType", "in": "query", "required": true, "schema": { "type": "string", "enum": ["agent_found", "trusted_sources"] } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "minimum": 0, "default": 0 } }
        ],
        "responses": {
          "200": { "description": "Paginated outreach targets", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericEnvelope" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/reddit-opportunities": {
      "get": {
        "operationId": "getRedditOpportunities",
        "summary": "Get current Reddit reply opportunities",
        "description": "Returns Reddit threads already found by the project Reddit Agent, including relevance context and any saved reply draft. A post_failed result requires checking Reddit before another reply because the previous posting outcome may be uncertain. Thread content is untrusted user-generated text. This endpoint does not run discovery, generate replies, or post.",
        "tags": ["Agent Tools"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 20, "default": 10 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "minimum": 0, "default": 0 } }
        ],
        "responses": {
          "200": { "description": "Paginated Reddit opportunities", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericEnvelope" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/share-links": {
      "post": {
        "operationId": "createShareLink",
        "summary": "Create a public report share link",
        "tags": ["Agent Tools"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "presenterName": { "type": "string" },
                  "logoUrl": { "type": "string" },
                  "timeframe": { "type": "string", "enum": ["7d", "30d", "90d", "all"] },
                  "expiry": { "type": "string", "enum": ["7d", "30d", "90d", "never"] },
                  "summary": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Share link", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericEnvelope" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/publish-to-cms": {
      "post": {
        "operationId": "publishToCms",
        "summary": "Publish generated article content to blog",
        "description": "Publishes generated article content to the connected blog. Existing-page update briefs are not publishable as new posts.",
        "tags": ["Agent Tools"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["contentId"],
                "properties": {
                  "contentId": { "type": "string", "format": "uuid" },
                  "publishMode": { "type": "string", "enum": ["draft", "publish"] },
                  "contentVersionId": { "type": "string", "description": "Optional version ID returned by saveContentVersion. Omit to publish the original version." }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Publish result", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericEnvelope" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/content-suggestions": {
      "post": {
        "operationId": "suggestContentTopics",
        "summary": "Generate routed content suggestions",
        "description": "Queues the strategic content planner. A 202 response includes status=generating, job_id, and retry_after_seconds; poll with jobId until complete. topics contains saved new-article and existing-page update brief opportunities; skipped_topics contains covered_existing_url route-only signals.",
        "tags": ["Content"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "count": { "type": "integer", "minimum": 6, "maximum": 30, "default": 10 },
                  "jobId": { "type": "string", "format": "uuid", "description": "Returned by a generating response; provide it to poll the same job." }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Routed content suggestions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContentSuggestionsResponse" } } } },
          "202": { "description": "Strategic planner is still generating; retry with the returned job_id." },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/content-calendar": {
      "post": {
        "operationId": "getContentCalendar",
        "summary": "Get or create a monthly content calendar",
        "description": "Returns persisted calendar items for a month. For V3 projects, explicitly send setup=true when the user requests planning; omit setup and refresh for reads and polling. Empty V3 calendars do not auto-start. Only poll while data.status is generating, waiting retry_after_seconds. Respect planning_outcome restrictions. Use refresh=true to explicitly retry or continue an existing V3 strategy, not initial setup. Legacy empty eligible months retain automatic generation. Planning does not generate articles or enable publishing.",
        "tags": ["Content"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "planningMonth": {
                    "type": "string",
                    "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
                    "description": "Calendar month in YYYY-MM format; defaults to the current UTC month."
                  },
                  "setup": {
                    "type": "boolean",
                    "default": false,
                    "description": "Explicitly build a V3 calendar, including when seed recommendations already exist. Omit for retrieval or polling. Does not generate articles or enable publishing."
                  },
                  "previewSetup": {
                    "type": "boolean",
                    "description": "Return a read-only setup preview with period, affected topics and revision."
                  },
                  "existingRecommendations": {
                    "type": "string",
                    "enum": ["keep", "replace_untouched"],
                    "default": "keep",
                    "description": "Replacement requires explicit user confirmation of the preview, setup=true and previewRevision."
                  },
                  "previewRevision": {
                    "type": "string",
                    "description": "Revision from the setup preview confirmed by the user."
                  },
                  "refresh": {
                    "type": "boolean",
                    "default": false,
                    "description": "Retry generation only when the selected current or future month previously completed with zero items."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Existing, created, empty, or not-ready calendar", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContentCalendarResponse" } } } },
          "202": { "description": "Calendar generation queued or processing", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ContentCalendarResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/content-library": {
      "get": {
        "operationId": "listContentLibrary",
        "summary": "List completed project articles",
        "description": "Lists completed generated blog articles and automatic Content Calendar articles without returning their full bodies. Automatic articles remain available when CMS delivery failed.",
        "tags": ["Content"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "limit", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } },
          { "name": "offset", "in": "query", "required": false, "description": "Zero-based result offset. Use next_offset from the previous response; deep offset pagination is capped at 10,000.", "schema": { "type": "integer", "minimum": 0, "maximum": 10000, "default": 0 } }
        ],
        "responses": {
          "200": { "description": "Paginated article library", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericEnvelope" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/content-library/{contentId}": {
      "get": {
        "operationId": "getContent",
        "summary": "Retrieve a complete project article",
        "description": "Returns a selected generated-content version as Markdown or an automatic Content Calendar article as HTML.",
        "tags": ["Content"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "contentId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "source", "in": "query", "required": true, "schema": { "type": "string", "enum": ["generated_content", "automated_post"] } },
          { "name": "versionId", "in": "query", "required": false, "description": "Generated content only. Omit for the latest saved version, use original for the original draft, or provide a saved version ID.", "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Complete article body and metadata", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenericEnvelope" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/custom-prompts": {
      "post": {
        "operationId": "addCustomPrompts",
        "summary": "Add custom prompts to an onboarded project",
        "description": "Adds prompts to the latest saved visibility test. This route never creates the first project or bypasses onboarding. Set testNow=true to queue background testing of only the added prompts.",
        "tags": ["Prompts"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["prompts"],
                "properties": {
                  "prompts": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 100,
                    "items": {
                      "type": "object",
                      "required": ["prompt"],
                      "properties": {
                        "prompt": { "type": "string", "minLength": 1, "maxLength": 500 },
                        "category": { "type": "string", "maxLength": 100, "default": "CUSTOM" }
                      }
                    }
                  },
                  "testNow": { "type": "boolean", "default": false }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Prompts saved" },
          "202": { "description": "Prompts saved and background testing queued" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/competitor-classification": {
      "post": {
        "operationId": "setCompetitorClassification",
        "summary": "Classify a detected company",
        "description": "Reversibly marks a company as a competitor or not a competitor without deleting visibility evidence.",
        "tags": ["Agent Tools"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["competitor", "classification"],
                "properties": {
                  "competitor": { "type": "string", "minLength": 1, "maxLength": 255 },
                  "classification": { "type": "string", "enum": ["competitor", "not_competitor"] }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Classification saved" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/projects/{projectId}/content-versions": {
      "post": {
        "operationId": "saveContentVersion",
        "summary": "Save agent-edited content as a new version",
        "description": "Appends complete edited content to an existing generated blog's version history. No AI model is called and the original draft remains unchanged.",
        "tags": ["Content"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["contentId", "content"],
                "properties": {
                  "contentId": { "type": "string", "format": "uuid" },
                  "content": { "type": "string", "minLength": 1, "maxLength": 200000 },
                  "sourceVersionId": { "type": "string", "maxLength": 100 },
                  "changeSummary": { "type": "string", "maxLength": 1000 }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Identical latest version already exists" },
          "201": { "description": "New content version saved" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/analyze/project": {
      "post": {
        "operationId": "analyzeProject",
        "summary": "Run visibility using saved project prompts",
        "tags": ["Analyze"],
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["projectId"],
                "properties": {
                  "projectId": { "type": "string", "format": "uuid", "description": "Project UUID" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Analysis completed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnalyzeResponse" } } } },
          "202": { "description": "Analysis accepted (async)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnalyzeResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/analyze/custom": {
      "post": {
        "operationId": "analyzeCustom",
        "summary": "Run custom visibility (ad hoc prompts)",
        "tags": ["Analyze"],
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["projectId", "brand", "prompts"],
                "properties": {
                  "projectId": { "type": "string", "format": "uuid" },
                  "brand": { "type": "string" },
                  "prompts": {
                    "type": "array",
                    "items": {
                      "oneOf": [{ "type": "string" }, { "type": "object", "properties": { "prompt": { "type": "string" } }, "required": ["prompt"] }]
                    },
                    "maxItems": 20
                  },
                  "competitors": { "type": "array", "items": { "type": "string" } }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Custom analysis completed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnalyzeCustomResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/analyze": {
      "post": {
        "operationId": "analyzeDispatcher",
        "summary": "Analyze (legacy dispatcher)",
        "description": "Supports testId, projectId+brand+prompts (custom), legacy crawl fields, or projectId alone (same as /analyze/project). Prefer /v1/analyze/project or /v1/analyze/custom.",
        "tags": ["Analyze"],
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "type": "object", "additionalProperties": true }
            }
          }
        },
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnalyzeResponse" } } } },
          "202": { "description": "Accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AnalyzeResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/generate-content": {
      "post": {
        "operationId": "generateContent",
        "summary": "Generate content or update briefs (blog, LinkedIn, thread)",
        "description": "Generates content from project context. For expand-vs-new decisions, call /v1/projects/{projectId}/content-suggestions first and pass its metadata into this route. Direct generation does not decide whether an existing owned page should be expanded.",
        "tags": ["Content"],
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["projectId", "platform", "topic"],
                "properties": {
                  "projectId": { "type": "string", "format": "uuid" },
                  "platform": { "type": "string", "enum": ["blog", "linkedin", "thread"] },
                  "topic": { "type": "string" },
                  "metadata": {
                    "type": "object",
                    "description": "Optional user-facing generation metadata. Unknown keys are ignored. For existing-page update briefs, include content_action=optimize_existing_url plus target_url and target_title from content-suggestions.",
                    "additionalProperties": true,
                    "properties": {
                      "audience": { "type": "string" },
                      "type": { "type": "string" },
                      "length": { "type": "string" },
                      "content_action": { "type": "string", "enum": ["new_article", "optimize_existing_url"] },
                      "contentAction": { "type": "string", "enum": ["new_article", "optimize_existing_url"] },
                      "target_url": { "type": "string", "nullable": true },
                      "targetUrl": { "type": "string", "nullable": true },
                      "target_title": { "type": "string", "nullable": true },
                      "targetTitle": { "type": "string", "nullable": true },
                      "content_type": { "type": "string", "nullable": true },
                      "contentType": { "type": "string", "nullable": true }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GenerateContentResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/readiness": {
      "post": {
        "operationId": "checkReadiness",
        "summary": "Run website LLM readiness check",
        "tags": ["Readiness"],
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["projectId"],
                "properties": {
                  "projectId": { "type": "string", "format": "uuid" },
                  "forceRerun": { "type": "boolean" },
                  "url": { "type": "string", "description": "Optional URL override" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReadinessResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/suggest-prompts": {
      "post": {
        "operationId": "suggestPrompts",
        "summary": "Suggest prompts from crawl record",
        "tags": ["Prompts"],
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["crawlRecordId"],
                "properties": {
                  "crawlRecordId": { "type": "string", "format": "uuid" },
                  "projectId": { "type": "string", "format": "uuid" },
                  "brand": { "type": "string" },
                  "competitors": { "type": "array", "items": { "type": "string" } }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuggestPromptsResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/reports/latest": {
      "get": {
        "operationId": "getLatestReport",
        "summary": "Get latest report for a project",
        "tags": ["Reports"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "projectId", "in": "query", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "name": "source", "in": "query", "required": false, "schema": { "type": "string", "enum": ["main", "custom"], "default": "main" } },
          { "name": "view", "in": "query", "required": false, "schema": { "type": "string", "enum": ["raw", "summary", "full"], "default": "summary" } },
          { "name": "answer_detail", "in": "query", "required": false, "schema": { "type": "string", "enum": ["summary", "full"], "default": "full" }, "description": "For full/raw, summary strips verbatim provider answers from rows and answer-gap cards." },
          { "name": "raw_results_limit", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0, "maximum": 100 }, "description": "Full/raw page size. Default 10 with answer_detail=summary on full or an explicit offset. Legacy 0 suppresses rows without pagination metadata." },
          { "name": "raw_results_offset", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "description": "Stored-result offset for full/raw, default 0. Nonzero offsets require /reports/:reportId using pagination.report_id; latest only accepts 0. Follow pagination.next until has_more=false." }
        ],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReportResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/reports/{reportId}": {
      "get": {
        "operationId": "getReportById",
        "summary": "Get report by id",
        "tags": ["Reports"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "reportId", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "view", "in": "query", "required": false, "schema": { "type": "string", "enum": ["raw", "summary", "full"], "default": "summary" } },
          { "name": "answer_detail", "in": "query", "required": false, "schema": { "type": "string", "enum": ["summary", "full"], "default": "full" }, "description": "For full/raw, summary strips verbatim provider answers from rows and answer-gap cards." },
          { "name": "raw_results_limit", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0, "maximum": 100 }, "description": "Full/raw page size. Default 10 with answer_detail=summary on full or an explicit offset. Legacy 0 suppresses rows without pagination metadata." },
          { "name": "raw_results_offset", "in": "query", "required": false, "schema": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "description": "Stored-result offset for full/raw, default 0. Nonzero offsets require /reports/:reportId using pagination.report_id; latest only accepts 0. Follow pagination.next until has_more=false." }
        ],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReportResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/reports/{reportId}/provider-answer": {
      "get": {
        "operationId": "getProviderAnswer",
        "summary": "Get one provider answer for a report prompt",
        "description": "Returns one stored provider answer with capped citations. Provider keys include the six current engines plus historical Claude records. Historical keys do not enable new collection. Execution models and provider metadata are not returned. The hosted MCP get_provider_answer tool currently exposes a narrower provider enum.",
        "tags": ["Reports"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [
          { "name": "reportId", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "promptKey", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "provider", "in": "query", "required": true, "schema": { "type": "string", "enum": ["openai", "claude", "perplexity", "gemini", "google_ai_mode", "google_ai_overviews", "copilot"] } }
        ],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProviderAnswerResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/keys": {
      "post": {
        "operationId": "createApiKey",
        "summary": "Create API key (JWT only)",
        "tags": ["Keys"],
        "security": [{ "bearerAuth": [] }],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": { "type": "string" },
                  "expires_at": { "type": "string", "format": "date-time", "nullable": true }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateKeyResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      },
      "get": {
        "operationId": "listApiKeys",
        "summary": "List API keys (JWT only)",
        "tags": ["Keys"],
        "security": [{ "bearerAuth": [] }],
        "responses": {
          "200": { "description": "OK", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KeyListResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    },
    "/v1/keys/{keyId}": {
      "delete": {
        "operationId": "deleteApiKey",
        "summary": "Delete API key (JWT only)",
        "tags": ["Keys"],
        "security": [{ "bearerAuth": [] }],
        "parameters": [{ "name": "keyId", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": {
          "200": { "description": "Deleted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DeleteKeyResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/InternalError" },
          "default": { "$ref": "#/components/responses/UnexpectedError" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT or API key",
        "description": "Send `Authorization: Bearer <token>`. Use a Supabase access token or a user API key (`sgpt_...`). Key management routes require a JWT."
      }
    },
    "schemas": {
      "ApiError": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message", "request_id"],
            "properties": {
              "code": { "type": "string", "example": "RATE_LIMITED" },
              "message": { "type": "string" },
              "details": { "nullable": true },
              "request_id": { "type": "string", "format": "uuid" }
            }
          }
        }
      },
      "BillingMeta": {
        "type": "object",
        "description": "Plan and quota snapshot (shape may evolve)",
        "additionalProperties": true
      },
      "GenericEnvelope": {
        "type": "object",
        "properties": {
          "data": { "type": "object", "additionalProperties": true },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "ProjectListResponse": {
        "type": "object",
        "properties": {
          "data": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "AnalyzeResponse": {
        "type": "object",
        "properties": {
          "data": { "type": "object", "additionalProperties": true },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "AnalyzeCustomResponse": {
        "type": "object",
        "properties": {
          "data": { "type": "object", "additionalProperties": true },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "GenerateContentResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "id": { "type": "string", "nullable": true },
              "status": { "type": "string" },
              "message": { "type": "string" },
              "report_url": { "type": "string", "nullable": true },
              "context_quality": { "type": "string", "enum": ["full", "limited"] },
              "context_message": { "type": "string" }
            }
          },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "ContentSuggestionTopic": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "nullable": true },
          "title": { "type": "string" },
          "type": { "type": "string", "nullable": true },
          "audience": { "type": "string", "nullable": true },
          "source": { "type": "string", "nullable": true },
          "reason": { "type": "string", "nullable": true },
          "content_action": { "type": "string", "enum": ["new_article", "optimize_existing_url", "covered_existing_url"] },
          "target_url": { "type": "string", "nullable": true },
          "target_title": { "type": "string", "nullable": true },
          "coverage_status": { "type": "string", "nullable": true },
          "match_reason": { "type": "string", "nullable": true },
          "created_at": { "type": "string", "nullable": true },
          "persisted": {
            "type": "boolean",
            "description": "True when this suggestion was saved as a generated topic. covered_existing_url items are route-only skip signals and return false."
          },
          "position": { "type": "integer" }
        }
      },
      "ContentSuggestionsResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "status": { "type": "string" },
              "project_id": { "type": "string", "format": "uuid" },
              "topic_count": { "type": "integer" },
              "created_count": { "type": "integer" },
              "skipped_count": {
                "type": "integer",
                "description": "Number of returned covered_existing_url route-only skip signals."
              },
              "href": { "type": "string" },
              "titles": { "type": "array", "items": { "type": "string" } },
              "topics": {
                "type": "array",
                "items": { "$ref": "#/components/schemas/ContentSuggestionTopic" }
              },
              "skipped_topics": {
                "type": "array",
                "description": "Route-only covered_existing_url items that should be skipped instead of generated.",
                "items": { "$ref": "#/components/schemas/ContentSuggestionTopic" }
              }
            }
          },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "ContentCalendarItem": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid", "nullable": true },
          "title": { "type": "string" },
          "topic_line": { "type": "string", "nullable": true },
          "content_type": { "type": "string" },
          "source": { "type": "string" },
          "source_label": { "type": "string", "nullable": true },
          "status": { "type": "string" },
          "scheduled_for": { "type": "string", "format": "date", "nullable": true },
          "why_scheduled": { "type": "string", "nullable": true },
          "signal": { "type": "string", "nullable": true },
          "priority": { "type": "number", "nullable": true },
          "score": { "type": "number", "nullable": true },
          "content_action": { "type": "string" },
          "target_url": { "type": "string", "nullable": true },
          "generated_content_id": { "type": "string", "format": "uuid", "nullable": true },
          "automated_post_id": { "type": "string", "format": "uuid", "nullable": true }
        }
      },
      "ContentCalendarResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "project_id": { "type": "string", "format": "uuid" },
              "planning_month": { "type": "string" },
              "calendar_url": { "type": "string" },
              "status": { "type": "string", "enum": ["existing", "generating", "created", "empty", "not_ready"] },
              "created": { "type": "boolean", "description": "True for only one successful read after a generation attempt creates items." },
              "count": { "type": "integer" },
              "items": { "type": "array", "items": { "$ref": "#/components/schemas/ContentCalendarItem" } },
              "job_id": { "type": "string", "format": "uuid" },
              "message": { "type": "string" },
              "retry_after_seconds": { "type": "integer" },
              "missing_inputs": { "type": "array", "items": { "type": "string", "enum": ["crawl", "visibility"] } }
            }
          },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "ReadinessResponse": {
        "type": "object",
        "properties": {
          "data": { "type": "object", "additionalProperties": true },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "SuggestPromptsResponse": {
        "type": "object",
        "properties": {
          "data": { "type": "object", "additionalProperties": true },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "ReportResponse": {
        "type": "object",
        "properties": {
          "data": { "type": "object", "additionalProperties": true },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "ProviderAnswerResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "report_id": { "type": "string" },
              "prompt": { "type": "string" },
              "category": { "type": "string" },
              "provider": { "type": "string", "enum": ["openai", "claude", "perplexity", "gemini", "google_ai_mode", "google_ai_overviews", "copilot"] },
              "answer": { "type": "string" },
              "citations": { "type": "array", "items": { "type": "object", "additionalProperties": true } }
            }
          },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "CreateKeyResponse": {
        "type": "object",
        "properties": {
          "data": { "type": "object", "additionalProperties": true },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "KeyListResponse": {
        "type": "object",
        "properties": {
          "data": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      },
      "DeleteKeyResponse": {
        "type": "object",
        "properties": {
          "data": { "type": "object", "additionalProperties": true },
          "meta": { "$ref": "#/components/schemas/BillingMeta" }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid input",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "Unauthorized": {
        "description": "Missing or invalid bearer token",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "PaymentRequired": {
        "description": "Billing or quota gate",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "Forbidden": {
        "description": "Feature not available for this account or project",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "Conflict": {
        "description": "Conflict (e.g. API key limit)",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "RateLimited": {
        "description": "Rate limit exceeded. Responses include the current IETF `RateLimit` and `RateLimit-Policy` structured fields, compatibility `RateLimit-*` and `X-RateLimit-*` fields, and `Retry-After` on 429.",
        "headers": {
          "Retry-After": { "schema": { "type": "string" }, "description": "Seconds until reset" },
          "RateLimit": { "schema": { "type": "string" }, "description": "Current service limit, for example `\"default\";r=0;t=30`" },
          "RateLimit-Policy": { "schema": { "type": "string" }, "description": "Evaluated quota policy, for example `\"default\";q=10;w=60`" },
          "RateLimit-Limit": { "schema": { "type": "string" }, "description": "Maximum requests in the evaluated window" },
          "RateLimit-Remaining": { "schema": { "type": "string" }, "description": "Requests remaining in the evaluated window" },
          "RateLimit-Reset": { "schema": { "type": "string" }, "description": "Seconds until reset" },
          "X-RateLimit-Limit": { "schema": { "type": "string" } },
          "X-RateLimit-Remaining": { "schema": { "type": "string" } },
          "X-RateLimit-Reset": { "schema": { "type": "string" }, "description": "Unix timestamp" }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "InternalError": {
        "description": "Server error",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      },
      "UnexpectedError": {
        "description": "Any documented or unexpected 4xx or 5xx response uses the same typed error envelope.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiError" } } }
      }
    }
  }
}
