openapi: 3.1.0
info:
  title: AgentKit API
  version: 1.0.0
  description: |
    API for AgentKit services. Provides access to AI-powered video processing via VidCap.xyz
    and SEO auditing via ReviewWeb.site through secure proxy endpoints.

    ## Authentication
    All proxy endpoints require a valid AgentKit API key passed in the `X-API-Key` header.
    API keys can be created and managed via the dashboard or the `/api/keys` endpoints.

    ## Rate Limiting
    API requests are rate-limited based on your subscription plan.
    Rate limit headers are included in all responses.
  contact:
    name: AgentKit Support
    email: support@agentkit.best
    url: https://agentkit.best
  license:
    name: Proprietary
    url: https://agentkit.best/terms

servers:
  - url: https://agentkit.best/api
    description: Production
  - url: http://localhost:3000/api
    description: Local Development

security:
  - ApiKeyAuth: []

tags:
  - name: API Keys
    description: Manage your API keys
  - name: Referrals
    description: Referral dashboard and conversion list endpoints
  - name: AgentKit Feedback
    description: AgentKit client feedback intake
  - name: Build with AK - Listings
    description: Customer product showcase listing and draft revision management endpoints
  - name: Build with AK - Media
    description: Media upload intent authorization and finalization for product listings
  - name: Build with AK - Directory
    description: Public customer product directory, community upvotes, and outbound engagement telemetry
  - name: VidCap - Health
    description: VidCap service health and model endpoints
  - name: VidCap - YouTube
    description: YouTube video processing endpoints
  - name: VidCap - Video
    description: Video retrieval and processing
  - name: ReviewWeb - Health
    description: ReviewWeb service health and profile endpoints
  - name: ReviewWeb - Screenshots
    description: Webpage screenshot capture
  - name: ReviewWeb - Reviews
    description: Website review endpoints
  - name: ReviewWeb - Scraping
    description: Web scraping and link extraction
  - name: ReviewWeb - Extraction
    description: Content extraction from URLs
  - name: ReviewWeb - Conversion
    description: URL to markdown conversion
  - name: ReviewWeb - Summarization
    description: AI-powered URL and website summarization
  - name: ReviewWeb - SEO
    description: SEO insights and analysis

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: AgentKit API key (ck_...)
    BearerAuth:
      type: http
      scheme: bearer
      description: Session token for dashboard endpoints
    BuildWithAkDelegatedAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: "API-audience OAuth token issued through confidential hosted MCP token exchange. MCP-audience tokens are not accepted directly. Reads require build-with-ak:read; mutations require build-with-ak:write."
    CookieAuth:
      type: apiKey
      in: cookie
      name: session
      description: Authenticated dashboard session cookie

  schemas:
    BuildWithAkAnalyticsCounts:
      type: object
      additionalProperties: false
      required:
        - views
        - outboundClicks
        - referralConversions
      properties:
        views:
          type: integer
          minimum: 0
          description: Views deduplicated per listing, IP + user-agent fingerprint and UTC day; not monthly unique users.
        outboundClicks:
          type: integer
          minimum: 0
          description: Outbound redirect requests including repeats, not unique visitors.
        referralConversions:
          type: 'null'
          description: Unavailable; conversion tracking has no production writer. Do not infer conversion rates or external product sales.
    BuildWithAkAnalyticsResponse:
      type: object
      additionalProperties: false
      required:
        - listing
        - period
        - source
        - generatedAt
        - totals
        - daily
      properties:
        listing:
          type: object
          required:
            - id
            - slug
          properties:
            id:
              type: string
              format: uuid
            slug:
              type: string
        period:
          type: object
          required:
            - from
            - to
            - timezone
          properties:
            from:
              type: string
              format: date
            to:
              type: string
              format: date
            timezone:
              type: string
              enum:
                - UTC
        source:
          type: string
          enum:
            - first_party_postgresql
        generatedAt:
          type: string
          format: date-time
        totals:
          $ref: '#/components/schemas/BuildWithAkAnalyticsCounts'
        daily:
          type: array
          minItems: 1
          maxItems: 366
          description: Ascending inclusive UTC calendar dates, zero-filled when no events are recorded.
          items:
            type: object
            additionalProperties: false
            required:
              - date
              - views
              - outboundClicks
              - referralConversions
            properties:
              date:
                type: string
                format: date
              views:
                type: integer
                minimum: 0
                description: Views deduplicated per listing, IP + user-agent fingerprint and UTC day; not monthly unique users.
              outboundClicks:
                type: integer
                minimum: 0
                description: Outbound redirect requests including repeats, not unique visitors.
              referralConversions:
                type: 'null'
                description: Unavailable; conversion tracking has no production writer. Do not infer conversion rates or external product sales.
    Error:
      type: object
      properties:
        error:
          type: string
          description: Error message
        code:
          type: string
          description: Error code for programmatic handling
      required:
        - error

    FeedbackRequest:
      type: object
      required:
        - schemaVersion
        - type
        - title
        - renderedBody
        - fields
      properties:
        schemaVersion:
          type: integer
          const: 1
        type:
          type: string
          enum: [bug, feature, enhancement]
        title:
          type: string
          minLength: 1
          maxLength: 160
        renderedBody:
          type: string
          maxLength: 60000
          description: Client-rendered preview body. Server redacts but does not re-render.
        fields:
          type: object
          additionalProperties:
            type: string
          properties:
            area:
              type: string
              maxLength: 120
            body:
              type: string
              maxLength: 20000
            repro:
              type: string
              maxLength: 20000
            expected:
              type: string
              maxLength: 20000
            actual:
              type: string
              maxLength: 20000
            outcome:
              type: string
              maxLength: 20000
            motivation:
              type: string
              maxLength: 20000
        clientVersion:
          type: string
          maxLength: 160
        platform:
          type: string
          maxLength: 160
        diagnosticsSummary:
          type: string
          maxLength: 40000

    FeedbackResponse:
      type: object
      required:
        - schemaVersion
        - issueUrl
        - issueNumber
      properties:
        schemaVersion:
          type: integer
          const: 1
        issueUrl:
          type: string
          format: uri
        issueNumber:
          type: integer

    FeedbackError:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          enum: [not_live, unauthorized, rate_limited, invalid, network]
        message:
          type: string
        retryAfter:
          type: string
          description: Seconds until retry, present for rate_limited responses.

    ApiKey:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          maxLength: 100
        prefix:
          type: string
          description: Key prefix for identification (e.g., ck_abc123)
        keyPreview:
          type: string
          description: Masked key preview (e.g., ck_abc123...****)
        rateLimit:
          type: integer
          description: Requests per minute
        isActive:
          type: boolean
        lastUsedAt:
          type:
            - string
            - 'null'
          format: date-time
        usageCount:
          type: integer
        expiresAt:
          type:
            - string
            - 'null'
          format: date-time
        createdAt:
          type: string
          format: date-time

    ApiKeyCreate:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 100
          description: Friendly name for the API key
      required:
        - name

    ApiKeyCreated:
      type: object
      properties:
        id:
          type: string
          format: uuid
        key:
          type: string
          description: Full API key (only shown once on creation)
        name:
          type: string
        message:
          type: string

    ValidationResult:
      type: object
      properties:
        valid:
          type: boolean
        userId:
          type: string
          format: uuid
        rateLimit:
          type: integer
        isActive:
          type: boolean

    ReferralReferee:
      type: object
      properties:
        commissionId:
          type: string
          format: uuid
        status:
          type: string
          enum: [pending, approved, paid, cancelled]
        amount:
          type: integer
          description: Commission amount in cents
        currency:
          type: string
          example: USD
        createdAt:
          type: string
          format: date-time
        approvedAt:
          type:
            - string
            - 'null'
          format: date-time
        paidAt:
          type:
            - string
            - 'null'
          format: date-time
        order:
          type: object
          properties:
            displayId:
              type: string
              description: Non-authoritative display prefix, not the full order UUID
            productType:
              type:
                - string
                - 'null'
            status:
              type:
                - string
                - 'null'
              description: Opaque order state for display
        referee:
          type: object
          properties:
            name:
              type:
                - string
                - 'null'
            avatarUrl:
              type:
                - string
                - 'null'
              format: uri
            emailMasked:
              type:
                - string
                - 'null'
              description: Masked email only; raw email is never returned
      required:
        - commissionId
        - status
        - amount
        - currency
        - createdAt
        - order
        - referee

    ReferralRefereesResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/ReferralReferee'
        pagination:
          type: object
          properties:
            limit:
              type: integer
            offset:
              type: integer
            total:
              type: integer
            hasMore:
              type: boolean
          required: [limit, offset, total, hasMore]
        filters:
          type: object
          properties:
            status:
              type: string
              enum: [all, pending, approved, paid, cancelled]
            sort:
              type: string
              enum: [createdAt, amount, status]
            order:
              type: string
              enum: [asc, desc]
            orderRef:
              type: string
              description: Optional exact order display id used to filter rows
          required: [status, sort, order]
      required:
        - data
        - pagination
        - filters

    YouTubeInfo:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
        description:
          type: string
        channel:
          type: string
        duration:
          type: integer
        thumbnail:
          type: string
          format: uri

    YouTubeMedia:
      type: object
      properties:
        formats:
          type: array
          items:
            type: object
            properties:
              itag:
                type: integer
              quality:
                type: string
              mimeType:
                type: string
              contentLength:
                type: string

    YouTubeSummary:
      type: object
      properties:
        summary:
          type: string
        keyPoints:
          type: array
          items:
            type: string
        duration:
          type: integer

    YouTubeArticle:
      type: object
      properties:
        title:
          type: string
        content:
          type: string
          description: Markdown formatted article content
        wordCount:
          type: integer

    YouTubeCaption:
      type: object
      properties:
        captions:
          type: array
          items:
            type: object
            properties:
              text:
                type: string
              start:
                type: number
              duration:
                type: number
        language:
          type: string

    YouTubeScreenshot:
      type: object
      properties:
        url:
          type: string
          format: uri
        timestamp:
          type: number

    YouTubeComments:
      type: object
      properties:
        comments:
          type: array
          items:
            type: object
            properties:
              author:
                type: string
              text:
                type: string
              likes:
                type: integer
              publishedAt:
                type: string
        nextPageToken:
          type:
            - string
            - 'null'

    YouTubeSearchResult:
      type: object
      properties:
        results:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              title:
                type: string
              channel:
                type: string
              thumbnail:
                type: string
        nextPageToken:
          type:
            - string
            - 'null'

    HealthCheck:
      type: object
      properties:
        status:
          type: string
          enum: [ok, degraded, down]
        version:
          type: string

    AIModels:
      type: object
      properties:
        models:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              description:
                type: string

    # ReviewWeb Schemas
    ReviewWebProfile:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
        credits:
          type: integer
        plan:
          type: string

    ReviewWebScreenshot:
      type: object
      properties:
        id:
          type: string
        url:
          type: string
          format: uri
        imageUrl:
          type: string
          format: uri
        width:
          type: integer
        height:
          type: integer
        createdAt:
          type: string
          format: date-time

    ReviewWebReview:
      type: object
      properties:
        id:
          type: string
        url:
          type: string
          format: uri
        status:
          type: string
          enum: [pending, processing, completed, failed]
        results:
          type: object
        createdAt:
          type: string
          format: date-time

    ScrapeResult:
      type: object
      properties:
        url:
          type: string
          format: uri
        title:
          type: string
        content:
          type: string
        html:
          type: string
        metadata:
          type: object

    ExtractResult:
      type: object
      properties:
        url:
          type: string
          format: uri
        title:
          type: string
        content:
          type: string
        author:
          type:
            - string
            - 'null'
        publishedDate:
          type:
            - string
            - 'null'

    MarkdownResult:
      type: object
      properties:
        url:
          type: string
          format: uri
        markdown:
          type: string
        title:
          type: string

    SummarizeResult:
      type: object
      properties:
        url:
          type: string
          format: uri
        summary:
          type: string
        keyPoints:
          type: array
          items:
            type: string

    UrlCheckResult:
      type: object
      properties:
        url:
          type: string
          format: uri
        isAlive:
          type: boolean
        statusCode:
          type: integer
        responseTime:
          type: number

    SeoBacklinks:
      type: object
      properties:
        domain:
          type: string
        totalBacklinks:
          type: integer
        backlinks:
          type: array
          items:
            type: object
            properties:
              sourceUrl:
                type: string
              targetUrl:
                type: string
              anchorText:
                type: string

    SeoKeywordIdeas:
      type: object
      properties:
        keyword:
          type: string
        ideas:
          type: array
          items:
            type: object
            properties:
              keyword:
                type: string
              searchVolume:
                type: integer
              difficulty:
                type: number

    SeoTraffic:
      type: object
      properties:
        domain:
          type: string
        estimatedTraffic:
          type: integer
        topPages:
          type: array
          items:
            type: object
            properties:
              url:
                type: string
              traffic:
                type: integer

    BuildWithAkUploadIntentRequest:
      type: object
      required:
        - kind
        - mimeType
      properties:
        kind:
          type: string
          enum: [logo, cover, screenshot]
          description: Media asset kind
        mimeType:
          type: string
          enum: [image/png, image/jpeg, image/webp]
          description: Image MIME type

    BuildWithAkUploadIntentResponse:
      type: object
      required:
        - intentId
        - presignedUrl
        - stagingKey
        - maxByteSize
        - expiresIn
      properties:
        intentId:
          type: string
          format: uuid
          description: Upload intent identifier
        presignedUrl:
          type: string
          description: Presigned S3/R2 PUT URL for staging upload
        stagingKey:
          type: string
          description: Temporary storage staging key
        maxByteSize:
          type: integer
          description: Maximum allowed payload size in bytes
        expiresIn:
          type: integer
          description: Presigned URL expiration time in seconds

    BuildWithAkMediaFinalizeRequest:
      type: object
      required:
        - stagingKey
        - kind
      properties:
        stagingKey:
          type: string
          description: Temporary staging key returned by upload-intent
        kind:
          type: string
          enum: [logo, cover, screenshot]
          description: Media asset kind

    BuildWithAkMediaFinalizeResponse:
      type: object
      required:
        - success
        - assetId
        - assetUrl
        - mime
        - width
        - height
      properties:
        success:
          type: boolean
        assetId:
          type: string
          format: uuid
          description: Immutable asset ID
        assetUrl:
          type: string
          description: Public CDN URL for the finalized asset
        mime:
          type: string
        width:
          type: integer
        height:
          type: integer

    BuildWithAkCategory:
      type: string
      enum:
        - developer_tools
        - ai_agents
        - saas
        - productivity
        - ecommerce
        - marketing_sales
        - education
        - other
      description: Category taxonomy for product listings

    BuildWithAkBlockHeroBanner:
      type: object
      required:
        - type
        - title
        - tagline
      properties:
        type:
          type: string
          enum: [hero_banner]
        title:
          type: string
        tagline:
          type: string
        badges:
          type: array
          items:
            type: string

    BuildWithAkBlockColumns:
      type: object
      required:
        - type
        - variant
        - items
      properties:
        type:
          type: string
          enum: [columns]
        variant:
          type: string
          enum: [two, three, bento]
        items:
          type: array
          items:
            type: object
            required:
              - heading
              - body
            properties:
              heading:
                type: string
              body:
                type: string

    BuildWithAkBlockAgentkitStory:
      type: object
      required:
        - type
        - body
        - usedKits
      properties:
        type:
          type: string
          enum: [agentkit_story]
        body:
          type: string
        usedKits:
          type: array
          items:
            type: string
            enum: [engineer, marketing, combo, app]

    BuildWithAkBlockTechStack:
      type: object
      required:
        - type
        - tags
      properties:
        type:
          type: string
          enum: [tech_stack]
        tags:
          type: array
          items:
            type: string

    BuildWithAkBlockScreenshotGallery:
      type: object
      required:
        - type
        - images
      properties:
        type:
          type: string
          enum: [screenshot_gallery]
        images:
          type: array
          items:
            type: object
            required:
              - assetId
              - alt
            properties:
              assetId:
                type: string
                format: uuid
              alt:
                type: string

    BuildWithAkBlockImageFull:
      type: object
      required:
        - type
        - assetId
        - alt
      properties:
        type:
          type: string
          enum: [image_full]
        assetId:
          type: string
          format: uuid
        alt:
          type: string
        caption:
          type: string

    BuildWithAkBlockCarouselGallery:
      type: object
      required:
        - type
        - images
      properties:
        type:
          type: string
          enum: [carousel_gallery]
        images:
          type: array
          items:
            type: object
            required:
              - assetId
              - alt
            properties:
              assetId:
                type: string
                format: uuid
              alt:
                type: string
              caption:
                type: string

    BuildWithAkBlockMakerQuote:
      type: object
      required:
        - type
        - quote
        - attribution
        - quoteSource
      properties:
        type:
          type: string
          enum: [maker_quote]
        quote:
          type: string
        attribution:
          type: string
        quoteSource:
          type: string

    BuildWithAkBlockOutboundCta:
      type: object
      required:
        - type
        - label
      properties:
        type:
          type: string
          enum: [outbound_cta]
        label:
          type: string
        note:
          type: string


    BuildWithAkBlockVideo:
      type: object
      required:
        - type
        - url
      properties:
        type:
          type: string
          enum: [video]
        url:
          type: string
          description: YouTube video URL
        title:
          type: string
        caption:
          type: string
    BuildWithAkBlockActivities:
      type: object
      required: [type]
      description: Owner-managed product updates, rendered newest date first.
      properties:
        type:
          type: string
          enum: [activities]
        title:
          type: string
          maxLength: 120
          default: Activities
        items:
          type: array
          maxItems: 50
          default: []
          items:
            type: object
            required: [title, description, date]
            properties:
              title:
                type: string
                maxLength: 120
              description:
                type: string
                maxLength: 2000
              date:
                type: string
                format: date
                description: Calendar date in YYYY-MM-DD format.
              url:
                type: string
                format: uri
                maxLength: 2000
                description: Optional public HTTPS link; credentials and private IP addresses are rejected.

    BuildWithAkBlockPulse:
      type: object
      required: [type]
      description: Recorded checks of the published main website. No custom target or client-supplied health data; previews show unknown status.
      properties:
        type:
          type: string
          enum: [pulse]
        title:
          type: string
          maxLength: 120
          default: Pulse

    BuildWithAkBlock:
      type: object
      required:
        - id
        - order
        - content
      properties:
        id:
          type: string
        order:
          type: integer
        content:
          oneOf:
            - $ref: '#/components/schemas/BuildWithAkBlockHeroBanner'
            - $ref: '#/components/schemas/BuildWithAkBlockColumns'
            - $ref: '#/components/schemas/BuildWithAkBlockAgentkitStory'
            - $ref: '#/components/schemas/BuildWithAkBlockTechStack'
            - $ref: '#/components/schemas/BuildWithAkBlockScreenshotGallery'
            - $ref: '#/components/schemas/BuildWithAkBlockImageFull'
            - $ref: '#/components/schemas/BuildWithAkBlockCarouselGallery'
            - $ref: '#/components/schemas/BuildWithAkBlockMakerQuote'
            - $ref: '#/components/schemas/BuildWithAkBlockOutboundCta'
            - $ref: '#/components/schemas/BuildWithAkBlockActivities'
            - $ref: '#/components/schemas/BuildWithAkBlockPulse'
            - $ref: '#/components/schemas/BuildWithAkBlockVideo'

    BuildWithAkUpsertListingRequest:
      type: object
      required:
        - name
        - slug
        - tagline
        - category
        - websiteUrl
        - logoAssetId
      properties:
        name:
          type: string
          description: Product name (1-64 chars)
        slug:
          type: string
          pattern: '^[a-z0-9]+(?:-[a-z0-9]+)*$'
          description: URL-safe slug (3-48 lowercase alphanumeric with hyphens)
        tagline:
          type: string
          description: Product pitch (5-120 chars)
        category:
          $ref: '#/components/schemas/BuildWithAkCategory'
        websiteUrl:
          type: string
          format: uri
          description: Canonical HTTPS website destination URL
        demoUrl:
          type: string
          format: uri
          description: Optional HTTPS product demo URL
        githubUrl:
          type: string
          format: uri
          description: Optional HTTPS GitHub repository URL
        twitterUrl:
          type: string
          format: uri
          description: Optional HTTPS Twitter/X profile or post URL
        logoAssetId:
          type: string
          format: uuid
          description: Mandatory uploaded thumbnail asset ID (kind=logo)
        coverAssetId:
          type: string
          format: uuid
          description: Optional uploaded banner cover asset ID (kind=cover)
        blocks:
          type: array
          items:
            $ref: '#/components/schemas/BuildWithAkBlock'
        expectedDraftRevisionId:
          type: string
          format: uuid
          description: Optional optimistic CAS locking revision ID

    BuildWithAkSaveDraftResponse:
      type: object
      required:
        - success
        - listingId
        - draftRevisionId
        - slug
        - contentHash
      properties:
        success:
          type: boolean
        listingId:
          type: string
          format: uuid
        draftRevisionId:
          type: string
          format: uuid
        slug:
          type: string
        contentHash:
          type: string

    BuildWithAkGetListingResponse:
      type: object
      properties:
        listing:
          type: object
          nullable: true
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
            slug:
              type: string
            tagline:
              type: string
            category:
              type: string
            status:
              type: string
              enum: [draft, in_review, changes_requested, approved, published, unpublished, rejected, archived]
            isFeatured:
              type: boolean
            featuredRank:
              type: integer
            upvotesCount:
              type: integer
            publishedAt:
              type: string
              format: date-time
              nullable: true
            updatedAt:
              type: string
              format: date-time
        revision:
          type: object
          nullable: true
          properties:
            id:
              type: string
              format: uuid
            metadata:
              type: object
            blocks:
              type: array
              items:
                $ref: '#/components/schemas/BuildWithAkBlock'
            contentHash:
              type: string
            createdAt:
              type: string
              format: date-time

    BuildWithAkSubmitListingRequest:
      type: object
      required:
        - listingId
      properties:
        listingId:
          type: string
          format: uuid

    BuildWithAkSubmitListingResponse:
      type: object
      required:
        - success
        - listing
      properties:
        success:
          type: boolean
        listing:
          type: object

    BuildWithAkUpvoteStatusResponse:
      type: object
      required:
        - upvoted
        - upvotesCount
      properties:
        upvoted:
          type: boolean
        upvotesCount:
          type: integer

    BuildWithAkToggleUpvoteResponse:
      type: object
      required:
        - success
        - upvoted
        - upvotesCount
      properties:
        success:
          type: boolean
        upvoted:
          type: boolean
        upvotesCount:
          type: integer

    BuildWithAkViewBeaconResponse:
      type: object
      required:
        - success
      properties:
        success:
          type: boolean

    BuildWithAkBlockContent:
      type: object
      description: Discriminated union of product showcase block content
      oneOf:
        - $ref: '#/components/schemas/BuildWithAkBlockHeroBanner'
        - $ref: '#/components/schemas/BuildWithAkBlockColumns'
        - $ref: '#/components/schemas/BuildWithAkBlockAgentkitStory'
        - $ref: '#/components/schemas/BuildWithAkBlockTechStack'
        - $ref: '#/components/schemas/BuildWithAkBlockScreenshotGallery'
        - $ref: '#/components/schemas/BuildWithAkBlockImageFull'
        - $ref: '#/components/schemas/BuildWithAkBlockCarouselGallery'
        - $ref: '#/components/schemas/BuildWithAkBlockMakerQuote'
        - $ref: '#/components/schemas/BuildWithAkBlockOutboundCta'
        - $ref: '#/components/schemas/BuildWithAkBlockActivities'
        - $ref: '#/components/schemas/BuildWithAkBlockPulse'
        - $ref: '#/components/schemas/BuildWithAkBlockVideo'

    BuildWithAkInsertBlockRequest:
      type: object
      required:
        - content
      properties:
        id:
          type: string
          description: Optional custom block ID (auto-generated if omitted)
        order:
          type: integer
          description: Optional 1-based order index
        content:
          $ref: '#/components/schemas/BuildWithAkBlockContent'

    BuildWithAkInsertBlockResponse:
      type: object
      required:
        - success
        - block
        - blocks
      properties:
        success:
          type: boolean
        block:
          $ref: '#/components/schemas/BuildWithAkBlock'
        blocks:
          type: array
          items:
            $ref: '#/components/schemas/BuildWithAkBlock'

    BuildWithAkPatchBlockRequest:
      type: object
      properties:
        order:
          type: integer
        content:
          $ref: '#/components/schemas/BuildWithAkBlockContent'

    BuildWithAkPatchBlockResponse:
      type: object
      required:
        - success
        - block
        - blocks
      properties:
        success:
          type: boolean
        block:
          $ref: '#/components/schemas/BuildWithAkBlock'
        blocks:
          type: array
          items:
            $ref: '#/components/schemas/BuildWithAkBlock'

    BuildWithAkGetBlocksResponse:
      type: object
      required:
        - success
        - listingId
        - draftRevisionId
        - blocks
      properties:
        success:
          type: boolean
        listingId:
          type: string
          format: uuid
        draftRevisionId:
          type: string
          format: uuid
        blocks:
          type: array
          items:
            $ref: '#/components/schemas/BuildWithAkBlock'

    BuildWithAkUpdateBlocksRequest:
      type: object
      required:
        - blocks
      properties:
        blocks:
          type: array
          items:
            $ref: '#/components/schemas/BuildWithAkBlock'

    BuildWithAkReorderBlocksRequest:
      type: object
      required:
        - blockIds
      properties:
        blockIds:
          type: array
          items:
            type: string
          description: Complete ordered list of block IDs

    BuildWithAkReorderBlocksResponse:
      type: object
      required:
        - success
        - blocks
      properties:
        success:
          type: boolean
        blocks:
          type: array
          items:
            $ref: '#/components/schemas/BuildWithAkBlock'

    BuildWithAkDeleteBlockResponse:
      type: object
      required:
        - success
        - message
        - blocks
      properties:
        success:
          type: boolean
        message:
          type: string
        blocks:
          type: array
          items:
            $ref: '#/components/schemas/BuildWithAkBlock'

paths:
  # Build with AK Customer Listing & Revision Endpoints
  /build-with-ak/listing/analytics:
    get:
      tags:
        - Build with AK - Listings
      operationId: getBuildWithAkListingAnalytics
      summary: Get owned product page analytics
      description: |
        Customer API key or session required; current entitlement rechecked. Only owned listings are accessible,
        including archived/rejected listings when listingId is explicit. Omit listingId for the active listing.
        Inclusive UTC dates; default to today and from 29 days before to. Maximum 366 days. Future to, unknown
        and duplicate parameters rejected. Aggregate first-party PostgreSQL telemetry only; no visitor IDs or
        order details. Limit: 30 requests/minute per user per server process. All responses use Cache-Control:
        private, no-store.
      security:
        - ApiKeyAuth: []
        - CookieAuth: []
        - BuildWithAkDelegatedAuth: []
      x-required-oauth-scope: build-with-ak:read
      parameters:
        - name: listingId
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: An owned listing ID. Defaults to the active listing.
        - name: from
          in: query
          required: false
          schema:
            type: string
            format: date
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Inclusive UTC calendar date (YYYY-MM-DD).
        - name: to
          in: query
          required: false
          schema:
            type: string
            format: date
            pattern: ^\d{4}-\d{2}-\d{2}$
          description: Inclusive UTC calendar date (YYYY-MM-DD).
      responses:
        '200':
          description: Owned product analytics
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkAnalyticsResponse'
        '400':
          description: Invalid date range or query
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                  - code
                properties:
                  error:
                    type: string
                  code:
                    type: string
        '401':
          description: Missing or invalid credentials
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                  - code
                properties:
                  error:
                    type: string
                  code:
                    type: string
        '403':
          description: Customer not eligible
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                  - code
                properties:
                  error:
                    type: string
                  code:
                    type: string
        '404':
          description: Listing missing or not owned
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                  - code
                properties:
                  error:
                    type: string
                  code:
                    type: string
        '429':
          description: Too many requests
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                  - code
                properties:
                  error:
                    type: string
                  code:
                    type: string
        '500':
          description: Analytics unavailable
          content:
            application/json:
              schema:
                type: object
                required:
                  - error
                  - code
                properties:
                  error:
                    type: string
                  code:
                    type: string
  /build-with-ak/listing:
    get:
      summary: Get customer product listing and active revision
      description: Fetches the authenticated customer's current product listing, draft revision, and published revision.
      tags: [Build with AK - Listings]
      security:
        - ApiKeyAuth: []
        - CookieAuth: []
        - BuildWithAkDelegatedAuth: []
      x-required-oauth-scope: build-with-ak:read
      responses:
        '200':
          description: Listing and revision fetched successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkGetListingResponse'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Customer verification required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    put:
      summary: Upsert customer draft product listing
      description: Creates or updates the customer's draft product listing and creates an immutable draft revision with copy-on-write CAS.
      tags: [Build with AK - Listings]
      security:
        - ApiKeyAuth: []
        - CookieAuth: []
        - BuildWithAkDelegatedAuth: []
      x-required-oauth-scope: build-with-ak:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BuildWithAkUpsertListingRequest'
      responses:
        '200':
          description: Draft revision saved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkSaveDraftResponse'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Customer verification required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Stale revision conflict (CAS failure)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Validation error or invalid unfinalized asset reference
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /build-with-ak/listing/submit:
    post:
      summary: Submit product listing for review
      description: Submits the customer's draft revision for administrator moderation and claim verification.
      tags: [Build with AK - Listings]
      security:
        - ApiKeyAuth: []
        - CookieAuth: []
        - BuildWithAkDelegatedAuth: []
      x-required-oauth-scope: build-with-ak:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BuildWithAkSubmitListingRequest'
      responses:
        '200':
          description: Listing submitted for review successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkSubmitListingResponse'
        '400':
          description: Illegal state transition
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Customer verification required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Listing not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Invalid request payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # Build with AK Public Directory & Telemetry Endpoints

  /build-with-ak/listing/blocks:
    get:
      summary: Get current draft product layout blocks
      description: Returns the full ordered array of layout blocks in the customer's active draft revision.
      tags: [Build with AK - Listings]
      security:
        - ApiKeyAuth: []
        - CookieAuth: []
        - BuildWithAkDelegatedAuth: []
      x-required-oauth-scope: build-with-ak:read
      responses:
        '200':
          description: Draft layout blocks retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkGetBlocksResponse'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Customer verification required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Listing or draft revision not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    put:
      summary: Replace all draft layout blocks
      description: Replaces the full ordered array of layout blocks in the draft revision and creates a new immutable revision.
      tags: [Build with AK - Listings]
      security:
        - ApiKeyAuth: []
        - CookieAuth: []
        - BuildWithAkDelegatedAuth: []
      x-required-oauth-scope: build-with-ak:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BuildWithAkUpdateBlocksRequest'
      responses:
        '200':
          description: Draft layout blocks updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkGetBlocksResponse'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Customer verification required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Invalid block schema or unfinalized asset reference
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      summary: Add a new block to product layout
      description: Appends or inserts a new layout block into the customer's draft revision.
      tags: [Build with AK - Listings]
      security:
        - ApiKeyAuth: []
        - CookieAuth: []
        - BuildWithAkDelegatedAuth: []
      x-required-oauth-scope: build-with-ak:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BuildWithAkInsertBlockRequest'
      responses:
        '200':
          description: Block added successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkInsertBlockResponse'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Customer verification required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Invalid block payload or asset reference
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /build-with-ak/listing/blocks/{blockId}:
    patch:
      summary: Update a specific block's content or order
      description: Updates the content or display order of an existing layout block in the customer's draft revision.
      tags: [Build with AK - Listings]
      security:
        - ApiKeyAuth: []
        - CookieAuth: []
        - BuildWithAkDelegatedAuth: []
      x-required-oauth-scope: build-with-ak:write
      parameters:
        - name: blockId
          in: path
          required: true
          schema:
            type: string
          description: Unique block ID to update
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BuildWithAkPatchBlockRequest'
      responses:
        '200':
          description: Block updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkPatchBlockResponse'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Customer verification required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Block not found in draft revision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Invalid patch payload or asset reference
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      summary: Delete a block from product layout
      description: Removes a layout block by ID from the customer's draft revision.
      tags: [Build with AK - Listings]
      security:
        - ApiKeyAuth: []
        - CookieAuth: []
        - BuildWithAkDelegatedAuth: []
      x-required-oauth-scope: build-with-ak:write
      parameters:
        - name: blockId
          in: path
          required: true
          schema:
            type: string
          description: Unique block ID to delete
      responses:
        '200':
          description: Block deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkDeleteBlockResponse'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Customer verification required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Block not found in draft revision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /build-with-ak/listing/blocks/reorder:
    post:
      summary: Reorder layout blocks
      description: Reorders the layout blocks in the customer's draft revision according to the provided block IDs sequence.
      tags: [Build with AK - Listings]
      security:
        - ApiKeyAuth: []
        - CookieAuth: []
        - BuildWithAkDelegatedAuth: []
      x-required-oauth-scope: build-with-ak:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BuildWithAkReorderBlocksRequest'
      responses:
        '200':
          description: Blocks reordered successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkReorderBlocksResponse'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Customer verification required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Invalid reorder payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /build-with-ak/{slug}/upvote:
    get:
      summary: Get product upvote status
      description: Returns community upvote status and total count for a published product listing.
      tags: [Build with AK - Directory]
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
          description: Product listing slug
        - name: x-bwak-fingerprint
          in: header
          required: false
          schema:
            type: string
          description: Optional client fingerprint for anonymous visitor upvote deduplication
      responses:
        '200':
          description: Upvote status retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkUpvoteStatusResponse'
    post:
      summary: Toggle product upvote
      description: Toggles a community upvote for a published product listing using authenticated session or anonymous fingerprint.
      tags: [Build with AK - Directory]
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
          description: Product listing slug
        - name: x-bwak-fingerprint
          in: header
          required: false
          schema:
            type: string
          description: Optional client fingerprint for anonymous visitor upvote deduplication
      responses:
        '200':
          description: Upvote toggled successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkToggleUpvoteResponse'
        '404':
          description: Listing not found or inactive
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /build-with-ak/{slug}/visit:
    get:
      summary: Outbound website referral redirect
      description: Records an outbound click telemetry event and redirects the visitor to the product's external website with standard UTM attribution.
      tags: [Build with AK - Directory]
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
          description: Product listing slug
      responses:
        '307':
          description: Temporary redirect to external product website with UTM attribution
        '308':
          description: Permanent redirect to canonical product slug
        '400':
          description: Invalid destination website URL
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Product listing not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /build-with-ak/{slug}/view:
    post:
      summary: Record product view beacon
      description: Records a deduplicated page view impression telemetry event for the published product showcase.
      tags: [Build with AK - Directory]
      parameters:
        - name: slug
          in: path
          required: true
          schema:
            type: string
          description: Product listing slug
      responses:
        '200':
          description: View beacon recorded successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkViewBeaconResponse'
        '404':
          description: Product listing not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # Build with AK Media Endpoints
  /build-with-ak/media/upload-intent:
    post:
      summary: Authorize one-time media upload intent
      description: Authorizes a presigned staging upload URL with size limits and anti-replay intent tracking.
      tags: [Build with AK - Media]
      security:
        - ApiKeyAuth: []
        - CookieAuth: []
        - BuildWithAkDelegatedAuth: []
      x-required-oauth-scope: build-with-ak:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BuildWithAkUploadIntentRequest'
      responses:
        '200':
          description: Upload intent generated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkUploadIntentResponse'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Customer verification required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Invalid payload or unsupported MIME type
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Storage service unconfigured or server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /build-with-ak/media/finalize:
    post:
      summary: Finalize and verify uploaded media asset
      description: Consumes the upload intent atomically, validates magic-bytes/dimensions, and writes immutable asset record.
      tags: [Build with AK - Media]
      security:
        - ApiKeyAuth: []
        - CookieAuth: []
        - BuildWithAkDelegatedAuth: []
      x-required-oauth-scope: build-with-ak:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BuildWithAkMediaFinalizeRequest'
      responses:
        '200':
          description: Media asset verified and finalized successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BuildWithAkMediaFinalizeResponse'
        '400':
          description: Invalid, expired, or already consumed upload intent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Staging key ownership mismatch or unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Staging file not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Concurrent race condition or already finalized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '413':
          description: File size exceeds allowed limit
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          description: Structural decode or image dimension validation failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal database or storage error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  # AgentKit Feedback
  /agentkit/feedback:
    post:
      summary: Submit AgentKit client feedback
      description: |
        Accept feedback from authenticated AgentKit clients, re-redact the submitted
        preview and fields, rate limit by account or license, and create a GitHub
        issue through the server-owned integration.
      tags: [AgentKit Feedback]
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
        - CookieAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FeedbackRequest'
      responses:
        '200':
          description: Feedback issue created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackResponse'
        '400':
          description: Invalid schema, validation, or size failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackError'
        '401':
          description: Missing, expired, or unsupported token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackError'
        '429':
          description: Feedback rate limit exceeded
          headers:
            Retry-After:
              schema:
                type: string
              description: Seconds until the client should retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackError'
        '502':
          description: Transient GitHub/upstream failure
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackError'
        '503':
          description: Endpoint disabled or GitHub integration unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackError'

  # Referrals
  /referrals/referees:
    get:
      summary: List referred conversion rows
      description: |
        Return the current authenticated user's referral conversion rows with commission status,
        display-only order fields, and minimal referee profile summary. Each row represents one
        commission/conversion, not a deduplicated person.
      tags: [Referrals]
      security:
        - ApiKeyAuth: []
      parameters:
        - name: status
          in: query
          required: false
          schema:
            type: string
            enum: [all, pending, approved, paid, cancelled]
            default: all
          description: Filter by commission status.
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
          description: Page size.
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            minimum: 0
            maximum: 5000
            default: 0
          description: Offset into the current filtered result set.
        - name: orderRef
          in: query
          required: false
          schema:
            type: string
            minLength: 8
            maxLength: 8
            pattern: '^[0-9a-fA-F]{8}$'
          description: Exact match on order display id, for example `ff95c7f6` from `order.displayId`.
        - name: sort
          in: query
          required: false
          schema:
            type: string
            enum: [createdAt, amount, status]
            default: createdAt
          description: Sort column.
        - name: order
          in: query
          required: false
          schema:
            type: string
            enum: [asc, desc]
            default: desc
          description: Sort direction.
      responses:
        '200':
          description: Owner-scoped referral conversion rows
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReferralRefereesResponse'
              examples:
                default:
                  value:
                    data:
                      - commissionId: "11111111-1111-4111-8111-111111111111"
                        status: pending
                        amount: 990
                        currency: USD
                        createdAt: "2026-05-01T00:00:00.000Z"
                        approvedAt: null
                        paidAt: null
                        order:
                          displayId: "ff95c7f6"
                          productType: engineer_kit
                          status: completed
                        referee:
                          name: Buyer
                          avatarUrl: null
                          emailMasked: "b****@example.com"
                    pagination:
                      limit: 20
                      offset: 0
                      total: 1
                      hasMore: false
                    filters:
                      status: all
                      sort: createdAt
                      order: desc
                      orderRef: ff95c7f6
        '400':
          description: Invalid query parameter
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: User is not eligible for referral data
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
                  - type: object
                    properties:
                      eligible:
                        type: boolean
                        const: false
        '429':
          description: Rate limited
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error or insecure session configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # API Key Management
  /keys:
    get:
      summary: List API keys
      description: Get all API keys for the authenticated user
      tags: [API Keys]
      security:
        - BearerAuth: []
      responses:
        '200':
          description: List of API keys
          content:
            application/json:
              schema:
                type: object
                properties:
                  keys:
                    type: array
                    items:
                      $ref: '#/components/schemas/ApiKey'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      summary: Create API key
      description: Create a new API key. The full key is only shown once.
      tags: [API Keys]
      security:
        - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApiKeyCreate'
      responses:
        '201':
          description: API key created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyCreated'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: License required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /keys/{id}:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get API key details
      tags: [API Keys]
      security:
        - BearerAuth: []
      responses:
        '200':
          description: API key details
          content:
            application/json:
              schema:
                type: object
                properties:
                  key:
                    $ref: '#/components/schemas/ApiKey'
        '404':
          description: Key not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      summary: Delete API key
      description: Soft delete (deactivate) an API key
      tags: [API Keys]
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Key deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '404':
          description: Key not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /keys/{id}/rotate:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      summary: Rotate API key
      description: Generate a new key value with optional grace period for old key
      tags: [API Keys]
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Key rotated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiKeyCreated'
        '404':
          description: Key not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /keys/{id}/revoke:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    post:
      summary: Revoke API key
      description: Immediately invalidate an API key
      tags: [API Keys]
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Key revoked
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
        '404':
          description: Key not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /keys/{id}/usage:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
    get:
      summary: Get API key usage stats
      description: Get usage statistics from ClickHouse and current rate limit info
      tags: [API Keys]
      security:
        - BearerAuth: []
      parameters:
        - name: days
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 30
            default: 7
          description: Number of days for usage stats (1-30)
      responses:
        '200':
          description: Usage statistics
          content:
            application/json:
              schema:
                type: object
                properties:
                  totalRequests:
                    type: integer
                  successfulRequests:
                    type: integer
                  failedRequests:
                    type: integer
                  avgResponseTime:
                    type: number
                  totalUsageCount:
                    type: integer
                    description: Lifetime usage count
                  rateLimit:
                    type: object
                    properties:
                      limit:
                        type: integer
                      used:
                        type: integer
                      remaining:
                        type: integer
                      windowHours:
                        type: integer
        '404':
          description: Key not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /keys/validate:
    post:
      summary: Validate API key
      description: Check if an API key is valid and return metadata
      tags: [API Keys]
      security:
        - ApiKeyAuth: []
      responses:
        '200':
          description: Key is valid
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationResult'
        '400':
          description: API key required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # VidCap Health & Models
  /proxy/vidcap/v1/healthz:
    get:
      summary: VidCap health check
      description: Check VidCap service health status
      tags: [VidCap - Health]
      responses:
        '200':
          description: Service healthy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthCheck'

  /proxy/vidcap/v1/ai/models:
    get:
      summary: List AI models
      description: Get available AI models for summary and article generation
      tags: [VidCap - Health]
      responses:
        '200':
          description: List of models
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AIModels'

  # VidCap Video
  /proxy/vidcap/v1/video/{videoId}:
    parameters:
      - name: videoId
        in: path
        required: true
        schema:
          type: string
        description: Video ID from VidCap
    get:
      summary: Get video by ID
      description: Retrieve processed video by VidCap video ID
      tags: [VidCap - Video]
      responses:
        '200':
          description: Video data
          content:
            application/json:
              schema:
                type: object
        '404':
          description: Video not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # VidCap YouTube Operations
  /proxy/vidcap/v1/youtube/info:
    get:
      summary: Get YouTube video info
      description: Retrieve metadata for a YouTube video
      tags: [VidCap - YouTube]
      parameters:
        - name: url
          in: query
          required: true
          schema:
            type: string
          description: YouTube video URL
      responses:
        '200':
          description: Video info
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/YouTubeInfo'
        '400':
          description: Invalid URL
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  /proxy/vidcap/v1/youtube/media:
    get:
      summary: Get available media formats
      description: List available download formats for a YouTube video
      tags: [VidCap - YouTube]
      parameters:
        - name: url
          in: query
          required: true
          schema:
            type: string
          description: YouTube video URL
      responses:
        '200':
          description: Available formats
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/YouTubeMedia'

  /proxy/vidcap/v1/youtube/download:
    get:
      summary: Download video
      description: Download YouTube video in specified format
      tags: [VidCap - YouTube]
      parameters:
        - name: url
          in: query
          required: true
          schema:
            type: string
          description: YouTube video URL
        - name: itag
          in: query
          schema:
            type: integer
          description: Format itag (from media endpoint)
        - name: quality
          in: query
          schema:
            type: string
            enum: [highest, lowest, audio]
          description: Quality preset
      responses:
        '200':
          description: Video stream or download URL
          content:
            application/json:
              schema:
                type: object
                properties:
                  downloadUrl:
                    type: string
                    format: uri

  /proxy/vidcap/v1/youtube/caption:
    get:
      summary: Get video captions
      description: Extract captions/subtitles from a YouTube video
      tags: [VidCap - YouTube]
      parameters:
        - name: url
          in: query
          required: true
          schema:
            type: string
          description: YouTube video URL
        - name: lang
          in: query
          schema:
            type: string
            default: en
          description: Caption language code
      responses:
        '200':
          description: Video captions
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/YouTubeCaption'

  /proxy/vidcap/v1/youtube/summary:
    get:
      summary: AI video summary
      description: Generate AI-powered summary of a YouTube video
      tags: [VidCap - YouTube]
      parameters:
        - name: url
          in: query
          required: true
          schema:
            type: string
          description: YouTube video URL
        - name: model
          in: query
          schema:
            type: string
          description: AI model to use (from /ai/models)
      responses:
        '200':
          description: Video summary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/YouTubeSummary'

  /proxy/vidcap/v1/youtube/summary-custom:
    post:
      summary: Custom AI summary
      description: Generate summary with custom prompt
      tags: [VidCap - YouTube]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  description: YouTube video URL
                prompt:
                  type: string
                  description: Custom prompt for summary
                model:
                  type: string
                  description: AI model to use
              required:
                - url
                - prompt
      responses:
        '200':
          description: Custom summary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/YouTubeSummary'

  /proxy/vidcap/v1/youtube/article:
    get:
      summary: Convert to article
      description: Convert YouTube video to written article format
      tags: [VidCap - YouTube]
      parameters:
        - name: url
          in: query
          required: true
          schema:
            type: string
          description: YouTube video URL
        - name: model
          in: query
          schema:
            type: string
          description: AI model to use
      responses:
        '200':
          description: Article content
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/YouTubeArticle'

  /proxy/vidcap/v1/youtube/screenshot:
    get:
      summary: Take screenshot
      description: Capture screenshot at specified timestamp
      tags: [VidCap - YouTube]
      parameters:
        - name: url
          in: query
          required: true
          schema:
            type: string
          description: YouTube video URL
        - name: timestamp
          in: query
          schema:
            type: number
          description: Timestamp in seconds
      responses:
        '200':
          description: Screenshot URL
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/YouTubeScreenshot'

  /proxy/vidcap/v1/youtube/screenshot-multiple:
    get:
      summary: Take multiple screenshots
      description: Capture screenshots at multiple timestamps
      tags: [VidCap - YouTube]
      parameters:
        - name: url
          in: query
          required: true
          schema:
            type: string
          description: YouTube video URL
        - name: timestamps
          in: query
          schema:
            type: string
          description: Comma-separated timestamps in seconds
        - name: count
          in: query
          schema:
            type: integer
          description: Number of evenly-spaced screenshots
      responses:
        '200':
          description: Screenshot URLs
          content:
            application/json:
              schema:
                type: object
                properties:
                  screenshots:
                    type: array
                    items:
                      $ref: '#/components/schemas/YouTubeScreenshot'

  /proxy/vidcap/v1/youtube/comments:
    get:
      summary: Get video comments
      description: Retrieve comments from a YouTube video
      tags: [VidCap - YouTube]
      parameters:
        - name: url
          in: query
          required: true
          schema:
            type: string
          description: YouTube video URL
        - name: pageToken
          in: query
          schema:
            type: string
          description: Pagination token
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
          description: Number of comments to return
      responses:
        '200':
          description: Video comments
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/YouTubeComments'

  /proxy/vidcap/v1/youtube/search:
    get:
      summary: Search YouTube videos
      description: Search for YouTube videos
      tags: [VidCap - YouTube]
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
          description: Search query
        - name: pageToken
          in: query
          schema:
            type: string
          description: Pagination token
        - name: limit
          in: query
          schema:
            type: integer
            default: 10
          description: Number of results
      responses:
        '200':
          description: Search results
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/YouTubeSearchResult'

  # ReviewWeb - Health & Profile
  /proxy/reviewweb/v1/healthz:
    get:
      summary: Health check
      description: Check ReviewWeb service health
      tags: [ReviewWeb - Health]
      responses:
        '200':
          description: Service is healthy

  /proxy/reviewweb/v1/profile:
    get:
      summary: Get profile
      description: Get account profile and credits
      tags: [ReviewWeb - Health]
      responses:
        '200':
          description: Profile info
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReviewWebProfile'

  # ReviewWeb - Screenshots
  /proxy/reviewweb/v1/screenshot:
    post:
      summary: Take screenshot
      description: Capture webpage screenshot
      tags: [ReviewWeb - Screenshots]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
                width:
                  type: integer
                  default: 1280
                height:
                  type: integer
                  default: 720
              required: [url]
      responses:
        '200':
          description: Screenshot result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReviewWebScreenshot'

  /proxy/reviewweb/v1/screenshot/{id}:
    get:
      summary: Get screenshot
      description: Get screenshot by ID
      tags: [ReviewWeb - Screenshots]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Screenshot details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReviewWebScreenshot'

  # ReviewWeb - Reviews
  /proxy/reviewweb/v1/review:
    post:
      summary: Create review
      description: Create website review
      tags: [ReviewWeb - Reviews]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
              required: [url]
      responses:
        '200':
          description: Review created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReviewWebReview'

  /proxy/reviewweb/v1/review/{reviewId}:
    get:
      summary: Get review
      description: Get review by ID
      tags: [ReviewWeb - Reviews]
      parameters:
        - name: reviewId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Review details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReviewWebReview'

  # ReviewWeb - Scraping
  /proxy/reviewweb/v1/scrape:
    post:
      summary: Scrape URL
      description: Scrape single URL content
      tags: [ReviewWeb - Scraping]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
              required: [url]
      responses:
        '200':
          description: Scraped content
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScrapeResult'

  /proxy/reviewweb/v1/scrape/urls:
    post:
      summary: Scrape multiple URLs
      description: Scrape multiple URLs in batch
      tags: [ReviewWeb - Scraping]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                urls:
                  type: array
                  items:
                    type: string
                    format: uri
              required: [urls]
      responses:
        '200':
          description: Scraped results
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ScrapeResult'

  /proxy/reviewweb/v1/scrape/links-map:
    post:
      summary: Get links map
      description: Extract all links from page
      tags: [ReviewWeb - Scraping]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
              required: [url]
      responses:
        '200':
          description: Links map
          content:
            application/json:
              schema:
                type: object
                properties:
                  internal:
                    type: array
                    items:
                      type: string
                  external:
                    type: array
                    items:
                      type: string

  # ReviewWeb - Extraction
  /proxy/reviewweb/v1/extract:
    post:
      summary: Extract content
      description: Extract main content from URL
      tags: [ReviewWeb - Extraction]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
              required: [url]
      responses:
        '200':
          description: Extracted content
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExtractResult'

  /proxy/reviewweb/v1/extract/urls:
    post:
      summary: Extract from multiple URLs
      description: Extract content from multiple URLs
      tags: [ReviewWeb - Extraction]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                urls:
                  type: array
                  items:
                    type: string
                    format: uri
              required: [urls]
      responses:
        '200':
          description: Extracted results
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ExtractResult'

  # ReviewWeb - Conversion
  /proxy/reviewweb/v1/convert/markdown:
    post:
      summary: Convert to markdown
      description: Convert URL to markdown format
      tags: [ReviewWeb - Conversion]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
              required: [url]
      responses:
        '200':
          description: Markdown content
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MarkdownResult'

  /proxy/reviewweb/v1/convert/markdown/urls:
    post:
      summary: Convert multiple to markdown
      description: Convert multiple URLs to markdown
      tags: [ReviewWeb - Conversion]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                urls:
                  type: array
                  items:
                    type: string
                    format: uri
              required: [urls]
      responses:
        '200':
          description: Markdown results
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/MarkdownResult'

  # ReviewWeb - Summarization
  /proxy/reviewweb/v1/summarize/url:
    post:
      summary: Summarize URL
      description: AI-powered URL summarization
      tags: [ReviewWeb - Summarization]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
              required: [url]
      responses:
        '200':
          description: Summary result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SummarizeResult'

  /proxy/reviewweb/v1/summarize/website:
    post:
      summary: Summarize website
      description: Summarize entire website
      tags: [ReviewWeb - Summarization]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
              required: [url]
      responses:
        '200':
          description: Website summary
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SummarizeResult'

  /proxy/reviewweb/v1/summarize/urls:
    post:
      summary: Summarize multiple URLs
      description: Summarize multiple URLs
      tags: [ReviewWeb - Summarization]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                urls:
                  type: array
                  items:
                    type: string
                    format: uri
              required: [urls]
      responses:
        '200':
          description: Summary results
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SummarizeResult'

  # ReviewWeb - URL Utilities
  /proxy/reviewweb/v1/url/is-alive:
    post:
      summary: Check URL alive
      description: Check if URL is accessible
      tags: [ReviewWeb - Health]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
              required: [url]
      responses:
        '200':
          description: URL check result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UrlCheckResult'

  /proxy/reviewweb/v1/url/get-url-after-redirects:
    post:
      summary: Get final URL
      description: Get URL after following redirects
      tags: [ReviewWeb - Health]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
              required: [url]
      responses:
        '200':
          description: Final URL
          content:
            application/json:
              schema:
                type: object
                properties:
                  originalUrl:
                    type: string
                  finalUrl:
                    type: string
                  redirectCount:
                    type: integer

  # ReviewWeb - SEO Insights
  /proxy/reviewweb/v1/seo-insights/backlinks:
    post:
      summary: Analyze backlinks
      description: Get backlink analysis for domain
      tags: [ReviewWeb - SEO]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                domain:
                  type: string
              required: [domain]
      responses:
        '200':
          description: Backlinks analysis
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SeoBacklinks'

  /proxy/reviewweb/v1/seo-insights/keyword-ideas:
    post:
      summary: Generate keyword ideas
      description: Get keyword suggestions
      tags: [ReviewWeb - SEO]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                keyword:
                  type: string
              required: [keyword]
      responses:
        '200':
          description: Keyword ideas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SeoKeywordIdeas'

  /proxy/reviewweb/v1/seo-insights/keyword-difficulty:
    post:
      summary: Check keyword difficulty
      description: Analyze keyword competition
      tags: [ReviewWeb - SEO]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                keyword:
                  type: string
              required: [keyword]
      responses:
        '200':
          description: Difficulty analysis
          content:
            application/json:
              schema:
                type: object
                properties:
                  keyword:
                    type: string
                  difficulty:
                    type: number
                  searchVolume:
                    type: integer

  /proxy/reviewweb/v1/seo-insights/traffic:
    post:
      summary: Analyze traffic
      description: Get traffic estimates for domain
      tags: [ReviewWeb - SEO]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                domainOrUrl:
                  type: string
                  description: Domain or full URL to analyze
                mode:
                  type: string
                  enum: [subdomains, exact]
                country:
                  type: string
              required: [domainOrUrl]
      responses:
        '200':
          description: Traffic analysis
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SeoTraffic'
