{
  "openapi": "3.0.3",
  "info": {
    "title": "SEOforGPT Public API",
    "version": "1.0.0",
    "description": "HTTP API for scripts, CI, and backends used by the SEOforGPT 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://www.seoforgpt.io/api-versioning-policy.md",
    "contact": {
      "email": "hey@seoforgpt.io"
    }
  },
  "externalDocs": {
    "description": "API versioning and deprecation policy",
    "url": "https://www.seoforgpt.io/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://www.seoforgpt.io/api-versioning-policy.md"
  },
  "servers": [{ "url": "https://www.seoforgpt.io/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", "google_ai_mode", "google_ai_overviews", "copilot"] } },
          { "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 provider answer with capped citations. The provider name identifies the measured platform; execution models and provider metadata are not returned.",
        "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" } } }
      }
    }
  }
}
